aidial-client 0.11.0.dev4__tar.gz → 0.12.0.dev1__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.dev4 → aidial_client-0.12.0.dev1}/PKG-INFO +59 -2
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/README.md +58 -1
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_client.py +29 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_client_pool.py +30 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/_async.py +31 -11
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/_base.py +19 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/_sync.py +4 -11
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/helpers/storage_resource.py +24 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/files.py +21 -31
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/file.py +8 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/pyproject.toml +1 -1
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/LICENSE +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_auth.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/openai.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/pydantic.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/pydantic_v1.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_constants.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_exception.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/_sse.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_defaults.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_generic.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_http_request.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_json_rpc.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_model.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_log.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_alias.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_dict.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_openai.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_response_processing.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_type_guard.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/helpers/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/helpers/_url.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/py.typed +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/application.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/base.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/bucket.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/chat/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/chat/completions.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/client_channel.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/deployments.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/metadata.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/model.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/prompts.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/resource_permissions.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/toolset.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/application.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/bucket.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/function.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/legacy/__init__.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/legacy/application_request.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/legacy/chat_completion.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/request.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/request_param.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/response.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/tool.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/client_channel.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/deployment.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/metadata.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/model.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/prompt.py +0 -0
- {aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/toolset.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aidial-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0.dev1
|
|
4
4
|
Summary: A Python client library for the AI DIAL API
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -39,6 +39,7 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|
- [Authentication](#authentication)
|
|
40
40
|
- [API Keys](#api-keys)
|
|
41
41
|
- [Bearer Token](#bearer-token)
|
|
42
|
+
- [Lifecycle Management](#lifecycle-management)
|
|
42
43
|
- [Deployments](#deployments)
|
|
43
44
|
- [List Deployments](#list-deployments)
|
|
44
45
|
- [Get Deployment by Id](#get-deployment-by-id)
|
|
@@ -143,6 +144,45 @@ async_client = AsyncDial(
|
|
|
143
144
|
)
|
|
144
145
|
```
|
|
145
146
|
|
|
147
|
+
### Lifecycle Management
|
|
148
|
+
|
|
149
|
+
For deterministic shutdown of underlying HTTP clients, both client types and
|
|
150
|
+
client pools expose lifecycle APIs.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from aidial_client import AsyncDial, AsyncDialClientPool, Dial, DialClientPool
|
|
154
|
+
|
|
155
|
+
# Sync client
|
|
156
|
+
with Dial(api_key="your_api_key", base_url="https://your-dial-instance.com") as client:
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
client = Dial(api_key="your_api_key", base_url="https://your-dial-instance.com")
|
|
160
|
+
client.close()
|
|
161
|
+
|
|
162
|
+
# Async client
|
|
163
|
+
async with AsyncDial(
|
|
164
|
+
api_key="your_api_key", base_url="https://your-dial-instance.com"
|
|
165
|
+
) as async_client:
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
async_client = AsyncDial(
|
|
169
|
+
api_key="your_api_key", base_url="https://your-dial-instance.com"
|
|
170
|
+
)
|
|
171
|
+
await async_client.aclose()
|
|
172
|
+
|
|
173
|
+
# Sync pool
|
|
174
|
+
with DialClientPool() as pool:
|
|
175
|
+
pooled_client = pool.create_client(
|
|
176
|
+
base_url="https://your-dial-instance.com", api_key="your-api-key"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Async pool
|
|
180
|
+
async with AsyncDialClientPool() as async_pool:
|
|
181
|
+
pooled_async_client = async_pool.create_client(
|
|
182
|
+
base_url="https://your-dial-instance.com", api_key="your-api-key"
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
146
186
|
You can also pass `bearer_token` as a function without parameters, that returns a `string`:
|
|
147
187
|
|
|
148
188
|
```python
|
|
@@ -514,6 +554,16 @@ result = await async_client.files.download(
|
|
|
514
554
|
)
|
|
515
555
|
```
|
|
516
556
|
|
|
557
|
+
For large async downloads, use `stream_download()` to process bytes as they arrive without buffering the full response in memory:
|
|
558
|
+
|
|
559
|
+
```python
|
|
560
|
+
async with async_client.files.stream_download(
|
|
561
|
+
url=await async_client.my_files_home() / "relative_folder/my-file.txt"
|
|
562
|
+
) as result:
|
|
563
|
+
async for bytes_chunk in result:
|
|
564
|
+
...
|
|
565
|
+
```
|
|
566
|
+
|
|
517
567
|
As a result, you will receive an object of type `FileDownloadResponse`, that you can iterate by byte chunks:
|
|
518
568
|
|
|
519
569
|
```python
|
|
@@ -530,6 +580,13 @@ all_content = result.get_content()
|
|
|
530
580
|
all_content = await result.aget_content()
|
|
531
581
|
```
|
|
532
582
|
|
|
583
|
+
or access response metadata:
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
headers = result.headers
|
|
587
|
+
content_type = result.content_type
|
|
588
|
+
```
|
|
589
|
+
|
|
533
590
|
or write it to the file:
|
|
534
591
|
|
|
535
592
|
```python
|
|
@@ -950,7 +1007,7 @@ second_client = client_pool.create_client(
|
|
|
950
1007
|
#### Asynchronous Client Pool
|
|
951
1008
|
|
|
952
1009
|
```python
|
|
953
|
-
from
|
|
1010
|
+
from aidial_client import (
|
|
954
1011
|
AsyncDialClientPool,
|
|
955
1012
|
)
|
|
956
1013
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- [Authentication](#authentication)
|
|
18
18
|
- [API Keys](#api-keys)
|
|
19
19
|
- [Bearer Token](#bearer-token)
|
|
20
|
+
- [Lifecycle Management](#lifecycle-management)
|
|
20
21
|
- [Deployments](#deployments)
|
|
21
22
|
- [List Deployments](#list-deployments)
|
|
22
23
|
- [Get Deployment by Id](#get-deployment-by-id)
|
|
@@ -121,6 +122,45 @@ async_client = AsyncDial(
|
|
|
121
122
|
)
|
|
122
123
|
```
|
|
123
124
|
|
|
125
|
+
### Lifecycle Management
|
|
126
|
+
|
|
127
|
+
For deterministic shutdown of underlying HTTP clients, both client types and
|
|
128
|
+
client pools expose lifecycle APIs.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from aidial_client import AsyncDial, AsyncDialClientPool, Dial, DialClientPool
|
|
132
|
+
|
|
133
|
+
# Sync client
|
|
134
|
+
with Dial(api_key="your_api_key", base_url="https://your-dial-instance.com") as client:
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
client = Dial(api_key="your_api_key", base_url="https://your-dial-instance.com")
|
|
138
|
+
client.close()
|
|
139
|
+
|
|
140
|
+
# Async client
|
|
141
|
+
async with AsyncDial(
|
|
142
|
+
api_key="your_api_key", base_url="https://your-dial-instance.com"
|
|
143
|
+
) as async_client:
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
async_client = AsyncDial(
|
|
147
|
+
api_key="your_api_key", base_url="https://your-dial-instance.com"
|
|
148
|
+
)
|
|
149
|
+
await async_client.aclose()
|
|
150
|
+
|
|
151
|
+
# Sync pool
|
|
152
|
+
with DialClientPool() as pool:
|
|
153
|
+
pooled_client = pool.create_client(
|
|
154
|
+
base_url="https://your-dial-instance.com", api_key="your-api-key"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Async pool
|
|
158
|
+
async with AsyncDialClientPool() as async_pool:
|
|
159
|
+
pooled_async_client = async_pool.create_client(
|
|
160
|
+
base_url="https://your-dial-instance.com", api_key="your-api-key"
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
124
164
|
You can also pass `bearer_token` as a function without parameters, that returns a `string`:
|
|
125
165
|
|
|
126
166
|
```python
|
|
@@ -492,6 +532,16 @@ result = await async_client.files.download(
|
|
|
492
532
|
)
|
|
493
533
|
```
|
|
494
534
|
|
|
535
|
+
For large async downloads, use `stream_download()` to process bytes as they arrive without buffering the full response in memory:
|
|
536
|
+
|
|
537
|
+
```python
|
|
538
|
+
async with async_client.files.stream_download(
|
|
539
|
+
url=await async_client.my_files_home() / "relative_folder/my-file.txt"
|
|
540
|
+
) as result:
|
|
541
|
+
async for bytes_chunk in result:
|
|
542
|
+
...
|
|
543
|
+
```
|
|
544
|
+
|
|
495
545
|
As a result, you will receive an object of type `FileDownloadResponse`, that you can iterate by byte chunks:
|
|
496
546
|
|
|
497
547
|
```python
|
|
@@ -508,6 +558,13 @@ all_content = result.get_content()
|
|
|
508
558
|
all_content = await result.aget_content()
|
|
509
559
|
```
|
|
510
560
|
|
|
561
|
+
or access response metadata:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
headers = result.headers
|
|
565
|
+
content_type = result.content_type
|
|
566
|
+
```
|
|
567
|
+
|
|
511
568
|
or write it to the file:
|
|
512
569
|
|
|
513
570
|
```python
|
|
@@ -928,7 +985,7 @@ second_client = client_pool.create_client(
|
|
|
928
985
|
#### Asynchronous Client Pool
|
|
929
986
|
|
|
930
987
|
```python
|
|
931
|
-
from
|
|
988
|
+
from aidial_client import (
|
|
932
989
|
AsyncDialClientPool,
|
|
933
990
|
)
|
|
934
991
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from pathlib import PurePosixPath
|
|
3
|
+
from types import TracebackType
|
|
3
4
|
from typing import Generic, TypeVar
|
|
4
5
|
from urllib.parse import urljoin
|
|
5
6
|
|
|
@@ -164,6 +165,20 @@ class Dial(BaseDialClient[SyncHTTPClient, SyncAuthValue]):
|
|
|
164
165
|
def auth_headers(self) -> dict[str, str]:
|
|
165
166
|
return self._http_client.auth_headers()
|
|
166
167
|
|
|
168
|
+
def close(self) -> None:
|
|
169
|
+
self._http_client.internal_http_client.close()
|
|
170
|
+
|
|
171
|
+
def __enter__(self) -> "Dial":
|
|
172
|
+
return self
|
|
173
|
+
|
|
174
|
+
def __exit__(
|
|
175
|
+
self,
|
|
176
|
+
exc_type: type[BaseException] | None,
|
|
177
|
+
exc_value: BaseException | None,
|
|
178
|
+
traceback: TracebackType | None,
|
|
179
|
+
) -> None:
|
|
180
|
+
self.close()
|
|
181
|
+
|
|
167
182
|
|
|
168
183
|
class AsyncDial(BaseDialClient[AsyncHTTPClient, AsyncAuthValue]):
|
|
169
184
|
def _init_resources(self) -> None:
|
|
@@ -253,3 +268,17 @@ class AsyncDial(BaseDialClient[AsyncHTTPClient, AsyncAuthValue]):
|
|
|
253
268
|
|
|
254
269
|
async def auth_headers(self) -> dict[str, str]:
|
|
255
270
|
return await self._http_client.auth_headers()
|
|
271
|
+
|
|
272
|
+
async def aclose(self) -> None:
|
|
273
|
+
await self._http_client.internal_http_client.aclose()
|
|
274
|
+
|
|
275
|
+
async def __aenter__(self) -> "AsyncDial":
|
|
276
|
+
return self
|
|
277
|
+
|
|
278
|
+
async def __aexit__(
|
|
279
|
+
self,
|
|
280
|
+
exc_type: type[BaseException] | None,
|
|
281
|
+
exc_value: BaseException | None,
|
|
282
|
+
traceback: TracebackType | None,
|
|
283
|
+
) -> None:
|
|
284
|
+
await self.aclose()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from types import TracebackType
|
|
2
|
+
|
|
1
3
|
import httpx
|
|
2
4
|
|
|
3
5
|
from aidial_client._auth import AsyncAuthValue, SyncAuthValue
|
|
@@ -44,6 +46,20 @@ class DialClientPool:
|
|
|
44
46
|
),
|
|
45
47
|
)
|
|
46
48
|
|
|
49
|
+
def close(self) -> None:
|
|
50
|
+
self._internal_http_client.close()
|
|
51
|
+
|
|
52
|
+
def __enter__(self) -> "DialClientPool":
|
|
53
|
+
return self
|
|
54
|
+
|
|
55
|
+
def __exit__(
|
|
56
|
+
self,
|
|
57
|
+
exc_type: type[BaseException] | None,
|
|
58
|
+
exc_value: BaseException | None,
|
|
59
|
+
traceback: TracebackType | None,
|
|
60
|
+
) -> None:
|
|
61
|
+
self.close()
|
|
62
|
+
|
|
47
63
|
|
|
48
64
|
class AsyncDialClientPool:
|
|
49
65
|
def __init__(
|
|
@@ -78,3 +94,17 @@ class AsyncDialClientPool:
|
|
|
78
94
|
internal_http_client=self._internal_http_client,
|
|
79
95
|
),
|
|
80
96
|
)
|
|
97
|
+
|
|
98
|
+
async def aclose(self) -> None:
|
|
99
|
+
await self._internal_http_client.aclose()
|
|
100
|
+
|
|
101
|
+
async def __aenter__(self) -> "AsyncDialClientPool":
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
async def __aexit__(
|
|
105
|
+
self,
|
|
106
|
+
exc_type: type[BaseException] | None,
|
|
107
|
+
exc_value: BaseException | None,
|
|
108
|
+
traceback: TracebackType | None,
|
|
109
|
+
) -> None:
|
|
110
|
+
await self.aclose()
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/_async.py
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from collections.abc import AsyncIterator,
|
|
2
|
+
from collections.abc import AsyncIterator, Mapping
|
|
3
3
|
from contextlib import asynccontextmanager, suppress
|
|
4
4
|
from http import HTTPStatus
|
|
5
5
|
from typing import Any
|
|
@@ -8,7 +8,7 @@ import httpx
|
|
|
8
8
|
|
|
9
9
|
from aidial_client._auth import AsyncAuthValue, aget_combined_auth_headers
|
|
10
10
|
from aidial_client._exception import DialException
|
|
11
|
-
from aidial_client._http_client._base import BaseHTTPClient
|
|
11
|
+
from aidial_client._http_client._base import BaseHTTPClient, ErrorHandler
|
|
12
12
|
from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven
|
|
13
13
|
from aidial_client._internal_types._generic import ResponseT
|
|
14
14
|
from aidial_client._internal_types._http_request import FinalRequestOptions
|
|
@@ -51,8 +51,7 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
51
51
|
options: FinalRequestOptions,
|
|
52
52
|
cast_to: type[ResponseT],
|
|
53
53
|
remaining_retries: int | None = None,
|
|
54
|
-
on_http_error:
|
|
55
|
-
| None = None,
|
|
54
|
+
on_http_error: ErrorHandler | None = None,
|
|
56
55
|
) -> ResponseT:
|
|
57
56
|
retries = self._remaining_retries(remaining_retries, options)
|
|
58
57
|
auth_headers = await self.auth_headers()
|
|
@@ -101,16 +100,37 @@ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient, AsyncAuthValue]):
|
|
|
101
100
|
cast_to=cast_to,
|
|
102
101
|
remaining_retries=retries,
|
|
103
102
|
)
|
|
104
|
-
|
|
105
|
-
custom_error = on_http_error(err) if on_http_error else None
|
|
106
|
-
# or fallback to default processing
|
|
107
|
-
raised_error = custom_error or self._make_dial_error_from_response(
|
|
108
|
-
err.response
|
|
109
|
-
)
|
|
110
|
-
raise raised_error from err
|
|
103
|
+
self._raise_for_status(response, on_http_error)
|
|
111
104
|
|
|
112
105
|
return process_block_response(cast_to=cast_to, response=response)
|
|
113
106
|
|
|
107
|
+
@asynccontextmanager
|
|
108
|
+
async def stream(
|
|
109
|
+
self,
|
|
110
|
+
*,
|
|
111
|
+
options: FinalRequestOptions,
|
|
112
|
+
on_http_error: ErrorHandler | None = None,
|
|
113
|
+
) -> AsyncIterator[httpx.Response]:
|
|
114
|
+
auth_headers = await self.auth_headers()
|
|
115
|
+
request = self._build_request(options, auth_headers)
|
|
116
|
+
try:
|
|
117
|
+
response = await self._internal_http_client.send(
|
|
118
|
+
request, stream=True
|
|
119
|
+
)
|
|
120
|
+
except httpx.TimeoutException as err:
|
|
121
|
+
raise DialException(
|
|
122
|
+
message="Request timed out",
|
|
123
|
+
status_code=HTTPStatus.REQUEST_TIMEOUT,
|
|
124
|
+
) from err
|
|
125
|
+
except httpx.HTTPError as err:
|
|
126
|
+
raise DialException(message=f"Request failed: {err}") from err
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
self._raise_for_status(response, on_http_error)
|
|
130
|
+
yield response
|
|
131
|
+
finally:
|
|
132
|
+
await response.aclose()
|
|
133
|
+
|
|
114
134
|
@asynccontextmanager
|
|
115
135
|
async def stream_sse(
|
|
116
136
|
self,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Callable
|
|
2
3
|
from http import HTTPStatus
|
|
3
4
|
from random import uniform
|
|
4
5
|
from typing import Generic, TypeVar
|
|
@@ -16,6 +17,8 @@ _HttpInternalClientT = TypeVar(
|
|
|
16
17
|
"_HttpInternalClientT", bound=httpx.Client | httpx.AsyncClient
|
|
17
18
|
)
|
|
18
19
|
|
|
20
|
+
ErrorHandler = Callable[[httpx.HTTPStatusError], DialException | None]
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
21
24
|
_internal_http_client: _HttpInternalClientT
|
|
@@ -106,6 +109,22 @@ class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]):
|
|
|
106
109
|
timeout = sleep_seconds + uniform(-0.5, 0.5) # noqa: S311
|
|
107
110
|
return max(0, timeout)
|
|
108
111
|
|
|
112
|
+
def _raise_for_status(
|
|
113
|
+
self,
|
|
114
|
+
response: httpx.Response,
|
|
115
|
+
on_http_error: ErrorHandler | None,
|
|
116
|
+
) -> None:
|
|
117
|
+
try:
|
|
118
|
+
response.raise_for_status()
|
|
119
|
+
except httpx.HTTPStatusError as err:
|
|
120
|
+
# Try to get a custom error from response status_code/code/message
|
|
121
|
+
custom_error = on_http_error(err) if on_http_error else None
|
|
122
|
+
# or fallback to default processing
|
|
123
|
+
raised_error = custom_error or self._make_dial_error_from_response(
|
|
124
|
+
err.response
|
|
125
|
+
)
|
|
126
|
+
raise raised_error from err
|
|
127
|
+
|
|
109
128
|
def _make_dial_error_from_response(
|
|
110
129
|
self,
|
|
111
130
|
response: httpx.Response,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import time
|
|
2
|
-
from collections.abc import
|
|
2
|
+
from collections.abc import Iterator, Mapping
|
|
3
3
|
from contextlib import contextmanager, suppress
|
|
4
4
|
from http import HTTPStatus
|
|
5
5
|
from typing import Any
|
|
@@ -8,7 +8,7 @@ import httpx
|
|
|
8
8
|
|
|
9
9
|
from aidial_client._auth import SyncAuthValue, get_combined_auth_headers
|
|
10
10
|
from aidial_client._exception import DialException
|
|
11
|
-
from aidial_client._http_client._base import BaseHTTPClient
|
|
11
|
+
from aidial_client._http_client._base import BaseHTTPClient, ErrorHandler
|
|
12
12
|
from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven
|
|
13
13
|
from aidial_client._internal_types._generic import ResponseT
|
|
14
14
|
from aidial_client._internal_types._http_request import FinalRequestOptions
|
|
@@ -50,8 +50,7 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
50
50
|
cast_to: type[ResponseT],
|
|
51
51
|
options: FinalRequestOptions,
|
|
52
52
|
remaining_retries: int | None = None,
|
|
53
|
-
on_http_error:
|
|
54
|
-
| None = None,
|
|
53
|
+
on_http_error: ErrorHandler | None = None,
|
|
55
54
|
) -> ResponseT:
|
|
56
55
|
retries = self._remaining_retries(remaining_retries, options)
|
|
57
56
|
auth_headers = self.auth_headers()
|
|
@@ -101,13 +100,7 @@ class SyncHTTPClient(BaseHTTPClient[httpx.Client, SyncAuthValue]):
|
|
|
101
100
|
cast_to=cast_to,
|
|
102
101
|
remaining_retries=retries,
|
|
103
102
|
)
|
|
104
|
-
|
|
105
|
-
custom_error = on_http_error(err) if on_http_error else None
|
|
106
|
-
# or fallback to default processing
|
|
107
|
-
raised_error = custom_error or self._make_dial_error_from_response(
|
|
108
|
-
err.response
|
|
109
|
-
)
|
|
110
|
-
raise raised_error from err
|
|
103
|
+
self._raise_for_status(response, on_http_error)
|
|
111
104
|
|
|
112
105
|
return process_block_response(cast_to=cast_to, response=response)
|
|
113
106
|
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/helpers/storage_resource.py
RENAMED
|
@@ -5,6 +5,8 @@ from urllib.parse import urljoin, urlparse
|
|
|
5
5
|
from aidial_client._compatibility.pydantic_v1 import BaseModel
|
|
6
6
|
from aidial_client._constants import API_PREFIX
|
|
7
7
|
from aidial_client._exception import InvalidDialURLError, NotDialURLError
|
|
8
|
+
from aidial_client._internal_types._http_request import FinalRequestOptions
|
|
9
|
+
from aidial_client._utils._dict import remove_none
|
|
8
10
|
from aidial_client.helpers._url import enforce_trailing_slash
|
|
9
11
|
|
|
10
12
|
StorageResourceType = Literal["files", "conversations", "prompts"]
|
|
@@ -156,3 +158,25 @@ class DialStorageResourceMixin(BaseModel):
|
|
|
156
158
|
Get the display name of the resource from the URL
|
|
157
159
|
"""
|
|
158
160
|
return self.get_storage_resource(url).bucket_path
|
|
161
|
+
|
|
162
|
+
def _prepare_download_request(
|
|
163
|
+
self,
|
|
164
|
+
url: str | PurePosixPath,
|
|
165
|
+
etag_if_match: str | None,
|
|
166
|
+
) -> tuple[FinalRequestOptions, str]:
|
|
167
|
+
storage_resource = self.get_storage_resource(str(url))
|
|
168
|
+
|
|
169
|
+
if storage_resource.filename is None:
|
|
170
|
+
raise InvalidDialURLError("URL points to a directory, not a file")
|
|
171
|
+
|
|
172
|
+
options = FinalRequestOptions(
|
|
173
|
+
method="GET",
|
|
174
|
+
url=urljoin(API_PREFIX, storage_resource.api_path),
|
|
175
|
+
headers=remove_none(
|
|
176
|
+
{
|
|
177
|
+
"If-Match": etag_if_match,
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return options, storage_resource.filename
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from contextlib import asynccontextmanager
|
|
1
3
|
from pathlib import PurePosixPath
|
|
2
4
|
from typing import Literal
|
|
3
5
|
from urllib.parse import urljoin
|
|
@@ -8,7 +10,6 @@ from aidial_client._constants import API_PREFIX
|
|
|
8
10
|
from aidial_client._exception import (
|
|
9
11
|
DialException,
|
|
10
12
|
EtagMismatchError,
|
|
11
|
-
InvalidDialURLError,
|
|
12
13
|
ResourceNotFoundError,
|
|
13
14
|
)
|
|
14
15
|
from aidial_client._internal_types._generic import NoneType
|
|
@@ -70,25 +71,13 @@ class Files(Resource, DialStorageResourceMixin):
|
|
|
70
71
|
url: str | PurePosixPath,
|
|
71
72
|
etag_if_match: str | None = None,
|
|
72
73
|
) -> FileDownloadResponse:
|
|
73
|
-
|
|
74
|
-
if storage_resource.filename is None:
|
|
75
|
-
raise InvalidDialURLError("URL points to a directory, not a file")
|
|
74
|
+
options, filename = self._prepare_download_request(url, etag_if_match)
|
|
76
75
|
response = self.http_client.request(
|
|
77
76
|
cast_to=httpx.Response,
|
|
78
|
-
options=
|
|
79
|
-
method="GET",
|
|
80
|
-
url=urljoin(API_PREFIX, storage_resource.api_path),
|
|
81
|
-
headers=remove_none(
|
|
82
|
-
{
|
|
83
|
-
"If-Match": etag_if_match,
|
|
84
|
-
}
|
|
85
|
-
),
|
|
86
|
-
),
|
|
77
|
+
options=options,
|
|
87
78
|
on_http_error=_files_error_processor,
|
|
88
79
|
)
|
|
89
|
-
return FileDownloadResponse(
|
|
90
|
-
response=response, filename=storage_resource.filename
|
|
91
|
-
)
|
|
80
|
+
return FileDownloadResponse(response=response, filename=filename)
|
|
92
81
|
|
|
93
82
|
def delete(
|
|
94
83
|
self,
|
|
@@ -188,25 +177,26 @@ class AsyncFiles(AsyncResource, DialStorageResourceMixin):
|
|
|
188
177
|
url: str | PurePosixPath,
|
|
189
178
|
etag_if_match: str | None = None,
|
|
190
179
|
) -> FileDownloadResponse:
|
|
191
|
-
|
|
192
|
-
if storage_resource.filename is None:
|
|
193
|
-
raise InvalidDialURLError("URL points to a directory, not a file")
|
|
180
|
+
options, filename = self._prepare_download_request(url, etag_if_match)
|
|
194
181
|
response = await self.http_client.request(
|
|
195
182
|
cast_to=httpx.Response,
|
|
196
|
-
options=
|
|
197
|
-
method="GET",
|
|
198
|
-
url=urljoin(API_PREFIX, storage_resource.api_path),
|
|
199
|
-
headers=remove_none(
|
|
200
|
-
{
|
|
201
|
-
"If-Match": etag_if_match,
|
|
202
|
-
}
|
|
203
|
-
),
|
|
204
|
-
),
|
|
183
|
+
options=options,
|
|
205
184
|
on_http_error=_files_error_processor,
|
|
206
185
|
)
|
|
207
|
-
return FileDownloadResponse(
|
|
208
|
-
|
|
209
|
-
|
|
186
|
+
return FileDownloadResponse(response=response, filename=filename)
|
|
187
|
+
|
|
188
|
+
@asynccontextmanager
|
|
189
|
+
async def stream_download(
|
|
190
|
+
self,
|
|
191
|
+
url: str | PurePosixPath,
|
|
192
|
+
etag_if_match: str | None = None,
|
|
193
|
+
) -> AsyncIterator[FileDownloadResponse]:
|
|
194
|
+
options, filename = self._prepare_download_request(url, etag_if_match)
|
|
195
|
+
async with self.http_client.stream(
|
|
196
|
+
options=options,
|
|
197
|
+
on_http_error=_files_error_processor,
|
|
198
|
+
) as response:
|
|
199
|
+
yield FileDownloadResponse(response=response, filename=filename)
|
|
210
200
|
|
|
211
201
|
async def delete(
|
|
212
202
|
self,
|
|
@@ -40,3 +40,11 @@ class FileDownloadResponse:
|
|
|
40
40
|
@property
|
|
41
41
|
def filename(self) -> str:
|
|
42
42
|
return self._filename
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def headers(self) -> httpx.Headers:
|
|
46
|
+
return self._response.headers
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def content_type(self) -> str | None:
|
|
50
|
+
return self.headers.get("content-type")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/__init__.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/openai.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/pydantic.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_compatibility/pydantic_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_http_client/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/__init__.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_defaults.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_generic.py
RENAMED
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_json_rpc.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_internal_types/_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/_utils/_response_processing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/application.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/chat/__init__.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/chat/completions.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/client_channel.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/resources/deployments.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/__init__.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/function.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/legacy/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/request_param.py
RENAMED
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/chat/response.py
RENAMED
|
File without changes
|
|
File without changes
|
{aidial_client-0.11.0.dev4 → aidial_client-0.12.0.dev1}/aidial_client/types/client_channel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|