stevafernandes commited on
Commit
d593f78
·
verified ·
1 Parent(s): 5c73a43

Deploy CellQuant

Browse files
cellquant/gemini_summary.py CHANGED
@@ -96,8 +96,26 @@ def _generate(prompt: str, overlay: Image.Image | None, model: str | None) -> st
96
  resp = client.models.generate_content(model=mid, contents=contents)
97
  text = (resp.text or "").strip()
98
  return text if text else "Gemini returned an empty response."
99
- except Exception as e: # noqa: BLE001 - try the next model id, then surface the error to the UI
100
  last = f"{type(e).__name__}: {e}"
101
- if "NOT_FOUND" not in str(e) and "not found" not in str(e).lower():
102
  break
103
- return f"Gemini summary failed ({last})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  resp = client.models.generate_content(model=mid, contents=contents)
97
  text = (resp.text or "").strip()
98
  return text if text else "Gemini returned an empty response."
99
+ except Exception as e: # noqa: BLE001 - try the next model id on a missing model or an exhausted quota
100
  last = f"{type(e).__name__}: {e}"
101
+ if not _retryable(e):
102
  break
103
+ return f"Gemini summary unavailable right now ({_short_error(last)}). The counts above do not depend on it."
104
+
105
+
106
+ def _retryable(e: Exception) -> bool:
107
+ """Move on to the next model when this one is unknown or its quota / rate limit is used up."""
108
+ msg = str(e)
109
+ return ("NOT_FOUND" in msg or "not found" in msg.lower()
110
+ or "RESOURCE_EXHAUSTED" in msg or "429" in msg[:40] or "quota" in msg.lower() or "rate limit" in msg.lower())
111
+
112
+
113
+ def _short_error(text: str, limit: int = 220) -> str:
114
+ """Status line of an API error plus the API's own message (not the JSON payload), trimmed."""
115
+ import re
116
+ head = text.split("{", 1)[0].strip().rstrip(".") or text
117
+ m = re.search(r"'message':\s*'([^']*)'", text) or re.search(r'"message":\s*"([^"]*)"', text)
118
+ if m:
119
+ msg = m.group(1).split(" Learn more")[0].strip()
120
+ head = f"{head}: {msg}"
121
+ return head[:limit]
tests/test_validation.py CHANGED
@@ -178,3 +178,9 @@ def test_gemini_prompts_and_disabled_paths(monkeypatch):
178
  assert "do not round" in txt and "training set" in txt
179
  assert g.summarize_comparison("C100", rows, COMPARISON_HEADERS).startswith("Gemini summary disabled")
180
  assert g.summarize([{"image": "a", "counts": {"Cell, Antibody +": 1}}]).startswith("Gemini summary disabled")
 
 
 
 
 
 
 
178
  assert "do not round" in txt and "training set" in txt
179
  assert g.summarize_comparison("C100", rows, COMPARISON_HEADERS).startswith("Gemini summary disabled")
180
  assert g.summarize([{"image": "a", "counts": {"Cell, Antibody +": 1}}]).startswith("Gemini summary disabled")
181
+ assert g._retryable(Exception("404 NOT_FOUND. model x")) and g._retryable(Exception("429 RESOURCE_EXHAUSTED. {'error': {}}"))
182
+ assert not g._retryable(Exception("400 INVALID_ARGUMENT. bad key"))
183
+ assert g._short_error("ClientError: 429 RESOURCE_EXHAUSTED. {'error': {'code': 429}}") == "ClientError: 429 RESOURCE_EXHAUSTED"
184
+ cap = ("ClientError: 429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'Your billing account has exceeded its "
185
+ "monthly spending cap. Please go to AI Studio. Learn more at https://x', 'status': 'RESOURCE_EXHAUSTED'}}")
186
+ assert g._short_error(cap) == "ClientError: 429 RESOURCE_EXHAUSTED: Your billing account has exceeded its monthly spending cap. Please go to AI Studio."