lm-deluge 0.0.56__py3-none-any.whl → 0.0.69__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.
- lm_deluge/__init__.py +12 -1
- lm_deluge/api_requests/anthropic.py +12 -1
- lm_deluge/api_requests/base.py +87 -5
- lm_deluge/api_requests/bedrock.py +3 -4
- lm_deluge/api_requests/chat_reasoning.py +4 -0
- lm_deluge/api_requests/gemini.py +7 -6
- lm_deluge/api_requests/mistral.py +8 -9
- lm_deluge/api_requests/openai.py +179 -124
- lm_deluge/batches.py +25 -9
- lm_deluge/client.py +280 -67
- lm_deluge/config.py +1 -1
- lm_deluge/file.py +382 -13
- lm_deluge/mock_openai.py +482 -0
- lm_deluge/models/__init__.py +12 -8
- lm_deluge/models/anthropic.py +12 -20
- lm_deluge/models/bedrock.py +0 -14
- lm_deluge/models/cohere.py +0 -16
- lm_deluge/models/google.py +0 -20
- lm_deluge/models/grok.py +48 -4
- lm_deluge/models/groq.py +2 -2
- lm_deluge/models/kimi.py +34 -0
- lm_deluge/models/meta.py +0 -8
- lm_deluge/models/minimax.py +10 -0
- lm_deluge/models/openai.py +28 -34
- lm_deluge/models/openrouter.py +64 -1
- lm_deluge/models/together.py +0 -16
- lm_deluge/prompt.py +138 -29
- lm_deluge/request_context.py +9 -11
- lm_deluge/tool.py +395 -19
- lm_deluge/tracker.py +11 -5
- lm_deluge/warnings.py +46 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/METADATA +3 -1
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/RECORD +36 -33
- lm_deluge/agent.py +0 -0
- lm_deluge/gemini_limits.py +0 -65
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/WHEEL +0 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/licenses/LICENSE +0 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/top_level.txt +0 -0
lm_deluge/file.py
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
from functools import cached_property
|
|
2
|
-
import os
|
|
3
|
-
import io
|
|
4
|
-
import requests
|
|
5
1
|
import base64
|
|
2
|
+
import io
|
|
6
3
|
import mimetypes
|
|
7
|
-
import
|
|
4
|
+
import os
|
|
8
5
|
from dataclasses import dataclass, field
|
|
6
|
+
from functools import cached_property
|
|
9
7
|
from pathlib import Path
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
import xxhash
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
@dataclass
|
|
13
15
|
class File:
|
|
14
16
|
# raw bytes, pathlike, http url, base64 data url, or file_id
|
|
15
|
-
data: bytes | io.BytesIO | Path | str
|
|
17
|
+
data: bytes | io.BytesIO | Path | str | None
|
|
16
18
|
media_type: str | None = None # inferred if None
|
|
19
|
+
type: str = field(init=False, default="file")
|
|
20
|
+
is_remote: bool = False
|
|
21
|
+
remote_provider: Literal["openai", "anthropic", "google"] | None = None
|
|
17
22
|
filename: str | None = None # optional filename for uploads
|
|
18
23
|
file_id: str | None = None # for OpenAI file uploads or Anthropic file API
|
|
19
|
-
|
|
24
|
+
|
|
25
|
+
def __post_init__(self):
|
|
26
|
+
if self.is_remote:
|
|
27
|
+
if self.remote_provider is None:
|
|
28
|
+
raise ValueError("remote_provider must be specified")
|
|
29
|
+
if self.file_id is None:
|
|
30
|
+
raise ValueError("file_id must be specified for remote files")
|
|
31
|
+
if self.file_id and not self.is_remote:
|
|
32
|
+
print("Warning: File ID specified by file not labeled as remote.")
|
|
20
33
|
|
|
21
34
|
# helpers -----------------------------------------------------------------
|
|
22
35
|
def _bytes(self) -> bytes:
|
|
@@ -75,17 +88,342 @@ class File:
|
|
|
75
88
|
@cached_property
|
|
76
89
|
def fingerprint(self) -> str:
|
|
77
90
|
# Hash the file contents for fingerprinting
|
|
91
|
+
if self.is_remote:
|
|
92
|
+
# For remote files, use provider:file_id for interpretability
|
|
93
|
+
return f"{self.remote_provider}:{self.file_id}"
|
|
78
94
|
file_bytes = self._bytes()
|
|
79
95
|
return xxhash.xxh64(file_bytes).hexdigest()
|
|
80
96
|
|
|
81
97
|
@cached_property
|
|
82
98
|
def size(self) -> int:
|
|
83
99
|
"""Return file size in bytes."""
|
|
100
|
+
if self.is_remote:
|
|
101
|
+
# For remote files, we don't have the bytes available
|
|
102
|
+
return 0
|
|
84
103
|
return len(self._bytes())
|
|
85
104
|
|
|
105
|
+
async def as_remote(
|
|
106
|
+
self, provider: Literal["openai", "anthropic", "google"]
|
|
107
|
+
) -> "File":
|
|
108
|
+
"""Upload file to provider's file API and return new File with file_id.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
provider: The provider to upload to ("openai", "anthropic", or "google")
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
A new File object with file_id set and is_remote=True
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
ValueError: If provider is unsupported or API key is missing
|
|
118
|
+
RuntimeError: If upload fails
|
|
119
|
+
"""
|
|
120
|
+
if self.is_remote:
|
|
121
|
+
# If already remote with same provider, return self
|
|
122
|
+
if self.remote_provider == provider:
|
|
123
|
+
return self
|
|
124
|
+
# Otherwise raise error about cross-provider incompatibility
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"File is already uploaded to {self.remote_provider}. "
|
|
127
|
+
f"Cannot re-upload to {provider}."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if provider == "openai":
|
|
131
|
+
return await self._upload_to_openai()
|
|
132
|
+
elif provider == "anthropic":
|
|
133
|
+
return await self._upload_to_anthropic()
|
|
134
|
+
elif provider == "google":
|
|
135
|
+
return await self._upload_to_google()
|
|
136
|
+
else:
|
|
137
|
+
raise ValueError(f"Unsupported provider: {provider}")
|
|
138
|
+
|
|
139
|
+
async def _upload_to_openai(self) -> "File":
|
|
140
|
+
"""Upload file to OpenAI's Files API."""
|
|
141
|
+
import aiohttp
|
|
142
|
+
|
|
143
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
144
|
+
if not api_key:
|
|
145
|
+
raise ValueError("OPENAI_API_KEY environment variable must be set")
|
|
146
|
+
|
|
147
|
+
url = "https://api.openai.com/v1/files"
|
|
148
|
+
headers = {"Authorization": f"Bearer {api_key}"}
|
|
149
|
+
|
|
150
|
+
# Get file bytes and metadata
|
|
151
|
+
file_bytes = self._bytes()
|
|
152
|
+
filename = self._filename()
|
|
153
|
+
|
|
154
|
+
# Create multipart form data
|
|
155
|
+
data = aiohttp.FormData()
|
|
156
|
+
data.add_field("purpose", "assistants")
|
|
157
|
+
data.add_field(
|
|
158
|
+
"file",
|
|
159
|
+
file_bytes,
|
|
160
|
+
filename=filename,
|
|
161
|
+
content_type=self._mime(),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
async with aiohttp.ClientSession() as session:
|
|
166
|
+
async with session.post(url, headers=headers, data=data) as response:
|
|
167
|
+
if response.status != 200:
|
|
168
|
+
text = await response.text()
|
|
169
|
+
raise RuntimeError(f"Failed to upload file to OpenAI: {text}")
|
|
170
|
+
|
|
171
|
+
response_data = await response.json()
|
|
172
|
+
file_id = response_data["id"]
|
|
173
|
+
|
|
174
|
+
# Return new File object with file_id
|
|
175
|
+
return File(
|
|
176
|
+
data=None,
|
|
177
|
+
media_type=self.media_type,
|
|
178
|
+
is_remote=True,
|
|
179
|
+
remote_provider="openai",
|
|
180
|
+
filename=filename,
|
|
181
|
+
file_id=file_id,
|
|
182
|
+
)
|
|
183
|
+
except aiohttp.ClientError as e:
|
|
184
|
+
raise RuntimeError(f"Failed to upload file to OpenAI: {e}")
|
|
185
|
+
|
|
186
|
+
async def _upload_to_anthropic(self) -> "File":
|
|
187
|
+
"""Upload file to Anthropic's Files API."""
|
|
188
|
+
import aiohttp
|
|
189
|
+
|
|
190
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
191
|
+
if not api_key:
|
|
192
|
+
raise ValueError("ANTHROPIC_API_KEY environment variable must be set")
|
|
193
|
+
|
|
194
|
+
url = "https://api.anthropic.com/v1/files"
|
|
195
|
+
headers = {
|
|
196
|
+
"x-api-key": api_key,
|
|
197
|
+
"anthropic-version": "2023-06-01",
|
|
198
|
+
"anthropic-beta": "files-api-2025-04-14",
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# Get file bytes and metadata
|
|
202
|
+
file_bytes = self._bytes()
|
|
203
|
+
filename = self._filename()
|
|
204
|
+
|
|
205
|
+
# Create multipart form data
|
|
206
|
+
data = aiohttp.FormData()
|
|
207
|
+
data.add_field(
|
|
208
|
+
"file",
|
|
209
|
+
file_bytes,
|
|
210
|
+
filename=filename,
|
|
211
|
+
content_type=self._mime(),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
async with aiohttp.ClientSession() as session:
|
|
216
|
+
async with session.post(url, headers=headers, data=data) as response:
|
|
217
|
+
if response.status != 200:
|
|
218
|
+
text = await response.text()
|
|
219
|
+
raise RuntimeError(
|
|
220
|
+
f"Failed to upload file to Anthropic: {text}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
response_data = await response.json()
|
|
224
|
+
file_id = response_data["id"]
|
|
225
|
+
|
|
226
|
+
# Return new File object with file_id
|
|
227
|
+
return File(
|
|
228
|
+
data=None,
|
|
229
|
+
media_type=self.media_type,
|
|
230
|
+
is_remote=True,
|
|
231
|
+
remote_provider="anthropic",
|
|
232
|
+
filename=filename,
|
|
233
|
+
file_id=file_id,
|
|
234
|
+
)
|
|
235
|
+
except aiohttp.ClientError as e:
|
|
236
|
+
raise RuntimeError(f"Failed to upload file to Anthropic: {e}")
|
|
237
|
+
|
|
238
|
+
async def _upload_to_google(self) -> "File":
|
|
239
|
+
"""Upload file to Google Gemini Files API."""
|
|
240
|
+
import json
|
|
241
|
+
|
|
242
|
+
import aiohttp
|
|
243
|
+
|
|
244
|
+
api_key = os.environ.get("GEMINI_API_KEY")
|
|
245
|
+
if not api_key:
|
|
246
|
+
raise ValueError("GEMINI_API_KEY environment variable must be set")
|
|
247
|
+
|
|
248
|
+
# Google uses a different URL structure with the API key as a parameter
|
|
249
|
+
url = f"https://generativelanguage.googleapis.com/upload/v1beta/files?key={api_key}"
|
|
250
|
+
|
|
251
|
+
# Get file bytes and metadata
|
|
252
|
+
file_bytes = self._bytes()
|
|
253
|
+
filename = self._filename()
|
|
254
|
+
mime_type = self._mime()
|
|
255
|
+
|
|
256
|
+
# Google expects a multipart request with metadata and file data
|
|
257
|
+
# Using the resumable upload protocol
|
|
258
|
+
headers = {
|
|
259
|
+
"X-Goog-Upload-Protocol": "multipart",
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
# Create multipart form data with metadata and file
|
|
263
|
+
data = aiohttp.FormData()
|
|
264
|
+
|
|
265
|
+
# Add metadata part as JSON
|
|
266
|
+
metadata = {"file": {"display_name": filename}}
|
|
267
|
+
data.add_field(
|
|
268
|
+
"metadata",
|
|
269
|
+
json.dumps(metadata),
|
|
270
|
+
content_type="application/json",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Add file data part
|
|
274
|
+
data.add_field(
|
|
275
|
+
"file",
|
|
276
|
+
file_bytes,
|
|
277
|
+
filename=filename,
|
|
278
|
+
content_type=mime_type,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
async with aiohttp.ClientSession() as session:
|
|
283
|
+
async with session.post(url, headers=headers, data=data) as response:
|
|
284
|
+
if response.status not in [200, 201]:
|
|
285
|
+
text = await response.text()
|
|
286
|
+
raise RuntimeError(f"Failed to upload file to Google: {text}")
|
|
287
|
+
|
|
288
|
+
response_data = await response.json()
|
|
289
|
+
# Google returns a file object with a 'name' field like 'files/abc123'
|
|
290
|
+
file_uri = response_data.get("file", {}).get(
|
|
291
|
+
"uri"
|
|
292
|
+
) or response_data.get("name")
|
|
293
|
+
if not file_uri:
|
|
294
|
+
raise RuntimeError(
|
|
295
|
+
f"No file URI in Google response: {response_data}"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Return new File object with file_id (using the file URI)
|
|
299
|
+
return File(
|
|
300
|
+
data=None,
|
|
301
|
+
media_type=self.media_type,
|
|
302
|
+
is_remote=True,
|
|
303
|
+
remote_provider="google",
|
|
304
|
+
filename=filename,
|
|
305
|
+
file_id=file_uri,
|
|
306
|
+
)
|
|
307
|
+
except aiohttp.ClientError as e:
|
|
308
|
+
raise RuntimeError(f"Failed to upload file to Google: {e}")
|
|
309
|
+
|
|
310
|
+
async def delete(self) -> bool:
|
|
311
|
+
"""Delete the uploaded file from the remote provider.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
True if deletion was successful, False otherwise
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
ValueError: If file is not a remote file or provider is unsupported
|
|
318
|
+
RuntimeError: If deletion fails
|
|
319
|
+
"""
|
|
320
|
+
if not self.is_remote:
|
|
321
|
+
raise ValueError(
|
|
322
|
+
"Cannot delete a non-remote file. Only remote files can be deleted."
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if not self.file_id:
|
|
326
|
+
raise ValueError("Cannot delete file without file_id")
|
|
327
|
+
|
|
328
|
+
if self.remote_provider == "openai":
|
|
329
|
+
return await self._delete_from_openai()
|
|
330
|
+
elif self.remote_provider == "anthropic":
|
|
331
|
+
return await self._delete_from_anthropic()
|
|
332
|
+
elif self.remote_provider == "google":
|
|
333
|
+
return await self._delete_from_google()
|
|
334
|
+
else:
|
|
335
|
+
raise ValueError(f"Unsupported provider: {self.remote_provider}")
|
|
336
|
+
|
|
337
|
+
async def _delete_from_openai(self) -> bool:
|
|
338
|
+
"""Delete file from OpenAI's Files API."""
|
|
339
|
+
import aiohttp
|
|
340
|
+
|
|
341
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
342
|
+
if not api_key:
|
|
343
|
+
raise ValueError("OPENAI_API_KEY environment variable must be set")
|
|
344
|
+
|
|
345
|
+
url = f"https://api.openai.com/v1/files/{self.file_id}"
|
|
346
|
+
headers = {"Authorization": f"Bearer {api_key}"}
|
|
347
|
+
|
|
348
|
+
try:
|
|
349
|
+
async with aiohttp.ClientSession() as session:
|
|
350
|
+
async with session.delete(url, headers=headers) as response:
|
|
351
|
+
if response.status == 200:
|
|
352
|
+
return True
|
|
353
|
+
else:
|
|
354
|
+
text = await response.text()
|
|
355
|
+
raise RuntimeError(f"Failed to delete file from OpenAI: {text}")
|
|
356
|
+
except aiohttp.ClientError as e:
|
|
357
|
+
raise RuntimeError(f"Failed to delete file from OpenAI: {e}")
|
|
358
|
+
|
|
359
|
+
async def _delete_from_anthropic(self) -> bool:
|
|
360
|
+
"""Delete file from Anthropic's Files API."""
|
|
361
|
+
import aiohttp
|
|
362
|
+
|
|
363
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
364
|
+
if not api_key:
|
|
365
|
+
raise ValueError("ANTHROPIC_API_KEY environment variable must be set")
|
|
366
|
+
|
|
367
|
+
url = f"https://api.anthropic.com/v1/files/{self.file_id}"
|
|
368
|
+
headers = {
|
|
369
|
+
"x-api-key": api_key,
|
|
370
|
+
"anthropic-version": "2023-06-01",
|
|
371
|
+
"anthropic-beta": "files-api-2025-04-14",
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
async with aiohttp.ClientSession() as session:
|
|
376
|
+
async with session.delete(url, headers=headers) as response:
|
|
377
|
+
if response.status == 200:
|
|
378
|
+
return True
|
|
379
|
+
else:
|
|
380
|
+
text = await response.text()
|
|
381
|
+
raise RuntimeError(
|
|
382
|
+
f"Failed to delete file from Anthropic: {text}"
|
|
383
|
+
)
|
|
384
|
+
except aiohttp.ClientError as e:
|
|
385
|
+
raise RuntimeError(f"Failed to delete file from Anthropic: {e}")
|
|
386
|
+
|
|
387
|
+
async def _delete_from_google(self) -> bool:
|
|
388
|
+
"""Delete file from Google Gemini Files API."""
|
|
389
|
+
import aiohttp
|
|
390
|
+
|
|
391
|
+
api_key = os.environ.get("GEMINI_API_KEY")
|
|
392
|
+
if not api_key:
|
|
393
|
+
raise ValueError("GEMINI_API_KEY environment variable must be set")
|
|
394
|
+
|
|
395
|
+
# Google file_id is the full URI like "https://generativelanguage.googleapis.com/v1beta/files/abc123"
|
|
396
|
+
# We need to extract just the file name part for the delete endpoint
|
|
397
|
+
assert self.file_id, "can't delete file with no file id"
|
|
398
|
+
if self.file_id.startswith("https://"):
|
|
399
|
+
# Extract the path after the domain
|
|
400
|
+
file_name = self.file_id.split("/v1beta/")[-1]
|
|
401
|
+
else:
|
|
402
|
+
file_name = self.file_id
|
|
403
|
+
|
|
404
|
+
url = f"https://generativelanguage.googleapis.com/v1beta/{file_name}?key={api_key}"
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
async with aiohttp.ClientSession() as session:
|
|
408
|
+
async with session.delete(url) as response:
|
|
409
|
+
if response.status in [200, 204]:
|
|
410
|
+
return True
|
|
411
|
+
else:
|
|
412
|
+
text = await response.text()
|
|
413
|
+
raise RuntimeError(f"Failed to delete file from Google: {text}")
|
|
414
|
+
except aiohttp.ClientError as e:
|
|
415
|
+
raise RuntimeError(f"Failed to delete file from Google: {e}")
|
|
416
|
+
|
|
86
417
|
# ── provider-specific emission ────────────────────────────────────────────
|
|
87
418
|
def oa_chat(self) -> dict:
|
|
88
419
|
"""For OpenAI Chat Completions - file content as base64 or file_id."""
|
|
420
|
+
# Validate provider compatibility
|
|
421
|
+
if self.is_remote and self.remote_provider != "openai":
|
|
422
|
+
raise ValueError(
|
|
423
|
+
f"Cannot emit file uploaded to {self.remote_provider} as OpenAI format. "
|
|
424
|
+
f"File must be uploaded to OpenAI or provided as raw data."
|
|
425
|
+
)
|
|
426
|
+
|
|
89
427
|
if self.file_id:
|
|
90
428
|
return {
|
|
91
429
|
"type": "file",
|
|
@@ -104,6 +442,13 @@ class File:
|
|
|
104
442
|
|
|
105
443
|
def oa_resp(self) -> dict:
|
|
106
444
|
"""For OpenAI Responses API - file content as base64 or file_id."""
|
|
445
|
+
# Validate provider compatibility
|
|
446
|
+
if self.is_remote and self.remote_provider != "openai":
|
|
447
|
+
raise ValueError(
|
|
448
|
+
f"Cannot emit file uploaded to {self.remote_provider} as OpenAI format. "
|
|
449
|
+
f"File must be uploaded to OpenAI or provided as raw data."
|
|
450
|
+
)
|
|
451
|
+
|
|
107
452
|
if self.file_id:
|
|
108
453
|
return {
|
|
109
454
|
"type": "input_file",
|
|
@@ -118,6 +463,13 @@ class File:
|
|
|
118
463
|
|
|
119
464
|
def anthropic(self) -> dict:
|
|
120
465
|
"""For Anthropic Messages API - file content as base64 or file_id."""
|
|
466
|
+
# Validate provider compatibility
|
|
467
|
+
if self.is_remote and self.remote_provider != "anthropic":
|
|
468
|
+
raise ValueError(
|
|
469
|
+
f"Cannot emit file uploaded to {self.remote_provider} as Anthropic format. "
|
|
470
|
+
f"File must be uploaded to Anthropic or provided as raw data."
|
|
471
|
+
)
|
|
472
|
+
|
|
121
473
|
if self.file_id:
|
|
122
474
|
return {
|
|
123
475
|
"type": "document",
|
|
@@ -145,13 +497,30 @@ class File:
|
|
|
145
497
|
return filename, content, media_type
|
|
146
498
|
|
|
147
499
|
def gemini(self) -> dict:
|
|
148
|
-
"""For Gemini API - files are provided as inline data."""
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"
|
|
500
|
+
"""For Gemini API - files are provided as inline data or file URI."""
|
|
501
|
+
# Validate provider compatibility
|
|
502
|
+
if self.is_remote and self.remote_provider != "google":
|
|
503
|
+
raise ValueError(
|
|
504
|
+
f"Cannot emit file uploaded to {self.remote_provider} as Google format. "
|
|
505
|
+
f"File must be uploaded to Google or provided as raw data."
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
if self.file_id:
|
|
509
|
+
# Use file URI for uploaded files
|
|
510
|
+
return {
|
|
511
|
+
"fileData": {
|
|
512
|
+
"mimeType": self._mime(),
|
|
513
|
+
"fileUri": self.file_id,
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else:
|
|
517
|
+
# Use inline data for non-uploaded files
|
|
518
|
+
return {
|
|
519
|
+
"inlineData": {
|
|
520
|
+
"mimeType": self._mime(),
|
|
521
|
+
"data": self._base64(include_header=False),
|
|
522
|
+
}
|
|
153
523
|
}
|
|
154
|
-
}
|
|
155
524
|
|
|
156
525
|
def mistral(self) -> dict:
|
|
157
526
|
"""For Mistral API - not yet supported."""
|