Calls the Google Gemini API to judge whether an image meets a key point.
| 73 | |
| 74 | |
| 75 | class GeminiJudge: |
| 76 | """Calls the Google Gemini API to judge whether an image meets a key point.""" |
| 77 | |
| 78 | def __init__( |
| 79 | self, |
| 80 | model: str = "gemini-2.5-pro", |
| 81 | api_key: Optional[str] = None, |
| 82 | max_retries: int = 3, |
| 83 | retry_delay: float = 1.0, |
| 84 | ): |
| 85 | if genai is None: |
| 86 | raise ImportError( |
| 87 | "google-genai is required for the default judge. " |
| 88 | "Install it with `pip install google-genai`." |
| 89 | ) |
| 90 | api_key = api_key or os.getenv("GEMINI_API_KEY") |
| 91 | if not api_key: |
| 92 | raise ValueError( |
| 93 | "No Gemini API key found. Set the GEMINI_API_KEY environment " |
| 94 | "variable or pass api_key. Get a key at " |
| 95 | "https://aistudio.google.com/apikey" |
| 96 | ) |
| 97 | self.client = genai.Client(api_key=api_key) |
| 98 | self.model = model |
| 99 | self.max_retries = max_retries |
| 100 | self.retry_delay = retry_delay |
| 101 | |
| 102 | def __call__(self, instruction: str, image: str) -> Optional[str]: |
| 103 | """Return the judge's raw text response, or ``None`` if all retries fail.""" |
| 104 | data, mime_type = _load_image_bytes(image) |
| 105 | image_part = types.Part.from_bytes(data=data, mime_type=mime_type) |
| 106 | |
| 107 | for attempt in range(1, self.max_retries + 1): |
| 108 | try: |
| 109 | response = self.client.models.generate_content( |
| 110 | model=self.model, |
| 111 | contents=[image_part, instruction], |
| 112 | ) |
| 113 | if response.text: |
| 114 | return response.text |
| 115 | except Exception as exc: # noqa: BLE001 - retry on any API error |
| 116 | logger.warning("Judge call failed (attempt %d): %s", attempt, exc) |
| 117 | time.sleep(self.retry_delay) |
| 118 | return None |
| 119 | |
| 120 | |
| 121 | def parse_verdict(message: Optional[str]) -> int: |