| 372 | |
| 373 | |
| 374 | class AsyncAPIResponse(BaseAPIResponse[R]): |
| 375 | @overload |
| 376 | async def parse(self, *, to: type[_T]) -> _T: ... |
| 377 | |
| 378 | @overload |
| 379 | async def parse(self) -> R: ... |
| 380 | |
| 381 | async def parse(self, *, to: type[_T] | None = None) -> R | _T: |
| 382 | """Returns the rich python representation of this response's data. |
| 383 | |
| 384 | For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. |
| 385 | |
| 386 | You can customise the type that the response is parsed into through |
| 387 | the `to` argument, e.g. |
| 388 | |
| 389 | ```py |
| 390 | from opencode_ai import BaseModel |
| 391 | |
| 392 | |
| 393 | class MyModel(BaseModel): |
| 394 | foo: str |
| 395 | |
| 396 | |
| 397 | obj = response.parse(to=MyModel) |
| 398 | print(obj.foo) |
| 399 | ``` |
| 400 | |
| 401 | We support parsing: |
| 402 | - `BaseModel` |
| 403 | - `dict` |
| 404 | - `list` |
| 405 | - `Union` |
| 406 | - `str` |
| 407 | - `httpx.Response` |
| 408 | """ |
| 409 | cache_key = to if to is not None else self._cast_to |
| 410 | cached = self._parsed_by_type.get(cache_key) |
| 411 | if cached is not None: |
| 412 | return cached # type: ignore[no-any-return] |
| 413 | |
| 414 | if not self._is_sse_stream: |
| 415 | await self.read() |
| 416 | |
| 417 | parsed = self._parse(to=to) |
| 418 | if is_given(self._options.post_parser): |
| 419 | parsed = self._options.post_parser(parsed) |
| 420 | |
| 421 | self._parsed_by_type[cache_key] = parsed |
| 422 | return parsed |
| 423 | |
| 424 | async def read(self) -> bytes: |
| 425 | """Read and return the binary response content.""" |
| 426 | try: |
| 427 | return await self.http_response.aread() |
| 428 | except httpx.StreamConsumed as exc: |
| 429 | # the default error raised by httpx isn't very |
| 430 | # helpful in our case so we re-raise it with |
| 431 | # a different error message |
no outgoing calls