ccs-llmconnector 1.1.2__py3-none-any.whl → 1.1.4__py3-none-any.whl
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.
- {ccs_llmconnector-1.1.2.dist-info → ccs_llmconnector-1.1.4.dist-info}/METADATA +1 -1
- ccs_llmconnector-1.1.4.dist-info/RECORD +16 -0
- {ccs_llmconnector-1.1.2.dist-info → ccs_llmconnector-1.1.4.dist-info}/WHEEL +1 -1
- llmconnector/__init__.py +23 -21
- llmconnector/anthropic_client.py +266 -266
- llmconnector/client.py +566 -301
- llmconnector/client_cli.py +42 -42
- llmconnector/gemini_client.py +411 -96
- llmconnector/grok_client.py +270 -270
- llmconnector/openai_client.py +407 -263
- llmconnector/types.py +66 -48
- llmconnector/utils.py +77 -77
- ccs_llmconnector-1.1.2.dist-info/RECORD +0 -16
- {ccs_llmconnector-1.1.2.dist-info → ccs_llmconnector-1.1.4.dist-info}/entry_points.txt +0 -0
- {ccs_llmconnector-1.1.2.dist-info → ccs_llmconnector-1.1.4.dist-info}/licenses/LICENSE +0 -0
- {ccs_llmconnector-1.1.2.dist-info → ccs_llmconnector-1.1.4.dist-info}/top_level.txt +0 -0
llmconnector/client.py
CHANGED
|
@@ -1,99 +1,131 @@
|
|
|
1
|
-
"""Provider-agnostic entry point for working with large language models."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
from typing import Dict, Optional, Protocol, Sequence
|
|
7
|
-
|
|
8
|
-
from .types import ImageInput, MessageSequence
|
|
9
|
-
from .utils import run_sync_in_thread
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class SupportsGenerateResponse(Protocol):
|
|
13
|
-
"""Protocol describing provider clients."""
|
|
14
|
-
|
|
15
|
-
def generate_response(
|
|
16
|
-
self,
|
|
17
|
-
*,
|
|
18
|
-
api_key: str,
|
|
19
|
-
prompt: Optional[str] = None,
|
|
20
|
-
model: str,
|
|
21
|
-
max_tokens: int = 32000,
|
|
22
|
-
reasoning_effort: Optional[str] = None,
|
|
23
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
24
|
-
messages: Optional[MessageSequence] = None,
|
|
25
|
-
request_id: Optional[str] = None,
|
|
26
|
-
timeout_s: Optional[float] = None,
|
|
27
|
-
max_retries: Optional[int] = None,
|
|
28
|
-
retry_backoff_s: float = 0.5,
|
|
29
|
-
) -> str:
|
|
30
|
-
...
|
|
31
|
-
|
|
32
|
-
async def async_generate_response(
|
|
33
|
-
self,
|
|
34
|
-
*,
|
|
35
|
-
api_key: str,
|
|
36
|
-
prompt: Optional[str] = None,
|
|
37
|
-
model: str,
|
|
38
|
-
max_tokens: int = 32000,
|
|
39
|
-
reasoning_effort: Optional[str] = None,
|
|
40
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
41
|
-
messages: Optional[MessageSequence] = None,
|
|
42
|
-
request_id: Optional[str] = None,
|
|
43
|
-
timeout_s: Optional[float] = None,
|
|
44
|
-
max_retries: Optional[int] = None,
|
|
45
|
-
retry_backoff_s: float = 0.5,
|
|
46
|
-
) -> str:
|
|
47
|
-
...
|
|
48
|
-
|
|
49
|
-
def generate_image(
|
|
50
|
-
self,
|
|
51
|
-
*,
|
|
52
|
-
api_key: str,
|
|
53
|
-
prompt: str,
|
|
1
|
+
"""Provider-agnostic entry point for working with large language models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Dict, Optional, Protocol, Sequence, Union
|
|
7
|
+
|
|
8
|
+
from .types import EmbeddingVector, ImageInput, LLMResponse, MessageSequence
|
|
9
|
+
from .utils import run_sync_in_thread
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SupportsGenerateResponse(Protocol):
|
|
13
|
+
"""Protocol describing provider clients."""
|
|
14
|
+
|
|
15
|
+
def generate_response(
|
|
16
|
+
self,
|
|
17
|
+
*,
|
|
18
|
+
api_key: str,
|
|
19
|
+
prompt: Optional[str] = None,
|
|
20
|
+
model: str,
|
|
21
|
+
max_tokens: int = 32000,
|
|
22
|
+
reasoning_effort: Optional[str] = None,
|
|
23
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
24
|
+
messages: Optional[MessageSequence] = None,
|
|
25
|
+
request_id: Optional[str] = None,
|
|
26
|
+
timeout_s: Optional[float] = None,
|
|
27
|
+
max_retries: Optional[int] = None,
|
|
28
|
+
retry_backoff_s: float = 0.5,
|
|
29
|
+
) -> str:
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
async def async_generate_response(
|
|
33
|
+
self,
|
|
34
|
+
*,
|
|
35
|
+
api_key: str,
|
|
36
|
+
prompt: Optional[str] = None,
|
|
37
|
+
model: str,
|
|
38
|
+
max_tokens: int = 32000,
|
|
39
|
+
reasoning_effort: Optional[str] = None,
|
|
40
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
41
|
+
messages: Optional[MessageSequence] = None,
|
|
42
|
+
request_id: Optional[str] = None,
|
|
43
|
+
timeout_s: Optional[float] = None,
|
|
44
|
+
max_retries: Optional[int] = None,
|
|
45
|
+
retry_backoff_s: float = 0.5,
|
|
46
|
+
) -> str:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
def generate_image(
|
|
50
|
+
self,
|
|
51
|
+
*,
|
|
52
|
+
api_key: str,
|
|
53
|
+
prompt: str,
|
|
54
54
|
model: str,
|
|
55
55
|
image_size: Optional[str] = None,
|
|
56
56
|
aspect_ratio: Optional[str] = None,
|
|
57
|
-
image: Optional[ImageInput] = None,
|
|
58
|
-
) -> bytes:
|
|
59
|
-
...
|
|
60
|
-
|
|
61
|
-
async def async_generate_image(
|
|
62
|
-
self,
|
|
63
|
-
*,
|
|
64
|
-
api_key: str,
|
|
65
|
-
prompt: str,
|
|
66
|
-
model: str,
|
|
67
|
-
image_size: Optional[str] = None,
|
|
68
|
-
aspect_ratio: Optional[str] = None,
|
|
69
|
-
image: Optional[ImageInput] = None,
|
|
70
|
-
) -> bytes:
|
|
71
|
-
...
|
|
72
|
-
|
|
73
|
-
def list_models(
|
|
74
|
-
self,
|
|
75
|
-
*,
|
|
76
|
-
api_key: str,
|
|
77
|
-
request_id: Optional[str] = None,
|
|
78
|
-
timeout_s: Optional[float] = None,
|
|
79
|
-
max_retries: Optional[int] = None,
|
|
80
|
-
retry_backoff_s: float = 0.5,
|
|
81
|
-
) -> Sequence[dict[str, Optional[str]]]:
|
|
82
|
-
...
|
|
83
|
-
|
|
84
|
-
async def async_list_models(
|
|
85
|
-
self,
|
|
86
|
-
*,
|
|
87
|
-
api_key: str,
|
|
88
|
-
request_id: Optional[str] = None,
|
|
89
|
-
timeout_s: Optional[float] = None,
|
|
90
|
-
max_retries: Optional[int] = None,
|
|
91
|
-
retry_backoff_s: float = 0.5,
|
|
92
|
-
) -> Sequence[dict[str, Optional[str]]]:
|
|
93
|
-
...
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class
|
|
57
|
+
image: Optional[ImageInput] = None,
|
|
58
|
+
) -> bytes:
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
async def async_generate_image(
|
|
62
|
+
self,
|
|
63
|
+
*,
|
|
64
|
+
api_key: str,
|
|
65
|
+
prompt: str,
|
|
66
|
+
model: str,
|
|
67
|
+
image_size: Optional[str] = None,
|
|
68
|
+
aspect_ratio: Optional[str] = None,
|
|
69
|
+
image: Optional[ImageInput] = None,
|
|
70
|
+
) -> bytes:
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
def list_models(
|
|
74
|
+
self,
|
|
75
|
+
*,
|
|
76
|
+
api_key: str,
|
|
77
|
+
request_id: Optional[str] = None,
|
|
78
|
+
timeout_s: Optional[float] = None,
|
|
79
|
+
max_retries: Optional[int] = None,
|
|
80
|
+
retry_backoff_s: float = 0.5,
|
|
81
|
+
) -> Sequence[dict[str, Optional[str]]]:
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
async def async_list_models(
|
|
85
|
+
self,
|
|
86
|
+
*,
|
|
87
|
+
api_key: str,
|
|
88
|
+
request_id: Optional[str] = None,
|
|
89
|
+
timeout_s: Optional[float] = None,
|
|
90
|
+
max_retries: Optional[int] = None,
|
|
91
|
+
retry_backoff_s: float = 0.5,
|
|
92
|
+
) -> Sequence[dict[str, Optional[str]]]:
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class SupportsEmbeddings(Protocol):
|
|
97
|
+
def embed_content(
|
|
98
|
+
self,
|
|
99
|
+
*,
|
|
100
|
+
api_key: str,
|
|
101
|
+
model: str,
|
|
102
|
+
contents: Union[str, Sequence[str]],
|
|
103
|
+
task_type: Optional[str] = None,
|
|
104
|
+
output_dimensionality: Optional[int] = None,
|
|
105
|
+
request_id: Optional[str] = None,
|
|
106
|
+
timeout_s: Optional[float] = None,
|
|
107
|
+
max_retries: Optional[int] = None,
|
|
108
|
+
retry_backoff_s: float = 0.5,
|
|
109
|
+
) -> list[EmbeddingVector]:
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
async def async_embed_content(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
api_key: str,
|
|
116
|
+
model: str,
|
|
117
|
+
contents: Union[str, Sequence[str]],
|
|
118
|
+
task_type: Optional[str] = None,
|
|
119
|
+
output_dimensionality: Optional[int] = None,
|
|
120
|
+
request_id: Optional[str] = None,
|
|
121
|
+
timeout_s: Optional[float] = None,
|
|
122
|
+
max_retries: Optional[int] = None,
|
|
123
|
+
retry_backoff_s: float = 0.5,
|
|
124
|
+
) -> list[EmbeddingVector]:
|
|
125
|
+
...
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class LLMClient:
|
|
97
129
|
"""Central client capable of routing requests to different providers."""
|
|
98
130
|
|
|
99
131
|
def __init__(
|
|
@@ -120,22 +152,22 @@ class LLMClient:
|
|
|
120
152
|
|
|
121
153
|
self._providers[name.lower()] = client
|
|
122
154
|
|
|
123
|
-
def generate_response(
|
|
124
|
-
self,
|
|
125
|
-
*,
|
|
126
|
-
provider: str,
|
|
127
|
-
api_key: str,
|
|
128
|
-
prompt: Optional[str] = None,
|
|
129
|
-
model: str,
|
|
130
|
-
max_tokens: int = 32000,
|
|
131
|
-
reasoning_effort: Optional[str] = None,
|
|
132
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
133
|
-
messages: Optional[MessageSequence] = None,
|
|
134
|
-
request_id: Optional[str] = None,
|
|
135
|
-
timeout_s: Optional[float] = None,
|
|
136
|
-
max_retries: Optional[int] = None,
|
|
137
|
-
retry_backoff_s: float = 0.5,
|
|
138
|
-
) -> str:
|
|
155
|
+
def generate_response(
|
|
156
|
+
self,
|
|
157
|
+
*,
|
|
158
|
+
provider: str,
|
|
159
|
+
api_key: str,
|
|
160
|
+
prompt: Optional[str] = None,
|
|
161
|
+
model: str,
|
|
162
|
+
max_tokens: int = 32000,
|
|
163
|
+
reasoning_effort: Optional[str] = None,
|
|
164
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
165
|
+
messages: Optional[MessageSequence] = None,
|
|
166
|
+
request_id: Optional[str] = None,
|
|
167
|
+
timeout_s: Optional[float] = None,
|
|
168
|
+
max_retries: Optional[int] = None,
|
|
169
|
+
retry_backoff_s: float = 0.5,
|
|
170
|
+
) -> str:
|
|
139
171
|
"""Generate a response using the selected provider."""
|
|
140
172
|
if not provider:
|
|
141
173
|
raise ValueError("provider must be provided.")
|
|
@@ -147,84 +179,142 @@ class LLMClient:
|
|
|
147
179
|
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
148
180
|
)
|
|
149
181
|
|
|
150
|
-
return provider_client.generate_response(
|
|
151
|
-
api_key=api_key,
|
|
152
|
-
prompt=prompt,
|
|
153
|
-
model=model,
|
|
154
|
-
max_tokens=max_tokens,
|
|
155
|
-
reasoning_effort=reasoning_effort,
|
|
156
|
-
images=images,
|
|
157
|
-
messages=messages,
|
|
158
|
-
request_id=request_id,
|
|
159
|
-
timeout_s=timeout_s,
|
|
160
|
-
max_retries=max_retries,
|
|
161
|
-
retry_backoff_s=retry_backoff_s,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
self,
|
|
166
|
-
*,
|
|
167
|
-
provider: str,
|
|
168
|
-
api_key: str,
|
|
169
|
-
prompt: Optional[str] = None,
|
|
170
|
-
model: str,
|
|
171
|
-
max_tokens: int = 32000,
|
|
172
|
-
reasoning_effort: Optional[str] = None,
|
|
173
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
174
|
-
messages: Optional[MessageSequence] = None,
|
|
175
|
-
request_id: Optional[str] = None,
|
|
176
|
-
timeout_s: Optional[float] = None,
|
|
177
|
-
max_retries: Optional[int] = None,
|
|
178
|
-
retry_backoff_s: float = 0.5,
|
|
179
|
-
) ->
|
|
180
|
-
"""Generate a response using the selected provider (
|
|
181
|
-
if not provider:
|
|
182
|
-
raise ValueError("provider must be provided.")
|
|
183
|
-
|
|
184
|
-
provider_client = self._providers.get(provider.lower())
|
|
185
|
-
if provider_client is None:
|
|
186
|
-
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
187
|
-
raise ValueError(
|
|
188
|
-
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if
|
|
193
|
-
return
|
|
194
|
-
api_key=api_key,
|
|
195
|
-
prompt=prompt,
|
|
196
|
-
model=model,
|
|
197
|
-
max_tokens=max_tokens,
|
|
198
|
-
reasoning_effort=reasoning_effort,
|
|
199
|
-
images=images,
|
|
200
|
-
messages=messages,
|
|
201
|
-
request_id=request_id,
|
|
202
|
-
timeout_s=timeout_s,
|
|
203
|
-
max_retries=max_retries,
|
|
204
|
-
retry_backoff_s=retry_backoff_s,
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
182
|
+
return provider_client.generate_response(
|
|
183
|
+
api_key=api_key,
|
|
184
|
+
prompt=prompt,
|
|
185
|
+
model=model,
|
|
186
|
+
max_tokens=max_tokens,
|
|
187
|
+
reasoning_effort=reasoning_effort,
|
|
188
|
+
images=images,
|
|
189
|
+
messages=messages,
|
|
190
|
+
request_id=request_id,
|
|
191
|
+
timeout_s=timeout_s,
|
|
192
|
+
max_retries=max_retries,
|
|
193
|
+
retry_backoff_s=retry_backoff_s,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def generate_response_with_usage(
|
|
197
|
+
self,
|
|
198
|
+
*,
|
|
199
|
+
provider: str,
|
|
200
|
+
api_key: str,
|
|
201
|
+
prompt: Optional[str] = None,
|
|
202
|
+
model: str,
|
|
203
|
+
max_tokens: int = 32000,
|
|
204
|
+
reasoning_effort: Optional[str] = None,
|
|
205
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
206
|
+
messages: Optional[MessageSequence] = None,
|
|
207
|
+
request_id: Optional[str] = None,
|
|
208
|
+
timeout_s: Optional[float] = None,
|
|
209
|
+
max_retries: Optional[int] = None,
|
|
210
|
+
retry_backoff_s: float = 0.5,
|
|
211
|
+
) -> LLMResponse:
|
|
212
|
+
"""Generate a response using the selected provider (with token usage when available)."""
|
|
213
|
+
if not provider:
|
|
214
|
+
raise ValueError("provider must be provided.")
|
|
215
|
+
|
|
216
|
+
provider_client = self._providers.get(provider.lower())
|
|
217
|
+
if provider_client is None:
|
|
218
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
method = getattr(provider_client, "generate_response_with_usage", None)
|
|
224
|
+
if callable(method):
|
|
225
|
+
return method(
|
|
226
|
+
api_key=api_key,
|
|
227
|
+
prompt=prompt,
|
|
228
|
+
model=model,
|
|
229
|
+
max_tokens=max_tokens,
|
|
230
|
+
reasoning_effort=reasoning_effort,
|
|
231
|
+
images=images,
|
|
232
|
+
messages=messages,
|
|
233
|
+
request_id=request_id,
|
|
234
|
+
timeout_s=timeout_s,
|
|
235
|
+
max_retries=max_retries,
|
|
236
|
+
retry_backoff_s=retry_backoff_s,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
text = provider_client.generate_response(
|
|
240
|
+
api_key=api_key,
|
|
241
|
+
prompt=prompt,
|
|
242
|
+
model=model,
|
|
243
|
+
max_tokens=max_tokens,
|
|
244
|
+
reasoning_effort=reasoning_effort,
|
|
245
|
+
images=images,
|
|
246
|
+
messages=messages,
|
|
247
|
+
request_id=request_id,
|
|
248
|
+
timeout_s=timeout_s,
|
|
249
|
+
max_retries=max_retries,
|
|
250
|
+
retry_backoff_s=retry_backoff_s,
|
|
251
|
+
)
|
|
252
|
+
return LLMResponse(text=text, usage=None, provider=provider, model=model)
|
|
253
|
+
|
|
254
|
+
async def async_generate_response(
|
|
255
|
+
self,
|
|
256
|
+
*,
|
|
257
|
+
provider: str,
|
|
258
|
+
api_key: str,
|
|
259
|
+
prompt: Optional[str] = None,
|
|
260
|
+
model: str,
|
|
261
|
+
max_tokens: int = 32000,
|
|
262
|
+
reasoning_effort: Optional[str] = None,
|
|
263
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
264
|
+
messages: Optional[MessageSequence] = None,
|
|
265
|
+
request_id: Optional[str] = None,
|
|
266
|
+
timeout_s: Optional[float] = None,
|
|
267
|
+
max_retries: Optional[int] = None,
|
|
268
|
+
retry_backoff_s: float = 0.5,
|
|
269
|
+
) -> str:
|
|
270
|
+
"""Generate a response using the selected provider (async)."""
|
|
271
|
+
if not provider:
|
|
272
|
+
raise ValueError("provider must be provided.")
|
|
273
|
+
|
|
274
|
+
provider_client = self._providers.get(provider.lower())
|
|
275
|
+
if provider_client is None:
|
|
276
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
277
|
+
raise ValueError(
|
|
278
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
async_method = getattr(provider_client, "async_generate_response", None)
|
|
282
|
+
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
283
|
+
return await async_method(
|
|
284
|
+
api_key=api_key,
|
|
285
|
+
prompt=prompt,
|
|
286
|
+
model=model,
|
|
287
|
+
max_tokens=max_tokens,
|
|
288
|
+
reasoning_effort=reasoning_effort,
|
|
289
|
+
images=images,
|
|
290
|
+
messages=messages,
|
|
291
|
+
request_id=request_id,
|
|
292
|
+
timeout_s=timeout_s,
|
|
293
|
+
max_retries=max_retries,
|
|
294
|
+
retry_backoff_s=retry_backoff_s,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
return await run_sync_in_thread(
|
|
298
|
+
lambda: provider_client.generate_response(
|
|
299
|
+
api_key=api_key,
|
|
300
|
+
prompt=prompt,
|
|
301
|
+
model=model,
|
|
302
|
+
max_tokens=max_tokens,
|
|
303
|
+
reasoning_effort=reasoning_effort,
|
|
304
|
+
images=images,
|
|
305
|
+
messages=messages,
|
|
306
|
+
request_id=request_id,
|
|
307
|
+
timeout_s=timeout_s,
|
|
308
|
+
max_retries=max_retries,
|
|
309
|
+
retry_backoff_s=retry_backoff_s,
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def generate_image(
|
|
314
|
+
self,
|
|
315
|
+
*,
|
|
316
|
+
provider: str,
|
|
317
|
+
api_key: str,
|
|
228
318
|
prompt: str,
|
|
229
319
|
model: str,
|
|
230
320
|
image_size: Optional[str] = None,
|
|
@@ -242,69 +332,69 @@ class LLMClient:
|
|
|
242
332
|
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
243
333
|
)
|
|
244
334
|
|
|
245
|
-
return provider_client.generate_image(
|
|
246
|
-
api_key=api_key,
|
|
247
|
-
prompt=prompt,
|
|
248
|
-
model=model,
|
|
249
|
-
image_size=image_size,
|
|
250
|
-
aspect_ratio=aspect_ratio,
|
|
251
|
-
image=image,
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
async def async_generate_image(
|
|
255
|
-
self,
|
|
256
|
-
*,
|
|
257
|
-
provider: str,
|
|
258
|
-
api_key: str,
|
|
259
|
-
prompt: str,
|
|
260
|
-
model: str,
|
|
261
|
-
image_size: Optional[str] = None,
|
|
262
|
-
aspect_ratio: Optional[str] = None,
|
|
263
|
-
image: Optional[ImageInput] = None,
|
|
264
|
-
) -> bytes:
|
|
265
|
-
"""Generate an image using the selected provider (async)."""
|
|
266
|
-
if not provider:
|
|
267
|
-
raise ValueError("provider must be provided.")
|
|
268
|
-
|
|
269
|
-
provider_client = self._providers.get(provider.lower())
|
|
270
|
-
if provider_client is None:
|
|
271
|
-
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
272
|
-
raise ValueError(
|
|
273
|
-
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
async_method = getattr(provider_client, "async_generate_image", None)
|
|
277
|
-
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
278
|
-
return await async_method(
|
|
279
|
-
api_key=api_key,
|
|
280
|
-
prompt=prompt,
|
|
281
|
-
model=model,
|
|
282
|
-
image_size=image_size,
|
|
283
|
-
aspect_ratio=aspect_ratio,
|
|
284
|
-
image=image,
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
return await run_sync_in_thread(
|
|
288
|
-
lambda: provider_client.generate_image(
|
|
289
|
-
api_key=api_key,
|
|
290
|
-
prompt=prompt,
|
|
291
|
-
model=model,
|
|
292
|
-
image_size=image_size,
|
|
293
|
-
aspect_ratio=aspect_ratio,
|
|
294
|
-
image=image,
|
|
295
|
-
)
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
def list_models(
|
|
299
|
-
self,
|
|
300
|
-
*,
|
|
301
|
-
provider: str,
|
|
302
|
-
api_key: str,
|
|
303
|
-
request_id: Optional[str] = None,
|
|
304
|
-
timeout_s: Optional[float] = None,
|
|
305
|
-
max_retries: Optional[int] = None,
|
|
306
|
-
retry_backoff_s: float = 0.5,
|
|
307
|
-
) -> Sequence[dict[str, Optional[str]]]:
|
|
335
|
+
return provider_client.generate_image(
|
|
336
|
+
api_key=api_key,
|
|
337
|
+
prompt=prompt,
|
|
338
|
+
model=model,
|
|
339
|
+
image_size=image_size,
|
|
340
|
+
aspect_ratio=aspect_ratio,
|
|
341
|
+
image=image,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
async def async_generate_image(
|
|
345
|
+
self,
|
|
346
|
+
*,
|
|
347
|
+
provider: str,
|
|
348
|
+
api_key: str,
|
|
349
|
+
prompt: str,
|
|
350
|
+
model: str,
|
|
351
|
+
image_size: Optional[str] = None,
|
|
352
|
+
aspect_ratio: Optional[str] = None,
|
|
353
|
+
image: Optional[ImageInput] = None,
|
|
354
|
+
) -> bytes:
|
|
355
|
+
"""Generate an image using the selected provider (async)."""
|
|
356
|
+
if not provider:
|
|
357
|
+
raise ValueError("provider must be provided.")
|
|
358
|
+
|
|
359
|
+
provider_client = self._providers.get(provider.lower())
|
|
360
|
+
if provider_client is None:
|
|
361
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
362
|
+
raise ValueError(
|
|
363
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
async_method = getattr(provider_client, "async_generate_image", None)
|
|
367
|
+
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
368
|
+
return await async_method(
|
|
369
|
+
api_key=api_key,
|
|
370
|
+
prompt=prompt,
|
|
371
|
+
model=model,
|
|
372
|
+
image_size=image_size,
|
|
373
|
+
aspect_ratio=aspect_ratio,
|
|
374
|
+
image=image,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
return await run_sync_in_thread(
|
|
378
|
+
lambda: provider_client.generate_image(
|
|
379
|
+
api_key=api_key,
|
|
380
|
+
prompt=prompt,
|
|
381
|
+
model=model,
|
|
382
|
+
image_size=image_size,
|
|
383
|
+
aspect_ratio=aspect_ratio,
|
|
384
|
+
image=image,
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
def list_models(
|
|
389
|
+
self,
|
|
390
|
+
*,
|
|
391
|
+
provider: str,
|
|
392
|
+
api_key: str,
|
|
393
|
+
request_id: Optional[str] = None,
|
|
394
|
+
timeout_s: Optional[float] = None,
|
|
395
|
+
max_retries: Optional[int] = None,
|
|
396
|
+
retry_backoff_s: float = 0.5,
|
|
397
|
+
) -> Sequence[dict[str, Optional[str]]]:
|
|
308
398
|
"""List models available for the specified provider."""
|
|
309
399
|
if not provider:
|
|
310
400
|
raise ValueError("provider must be provided.")
|
|
@@ -316,57 +406,232 @@ class LLMClient:
|
|
|
316
406
|
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
317
407
|
)
|
|
318
408
|
|
|
319
|
-
return provider_client.list_models(
|
|
320
|
-
api_key=api_key,
|
|
321
|
-
request_id=request_id,
|
|
322
|
-
timeout_s=timeout_s,
|
|
323
|
-
max_retries=max_retries,
|
|
324
|
-
retry_backoff_s=retry_backoff_s,
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
async def async_list_models(
|
|
328
|
-
self,
|
|
329
|
-
*,
|
|
330
|
-
provider: str,
|
|
331
|
-
api_key: str,
|
|
332
|
-
request_id: Optional[str] = None,
|
|
333
|
-
timeout_s: Optional[float] = None,
|
|
334
|
-
max_retries: Optional[int] = None,
|
|
335
|
-
retry_backoff_s: float = 0.5,
|
|
336
|
-
) -> Sequence[dict[str, Optional[str]]]:
|
|
337
|
-
"""List models available for the specified provider (async)."""
|
|
338
|
-
if not provider:
|
|
339
|
-
raise ValueError("provider must be provided.")
|
|
340
|
-
|
|
341
|
-
provider_client = self._providers.get(provider.lower())
|
|
342
|
-
if provider_client is None:
|
|
343
|
-
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
344
|
-
raise ValueError(
|
|
345
|
-
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
async_method = getattr(provider_client, "async_list_models", None)
|
|
349
|
-
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
350
|
-
return await async_method(
|
|
351
|
-
api_key=api_key,
|
|
352
|
-
request_id=request_id,
|
|
353
|
-
timeout_s=timeout_s,
|
|
354
|
-
max_retries=max_retries,
|
|
355
|
-
retry_backoff_s=retry_backoff_s,
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
return await run_sync_in_thread(
|
|
359
|
-
lambda: provider_client.list_models(
|
|
360
|
-
api_key=api_key,
|
|
361
|
-
request_id=request_id,
|
|
362
|
-
timeout_s=timeout_s,
|
|
363
|
-
max_retries=max_retries,
|
|
364
|
-
retry_backoff_s=retry_backoff_s,
|
|
365
|
-
)
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
409
|
+
return provider_client.list_models(
|
|
410
|
+
api_key=api_key,
|
|
411
|
+
request_id=request_id,
|
|
412
|
+
timeout_s=timeout_s,
|
|
413
|
+
max_retries=max_retries,
|
|
414
|
+
retry_backoff_s=retry_backoff_s,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
async def async_list_models(
|
|
418
|
+
self,
|
|
419
|
+
*,
|
|
420
|
+
provider: str,
|
|
421
|
+
api_key: str,
|
|
422
|
+
request_id: Optional[str] = None,
|
|
423
|
+
timeout_s: Optional[float] = None,
|
|
424
|
+
max_retries: Optional[int] = None,
|
|
425
|
+
retry_backoff_s: float = 0.5,
|
|
426
|
+
) -> Sequence[dict[str, Optional[str]]]:
|
|
427
|
+
"""List models available for the specified provider (async)."""
|
|
428
|
+
if not provider:
|
|
429
|
+
raise ValueError("provider must be provided.")
|
|
430
|
+
|
|
431
|
+
provider_client = self._providers.get(provider.lower())
|
|
432
|
+
if provider_client is None:
|
|
433
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
434
|
+
raise ValueError(
|
|
435
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
async_method = getattr(provider_client, "async_list_models", None)
|
|
439
|
+
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
440
|
+
return await async_method(
|
|
441
|
+
api_key=api_key,
|
|
442
|
+
request_id=request_id,
|
|
443
|
+
timeout_s=timeout_s,
|
|
444
|
+
max_retries=max_retries,
|
|
445
|
+
retry_backoff_s=retry_backoff_s,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return await run_sync_in_thread(
|
|
449
|
+
lambda: provider_client.list_models(
|
|
450
|
+
api_key=api_key,
|
|
451
|
+
request_id=request_id,
|
|
452
|
+
timeout_s=timeout_s,
|
|
453
|
+
max_retries=max_retries,
|
|
454
|
+
retry_backoff_s=retry_backoff_s,
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
async def async_generate_response_with_usage(
|
|
459
|
+
self,
|
|
460
|
+
*,
|
|
461
|
+
provider: str,
|
|
462
|
+
api_key: str,
|
|
463
|
+
prompt: Optional[str] = None,
|
|
464
|
+
model: str,
|
|
465
|
+
max_tokens: int = 32000,
|
|
466
|
+
reasoning_effort: Optional[str] = None,
|
|
467
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
468
|
+
messages: Optional[MessageSequence] = None,
|
|
469
|
+
request_id: Optional[str] = None,
|
|
470
|
+
timeout_s: Optional[float] = None,
|
|
471
|
+
max_retries: Optional[int] = None,
|
|
472
|
+
retry_backoff_s: float = 0.5,
|
|
473
|
+
) -> LLMResponse:
|
|
474
|
+
"""Generate a response using the selected provider (async, with token usage when available)."""
|
|
475
|
+
if not provider:
|
|
476
|
+
raise ValueError("provider must be provided.")
|
|
477
|
+
|
|
478
|
+
provider_client = self._providers.get(provider.lower())
|
|
479
|
+
if provider_client is None:
|
|
480
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
481
|
+
raise ValueError(
|
|
482
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
async_method = getattr(provider_client, "async_generate_response_with_usage", None)
|
|
486
|
+
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
487
|
+
return await async_method(
|
|
488
|
+
api_key=api_key,
|
|
489
|
+
prompt=prompt,
|
|
490
|
+
model=model,
|
|
491
|
+
max_tokens=max_tokens,
|
|
492
|
+
reasoning_effort=reasoning_effort,
|
|
493
|
+
images=images,
|
|
494
|
+
messages=messages,
|
|
495
|
+
request_id=request_id,
|
|
496
|
+
timeout_s=timeout_s,
|
|
497
|
+
max_retries=max_retries,
|
|
498
|
+
retry_backoff_s=retry_backoff_s,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
method = getattr(provider_client, "generate_response_with_usage", None)
|
|
502
|
+
if callable(method):
|
|
503
|
+
return await run_sync_in_thread(
|
|
504
|
+
lambda: method(
|
|
505
|
+
api_key=api_key,
|
|
506
|
+
prompt=prompt,
|
|
507
|
+
model=model,
|
|
508
|
+
max_tokens=max_tokens,
|
|
509
|
+
reasoning_effort=reasoning_effort,
|
|
510
|
+
images=images,
|
|
511
|
+
messages=messages,
|
|
512
|
+
request_id=request_id,
|
|
513
|
+
timeout_s=timeout_s,
|
|
514
|
+
max_retries=max_retries,
|
|
515
|
+
retry_backoff_s=retry_backoff_s,
|
|
516
|
+
)
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
text = await self.async_generate_response(
|
|
520
|
+
provider=provider,
|
|
521
|
+
api_key=api_key,
|
|
522
|
+
prompt=prompt,
|
|
523
|
+
model=model,
|
|
524
|
+
max_tokens=max_tokens,
|
|
525
|
+
reasoning_effort=reasoning_effort,
|
|
526
|
+
images=images,
|
|
527
|
+
messages=messages,
|
|
528
|
+
request_id=request_id,
|
|
529
|
+
timeout_s=timeout_s,
|
|
530
|
+
max_retries=max_retries,
|
|
531
|
+
retry_backoff_s=retry_backoff_s,
|
|
532
|
+
)
|
|
533
|
+
return LLMResponse(text=text, usage=None, provider=provider, model=model)
|
|
534
|
+
|
|
535
|
+
def embed_content(
|
|
536
|
+
self,
|
|
537
|
+
*,
|
|
538
|
+
provider: str,
|
|
539
|
+
api_key: str,
|
|
540
|
+
model: str,
|
|
541
|
+
contents: Union[str, Sequence[str]],
|
|
542
|
+
task_type: Optional[str] = None,
|
|
543
|
+
output_dimensionality: Optional[int] = None,
|
|
544
|
+
request_id: Optional[str] = None,
|
|
545
|
+
timeout_s: Optional[float] = None,
|
|
546
|
+
max_retries: Optional[int] = None,
|
|
547
|
+
retry_backoff_s: float = 0.5,
|
|
548
|
+
) -> list[EmbeddingVector]:
|
|
549
|
+
"""Generate embeddings using the selected provider."""
|
|
550
|
+
if not provider:
|
|
551
|
+
raise ValueError("provider must be provided.")
|
|
552
|
+
|
|
553
|
+
provider_client = self._providers.get(provider.lower())
|
|
554
|
+
if provider_client is None:
|
|
555
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
556
|
+
raise ValueError(
|
|
557
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
embed_method = getattr(provider_client, "embed_content", None)
|
|
561
|
+
if not callable(embed_method):
|
|
562
|
+
raise ValueError(f"Provider '{provider}' does not support embeddings.")
|
|
563
|
+
|
|
564
|
+
return embed_method(
|
|
565
|
+
api_key=api_key,
|
|
566
|
+
model=model,
|
|
567
|
+
contents=contents,
|
|
568
|
+
task_type=task_type,
|
|
569
|
+
output_dimensionality=output_dimensionality,
|
|
570
|
+
request_id=request_id,
|
|
571
|
+
timeout_s=timeout_s,
|
|
572
|
+
max_retries=max_retries,
|
|
573
|
+
retry_backoff_s=retry_backoff_s,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
async def async_embed_content(
|
|
577
|
+
self,
|
|
578
|
+
*,
|
|
579
|
+
provider: str,
|
|
580
|
+
api_key: str,
|
|
581
|
+
model: str,
|
|
582
|
+
contents: Union[str, Sequence[str]],
|
|
583
|
+
task_type: Optional[str] = None,
|
|
584
|
+
output_dimensionality: Optional[int] = None,
|
|
585
|
+
request_id: Optional[str] = None,
|
|
586
|
+
timeout_s: Optional[float] = None,
|
|
587
|
+
max_retries: Optional[int] = None,
|
|
588
|
+
retry_backoff_s: float = 0.5,
|
|
589
|
+
) -> list[EmbeddingVector]:
|
|
590
|
+
"""Generate embeddings using the selected provider (async)."""
|
|
591
|
+
if not provider:
|
|
592
|
+
raise ValueError("provider must be provided.")
|
|
593
|
+
|
|
594
|
+
provider_client = self._providers.get(provider.lower())
|
|
595
|
+
if provider_client is None:
|
|
596
|
+
available = ", ".join(sorted(self._providers)) or "<none>"
|
|
597
|
+
raise ValueError(
|
|
598
|
+
f"Unknown provider '{provider}'. Available providers: {available}."
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
async_method = getattr(provider_client, "async_embed_content", None)
|
|
602
|
+
if async_method is not None and asyncio.iscoroutinefunction(async_method):
|
|
603
|
+
return await async_method(
|
|
604
|
+
api_key=api_key,
|
|
605
|
+
model=model,
|
|
606
|
+
contents=contents,
|
|
607
|
+
task_type=task_type,
|
|
608
|
+
output_dimensionality=output_dimensionality,
|
|
609
|
+
request_id=request_id,
|
|
610
|
+
timeout_s=timeout_s,
|
|
611
|
+
max_retries=max_retries,
|
|
612
|
+
retry_backoff_s=retry_backoff_s,
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
embed_method = getattr(provider_client, "embed_content", None)
|
|
616
|
+
if not callable(embed_method):
|
|
617
|
+
raise ValueError(f"Provider '{provider}' does not support embeddings.")
|
|
618
|
+
|
|
619
|
+
return await run_sync_in_thread(
|
|
620
|
+
lambda: embed_method(
|
|
621
|
+
api_key=api_key,
|
|
622
|
+
model=model,
|
|
623
|
+
contents=contents,
|
|
624
|
+
task_type=task_type,
|
|
625
|
+
output_dimensionality=output_dimensionality,
|
|
626
|
+
request_id=request_id,
|
|
627
|
+
timeout_s=timeout_s,
|
|
628
|
+
max_retries=max_retries,
|
|
629
|
+
retry_backoff_s=retry_backoff_s,
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
@staticmethod
|
|
634
|
+
def _discover_default_providers() -> Dict[str, SupportsGenerateResponse]:
|
|
370
635
|
providers: Dict[str, SupportsGenerateResponse] = {}
|
|
371
636
|
try:
|
|
372
637
|
from .openai_client import OpenAIResponsesClient # type: ignore
|