File size: 3,220 Bytes
88c4bb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import time
import sys
import base64
from io import BytesIO
import traceback
from PIL import Image
from .llm_client import gemini_client, groq_client

def extract_text_from_image(image: Image.Image) -> str:
    """
    Extracts text, handwriting, and structural info from an image using a cascading fallback strategy.
    Tier 1: Google Gemini 2.5 Flash
    Tier 2: Groq Llama 3.2 11B Vision
    """
    prompt = "Extract all text, handwriting, and structural information from this image perfectly as markdown. Ignore non-informational background elements or formatting artifacts. Render all mathematical formulas strictly using LaTeX."
    
    print("LOG: [Vision Client] -> Started...")
    start_time = time.time()
    
    # Tier 1: Gemini 2.5 Flash
    if gemini_client:
        try:
            print("Attempting Tier 1 Vision (Google Gemini 2.5 Flash)...")
            response = gemini_client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[image, prompt]
            )
            elapsed = time.time() - start_time
            print(f"LOG: [Vision Client] -> Completed in {elapsed:.2f}s (Gemini)")
            return response.text if response.text else ""
        except Exception as e:
            print(f"[Tier 1 Vision Error] Gemini failed: {e}", file=sys.stderr)
    else:
        print("[Tier 1 Vision Warning] Gemini API Key missing.", file=sys.stderr)

    # Tier 2: Groq Llama Vision Fallback
    if groq_client:
        try:
            print("Attempting Tier 2 Vision (Groq llama-3.2-11b-vision-preview)...")
            # Convert PIL image to base64
            buffered = BytesIO()
            if image.mode != "RGB":
                image = image.convert("RGB")
            image.save(buffered, format="JPEG")
            base64_image = base64.b64encode(buffered.getvalue()).decode('utf-8')
            
            response = groq_client.chat.completions.create(
                model="llama-3.2-11b-vision-preview",
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {"type": "text", "text": prompt},
                            {
                                "type": "image_url",
                                "image_url": {
                                    "url": f"data:image/jpeg;base64,{base64_image}"
                                }
                            }
                        ]
                    }
                ]
            )
            elapsed = time.time() - start_time
            print(f"LOG: [Vision Client] -> Completed in {elapsed:.2f}s (Groq)")
            return response.choices[0].message.content
        except Exception as e:
            print(f"[Tier 2 Vision Error] Groq failed: {e}", file=sys.stderr)
    else:
        print("[Tier 2 Vision Warning] Groq API Key missing.", file=sys.stderr)

    # Total Failure
    print("[CRITICAL Vision] All Vision endpoints failed.", file=sys.stderr)
    elapsed = time.time() - start_time
    print(f"LOG: [Vision Client] -> Completed in {elapsed:.2f}s (Failed)")
    return "[Error: Vision extraction failed. All OCR endpoints are currently unreachable.]"