NgBaoAnn commited on
Commit
0e5461b
·
1 Parent(s): 7d45c2a

fix: add Groq Vision (chess Q04) + Groq Whisper (audio Q10/Q14) — target 95-100%

Browse files
Files changed (1) hide show
  1. app.py +86 -52
app.py CHANGED
@@ -34,6 +34,69 @@ QUESTIONS_URL = f"{API_URL}/questions"
34
  FILES_URL = f"{API_URL}/files"
35
  SUBMIT_URL = f"{API_URL}/submit"
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # ─────────────────────────────────────────────────────────────────────────────
38
  # TOOLS
39
  # ─────────────────────────────────────────────────────────────────────────────
@@ -282,67 +345,38 @@ def download_and_read_file(task_id: str) -> str:
282
 
283
  # ── Audio (MP3 / WAV) ─────────────────────────────────────────────
284
  if ext in ("mp3", "wav", "m4a", "ogg", "flac") or "audio" in content_type:
285
- # Try speech-to-text with SpeechRecognition + pydub
 
 
 
286
  try:
287
- import speech_recognition as sr
288
- from pydub import AudioSegment
289
-
290
- with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
291
- tmp.write(raw)
292
- tmp_path = tmp.name
293
-
294
- # Convert to WAV if needed
295
- if ext != "wav":
296
- audio = AudioSegment.from_file(tmp_path)
297
- wav_path = tmp_path.replace(f".{ext}", ".wav")
298
- audio.export(wav_path, format="wav")
299
- else:
300
- wav_path = tmp_path
301
-
302
- recognizer = sr.Recognizer()
303
- with sr.AudioFile(wav_path) as source:
304
- audio_data = recognizer.record(source)
305
- transcript = recognizer.recognize_google(audio_data)
306
-
307
- # Cleanup
308
  try:
309
  os.unlink(tmp_path)
310
- if wav_path != tmp_path:
311
- os.unlink(wav_path)
312
  except Exception:
313
  pass
314
-
315
- return f"[Audio transcript]\n{transcript}"
316
- except ImportError:
317
- return (
318
- f"[Audio file — {len(raw)} bytes — {ext.upper()}]\n"
319
- "Speech recognition libraries not available. "
320
- "The audio content cannot be transcribed automatically. "
321
- "Please use other context clues from the question to answer."
322
- )
323
- except Exception as e:
324
- return f"[Audio file — {len(raw)} bytes] Transcription failed: {e}"
325
 
326
  # ── Image ─────────────────────────────────────────────────────────
327
  if ext in ("png", "jpg", "jpeg", "gif", "bmp", "webp") or "image" in content_type:
328
- result = f"[Image file {filename} {len(raw)} bytes]\n"
329
- # Try OCR
330
- try:
331
- from PIL import Image
332
- import pytesseract
333
- img = Image.open(io.BytesIO(raw))
334
- result += f"Size: {img.size}, Mode: {img.mode}\n"
335
- ocr_text = pytesseract.image_to_string(img).strip()
336
- if ocr_text:
337
- result += f"\n[OCR Text]\n{ocr_text}"
338
- else:
339
- result += "\n[No text detected by OCR]"
340
- except Exception as ocr_err:
341
- result += f"\n[OCR not available: {ocr_err}]"
342
- # Return base64 for visual analysis by multimodal models
343
  b64 = base64.b64encode(raw).decode()
344
- result += f"\n\n[Image base64 (first 500 chars)]\n{b64[:500]}..."
345
- return result
 
 
 
 
 
 
 
 
 
 
 
346
 
347
  # ── Plain text / fallback ─────────────────────────────────────────
348
  try:
 
34
  FILES_URL = f"{API_URL}/files"
35
  SUBMIT_URL = f"{API_URL}/submit"
36
 
37
+ # ─────────────────────────────────────────────────────────────────────────────
38
+ # GROQ HELPERS — Vision (llama-3.2-11b-vision) & Audio (whisper-large-v3)
39
+ # ─────────────────────────────────────────────────────────────────────────────
40
+
41
+ def _groq_client():
42
+ """Return a raw Groq HTTP client (uses requests, no extra SDK needed)."""
43
+ api_key = os.environ.get("GROQ_API_KEY")
44
+ if not api_key:
45
+ raise RuntimeError("GROQ_API_KEY not set")
46
+ return api_key
47
+
48
+
49
+ def _transcribe_with_groq_whisper(audio_path: str) -> str:
50
+ """Send an audio file to Groq Whisper API and return the transcript."""
51
+ api_key = _groq_client()
52
+ with open(audio_path, "rb") as f:
53
+ audio_bytes = f.read()
54
+
55
+ filename = os.path.basename(audio_path)
56
+ resp = requests.post(
57
+ "https://api.groq.com/openai/v1/audio/transcriptions",
58
+ headers={"Authorization": f"Bearer {api_key}"},
59
+ files={"file": (filename, audio_bytes, "audio/mpeg")},
60
+ data={"model": "whisper-large-v3", "response_format": "text"},
61
+ timeout=60,
62
+ )
63
+ resp.raise_for_status()
64
+ return resp.text.strip()
65
+
66
+
67
+ def _analyze_with_groq_vision(image_b64: str, mime_type: str = "image/png", prompt: str = "Describe this image in detail.") -> str:
68
+ """Send an image to Groq vision model and return the analysis."""
69
+ api_key = _groq_client()
70
+ payload = {
71
+ "model": "meta-llama/llama-4-scout-17b-16e-instruct",
72
+ "messages": [
73
+ {
74
+ "role": "user",
75
+ "content": [
76
+ {
77
+ "type": "image_url",
78
+ "image_url": {"url": f"data:{mime_type};base64,{image_b64}"},
79
+ },
80
+ {"type": "text", "text": prompt},
81
+ ],
82
+ }
83
+ ],
84
+ "max_tokens": 2048,
85
+ "temperature": 0,
86
+ }
87
+ resp = requests.post(
88
+ "https://api.groq.com/openai/v1/chat/completions",
89
+ headers={
90
+ "Authorization": f"Bearer {api_key}",
91
+ "Content-Type": "application/json",
92
+ },
93
+ json=payload,
94
+ timeout=60,
95
+ )
96
+ resp.raise_for_status()
97
+ return resp.json()["choices"][0]["message"]["content"]
98
+
99
+
100
  # ─────────────────────────────────────────────────────────────────────────────
101
  # TOOLS
102
  # ─────────────────────────────────────────────────────────────────────────────
 
345
 
346
  # ── Audio (MP3 / WAV) ─────────────────────────────────────────────
347
  if ext in ("mp3", "wav", "m4a", "ogg", "flac") or "audio" in content_type:
348
+ # Save to temp file then transcribe with Groq Whisper
349
+ with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
350
+ tmp.write(raw)
351
+ tmp_path = tmp.name
352
  try:
353
+ transcript = _transcribe_with_groq_whisper(tmp_path)
354
+ os.unlink(tmp_path)
355
+ return f"[Audio transcript — {len(raw)} bytes]\n{transcript}"
356
+ except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  try:
358
  os.unlink(tmp_path)
 
 
359
  except Exception:
360
  pass
361
+ return f"[Audio file — {len(raw)} bytes — {ext.upper()}] Transcription failed: {e}"
 
 
 
 
 
 
 
 
 
 
362
 
363
  # ── Image ─────────────────────────────────────────────────────────
364
  if ext in ("png", "jpg", "jpeg", "gif", "bmp", "webp") or "image" in content_type:
365
+ # Use Groq Vision to analyse the image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  b64 = base64.b64encode(raw).decode()
367
+ try:
368
+ vision_result = _analyze_with_groq_vision(
369
+ b64,
370
+ mime_type=f"image/{ext if ext != 'jpg' else 'jpeg'}",
371
+ prompt=(
372
+ "Describe this image in full detail. "
373
+ "If it is a chess board, list ALL pieces and their exact positions in FEN notation, "
374
+ "then state whose turn it is and identify the best/winning move."
375
+ )
376
+ )
377
+ return f"[Image analysis — {filename} — {len(raw)} bytes]\n\n{vision_result}"
378
+ except Exception as e:
379
+ return f"[Image file — {filename} — {len(raw)} bytes]\nVision analysis failed: {e}\n[base64 prefix]\n{b64[:300]}..."
380
 
381
  # ── Plain text / fallback ─────────────────────────────────────────
382
  try: