Hug0endob commited on
Commit
6bf7529
·
verified ·
1 Parent(s): f2f9d58

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -82
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(client, path: str, filename: str | None = None, purpose: str = "batch", timeout: int = 120, progress=None) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  fname = filename or os.path.basename(path)
251
- last_sdk_error = None
252
 
253
- # Try SDK upload: pass exactly one positional file-like argument
254
- try:
255
- if hasattr(client, "files") and hasattr(client.files, "upload"):
256
- with open(path, "rb") as fh:
257
- # many SDKs expect a single positional file arg
258
- res = client.files.upload(fh)
259
- # normalize response
260
- fid = getattr(res, "id", None) or (res.get("id") if isinstance(res, dict) else None)
261
- if not fid:
262
- try:
263
- fid = res["data"][0]["id"]
264
- except Exception:
265
- fid = None
266
- if fid:
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
- # REST fallback: multipart/form-data with field name "file" and purpose in form data
 
 
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
- def do_rest_upload(purpose_value: str):
284
- with open(path, "rb") as fh:
285
- files = {"file": (fname, fh)}
286
- data = {"purpose": purpose_value}
287
- r = requests.post(url, headers=headers, files=files, data=data, timeout=timeout)
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
- jr = do_rest_upload(p)
309
- if progress is not None:
310
- try:
311
- progress(0.65)
312
- except TypeError:
313
- progress(0.65, desc=f"Upload complete (REST, purpose={p})")
 
 
 
 
 
 
 
 
 
 
314
  fid = jr.get("id") or jr.get("data", [{}])[0].get("id")
315
- if fid:
316
- return fid
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
- if fid:
335
- return fid
336
- raise RuntimeError(f"REST upload returned no file id for purpose={p}: {jr}")
 
 
 
 
 
 
 
 
337
  except requests.exceptions.HTTPError as he:
338
- status = getattr(he.response, "status_code", None) or getattr(he.response, "status", None)
339
- last_rest_exception = he
 
340
  if status == 422:
341
- # try next purpose value
342
  continue
343
- else:
344
- err_msg = f"File upload failed. REST error: {he}"
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
- last_rest_exception = re
350
- err_msg = f"File upload failed. REST error: {re}"
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 last_rest_exception:
360
- err_msg += f" Last REST error: {last_rest_exception}"
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]: