|
1 | 1 | import time |
| 2 | +from contextlib import contextmanager |
2 | 3 | from http import HTTPStatus |
3 | | -from typing import Callable, Dict, Optional, Type |
| 4 | +from typing import Any, Callable, Dict, Iterator, Mapping, Optional, Type, Union |
4 | 5 |
|
5 | 6 | import httpx |
6 | 7 |
|
7 | 8 | from aidial_client._auth import SyncAuthValue, get_combined_auth_headers |
8 | 9 | from aidial_client._exception import DialException |
9 | 10 | from aidial_client._http_client._base import BaseHTTPClient |
| 11 | +from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven |
10 | 12 | from aidial_client._internal_types._generic import ResponseT |
11 | 13 | from aidial_client._internal_types._http_request import FinalRequestOptions |
12 | 14 | from aidial_client._log import logger |
@@ -108,3 +110,54 @@ def request( |
108 | 110 | raise raised_error from err |
109 | 111 |
|
110 | 112 | return process_block_response(cast_to=cast_to, response=response) |
| 113 | + |
| 114 | + @contextmanager |
| 115 | + def stream_sse( |
| 116 | + self, |
| 117 | + *, |
| 118 | + method: str, |
| 119 | + url: str, |
| 120 | + json_data: Any, |
| 121 | + headers: Optional[Mapping[str, str]] = None, |
| 122 | + timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, |
| 123 | + ) -> Iterator[httpx.Response]: |
| 124 | + """Open an SSE streaming response. Yields the open httpx.Response. |
| 125 | +
|
| 126 | + Auth headers are merged in. On non-2xx, reads the body and raises |
| 127 | + a DialException; transport errors (timeouts, network failures) are |
| 128 | + also wrapped so the caller always sees DialException. Retries are |
| 129 | + not performed for streaming requests. |
| 130 | +
|
| 131 | + ``timeout`` defaults to the client-wide timeout; pass an explicit |
| 132 | + ``None`` (or ``httpx.Timeout(None)``) for no timeout. |
| 133 | + """ |
| 134 | + merged_headers = {**self.auth_headers(), **(headers or {})} |
| 135 | + effective_timeout = ( |
| 136 | + self._timeout if isinstance(timeout, NotGiven) else timeout |
| 137 | + ) |
| 138 | + try: |
| 139 | + with self._internal_http_client.stream( |
| 140 | + method=method, |
| 141 | + url=self._prepare_url(url), |
| 142 | + headers=merged_headers, |
| 143 | + json=json_data, |
| 144 | + timeout=effective_timeout, |
| 145 | + ) as response: |
| 146 | + try: |
| 147 | + response.raise_for_status() |
| 148 | + except httpx.HTTPStatusError as err: |
| 149 | + try: |
| 150 | + response.read() |
| 151 | + except httpx.HTTPError: |
| 152 | + pass |
| 153 | + raise self._make_dial_error_from_response( |
| 154 | + err.response |
| 155 | + ) from err |
| 156 | + yield response |
| 157 | + except httpx.TimeoutException as err: |
| 158 | + raise DialException( |
| 159 | + message="Request timed out", |
| 160 | + status_code=HTTPStatus.REQUEST_TIMEOUT, |
| 161 | + ) from err |
| 162 | + except httpx.HTTPError as err: |
| 163 | + raise DialException(message=f"Request failed: {err}") from err |
0 commit comments