ccs-llmconnector 1.1.1__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.1.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.1.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 +390 -57
- 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.1.dist-info/RECORD +0 -16
- {ccs_llmconnector-1.1.1.dist-info → ccs_llmconnector-1.1.4.dist-info}/entry_points.txt +0 -0
- {ccs_llmconnector-1.1.1.dist-info → ccs_llmconnector-1.1.4.dist-info}/licenses/LICENSE +0 -0
- {ccs_llmconnector-1.1.1.dist-info → ccs_llmconnector-1.1.4.dist-info}/top_level.txt +0 -0
llmconnector/grok_client.py
CHANGED
|
@@ -2,54 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import base64
|
|
6
|
-
import mimetypes
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
import logging
|
|
9
|
-
from typing import Optional, Sequence
|
|
10
|
-
|
|
11
|
-
from xai_sdk import Client
|
|
12
|
-
from xai_sdk.chat import image as chat_image
|
|
13
|
-
from xai_sdk.chat import user
|
|
14
|
-
|
|
15
|
-
from .types import ImageInput, MessageSequence, normalize_messages
|
|
16
|
-
from .utils import clamp_retries, run_sync_in_thread, run_with_retries
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
5
|
+
import base64
|
|
6
|
+
import mimetypes
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Optional, Sequence
|
|
10
|
+
|
|
11
|
+
from xai_sdk import Client
|
|
12
|
+
from xai_sdk.chat import image as chat_image
|
|
13
|
+
from xai_sdk.chat import user
|
|
14
|
+
|
|
15
|
+
from .types import ImageInput, MessageSequence, normalize_messages
|
|
16
|
+
from .utils import clamp_retries, run_sync_in_thread, run_with_retries
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class GrokClient:
|
|
22
22
|
"""Convenience wrapper around the xAI Grok chat API."""
|
|
23
23
|
|
|
24
|
-
def generate_response(
|
|
25
|
-
self,
|
|
26
|
-
*,
|
|
27
|
-
api_key: str,
|
|
28
|
-
prompt: Optional[str] = None,
|
|
29
|
-
model: str,
|
|
30
|
-
max_tokens: int = 32000,
|
|
31
|
-
reasoning_effort: Optional[str] = None,
|
|
32
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
33
|
-
messages: Optional[MessageSequence] = None,
|
|
34
|
-
request_id: Optional[str] = None,
|
|
35
|
-
timeout_s: Optional[float] = None,
|
|
36
|
-
max_retries: Optional[int] = None,
|
|
37
|
-
retry_backoff_s: float = 0.5,
|
|
38
|
-
) -> str:
|
|
24
|
+
def generate_response(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
api_key: str,
|
|
28
|
+
prompt: Optional[str] = None,
|
|
29
|
+
model: str,
|
|
30
|
+
max_tokens: int = 32000,
|
|
31
|
+
reasoning_effort: Optional[str] = None,
|
|
32
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
33
|
+
messages: Optional[MessageSequence] = None,
|
|
34
|
+
request_id: Optional[str] = None,
|
|
35
|
+
timeout_s: Optional[float] = None,
|
|
36
|
+
max_retries: Optional[int] = None,
|
|
37
|
+
retry_backoff_s: float = 0.5,
|
|
38
|
+
) -> str:
|
|
39
39
|
"""Generate a response from the specified Grok model.
|
|
40
40
|
|
|
41
41
|
Args:
|
|
42
42
|
api_key: API key used to authenticate with xAI.
|
|
43
|
-
prompt: Natural-language instruction or query for the model.
|
|
44
|
-
model: Identifier of the Grok model to target (for example, ``"grok-3"``).
|
|
45
|
-
max_tokens: Cap for tokens in the generated response, defaults to 32000.
|
|
46
|
-
reasoning_effort: Optional hint for reasoning-focused models (``"low"`` or ``"high"``).
|
|
47
|
-
images: Optional collection of image references (local paths, URLs, or data URLs).
|
|
48
|
-
messages: Optional list of chat-style messages (role/content).
|
|
49
|
-
request_id: Optional request identifier for tracing/logging.
|
|
50
|
-
timeout_s: Optional request timeout in seconds (best-effort).
|
|
51
|
-
max_retries: Optional retry count for transient failures.
|
|
52
|
-
retry_backoff_s: Base delay (seconds) for exponential backoff between retries.
|
|
43
|
+
prompt: Natural-language instruction or query for the model.
|
|
44
|
+
model: Identifier of the Grok model to target (for example, ``"grok-3"``).
|
|
45
|
+
max_tokens: Cap for tokens in the generated response, defaults to 32000.
|
|
46
|
+
reasoning_effort: Optional hint for reasoning-focused models (``"low"`` or ``"high"``).
|
|
47
|
+
images: Optional collection of image references (local paths, URLs, or data URLs).
|
|
48
|
+
messages: Optional list of chat-style messages (role/content).
|
|
49
|
+
request_id: Optional request identifier for tracing/logging.
|
|
50
|
+
timeout_s: Optional request timeout in seconds (best-effort).
|
|
51
|
+
max_retries: Optional retry count for transient failures.
|
|
52
|
+
retry_backoff_s: Base delay (seconds) for exponential backoff between retries.
|
|
53
53
|
|
|
54
54
|
Returns:
|
|
55
55
|
The text output produced by the model.
|
|
@@ -57,243 +57,243 @@ class GrokClient:
|
|
|
57
57
|
Raises:
|
|
58
58
|
ValueError: If required arguments are missing or the request payload is empty.
|
|
59
59
|
RuntimeError: If the Grok response does not contain any textual content.
|
|
60
|
-
"""
|
|
61
|
-
if not api_key:
|
|
62
|
-
raise ValueError("api_key must be provided.")
|
|
63
|
-
if not prompt and not messages and not images:
|
|
64
|
-
raise ValueError("At least one of prompt, messages, or images must be provided.")
|
|
65
|
-
if not model:
|
|
66
|
-
raise ValueError("model must be provided.")
|
|
67
|
-
|
|
68
|
-
normalized_messages = normalize_messages(prompt=prompt, messages=messages)
|
|
69
|
-
message_parts: list[object] = []
|
|
70
|
-
if normalized_messages:
|
|
71
|
-
for message in normalized_messages:
|
|
72
|
-
if message["content"]:
|
|
73
|
-
message_parts.append(f"{message['role']}: {message['content']}")
|
|
74
|
-
|
|
75
|
-
if images:
|
|
76
|
-
for image in images:
|
|
77
|
-
message_parts.append(chat_image(self._to_image_url(image)))
|
|
78
|
-
|
|
79
|
-
if not message_parts:
|
|
80
|
-
raise ValueError("No content provided for response generation.")
|
|
81
|
-
|
|
82
|
-
retry_count = clamp_retries(max_retries)
|
|
83
|
-
|
|
84
|
-
def _run_request() -> str:
|
|
85
|
-
grok_client = Client(api_key=api_key)
|
|
86
|
-
|
|
87
|
-
create_kwargs = {
|
|
88
|
-
"model": model,
|
|
89
|
-
"max_tokens": max_tokens,
|
|
90
|
-
"messages": [user(*message_parts)],
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
normalized_effort = (reasoning_effort or "").strip().lower()
|
|
94
|
-
if normalized_effort in {"low", "high"}:
|
|
95
|
-
create_kwargs["reasoning_effort"] = normalized_effort
|
|
96
|
-
|
|
97
|
-
if timeout_s is not None:
|
|
98
|
-
create_kwargs["timeout"] = timeout_s
|
|
99
|
-
|
|
100
|
-
try:
|
|
101
|
-
chat = grok_client.chat.create(**create_kwargs)
|
|
102
|
-
response = chat.sample()
|
|
103
|
-
except Exception as exc:
|
|
104
|
-
logger.exception(
|
|
105
|
-
"xAI Grok chat request failed: %s request_id=%s",
|
|
106
|
-
exc,
|
|
107
|
-
request_id,
|
|
108
|
-
)
|
|
109
|
-
raise
|
|
110
|
-
|
|
111
|
-
content = getattr(response, "content", None)
|
|
112
|
-
if content:
|
|
113
|
-
logger.info(
|
|
114
|
-
"xAI chat succeeded: model=%s images=%d text_len=%d request_id=%s",
|
|
115
|
-
model,
|
|
116
|
-
len(images or []),
|
|
117
|
-
len(content or ""),
|
|
118
|
-
request_id,
|
|
119
|
-
)
|
|
120
|
-
return content
|
|
121
|
-
|
|
122
|
-
reasoning_content = getattr(response, "reasoning_content", None)
|
|
123
|
-
if reasoning_content:
|
|
124
|
-
logger.info(
|
|
125
|
-
"xAI chat succeeded (reasoning): model=%s images=%d text_len=%d request_id=%s",
|
|
126
|
-
model,
|
|
127
|
-
len(images or []),
|
|
128
|
-
len(reasoning_content or ""),
|
|
129
|
-
request_id,
|
|
130
|
-
)
|
|
131
|
-
return reasoning_content
|
|
132
|
-
|
|
133
|
-
# Treat successful calls without textual content as a successful, empty response
|
|
134
|
-
# rather than raising. This aligns with callers that handle empty outputs gracefully.
|
|
135
|
-
logger.info(
|
|
136
|
-
"xAI chat succeeded with no text: model=%s images=%d request_id=%s",
|
|
137
|
-
model,
|
|
138
|
-
len(images or []),
|
|
139
|
-
request_id,
|
|
140
|
-
)
|
|
141
|
-
return ""
|
|
142
|
-
|
|
143
|
-
return run_with_retries(
|
|
144
|
-
func=_run_request,
|
|
145
|
-
max_retries=retry_count,
|
|
146
|
-
retry_backoff_s=retry_backoff_s,
|
|
147
|
-
request_id=request_id,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
async def async_generate_response(
|
|
151
|
-
self,
|
|
152
|
-
*,
|
|
153
|
-
api_key: str,
|
|
154
|
-
prompt: Optional[str] = None,
|
|
155
|
-
model: str,
|
|
156
|
-
max_tokens: int = 32000,
|
|
157
|
-
reasoning_effort: Optional[str] = None,
|
|
158
|
-
images: Optional[Sequence[ImageInput]] = None,
|
|
159
|
-
messages: Optional[MessageSequence] = None,
|
|
160
|
-
request_id: Optional[str] = None,
|
|
161
|
-
timeout_s: Optional[float] = None,
|
|
162
|
-
max_retries: Optional[int] = None,
|
|
163
|
-
retry_backoff_s: float = 0.5,
|
|
164
|
-
) -> str:
|
|
165
|
-
return await run_sync_in_thread(
|
|
166
|
-
lambda: self.generate_response(
|
|
167
|
-
api_key=api_key,
|
|
168
|
-
prompt=prompt,
|
|
169
|
-
model=model,
|
|
170
|
-
max_tokens=max_tokens,
|
|
171
|
-
reasoning_effort=reasoning_effort,
|
|
172
|
-
images=images,
|
|
173
|
-
messages=messages,
|
|
174
|
-
request_id=request_id,
|
|
175
|
-
timeout_s=timeout_s,
|
|
176
|
-
max_retries=max_retries,
|
|
177
|
-
retry_backoff_s=retry_backoff_s,
|
|
178
|
-
)
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
def generate_image(
|
|
182
|
-
self,
|
|
183
|
-
*,
|
|
184
|
-
api_key: str,
|
|
185
|
-
prompt: str,
|
|
60
|
+
"""
|
|
61
|
+
if not api_key:
|
|
62
|
+
raise ValueError("api_key must be provided.")
|
|
63
|
+
if not prompt and not messages and not images:
|
|
64
|
+
raise ValueError("At least one of prompt, messages, or images must be provided.")
|
|
65
|
+
if not model:
|
|
66
|
+
raise ValueError("model must be provided.")
|
|
67
|
+
|
|
68
|
+
normalized_messages = normalize_messages(prompt=prompt, messages=messages)
|
|
69
|
+
message_parts: list[object] = []
|
|
70
|
+
if normalized_messages:
|
|
71
|
+
for message in normalized_messages:
|
|
72
|
+
if message["content"]:
|
|
73
|
+
message_parts.append(f"{message['role']}: {message['content']}")
|
|
74
|
+
|
|
75
|
+
if images:
|
|
76
|
+
for image in images:
|
|
77
|
+
message_parts.append(chat_image(self._to_image_url(image)))
|
|
78
|
+
|
|
79
|
+
if not message_parts:
|
|
80
|
+
raise ValueError("No content provided for response generation.")
|
|
81
|
+
|
|
82
|
+
retry_count = clamp_retries(max_retries)
|
|
83
|
+
|
|
84
|
+
def _run_request() -> str:
|
|
85
|
+
grok_client = Client(api_key=api_key)
|
|
86
|
+
|
|
87
|
+
create_kwargs = {
|
|
88
|
+
"model": model,
|
|
89
|
+
"max_tokens": max_tokens,
|
|
90
|
+
"messages": [user(*message_parts)],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
normalized_effort = (reasoning_effort or "").strip().lower()
|
|
94
|
+
if normalized_effort in {"low", "high"}:
|
|
95
|
+
create_kwargs["reasoning_effort"] = normalized_effort
|
|
96
|
+
|
|
97
|
+
if timeout_s is not None:
|
|
98
|
+
create_kwargs["timeout"] = timeout_s
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
chat = grok_client.chat.create(**create_kwargs)
|
|
102
|
+
response = chat.sample()
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
logger.exception(
|
|
105
|
+
"xAI Grok chat request failed: %s request_id=%s",
|
|
106
|
+
exc,
|
|
107
|
+
request_id,
|
|
108
|
+
)
|
|
109
|
+
raise
|
|
110
|
+
|
|
111
|
+
content = getattr(response, "content", None)
|
|
112
|
+
if content:
|
|
113
|
+
logger.info(
|
|
114
|
+
"xAI chat succeeded: model=%s images=%d text_len=%d request_id=%s",
|
|
115
|
+
model,
|
|
116
|
+
len(images or []),
|
|
117
|
+
len(content or ""),
|
|
118
|
+
request_id,
|
|
119
|
+
)
|
|
120
|
+
return content
|
|
121
|
+
|
|
122
|
+
reasoning_content = getattr(response, "reasoning_content", None)
|
|
123
|
+
if reasoning_content:
|
|
124
|
+
logger.info(
|
|
125
|
+
"xAI chat succeeded (reasoning): model=%s images=%d text_len=%d request_id=%s",
|
|
126
|
+
model,
|
|
127
|
+
len(images or []),
|
|
128
|
+
len(reasoning_content or ""),
|
|
129
|
+
request_id,
|
|
130
|
+
)
|
|
131
|
+
return reasoning_content
|
|
132
|
+
|
|
133
|
+
# Treat successful calls without textual content as a successful, empty response
|
|
134
|
+
# rather than raising. This aligns with callers that handle empty outputs gracefully.
|
|
135
|
+
logger.info(
|
|
136
|
+
"xAI chat succeeded with no text: model=%s images=%d request_id=%s",
|
|
137
|
+
model,
|
|
138
|
+
len(images or []),
|
|
139
|
+
request_id,
|
|
140
|
+
)
|
|
141
|
+
return ""
|
|
142
|
+
|
|
143
|
+
return run_with_retries(
|
|
144
|
+
func=_run_request,
|
|
145
|
+
max_retries=retry_count,
|
|
146
|
+
retry_backoff_s=retry_backoff_s,
|
|
147
|
+
request_id=request_id,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def async_generate_response(
|
|
151
|
+
self,
|
|
152
|
+
*,
|
|
153
|
+
api_key: str,
|
|
154
|
+
prompt: Optional[str] = None,
|
|
155
|
+
model: str,
|
|
156
|
+
max_tokens: int = 32000,
|
|
157
|
+
reasoning_effort: Optional[str] = None,
|
|
158
|
+
images: Optional[Sequence[ImageInput]] = None,
|
|
159
|
+
messages: Optional[MessageSequence] = None,
|
|
160
|
+
request_id: Optional[str] = None,
|
|
161
|
+
timeout_s: Optional[float] = None,
|
|
162
|
+
max_retries: Optional[int] = None,
|
|
163
|
+
retry_backoff_s: float = 0.5,
|
|
164
|
+
) -> str:
|
|
165
|
+
return await run_sync_in_thread(
|
|
166
|
+
lambda: self.generate_response(
|
|
167
|
+
api_key=api_key,
|
|
168
|
+
prompt=prompt,
|
|
169
|
+
model=model,
|
|
170
|
+
max_tokens=max_tokens,
|
|
171
|
+
reasoning_effort=reasoning_effort,
|
|
172
|
+
images=images,
|
|
173
|
+
messages=messages,
|
|
174
|
+
request_id=request_id,
|
|
175
|
+
timeout_s=timeout_s,
|
|
176
|
+
max_retries=max_retries,
|
|
177
|
+
retry_backoff_s=retry_backoff_s,
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def generate_image(
|
|
182
|
+
self,
|
|
183
|
+
*,
|
|
184
|
+
api_key: str,
|
|
185
|
+
prompt: str,
|
|
186
186
|
model: str,
|
|
187
187
|
image_size: str = "2K",
|
|
188
188
|
image: Optional[ImageInput] = None,
|
|
189
189
|
) -> bytes:
|
|
190
190
|
"""Generate an image using the Grok API.
|
|
191
191
|
|
|
192
|
-
Raises:
|
|
193
|
-
NotImplementedError: This method is not yet implemented for Grok.
|
|
194
|
-
"""
|
|
195
|
-
raise NotImplementedError("Image generation is not implemented for Grok.")
|
|
196
|
-
|
|
197
|
-
async def async_generate_image(
|
|
198
|
-
self,
|
|
199
|
-
*,
|
|
200
|
-
api_key: str,
|
|
201
|
-
prompt: str,
|
|
202
|
-
model: str,
|
|
203
|
-
image_size: str = "2K",
|
|
204
|
-
image: Optional[ImageInput] = None,
|
|
205
|
-
) -> bytes:
|
|
206
|
-
return await run_sync_in_thread(
|
|
207
|
-
lambda: self.generate_image(
|
|
208
|
-
api_key=api_key,
|
|
209
|
-
prompt=prompt,
|
|
210
|
-
model=model,
|
|
211
|
-
image_size=image_size,
|
|
212
|
-
image=image,
|
|
213
|
-
)
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
def list_models(
|
|
217
|
-
self,
|
|
218
|
-
*,
|
|
219
|
-
api_key: str,
|
|
220
|
-
request_id: Optional[str] = None,
|
|
221
|
-
timeout_s: Optional[float] = None,
|
|
222
|
-
max_retries: Optional[int] = None,
|
|
223
|
-
retry_backoff_s: float = 0.5,
|
|
224
|
-
) -> list[dict[str, Optional[str]]]:
|
|
225
|
-
"""Return the Grok language models available to the authenticated account."""
|
|
226
|
-
if not api_key:
|
|
227
|
-
raise ValueError("api_key must be provided.")
|
|
228
|
-
|
|
229
|
-
retry_count = clamp_retries(max_retries)
|
|
230
|
-
|
|
231
|
-
def _run_request() -> list[dict[str, Optional[str]]]:
|
|
232
|
-
grok_client = Client(api_key=api_key)
|
|
233
|
-
models: list[dict[str, Optional[str]]] = []
|
|
234
|
-
|
|
235
|
-
try:
|
|
236
|
-
iterator = grok_client.models.list_language_models()
|
|
237
|
-
except Exception as exc:
|
|
238
|
-
logger.exception(
|
|
239
|
-
"xAI list language models failed: %s request_id=%s",
|
|
240
|
-
exc,
|
|
241
|
-
request_id,
|
|
242
|
-
)
|
|
243
|
-
raise
|
|
244
|
-
|
|
245
|
-
for model in iterator:
|
|
246
|
-
model_id = getattr(model, "name", None)
|
|
247
|
-
if not model_id:
|
|
248
|
-
continue
|
|
249
|
-
|
|
250
|
-
aliases = getattr(model, "aliases", None)
|
|
251
|
-
display_name: Optional[str] = None
|
|
252
|
-
if aliases:
|
|
253
|
-
try:
|
|
254
|
-
display_name = next(iter(aliases)) or None
|
|
255
|
-
except (StopIteration, TypeError):
|
|
256
|
-
display_name = None
|
|
257
|
-
|
|
258
|
-
models.append(
|
|
259
|
-
{
|
|
260
|
-
"id": model_id,
|
|
261
|
-
"display_name": display_name,
|
|
262
|
-
}
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
logger.info(
|
|
266
|
-
"xAI list_language_models succeeded: count=%d request_id=%s",
|
|
267
|
-
len(models),
|
|
268
|
-
request_id,
|
|
269
|
-
)
|
|
270
|
-
return models
|
|
271
|
-
|
|
272
|
-
return run_with_retries(
|
|
273
|
-
func=_run_request,
|
|
274
|
-
max_retries=retry_count,
|
|
275
|
-
retry_backoff_s=retry_backoff_s,
|
|
276
|
-
request_id=request_id,
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
async def async_list_models(
|
|
280
|
-
self,
|
|
281
|
-
*,
|
|
282
|
-
api_key: str,
|
|
283
|
-
request_id: Optional[str] = None,
|
|
284
|
-
timeout_s: Optional[float] = None,
|
|
285
|
-
max_retries: Optional[int] = None,
|
|
286
|
-
retry_backoff_s: float = 0.5,
|
|
287
|
-
) -> list[dict[str, Optional[str]]]:
|
|
288
|
-
return await run_sync_in_thread(
|
|
289
|
-
lambda: self.list_models(
|
|
290
|
-
api_key=api_key,
|
|
291
|
-
request_id=request_id,
|
|
292
|
-
timeout_s=timeout_s,
|
|
293
|
-
max_retries=max_retries,
|
|
294
|
-
retry_backoff_s=retry_backoff_s,
|
|
295
|
-
)
|
|
296
|
-
)
|
|
192
|
+
Raises:
|
|
193
|
+
NotImplementedError: This method is not yet implemented for Grok.
|
|
194
|
+
"""
|
|
195
|
+
raise NotImplementedError("Image generation is not implemented for Grok.")
|
|
196
|
+
|
|
197
|
+
async def async_generate_image(
|
|
198
|
+
self,
|
|
199
|
+
*,
|
|
200
|
+
api_key: str,
|
|
201
|
+
prompt: str,
|
|
202
|
+
model: str,
|
|
203
|
+
image_size: str = "2K",
|
|
204
|
+
image: Optional[ImageInput] = None,
|
|
205
|
+
) -> bytes:
|
|
206
|
+
return await run_sync_in_thread(
|
|
207
|
+
lambda: self.generate_image(
|
|
208
|
+
api_key=api_key,
|
|
209
|
+
prompt=prompt,
|
|
210
|
+
model=model,
|
|
211
|
+
image_size=image_size,
|
|
212
|
+
image=image,
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def list_models(
|
|
217
|
+
self,
|
|
218
|
+
*,
|
|
219
|
+
api_key: str,
|
|
220
|
+
request_id: Optional[str] = None,
|
|
221
|
+
timeout_s: Optional[float] = None,
|
|
222
|
+
max_retries: Optional[int] = None,
|
|
223
|
+
retry_backoff_s: float = 0.5,
|
|
224
|
+
) -> list[dict[str, Optional[str]]]:
|
|
225
|
+
"""Return the Grok language models available to the authenticated account."""
|
|
226
|
+
if not api_key:
|
|
227
|
+
raise ValueError("api_key must be provided.")
|
|
228
|
+
|
|
229
|
+
retry_count = clamp_retries(max_retries)
|
|
230
|
+
|
|
231
|
+
def _run_request() -> list[dict[str, Optional[str]]]:
|
|
232
|
+
grok_client = Client(api_key=api_key)
|
|
233
|
+
models: list[dict[str, Optional[str]]] = []
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
iterator = grok_client.models.list_language_models()
|
|
237
|
+
except Exception as exc:
|
|
238
|
+
logger.exception(
|
|
239
|
+
"xAI list language models failed: %s request_id=%s",
|
|
240
|
+
exc,
|
|
241
|
+
request_id,
|
|
242
|
+
)
|
|
243
|
+
raise
|
|
244
|
+
|
|
245
|
+
for model in iterator:
|
|
246
|
+
model_id = getattr(model, "name", None)
|
|
247
|
+
if not model_id:
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
aliases = getattr(model, "aliases", None)
|
|
251
|
+
display_name: Optional[str] = None
|
|
252
|
+
if aliases:
|
|
253
|
+
try:
|
|
254
|
+
display_name = next(iter(aliases)) or None
|
|
255
|
+
except (StopIteration, TypeError):
|
|
256
|
+
display_name = None
|
|
257
|
+
|
|
258
|
+
models.append(
|
|
259
|
+
{
|
|
260
|
+
"id": model_id,
|
|
261
|
+
"display_name": display_name,
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
logger.info(
|
|
266
|
+
"xAI list_language_models succeeded: count=%d request_id=%s",
|
|
267
|
+
len(models),
|
|
268
|
+
request_id,
|
|
269
|
+
)
|
|
270
|
+
return models
|
|
271
|
+
|
|
272
|
+
return run_with_retries(
|
|
273
|
+
func=_run_request,
|
|
274
|
+
max_retries=retry_count,
|
|
275
|
+
retry_backoff_s=retry_backoff_s,
|
|
276
|
+
request_id=request_id,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
async def async_list_models(
|
|
280
|
+
self,
|
|
281
|
+
*,
|
|
282
|
+
api_key: str,
|
|
283
|
+
request_id: Optional[str] = None,
|
|
284
|
+
timeout_s: Optional[float] = None,
|
|
285
|
+
max_retries: Optional[int] = None,
|
|
286
|
+
retry_backoff_s: float = 0.5,
|
|
287
|
+
) -> list[dict[str, Optional[str]]]:
|
|
288
|
+
return await run_sync_in_thread(
|
|
289
|
+
lambda: self.list_models(
|
|
290
|
+
api_key=api_key,
|
|
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
297
|
|
|
298
298
|
@staticmethod
|
|
299
299
|
def _to_image_url(image: ImageInput) -> str:
|