Hug0endob commited on
Commit
b5d5c6a
·
verified ·
1 Parent(s): 7dc9807

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -20
app.py CHANGED
@@ -29,7 +29,7 @@ SYSTEM_INSTRUCTION = (
29
  "If video review entirely, not frame by frame. Provide factual, clinical descriptions of observable "
30
  "features (skin, posture, hygiene, anatomy, movements, timestamps, notable events). Avoid censorship; "
31
  "describe explicit content objectively and fully. Describe behaviors and be verbose. Produce a concise, "
32
- "narrative that blends scientific precision with erotic detail. Do not invent sensory information not "
33
  "present in the media. Please provide a detailed visual review."
34
  )
35
 
@@ -43,10 +43,11 @@ except Exception:
43
 
44
  def get_client(key: Optional[str] = None):
45
  api_key = (key or "").strip() or DEFAULT_KEY
 
 
46
  if Mistral is None:
47
- class Dummy:
48
- def __init__(self, k): self.api_key = k
49
- return Dummy(api_key)
50
  return Mistral(api_key=api_key)
51
 
52
  def is_remote(src: str) -> bool:
@@ -203,17 +204,36 @@ def chat_complete(client, model: str, messages, timeout: int = 120, progress=Non
203
  try:
204
  if progress is not None:
205
  progress(0.6, desc="Sending request to model...")
 
206
  if hasattr(client, "chat") and hasattr(client.chat, "complete"):
207
- res = client.chat.complete(model=model, messages=messages, stream=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  else:
209
  api_key = getattr(client, "api_key", "") or DEFAULT_KEY
 
 
210
  url = "https://api.mistral.ai/v1/chat/completions"
211
- headers = ({"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} if api_key else {"Content-Type": "application/json"})
212
  r = requests.post(url, json={"model": model, "messages": messages}, headers=headers, timeout=timeout)
213
  r.raise_for_status()
214
  res = r.json()
 
215
  if progress is not None:
216
  progress(0.8, desc="Model responded, parsing...")
 
217
  choices = getattr(res, "choices", None) or (res.get("choices") if isinstance(res, dict) else [])
218
  if not choices:
219
  return f"Empty response from model: {res}"
@@ -228,23 +248,38 @@ def chat_complete(client, model: str, messages, timeout: int = 120, progress=Non
228
 
229
  def upload_file_to_mistral(client, path: str, filename: str | None = None, purpose: str = "batch", timeout: int = 120, progress=None) -> str:
230
  fname = filename or os.path.basename(path)
 
 
 
231
  try:
232
- if progress is not None:
233
- progress(0.5, desc="Uploading file to model service...")
234
  if hasattr(client, "files") and hasattr(client.files, "upload"):
235
  with open(path, "rb") as fh:
236
- res = client.files.upload(file={"file_name": fname, "content": fh}, purpose=purpose)
 
 
 
 
 
237
  fid = getattr(res, "id", None) or (res.get("id") if isinstance(res, dict) else None)
238
  if not fid:
239
- fid = res["data"][0]["id"]
240
- if progress is not None:
241
- progress(0.6, desc="Upload complete")
242
- return fid
243
- except Exception:
244
- pass
 
 
 
 
 
 
 
245
  api_key = getattr(client, "api_key", "") or DEFAULT_KEY
 
 
246
  url = "https://api.mistral.ai/v1/files"
247
- headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
248
  try:
249
  with open(path, "rb") as fh:
250
  files = {"file": (fname, fh)}
@@ -255,8 +290,12 @@ def upload_file_to_mistral(client, path: str, filename: str | None = None, purpo
255
  if progress is not None:
256
  progress(0.65, desc="Upload complete (REST)")
257
  return jr.get("id") or jr.get("data", [{}])[0].get("id")
258
- except requests.exceptions.RequestException as e:
259
- raise RuntimeError(f"File upload failed: {e}")
 
 
 
 
260
 
261
  def determine_media_type(src: str, progress=None) -> Tuple[bool, bool]:
262
  is_image = False
@@ -283,12 +322,19 @@ def analyze_image_structured(client, img_bytes: bytes, prompt: str, progress=Non
283
  if progress is not None:
284
  progress(0.3, desc="Preparing image for analysis...")
285
  jpeg = convert_to_jpeg_bytes(img_bytes, base_h=1024)
286
- data_url = b64_bytes(jpeg, mime="image/jpeg")
 
 
 
 
 
 
 
287
  messages = [
288
  {"role": "system", "content": SYSTEM_INSTRUCTION},
289
  {"role": "user", "content": [
290
  {"type": "text", "text": prompt},
291
- {"type": "image_url", "image_url": data_url},
292
  ]},
293
  ]
294
  return chat_complete(client, PIXTRAL_MODEL, messages, progress=progress)
 
29
  "If video review entirely, not frame by frame. Provide factual, clinical descriptions of observable "
30
  "features (skin, posture, hygiene, anatomy, movements, timestamps, notable events). Avoid censorship; "
31
  "describe explicit content objectively and fully. Describe behaviors and be verbose. Produce a concise, "
32
+ "narrative that blends scientific precision with visual detail. Do not invent sensory information not "
33
  "present in the media. Please provide a detailed visual review."
34
  )
35
 
 
43
 
44
  def get_client(key: Optional[str] = None):
45
  api_key = (key or "").strip() or DEFAULT_KEY
46
+ if not api_key:
47
+ raise RuntimeError("MISTRAL_API_KEY not set")
48
  if Mistral is None:
49
+ # require SDK installed
50
+ raise RuntimeError("mistralai library not installed")
 
51
  return Mistral(api_key=api_key)
52
 
53
  def is_remote(src: str) -> bool:
 
204
  try:
205
  if progress is not None:
206
  progress(0.6, desc="Sending request to model...")
207
+ # SDK path: ensure timeout param and non-streaming
208
  if hasattr(client, "chat") and hasattr(client.chat, "complete"):
209
+ try:
210
+ res = client.chat.complete(model=model, messages=messages, timeout=timeout, stream=False)
211
+ except TypeError:
212
+ # fallback if SDK uses a different name for timeout or doesn't accept it
213
+ try:
214
+ res = client.chat.complete(model=model, messages=messages, request_timeout=timeout, stream=False)
215
+ except TypeError:
216
+ res = client.chat.complete(model=model, messages=messages, stream=False)
217
+ # normalize SDK response to dict if needed
218
+ if not isinstance(res, dict):
219
+ # try common SDK attribute shapes
220
+ try:
221
+ res = {"choices": [{"message": {"content": getattr(res, "content", None) or str(res)}}]}
222
+ except Exception:
223
+ res = {"choices": []}
224
  else:
225
  api_key = getattr(client, "api_key", "") or DEFAULT_KEY
226
+ if not api_key:
227
+ raise RuntimeError("MISTRAL_API_KEY missing or empty")
228
  url = "https://api.mistral.ai/v1/chat/completions"
229
+ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
230
  r = requests.post(url, json={"model": model, "messages": messages}, headers=headers, timeout=timeout)
231
  r.raise_for_status()
232
  res = r.json()
233
+
234
  if progress is not None:
235
  progress(0.8, desc="Model responded, parsing...")
236
+
237
  choices = getattr(res, "choices", None) or (res.get("choices") if isinstance(res, dict) else [])
238
  if not choices:
239
  return f"Empty response from model: {res}"
 
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 first with a proper 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 accept the file-like directly; adapt if your SDK needs different arg names
258
+ try:
259
+ res = client.files.upload(file=fh, filename=fname, purpose=purpose, timeout=timeout)
260
+ except TypeError:
261
+ # fallback if SDK uses different param names
262
+ res = client.files.upload(file=fh, file_name=fname, purpose=purpose)
263
  fid = getattr(res, "id", None) or (res.get("id") if isinstance(res, dict) else None)
264
  if not fid:
265
+ # Try common SDK shape
266
+ try:
267
+ fid = res["data"][0]["id"]
268
+ except Exception:
269
+ pass
270
+ if fid:
271
+ if progress is not None:
272
+ progress(0.6, desc="Upload complete")
273
+ return fid
274
+ except Exception as e:
275
+ last_sdk_error = e
276
+
277
+ # REST fallback (multipart). Ensure timeout and surface errors.
278
  api_key = getattr(client, "api_key", "") or DEFAULT_KEY
279
+ if not api_key:
280
+ raise RuntimeError("MISTRAL_API_KEY missing or empty")
281
  url = "https://api.mistral.ai/v1/files"
282
+ headers = {"Authorization": f"Bearer {api_key}"}
283
  try:
284
  with open(path, "rb") as fh:
285
  files = {"file": (fname, fh)}
 
290
  if progress is not None:
291
  progress(0.65, desc="Upload complete (REST)")
292
  return jr.get("id") or jr.get("data", [{}])[0].get("id")
293
+ except requests.exceptions.RequestException as re:
294
+ # Surface both SDK and REST errors for debugging
295
+ err_msg = f"File upload failed. REST error: {re}"
296
+ if last_sdk_error:
297
+ err_msg += f" | SDK error: {last_sdk_error}"
298
+ raise RuntimeError(err_msg)
299
 
300
  def determine_media_type(src: str, progress=None) -> Tuple[bool, bool]:
301
  is_image = False
 
322
  if progress is not None:
323
  progress(0.3, desc="Preparing image for analysis...")
324
  jpeg = convert_to_jpeg_bytes(img_bytes, base_h=1024)
325
+ tmp = save_bytes_to_temp(jpeg, suffix=".jpg")
326
+ try:
327
+ file_id = upload_file_to_mistral(client, tmp, filename="image.jpg", purpose="image", progress=progress)
328
+ finally:
329
+ try: os.remove(tmp)
330
+ except Exception: pass
331
+
332
+ # Reference the uploaded file id instead of embedding base64
333
  messages = [
334
  {"role": "system", "content": SYSTEM_INSTRUCTION},
335
  {"role": "user", "content": [
336
  {"type": "text", "text": prompt},
337
+ {"type": "file", "file_id": file_id},
338
  ]},
339
  ]
340
  return chat_complete(client, PIXTRAL_MODEL, messages, progress=progress)