import os import io import base64 import requests import re def hf_boss_answer(image, question, lang="en"): token = os.getenv("HF_TOKEN") if not token: return "Setup Required", "HF Token is missing.", "Please set HF_TOKEN." # Using Qwen 2.5 VL as requested model_id = "Qwen/Qwen2.5-VL-7B-Instruct:hyperbolic" try: api_url = "https://router.huggingface.co/v1/chat/completions" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } if image.mode != 'RGB': image = image.convert('RGB') # Consistent resizing for vision models image.thumbnail((1024, 1024)) buffered = io.BytesIO() image.save(buffered, format="JPEG", quality=85) img_b64 = base64.b64encode(buffered.getvalue()).decode("utf-8") img_data_url = f"data:image/jpeg;base64,{img_b64}" prompt = f"""Analyze this image and answer the question. Question: {question} CRITICAL FORMATTING RULES: 1. All output must be in language: {lang}. 2. You MUST use exactly these THREE labels: "Caption:", "Final Answer:", and "Explanation:". 3. Do not use any markdown formatting like **bold** in the labels. 4. "Caption:" must be 1 line. 5. "Final Answer:" must be 1 line. 6. "Explanation:" must be 2-3 sentences. Output Format: Caption: [1 line] Final Answer: [1 line] Explanation: [2-3 sentences]""" payload = { "model": model_id, "messages": [ { "role": "system", "content": "You are a professional VQA assistant. You follow formatting instructions perfectly." }, { "role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": img_data_url}} ] } ], "max_tokens": 1024, "temperature": 0.1 } response = requests.post(api_url, headers=headers, json=payload, timeout=90) # Fallback system if response.status_code != 200: model_id = "google/gemma-3-12b-it:featherless" payload["model"] = model_id response = requests.post(api_url, headers=headers, json=payload, timeout=90) if response.status_code != 200: model_id = "zai-org/GLM-4.5V:novita" payload["model"] = model_id response = requests.post(api_url, headers=headers, json=payload, timeout=90) if response.status_code != 200: return "HF Boss API Error", f"HTTP {response.status_code}", response.text data = response.json() raw_text = data["choices"][0]["message"]["content"] # AGGRESSIVE EXTRACTION LOGIC # This regex handles cases with or without bolding, and different spacings def extract(label, text): # Look for **Label**, Label:, etc. pattern = rf'(?:\*\*|#)?{label}(?:\*\*|#)?\s*[:\-]?\s*(.*?)(?=\s*(?:\n(?:\*\*|#)?[A-Z][a-z]+ [A-Z][a-z]+:?|\n(?:\*\*|#)?[A-Z][a-z]+:?)|$)' match = re.search(pattern, text, re.IGNORECASE | re.DOTALL) return match.group(1).strip() if match else None caption = extract("Caption", raw_text) answer = extract("Final Answer", raw_text) explanation = extract("Explanation", raw_text) # Emergency Fallback: If regex fails, use the raw text split by lines if not caption or not answer: lines = [l.strip() for l in raw_text.split('\n') if l.strip()] caption = caption or (lines[0] if len(lines) > 0 else "Analysis failed to parse.") answer = answer or (lines[1] if len(lines) > 1 else "No answer found in output.") explanation = explanation or (" ".join(lines[2:]) if len(lines) > 2 else raw_text) # Cleanup caption = caption.replace("**", "").replace("\n", " ").strip() answer = answer.replace("**", "").replace("\n", " ").strip() explanation = explanation.replace("**", "").strip() return caption, answer, explanation except Exception as e: return "HF Boss Internal Crash", "Parsing Error", f"Raw: {locals().get('raw_text', 'No text')} | Error: {repr(e)}"