aidial-client 0.11.0.dev1__tar.gz → 0.11.0.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/PKG-INFO +1 -1
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_auth.py +23 -17
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_client.py +21 -25
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_client_pool.py +6 -8
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_exception.py +5 -5
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/_async.py +12 -23
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/_base.py +11 -14
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/_sse.py +9 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/_sync.py +12 -14
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_generic.py +9 -11
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_http_request.py +24 -28
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_json_rpc.py +14 -12
- aidial_client-0.11.0.dev2/aidial_client/_utils/_dict.py +5 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_utils/_openai.py +1 -1
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_utils/_response_processing.py +6 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_utils/_type_guard.py +2 -3
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/helpers/storage_resource.py +6 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/application.py +2 -3
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/bucket.py +2 -3
- aidial_client-0.11.0.dev2/aidial_client/resources/chat/completions.py +374 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/client_channel.py +13 -13
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/deployments.py +5 -5
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/files.py +26 -29
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/metadata.py +4 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/prompts.py +17 -19
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/resource_permissions.py +8 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/application.py +5 -5
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/bucket.py +1 -2
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/addon.py +1 -3
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/function.py +2 -4
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/legacy/application_request.py +5 -5
- aidial_client-0.11.0.dev2/aidial_client/types/chat/legacy/chat_completion.py +167 -0
- aidial_client-0.11.0.dev2/aidial_client/types/chat/request.py +42 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/request_param.py +19 -19
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/response.py +31 -31
- aidial_client-0.11.0.dev2/aidial_client/types/deployment.py +56 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/file.py +3 -6
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/metadata.py +15 -15
- aidial_client-0.11.0.dev2/aidial_client/types/model.py +46 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/prompt.py +1 -3
- aidial_client-0.11.0.dev2/aidial_client/types/toolset.py +22 -0
- aidial_client-0.11.0.dev2/pyproject.toml +93 -0
- aidial_client-0.11.0.dev1/aidial_client/_utils/_dict.py +0 -5
- aidial_client-0.11.0.dev1/aidial_client/resources/chat/completions.py +0 -398
- aidial_client-0.11.0.dev1/aidial_client/types/chat/legacy/chat_completion.py +0 -168
- aidial_client-0.11.0.dev1/aidial_client/types/chat/request.py +0 -44
- aidial_client-0.11.0.dev1/aidial_client/types/deployment.py +0 -56
- aidial_client-0.11.0.dev1/aidial_client/types/model.py +0 -48
- aidial_client-0.11.0.dev1/aidial_client/types/toolset.py +0 -24
- aidial_client-0.11.0.dev1/pyproject.toml +0 -97
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/LICENSE +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/README.md +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_compatibility/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_compatibility/openai.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_compatibility/pydantic.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_compatibility/pydantic_v1.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_constants.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_defaults.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_model.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_log.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_utils/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_utils/_alias.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/helpers/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/helpers/_url.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/py.typed +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/base.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/chat/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/model.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/resources/toolset.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/legacy/__init__.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/chat/tool.py +0 -0
- {aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/types/client_channel.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
1
2
|
from inspect import isawaitable
|
|
2
|
-
from typing import
|
|
3
|
+
from typing import TypeVar
|
|
3
4
|
|
|
4
|
-
SyncAuthValue =
|
|
5
|
-
AsyncAuthValue =
|
|
5
|
+
SyncAuthValue = str | Callable[[], str]
|
|
6
|
+
AsyncAuthValue = SyncAuthValue | Callable[[], Awaitable[str]]
|
|
6
7
|
|
|
7
8
|
AuthValueT = TypeVar(
|
|
8
9
|
"AuthValueT",
|
|
9
|
-
bound=
|
|
10
|
+
bound=SyncAuthValue | AsyncAuthValue,
|
|
10
11
|
)
|
|
11
12
|
|
|
12
13
|
|
|
@@ -20,7 +21,8 @@ def get_auth_value(auth_value: SyncAuthValue) -> str:
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
21
22
|
assert_never(auth_value)
|
|
22
23
|
raise TypeError(
|
|
23
|
-
f"auth_value must be a string or a callable returning a string,
|
|
24
|
+
f"auth_value must be a string or a callable returning a string, "
|
|
25
|
+
f"got {type(auth_value).__name__}"
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
|
|
@@ -35,16 +37,17 @@ async def aget_auth_value(auth_value: AsyncAuthValue) -> str:
|
|
|
35
37
|
if TYPE_CHECKING:
|
|
36
38
|
assert_never(auth_value)
|
|
37
39
|
raise TypeError(
|
|
38
|
-
|
|
40
|
+
"auth_value must be a string or a callable, "
|
|
41
|
+
f"got {type(auth_value).__name__}"
|
|
39
42
|
)
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
def get_combined_auth_headers(
|
|
43
46
|
*,
|
|
44
|
-
api_key:
|
|
45
|
-
bearer_token:
|
|
46
|
-
) ->
|
|
47
|
-
headers:
|
|
47
|
+
api_key: SyncAuthValue | None = None,
|
|
48
|
+
bearer_token: SyncAuthValue | None = None,
|
|
49
|
+
) -> dict[str, str]:
|
|
50
|
+
headers: dict[str, str] = {}
|
|
48
51
|
|
|
49
52
|
if api_key is not None:
|
|
50
53
|
headers["api-key"] = get_auth_value(api_key)
|
|
@@ -58,11 +61,14 @@ def get_combined_auth_headers(
|
|
|
58
61
|
|
|
59
62
|
async def aget_combined_auth_headers(
|
|
60
63
|
*,
|
|
61
|
-
api_key:
|
|
62
|
-
bearer_token:
|
|
63
|
-
) ->
|
|
64
|
-
"""
|
|
65
|
-
headers
|
|
64
|
+
api_key: AsyncAuthValue | None = None,
|
|
65
|
+
bearer_token: AsyncAuthValue | None = None,
|
|
66
|
+
) -> dict[str, str]:
|
|
67
|
+
"""
|
|
68
|
+
Get combined authentication headers from both api_key and
|
|
69
|
+
bearer_token (async).
|
|
70
|
+
"""
|
|
71
|
+
headers: dict[str, str] = {}
|
|
66
72
|
|
|
67
73
|
if api_key is not None:
|
|
68
74
|
processed_api_key = await aget_auth_value(api_key)
|
|
@@ -77,8 +83,8 @@ async def aget_combined_auth_headers(
|
|
|
77
83
|
|
|
78
84
|
def validate_auth(
|
|
79
85
|
*,
|
|
80
|
-
api_key:
|
|
81
|
-
bearer_token:
|
|
86
|
+
api_key: AsyncAuthValue | None = None,
|
|
87
|
+
bearer_token: AsyncAuthValue | None = None,
|
|
82
88
|
) -> None:
|
|
83
89
|
"""Validate that at least one authentication method is provided."""
|
|
84
90
|
if not api_key and not bearer_token:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from pathlib import PurePosixPath
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
4
|
from urllib.parse import urljoin
|
|
5
5
|
|
|
6
6
|
import openai
|
|
@@ -24,30 +24,28 @@ from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven
|
|
|
24
24
|
from aidial_client.helpers._url import enforce_trailing_slash
|
|
25
25
|
from aidial_client.types.bucket import AppData
|
|
26
26
|
|
|
27
|
-
_HttpClientT = TypeVar(
|
|
28
|
-
"_HttpClientT", bound=Union[AsyncHTTPClient, SyncHTTPClient]
|
|
29
|
-
)
|
|
27
|
+
_HttpClientT = TypeVar("_HttpClientT", bound=AsyncHTTPClient | SyncHTTPClient)
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
class BaseDialClient(Generic[_HttpClientT, AuthValueT], ABC):
|
|
33
|
-
_api_key:
|
|
34
|
-
_bearer_token:
|
|
31
|
+
_api_key: AuthValueT | None
|
|
32
|
+
_bearer_token: AuthValueT | None
|
|
35
33
|
_base_url: str
|
|
36
34
|
_http_client: _HttpClientT
|
|
37
|
-
_auth_headers:
|
|
38
|
-
_my_bucket:
|
|
39
|
-
_my_appdata:
|
|
35
|
+
_auth_headers: dict[str, str]
|
|
36
|
+
_my_bucket: str | None
|
|
37
|
+
_my_appdata: AppData | None | NotGiven
|
|
40
38
|
|
|
41
39
|
def __init__(
|
|
42
40
|
self,
|
|
43
41
|
*,
|
|
44
42
|
base_url: str,
|
|
45
|
-
api_key:
|
|
46
|
-
bearer_token:
|
|
43
|
+
api_key: AuthValueT | None = None,
|
|
44
|
+
bearer_token: AuthValueT | None = None,
|
|
47
45
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
48
|
-
timeout:
|
|
49
|
-
api_version:
|
|
50
|
-
http_client:
|
|
46
|
+
timeout: float | Timeout | None = DEFAULT_TIMEOUT,
|
|
47
|
+
api_version: str | None = None,
|
|
48
|
+
http_client: _HttpClientT | None = None,
|
|
51
49
|
):
|
|
52
50
|
validate_auth(api_key=api_key, bearer_token=bearer_token)
|
|
53
51
|
self._api_key = api_key
|
|
@@ -79,12 +77,11 @@ class BaseDialClient(Generic[_HttpClientT, AuthValueT], ABC):
|
|
|
79
77
|
return self._base_url
|
|
80
78
|
|
|
81
79
|
@property
|
|
82
|
-
def api_version(self) ->
|
|
80
|
+
def api_version(self) -> str | None:
|
|
83
81
|
return self._api_version
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
class Dial(BaseDialClient[SyncHTTPClient, SyncAuthValue]):
|
|
87
|
-
|
|
88
85
|
def _init_resources(self) -> None:
|
|
89
86
|
openai_client = openai.AzureOpenAI(
|
|
90
87
|
api_key="-",
|
|
@@ -150,26 +147,25 @@ class Dial(BaseDialClient[SyncHTTPClient, SyncAuthValue]):
|
|
|
150
147
|
def my_prompts_home(self) -> PurePosixPath:
|
|
151
148
|
return "prompts" / PurePosixPath(self.my_bucket())
|
|
152
149
|
|
|
153
|
-
def _get_my_appdata(self) ->
|
|
150
|
+
def _get_my_appdata(self) -> AppData | None:
|
|
154
151
|
return self.bucket.get_appdata()
|
|
155
152
|
|
|
156
|
-
def my_appdata(self) ->
|
|
153
|
+
def my_appdata(self) -> AppData | None:
|
|
157
154
|
if isinstance(self._my_appdata, NotGiven):
|
|
158
155
|
self._my_appdata = self._get_my_appdata()
|
|
159
156
|
return self._my_appdata
|
|
160
157
|
|
|
161
|
-
def my_appdata_home(self) ->
|
|
158
|
+
def my_appdata_home(self) -> PurePosixPath | None:
|
|
162
159
|
appdata = self.my_appdata()
|
|
163
160
|
if appdata:
|
|
164
161
|
return PurePosixPath(appdata.raw)
|
|
165
162
|
return None
|
|
166
163
|
|
|
167
|
-
def auth_headers(self) ->
|
|
164
|
+
def auth_headers(self) -> dict[str, str]:
|
|
168
165
|
return self._http_client.auth_headers()
|
|
169
166
|
|
|
170
167
|
|
|
171
168
|
class AsyncDial(BaseDialClient[AsyncHTTPClient, AsyncAuthValue]):
|
|
172
|
-
|
|
173
169
|
def _init_resources(self) -> None:
|
|
174
170
|
openai_client = openai.AsyncAzureOpenAI(
|
|
175
171
|
api_key="-",
|
|
@@ -241,19 +237,19 @@ class AsyncDial(BaseDialClient[AsyncHTTPClient, AsyncAuthValue]):
|
|
|
241
237
|
async def my_prompts_home(self) -> PurePosixPath:
|
|
242
238
|
return "prompts" / PurePosixPath(await self.my_bucket())
|
|
243
239
|
|
|
244
|
-
async def _get_my_appdata(self) ->
|
|
240
|
+
async def _get_my_appdata(self) -> AppData | None:
|
|
245
241
|
return await self.bucket.get_appdata()
|
|
246
242
|
|
|
247
|
-
async def my_appdata(self) ->
|
|
243
|
+
async def my_appdata(self) -> AppData | None:
|
|
248
244
|
if isinstance(self._my_appdata, NotGiven):
|
|
249
245
|
self._my_appdata = await self._get_my_appdata()
|
|
250
246
|
return self._my_appdata
|
|
251
247
|
|
|
252
|
-
async def my_appdata_home(self) ->
|
|
248
|
+
async def my_appdata_home(self) -> PurePosixPath | None:
|
|
253
249
|
appdata = await self.my_appdata()
|
|
254
250
|
if appdata:
|
|
255
251
|
return PurePosixPath(appdata.raw)
|
|
256
252
|
return None
|
|
257
253
|
|
|
258
|
-
async def auth_headers(self) ->
|
|
254
|
+
async def auth_headers(self) -> dict[str, str]:
|
|
259
255
|
return await self._http_client.auth_headers()
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
2
|
-
|
|
3
1
|
import httpx
|
|
4
2
|
|
|
5
3
|
from aidial_client._auth import AsyncAuthValue, SyncAuthValue
|
|
@@ -27,10 +25,10 @@ class DialClientPool:
|
|
|
27
25
|
self,
|
|
28
26
|
*,
|
|
29
27
|
base_url: str,
|
|
30
|
-
api_key:
|
|
31
|
-
bearer_token:
|
|
28
|
+
api_key: SyncAuthValue | None = None,
|
|
29
|
+
bearer_token: SyncAuthValue | None = None,
|
|
32
30
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
33
|
-
timeout:
|
|
31
|
+
timeout: httpx.Timeout | float = DEFAULT_TIMEOUT,
|
|
34
32
|
) -> Dial:
|
|
35
33
|
return Dial(
|
|
36
34
|
base_url=base_url,
|
|
@@ -62,10 +60,10 @@ class AsyncDialClientPool:
|
|
|
62
60
|
self,
|
|
63
61
|
*,
|
|
64
62
|
base_url: str,
|
|
65
|
-
api_key:
|
|
66
|
-
bearer_token:
|
|
63
|
+
api_key: AsyncAuthValue | None = None,
|
|
64
|
+
bearer_token: AsyncAuthValue | None = None,
|
|
67
65
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
68
|
-
timeout:
|
|
66
|
+
timeout: httpx.Timeout | float = DEFAULT_TIMEOUT,
|
|
69
67
|
) -> AsyncDial:
|
|
70
68
|
return AsyncDial(
|
|
71
69
|
base_url=base_url,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
1
2
|
from http import HTTPStatus
|
|
2
|
-
from typing import Mapping, Optional
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class DialException(Exception):
|
|
@@ -7,10 +7,10 @@ class DialException(Exception):
|
|
|
7
7
|
self,
|
|
8
8
|
message: str,
|
|
9
9
|
status_code: int = 500,
|
|
10
|
-
type:
|
|
11
|
-
param:
|
|
12
|
-
code:
|
|
13
|
-
display_message:
|
|
10
|
+
type: str | None = "runtime_error",
|
|
11
|
+
param: str | None = None,
|
|
12
|
+
code: str | None = None,
|
|
13
|
+
display_message: str | None = None,
|
|
14
14
|
) -> None:
|
|
15
15
|
self.message = message
|
|
16
16
|
self.status_code = status_code
|
{aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_http_client/_async.py
RENAMED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import AsyncIterator, Callable, Mapping
|
|
3
|
+
from contextlib import asynccontextmanager, suppress
|
|
3
4
|
from http import HTTPStatus
|
|
4
|
-
from typing import
|
|
5
|
-
Any,
|
|
6
|
-
AsyncIterator,
|
|
7
|
-
Callable,
|
|
8
|
-
Dict,
|
|
9
|
-
Mapping,
|
|
10
|
-
Optional,
|
|
11
|
-
Type,
|
|
12
|
-
Union,
|
|
13
|
-
)
|
|
5
|
+
from typing import Any
|
|
14
6
|
|
|
15
7
|
import httpx
|
|
16
8
|
|
|
@@ -30,7 +22,7 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
30
22
|
timeout=self._timeout,
|
|
31
23
|
)
|
|
32
24
|
|
|
33
|
-
async def auth_headers(self) ->
|
|
25
|
+
async def auth_headers(self) -> dict[str, str]:
|
|
34
26
|
return await aget_combined_auth_headers(
|
|
35
27
|
api_key=self._api_key, bearer_token=self._bearer_token
|
|
36
28
|
)
|
|
@@ -39,7 +31,7 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
39
31
|
self,
|
|
40
32
|
*,
|
|
41
33
|
options: FinalRequestOptions,
|
|
42
|
-
cast_to:
|
|
34
|
+
cast_to: type[ResponseT],
|
|
43
35
|
remaining_retries: int,
|
|
44
36
|
) -> ResponseT:
|
|
45
37
|
remaining = remaining_retries - 1
|
|
@@ -57,11 +49,10 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
57
49
|
self,
|
|
58
50
|
*,
|
|
59
51
|
options: FinalRequestOptions,
|
|
60
|
-
cast_to:
|
|
61
|
-
remaining_retries:
|
|
62
|
-
on_http_error:
|
|
63
|
-
|
|
64
|
-
] = None,
|
|
52
|
+
cast_to: type[ResponseT],
|
|
53
|
+
remaining_retries: int | None = None,
|
|
54
|
+
on_http_error: Callable[[httpx.HTTPStatusError], DialException | None]
|
|
55
|
+
| None = None,
|
|
65
56
|
) -> ResponseT:
|
|
66
57
|
retries = self._remaining_retries(remaining_retries, options)
|
|
67
58
|
auth_headers = await self.auth_headers()
|
|
@@ -127,8 +118,8 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
127
118
|
method: str,
|
|
128
119
|
url: str,
|
|
129
120
|
json_data: Any,
|
|
130
|
-
headers:
|
|
131
|
-
timeout:
|
|
121
|
+
headers: Mapping[str, str] | None = None,
|
|
122
|
+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
132
123
|
) -> AsyncIterator[httpx.Response]:
|
|
133
124
|
"""Open an SSE streaming response. Yields the open httpx.Response.
|
|
134
125
|
|
|
@@ -155,10 +146,8 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
155
146
|
try:
|
|
156
147
|
response.raise_for_status()
|
|
157
148
|
except httpx.HTTPStatusError as err:
|
|
158
|
-
|
|
149
|
+
with suppress(httpx.HTTPError):
|
|
159
150
|
await response.aread()
|
|
160
|
-
except httpx.HTTPError:
|
|
161
|
-
pass
|
|
162
151
|
raise self._make_dial_error_from_response(
|
|
163
152
|
err.response
|
|
164
153
|
) from err
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from http import HTTPStatus
|
|
3
3
|
from random import uniform
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Generic, TypeVar
|
|
5
5
|
|
|
6
6
|
import httpx
|
|
7
7
|
|
|
@@ -13,23 +13,23 @@ from aidial_client._utils._type_guard import is_mapping
|
|
|
13
13
|
from aidial_client.helpers._url import enforce_trailing_slash
|
|
14
14
|
|
|
15
15
|
_HttpInternalClientT = TypeVar(
|
|
16
|
-
"_HttpInternalClientT", bound=
|
|
16
|
+
"_HttpInternalClientT", bound=httpx.Client | httpx.AsyncClient
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
21
21
|
_internal_http_client: _HttpInternalClientT
|
|
22
|
-
_api_key:
|
|
23
|
-
_bearer_token:
|
|
22
|
+
_api_key: AuthValueT | None
|
|
23
|
+
_bearer_token: AuthValueT | None
|
|
24
24
|
|
|
25
25
|
def __init__(
|
|
26
26
|
self,
|
|
27
27
|
base_url: str,
|
|
28
|
-
api_key:
|
|
29
|
-
bearer_token:
|
|
28
|
+
api_key: AuthValueT | None,
|
|
29
|
+
bearer_token: AuthValueT | None,
|
|
30
30
|
max_retries: int,
|
|
31
|
-
timeout:
|
|
32
|
-
internal_http_client:
|
|
31
|
+
timeout: float | httpx.Timeout | None,
|
|
32
|
+
internal_http_client: _HttpInternalClientT | None = None,
|
|
33
33
|
):
|
|
34
34
|
self.base_url = httpx.URL(enforce_trailing_slash(base_url))
|
|
35
35
|
self._api_key = api_key
|
|
@@ -57,7 +57,7 @@ class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
|
57
57
|
def _build_request(
|
|
58
58
|
self,
|
|
59
59
|
options: FinalRequestOptions,
|
|
60
|
-
auth_headers:
|
|
60
|
+
auth_headers: dict[str, str],
|
|
61
61
|
) -> httpx.Request:
|
|
62
62
|
custom_headers = options.headers or {}
|
|
63
63
|
return self._internal_http_client.build_request(
|
|
@@ -88,10 +88,7 @@ class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
|
88
88
|
if response.status_code == HTTPStatus.CONFLICT:
|
|
89
89
|
return True
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
return True
|
|
93
|
-
|
|
94
|
-
return False
|
|
91
|
+
return response.status_code == HTTPStatus.TOO_MANY_REQUESTS
|
|
95
92
|
|
|
96
93
|
def _calculate_retry_sleep_seconds(
|
|
97
94
|
self,
|
|
@@ -106,7 +103,7 @@ class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
|
106
103
|
sleep_seconds = min(
|
|
107
104
|
INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY
|
|
108
105
|
)
|
|
109
|
-
timeout = sleep_seconds + uniform(-0.5, 0.5)
|
|
106
|
+
timeout = sleep_seconds + uniform(-0.5, 0.5) # noqa: S311
|
|
110
107
|
return max(0, timeout)
|
|
111
108
|
|
|
112
109
|
def _make_dial_error_from_response(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import AsyncIterator, Iterator
|
|
2
2
|
|
|
3
3
|
from aidial_client._log import logger
|
|
4
4
|
|
|
@@ -9,20 +9,23 @@ _UNCOMMITTED_BUFFER_WARNING = (
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _strip_field(line: str, prefix: str) -> str:
|
|
12
|
-
"""
|
|
12
|
+
"""
|
|
13
|
+
Strip a single leading U+0020 SPACE after the field colon, per the SSE spec.
|
|
14
|
+
"""
|
|
13
15
|
value = line[len(prefix) :]
|
|
14
|
-
return value
|
|
16
|
+
return value.removeprefix(" ")
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def iter_data_events(lines: Iterator[str]) -> Iterator[str]:
|
|
18
|
-
"""
|
|
20
|
+
"""
|
|
21
|
+
Yield the payload of each complete ``data:`` event from an SSE line stream.
|
|
19
22
|
|
|
20
23
|
An event is complete when a blank line follows the ``data:`` line(s). Per
|
|
21
24
|
the SSE dispatch rule, a buffer that has not been terminated by a blank
|
|
22
25
|
line is discarded (we do NOT flush partial events at end of stream).
|
|
23
26
|
Comment lines (``:``) and other field names are ignored.
|
|
24
27
|
"""
|
|
25
|
-
buffer:
|
|
28
|
+
buffer: list[str] = []
|
|
26
29
|
for line in lines:
|
|
27
30
|
if line == "":
|
|
28
31
|
if buffer:
|
|
@@ -35,7 +38,7 @@ def iter_data_events(lines: Iterator[str]) -> Iterator[str]:
|
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
async def aiter_data_events(lines: AsyncIterator[str]) -> AsyncIterator[str]:
|
|
38
|
-
buffer:
|
|
41
|
+
buffer: list[str] = []
|
|
39
42
|
async for line in lines:
|
|
40
43
|
if line == "":
|
|
41
44
|
if buffer:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import time
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Callable, Iterator, Mapping
|
|
3
|
+
from contextlib import contextmanager, suppress
|
|
3
4
|
from http import HTTPStatus
|
|
4
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
import httpx
|
|
7
8
|
|
|
@@ -22,7 +23,7 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
22
23
|
def _retry_request(
|
|
23
24
|
self,
|
|
24
25
|
options: FinalRequestOptions,
|
|
25
|
-
cast_to:
|
|
26
|
+
cast_to: type[ResponseT],
|
|
26
27
|
remaining_retries: int,
|
|
27
28
|
) -> ResponseT:
|
|
28
29
|
remaining = remaining_retries - 1
|
|
@@ -38,7 +39,7 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
38
39
|
remaining_retries=remaining,
|
|
39
40
|
)
|
|
40
41
|
|
|
41
|
-
def auth_headers(self) ->
|
|
42
|
+
def auth_headers(self) -> dict[str, str]:
|
|
42
43
|
return get_combined_auth_headers(
|
|
43
44
|
api_key=self._api_key, bearer_token=self._bearer_token
|
|
44
45
|
)
|
|
@@ -46,12 +47,11 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
46
47
|
def request(
|
|
47
48
|
self,
|
|
48
49
|
*,
|
|
49
|
-
cast_to:
|
|
50
|
+
cast_to: type[ResponseT],
|
|
50
51
|
options: FinalRequestOptions,
|
|
51
|
-
remaining_retries:
|
|
52
|
-
on_http_error:
|
|
53
|
-
|
|
54
|
-
] = None,
|
|
52
|
+
remaining_retries: int | None = None,
|
|
53
|
+
on_http_error: Callable[[httpx.HTTPStatusError], DialException | None]
|
|
54
|
+
| None = None,
|
|
55
55
|
) -> ResponseT:
|
|
56
56
|
retries = self._remaining_retries(remaining_retries, options)
|
|
57
57
|
auth_headers = self.auth_headers()
|
|
@@ -118,8 +118,8 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
118
118
|
method: str,
|
|
119
119
|
url: str,
|
|
120
120
|
json_data: Any,
|
|
121
|
-
headers:
|
|
122
|
-
timeout:
|
|
121
|
+
headers: Mapping[str, str] | None = None,
|
|
122
|
+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
123
123
|
) -> Iterator[httpx.Response]:
|
|
124
124
|
"""Open an SSE streaming response. Yields the open httpx.Response.
|
|
125
125
|
|
|
@@ -146,10 +146,8 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
146
146
|
try:
|
|
147
147
|
response.raise_for_status()
|
|
148
148
|
except httpx.HTTPStatusError as err:
|
|
149
|
-
|
|
149
|
+
with suppress(httpx.HTTPError):
|
|
150
150
|
response.read()
|
|
151
|
-
except httpx.HTTPError:
|
|
152
|
-
pass
|
|
153
151
|
raise self._make_dial_error_from_response(
|
|
154
152
|
err.response
|
|
155
153
|
) from err
|
{aidial_client-0.11.0.dev1 → aidial_client-0.11.0.dev2}/aidial_client/_internal_types/_generic.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TypeVar
|
|
1
|
+
from typing import TypeVar
|
|
2
2
|
|
|
3
3
|
import httpx
|
|
4
4
|
|
|
@@ -10,15 +10,13 @@ from aidial_client.types.file import FileDownloadResponse
|
|
|
10
10
|
|
|
11
11
|
ResponseT = TypeVar(
|
|
12
12
|
"ResponseT",
|
|
13
|
-
bound=
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
None,
|
|
22
|
-
],
|
|
13
|
+
bound=ExtraAllowModel
|
|
14
|
+
| ExtraForbidModel
|
|
15
|
+
| bytes
|
|
16
|
+
| str
|
|
17
|
+
| dict
|
|
18
|
+
| httpx.Response
|
|
19
|
+
| FileDownloadResponse
|
|
20
|
+
| None,
|
|
23
21
|
)
|
|
24
22
|
NoneType = type(None)
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
1
2
|
from io import BufferedReader
|
|
2
3
|
from typing import (
|
|
3
4
|
IO,
|
|
4
5
|
Any,
|
|
5
6
|
Literal,
|
|
6
|
-
Mapping,
|
|
7
|
-
Optional,
|
|
8
|
-
Sequence,
|
|
9
|
-
Tuple,
|
|
10
|
-
Union,
|
|
11
7
|
final,
|
|
12
8
|
)
|
|
13
9
|
|
|
@@ -16,29 +12,29 @@ from httpx import Timeout
|
|
|
16
12
|
from aidial_client._compatibility.pydantic_v1 import BaseModel
|
|
17
13
|
from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven
|
|
18
14
|
|
|
19
|
-
FileContent =
|
|
20
|
-
IO[bytes]
|
|
21
|
-
bytes
|
|
22
|
-
str
|
|
23
|
-
# Somehow, pydantic doesn't recognize result of open('...', 'rb')
|
|
24
|
-
# even though BufferedReader is a subclass of IO[bytes]
|
|
25
|
-
BufferedReader
|
|
26
|
-
|
|
27
|
-
FileTypes =
|
|
15
|
+
FileContent = (
|
|
16
|
+
IO[bytes]
|
|
17
|
+
| bytes
|
|
18
|
+
| str
|
|
19
|
+
# Somehow, pydantic doesn't recognize result of open('...', 'rb')
|
|
20
|
+
# as IO[bytes] even though BufferedReader is a subclass of IO[bytes]
|
|
21
|
+
| BufferedReader
|
|
22
|
+
)
|
|
23
|
+
FileTypes = (
|
|
28
24
|
# file (or bytes)
|
|
29
|
-
FileContent
|
|
25
|
+
FileContent
|
|
30
26
|
# (filename, file (or bytes))
|
|
31
|
-
|
|
27
|
+
| tuple[str | None, FileContent]
|
|
32
28
|
# (filename, file (or bytes), content_type)
|
|
33
|
-
|
|
29
|
+
| tuple[str | None, FileContent, str | None]
|
|
34
30
|
# (filename, file (or bytes), content_type, headers)
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
| tuple[str | None, FileContent, str | None, Mapping[str, str]]
|
|
32
|
+
)
|
|
37
33
|
|
|
38
34
|
Params = Mapping[str, Any]
|
|
39
35
|
Headers = Mapping[str, Any]
|
|
40
36
|
Data = Mapping[str, Any]
|
|
41
|
-
RequestFiles =
|
|
37
|
+
RequestFiles = Mapping[str, FileTypes] | Sequence[tuple[str, FileTypes]]
|
|
42
38
|
|
|
43
39
|
|
|
44
40
|
@final
|
|
@@ -48,12 +44,12 @@ class FinalRequestOptions(BaseModel):
|
|
|
48
44
|
|
|
49
45
|
method: Literal["GET", "PUT", "POST", "DELETE"]
|
|
50
46
|
url: str
|
|
51
|
-
params:
|
|
52
|
-
headers:
|
|
53
|
-
max_retries:
|
|
54
|
-
timeout:
|
|
55
|
-
files:
|
|
56
|
-
json_data:
|
|
47
|
+
params: Params | None = None
|
|
48
|
+
headers: Headers | None = None
|
|
49
|
+
max_retries: int | NotGiven = NOT_GIVEN
|
|
50
|
+
timeout: float | Timeout | NotGiven | None = NOT_GIVEN
|
|
51
|
+
files: RequestFiles | None = None
|
|
52
|
+
json_data: Data | None = None
|
|
57
53
|
|
|
58
54
|
def get_max_retries(self, max_retries: int) -> int:
|
|
59
55
|
if isinstance(self.max_retries, NotGiven):
|
|
@@ -61,8 +57,8 @@ class FinalRequestOptions(BaseModel):
|
|
|
61
57
|
return self.max_retries
|
|
62
58
|
|
|
63
59
|
def get_timeout(
|
|
64
|
-
self, timeout:
|
|
65
|
-
) ->
|
|
60
|
+
self, timeout: float | Timeout | None
|
|
61
|
+
) -> float | Timeout | None:
|
|
66
62
|
if isinstance(self.timeout, NotGiven):
|
|
67
63
|
return timeout
|
|
68
64
|
return self.timeout
|