Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -246,76 +246,73 @@ def chat_complete(client, model: str, messages, timeout: int = 120, progress=Non
|
|
| 246 |
except Exception as e:
|
| 247 |
return f"Error during model call: {e}"
|
| 248 |
|
| 249 |
-
def upload_file_to_mistral(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
fname = filename or os.path.basename(path)
|
| 251 |
-
last_sdk_error = None
|
| 252 |
|
| 253 |
-
#
|
| 254 |
-
try
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
if progress is not None:
|
| 268 |
-
try:
|
| 269 |
-
progress(0.6)
|
| 270 |
-
except TypeError:
|
| 271 |
-
progress(0.6, desc="Upload complete (SDK)")
|
| 272 |
-
return fid
|
| 273 |
-
except Exception as e:
|
| 274 |
-
last_sdk_error = e
|
| 275 |
|
| 276 |
-
#
|
|
|
|
|
|
|
| 277 |
api_key = getattr(client, "api_key", "") or DEFAULT_KEY
|
| 278 |
if not api_key:
|
| 279 |
raise RuntimeError("MISTRAL_API_KEY missing or empty")
|
|
|
|
| 280 |
url = "https://api.mistral.ai/v1/files"
|
| 281 |
headers = {"Authorization": f"Bearer {api_key}"}
|
| 282 |
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
r.raise_for_status()
|
| 289 |
-
return r.json()
|
| 290 |
-
|
| 291 |
-
# Build purpose retry list (prefer image if file extension suggests image)
|
| 292 |
-
tried = set()
|
| 293 |
-
purposes_to_try = [purpose] if purpose else []
|
| 294 |
-
ext = os.path.splitext(fname)[1].lower()
|
| 295 |
-
if ext in (".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff"):
|
| 296 |
-
if "image" not in purposes_to_try:
|
| 297 |
-
purposes_to_try.append("image")
|
| 298 |
-
for p in ("batch", "fine-tune", "image"):
|
| 299 |
-
if p not in purposes_to_try:
|
| 300 |
-
purposes_to_try.append(p)
|
| 301 |
-
|
| 302 |
-
last_rest_exception = None
|
| 303 |
-
for p in purposes_to_try:
|
| 304 |
-
if p in tried:
|
| 305 |
-
continue
|
| 306 |
-
tried.add(p)
|
| 307 |
try:
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
fid = jr.get("id") or jr.get("data", [{}])[0].get("id")
|
| 315 |
-
if fid:
|
| 316 |
-
|
| 317 |
-
# fallback: search for any 'id' in the JSON
|
| 318 |
-
if isinstance(jr, dict):
|
| 319 |
def find_id(obj):
|
| 320 |
if isinstance(obj, dict):
|
| 321 |
if "id" in obj and isinstance(obj["id"], str):
|
|
@@ -330,36 +327,35 @@ def upload_file_to_mistral(client, path: str, filename: str | None = None, purpo
|
|
| 330 |
if found:
|
| 331 |
return found
|
| 332 |
return None
|
|
|
|
| 333 |
fid = find_id(jr)
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
except requests.exceptions.HTTPError as he:
|
| 338 |
-
|
| 339 |
-
|
|
|
|
| 340 |
if status == 422:
|
| 341 |
-
# try next purpose value
|
| 342 |
continue
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
if last_sdk_error:
|
| 346 |
-
err_msg += f" | SDK error: {last_sdk_error}"
|
| 347 |
-
raise RuntimeError(err_msg)
|
| 348 |
except requests.exceptions.RequestException as re:
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
if last_sdk_error:
|
| 352 |
-
err_msg += f" | SDK error: {last_sdk_error}"
|
| 353 |
-
raise RuntimeError(err_msg)
|
| 354 |
-
except Exception as exc:
|
| 355 |
-
last_rest_exception = exc
|
| 356 |
-
continue
|
| 357 |
|
|
|
|
| 358 |
err_msg = "File upload failed. REST attempts exhausted."
|
| 359 |
-
if
|
| 360 |
-
err_msg += f" Last REST error: {
|
| 361 |
-
if last_sdk_error:
|
| 362 |
-
err_msg += f" | SDK error: {last_sdk_error}"
|
| 363 |
raise RuntimeError(err_msg)
|
| 364 |
|
| 365 |
def determine_media_type(src: str, progress=None) -> Tuple[bool, bool]:
|
|
|
|
| 246 |
except Exception as e:
|
| 247 |
return f"Error during model call: {e}"
|
| 248 |
|
| 249 |
+
def upload_file_to_mistral(
|
| 250 |
+
client,
|
| 251 |
+
path: str,
|
| 252 |
+
filename: str | None = None,
|
| 253 |
+
purpose: str = "batch",
|
| 254 |
+
timeout: int = 120,
|
| 255 |
+
progress=None,
|
| 256 |
+
) -> str:
|
| 257 |
+
"""
|
| 258 |
+
Upload a file to Mistral using only the REST endpoint.
|
| 259 |
+
- Sends multipart/form‑data with field name **file**.
|
| 260 |
+
- Sends a form field **purpose** (string).
|
| 261 |
+
- If the request returns 422, retries with common allowed purposes
|
| 262 |
+
(image, batch, fine‑tune) and returns the first successful file id.
|
| 263 |
+
"""
|
| 264 |
fname = filename or os.path.basename(path)
|
|
|
|
| 265 |
|
| 266 |
+
# ------------------------------------------------------------------ #
|
| 267 |
+
# Build the list of purposes to try (original + sensible fallbacks)
|
| 268 |
+
# ------------------------------------------------------------------ #
|
| 269 |
+
purposes_to_try = [purpose]
|
| 270 |
+
|
| 271 |
+
# If the file looks like an image, try "image" first
|
| 272 |
+
ext = os.path.splitext(fname)[1].lower()
|
| 273 |
+
if ext in {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff"}:
|
| 274 |
+
purposes_to_try.append("image")
|
| 275 |
+
|
| 276 |
+
# Add other generic allowed values (avoid duplicates)
|
| 277 |
+
for p in ("batch", "fine-tune", "image"):
|
| 278 |
+
if p not in purposes_to_try:
|
| 279 |
+
purposes_to_try.append(p)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
+
# ------------------------------------------------------------------ #
|
| 282 |
+
# Prepare request details
|
| 283 |
+
# ------------------------------------------------------------------ #
|
| 284 |
api_key = getattr(client, "api_key", "") or DEFAULT_KEY
|
| 285 |
if not api_key:
|
| 286 |
raise RuntimeError("MISTRAL_API_KEY missing or empty")
|
| 287 |
+
|
| 288 |
url = "https://api.mistral.ai/v1/files"
|
| 289 |
headers = {"Authorization": f"Bearer {api_key}"}
|
| 290 |
|
| 291 |
+
# ------------------------------------------------------------------ #
|
| 292 |
+
# Try each purpose until we get a successful upload
|
| 293 |
+
# ------------------------------------------------------------------ #
|
| 294 |
+
last_error = None
|
| 295 |
+
for cur_purpose in purposes_to_try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
try:
|
| 297 |
+
with open(path, "rb") as fh:
|
| 298 |
+
files = {"file": (fname, fh)}
|
| 299 |
+
data = {"purpose": cur_purpose}
|
| 300 |
+
resp = requests.post(
|
| 301 |
+
url,
|
| 302 |
+
headers=headers,
|
| 303 |
+
files=files,
|
| 304 |
+
data=data,
|
| 305 |
+
timeout=timeout,
|
| 306 |
+
)
|
| 307 |
+
resp.raise_for_status()
|
| 308 |
+
jr = resp.json()
|
| 309 |
+
|
| 310 |
+
# ---------------------------------------------------------------- #
|
| 311 |
+
# Extract the file id from the JSON response (covers both shapes)
|
| 312 |
+
# ---------------------------------------------------------------- #
|
| 313 |
fid = jr.get("id") or jr.get("data", [{}])[0].get("id")
|
| 314 |
+
if not fid:
|
| 315 |
+
# deep‑search for any "id" key just in case
|
|
|
|
|
|
|
| 316 |
def find_id(obj):
|
| 317 |
if isinstance(obj, dict):
|
| 318 |
if "id" in obj and isinstance(obj["id"], str):
|
|
|
|
| 327 |
if found:
|
| 328 |
return found
|
| 329 |
return None
|
| 330 |
+
|
| 331 |
fid = find_id(jr)
|
| 332 |
+
|
| 333 |
+
if fid:
|
| 334 |
+
if progress is not None:
|
| 335 |
+
try:
|
| 336 |
+
progress(0.65)
|
| 337 |
+
except TypeError:
|
| 338 |
+
progress(0.65, desc=f"Upload complete (REST, purpose={cur_purpose})")
|
| 339 |
+
return fid
|
| 340 |
+
|
| 341 |
+
raise RuntimeError(f"REST upload succeeded but no file id returned (purpose={cur_purpose})")
|
| 342 |
+
|
| 343 |
except requests.exceptions.HTTPError as he:
|
| 344 |
+
# 422 → try next purpose; other codes → abort
|
| 345 |
+
status = getattr(he.response, "status_code", None)
|
| 346 |
+
last_error = he
|
| 347 |
if status == 422:
|
|
|
|
| 348 |
continue
|
| 349 |
+
raise RuntimeError(f"File upload failed. REST error: {he}")
|
| 350 |
+
|
|
|
|
|
|
|
|
|
|
| 351 |
except requests.exceptions.RequestException as re:
|
| 352 |
+
last_error = re
|
| 353 |
+
raise RuntimeError(f"File upload failed. REST error: {re}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
|
| 355 |
+
# If we exit the loop, all purpose attempts failed
|
| 356 |
err_msg = "File upload failed. REST attempts exhausted."
|
| 357 |
+
if last_error:
|
| 358 |
+
err_msg += f" Last REST error: {last_error}"
|
|
|
|
|
|
|
| 359 |
raise RuntimeError(err_msg)
|
| 360 |
|
| 361 |
def determine_media_type(src: str, progress=None) -> Tuple[bool, bool]:
|