Commit ·
cd5a36a
1
Parent(s): ca6fc55
Fix Gemini blocked content handling and improve MongoDB status endpoint
Browse files- Add explicit finish_reason checking for blocked content (17) and safety blocks (2)
- Add generation_config to request image/png response explicitly
- Better error messages for blocked content
- Improve /mongo-status endpoint to show recent 5 logs with full details
- This will help diagnose both Gemini API issues and verify MongoDB storage
- api/main.py +22 -3
- src/core.py +36 -8
api/main.py
CHANGED
|
@@ -491,9 +491,27 @@ def mongo_status(_: None = Depends(bearer_auth)) -> Dict[str, object]:
|
|
| 491 |
try:
|
| 492 |
count = mongo_logs.count_documents({})
|
| 493 |
status["api_logs_count"] = count
|
| 494 |
-
# Get latest
|
| 495 |
-
|
| 496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
status["latest_log"] = {
|
| 498 |
"_id": str(latest.get("_id")),
|
| 499 |
"output_id": latest.get("output_id"),
|
|
@@ -502,6 +520,7 @@ def mongo_status(_: None = Depends(bearer_auth)) -> Dict[str, object]:
|
|
| 502 |
}
|
| 503 |
except Exception as err:
|
| 504 |
status["api_logs_error"] = str(err)
|
|
|
|
| 505 |
|
| 506 |
return status
|
| 507 |
|
|
|
|
| 491 |
try:
|
| 492 |
count = mongo_logs.count_documents({})
|
| 493 |
status["api_logs_count"] = count
|
| 494 |
+
# Get latest 5 documents
|
| 495 |
+
latest_docs = list(mongo_logs.find().sort("timestamp", -1).limit(5))
|
| 496 |
+
status["recent_logs"] = []
|
| 497 |
+
for doc in latest_docs:
|
| 498 |
+
doc_dict = {
|
| 499 |
+
"_id": str(doc.get("_id")),
|
| 500 |
+
"output_id": doc.get("output_id"),
|
| 501 |
+
"status": doc.get("status"),
|
| 502 |
+
"timestamp": doc.get("timestamp").isoformat() if isinstance(doc.get("timestamp"), datetime) else str(doc.get("timestamp")),
|
| 503 |
+
}
|
| 504 |
+
if "input_image_id" in doc:
|
| 505 |
+
doc_dict["input_image_id"] = doc.get("input_image_id")
|
| 506 |
+
if "input_mask_id" in doc:
|
| 507 |
+
doc_dict["input_mask_id"] = doc.get("input_mask_id")
|
| 508 |
+
if "error" in doc:
|
| 509 |
+
doc_dict["error"] = doc.get("error")
|
| 510 |
+
status["recent_logs"].append(doc_dict)
|
| 511 |
+
|
| 512 |
+
# Get latest document for backward compatibility
|
| 513 |
+
if latest_docs:
|
| 514 |
+
latest = latest_docs[0]
|
| 515 |
status["latest_log"] = {
|
| 516 |
"_id": str(latest.get("_id")),
|
| 517 |
"output_id": latest.get("output_id"),
|
|
|
|
| 520 |
}
|
| 521 |
except Exception as err:
|
| 522 |
status["api_logs_error"] = str(err)
|
| 523 |
+
log.error("Error querying MongoDB: %s", err, exc_info=True)
|
| 524 |
|
| 525 |
return status
|
| 526 |
|
src/core.py
CHANGED
|
@@ -163,21 +163,46 @@ def _call_gemini_edit(
|
|
| 163 |
{"mime_type": "image/png", "data": mask_bytes},
|
| 164 |
]
|
| 165 |
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
output_img: Image.Image | None = None
|
| 169 |
|
| 170 |
-
#
|
| 171 |
-
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
# Extract first image from response parts
|
| 175 |
try:
|
| 176 |
-
candidates = getattr(response, "candidates", [])
|
| 177 |
log.debug("Number of candidates: %d", len(candidates))
|
| 178 |
|
| 179 |
for idx, candidate in enumerate(candidates):
|
| 180 |
-
log.debug("Candidate %d type: %s", idx, type(candidate))
|
| 181 |
parts = getattr(candidate, "content", None)
|
| 182 |
if not parts:
|
| 183 |
log.debug("Candidate %d has no content", idx)
|
|
@@ -189,7 +214,6 @@ def _call_gemini_edit(
|
|
| 189 |
log.debug("Candidate %d has %d parts", idx, len(response_parts))
|
| 190 |
|
| 191 |
for part_idx, part in enumerate(response_parts):
|
| 192 |
-
log.debug("Part %d type: %s", part_idx, type(part))
|
| 193 |
inline = getattr(part, "inline_data", None)
|
| 194 |
if inline:
|
| 195 |
log.debug("Part %d has inline_data, mime_type: %s", part_idx, getattr(inline, "mime_type", None))
|
|
@@ -214,11 +238,15 @@ def _call_gemini_edit(
|
|
| 214 |
# Log full response for debugging
|
| 215 |
try:
|
| 216 |
response_text = str(response)
|
| 217 |
-
log.error("Gemini generate_content returned no image. Full response (first
|
| 218 |
# Try to extract any error messages
|
| 219 |
if hasattr(response, "prompt_feedback"):
|
| 220 |
feedback = response.prompt_feedback
|
| 221 |
log.error("Prompt feedback: %s", feedback)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
except Exception:
|
| 223 |
pass
|
| 224 |
raise RuntimeError("Gemini generate_content returned no image. Check logs for details.")
|
|
|
|
| 163 |
{"mime_type": "image/png", "data": mask_bytes},
|
| 164 |
]
|
| 165 |
|
| 166 |
+
# Request image response explicitly
|
| 167 |
+
generation_config = {
|
| 168 |
+
"response_mime_type": "image/png",
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
try:
|
| 172 |
+
response = model.generate_content(
|
| 173 |
+
content,
|
| 174 |
+
generation_config=generation_config,
|
| 175 |
+
stream=False
|
| 176 |
+
)
|
| 177 |
+
except Exception as gen_err:
|
| 178 |
+
log.error("Gemini generate_content raised exception: %s", gen_err, exc_info=True)
|
| 179 |
+
raise RuntimeError(f"Gemini API error: {gen_err}")
|
| 180 |
|
| 181 |
output_img: Image.Image | None = None
|
| 182 |
|
| 183 |
+
# Check for blocked content or errors
|
| 184 |
+
candidates = getattr(response, "candidates", [])
|
| 185 |
+
if not candidates:
|
| 186 |
+
log.error("Gemini returned no candidates")
|
| 187 |
+
raise RuntimeError("Gemini API returned no candidates. The request may have been blocked.")
|
| 188 |
+
|
| 189 |
+
# Check finish_reason for blocked content
|
| 190 |
+
for idx, candidate in enumerate(candidates):
|
| 191 |
+
finish_reason = getattr(candidate, "finish_reason", None)
|
| 192 |
+
if finish_reason:
|
| 193 |
+
# finish_reason values: 0=STOP, 1=MAX_TOKENS, 2=SAFETY, 3=RECITATION, 4=OTHER, 17=BLOCKED
|
| 194 |
+
if finish_reason == 17 or finish_reason == 2:
|
| 195 |
+
safety_ratings = getattr(candidate, "safety_ratings", [])
|
| 196 |
+
log.error("Gemini blocked the request. Finish reason: %s, Safety ratings: %s", finish_reason, safety_ratings)
|
| 197 |
+
raise RuntimeError(f"Gemini API blocked the content (finish_reason={finish_reason}). The image may violate safety policies.")
|
| 198 |
+
elif finish_reason != 0: # 0 = STOP (normal completion)
|
| 199 |
+
log.warning("Gemini finished with non-zero reason: %s", finish_reason)
|
| 200 |
|
| 201 |
# Extract first image from response parts
|
| 202 |
try:
|
|
|
|
| 203 |
log.debug("Number of candidates: %d", len(candidates))
|
| 204 |
|
| 205 |
for idx, candidate in enumerate(candidates):
|
|
|
|
| 206 |
parts = getattr(candidate, "content", None)
|
| 207 |
if not parts:
|
| 208 |
log.debug("Candidate %d has no content", idx)
|
|
|
|
| 214 |
log.debug("Candidate %d has %d parts", idx, len(response_parts))
|
| 215 |
|
| 216 |
for part_idx, part in enumerate(response_parts):
|
|
|
|
| 217 |
inline = getattr(part, "inline_data", None)
|
| 218 |
if inline:
|
| 219 |
log.debug("Part %d has inline_data, mime_type: %s", part_idx, getattr(inline, "mime_type", None))
|
|
|
|
| 238 |
# Log full response for debugging
|
| 239 |
try:
|
| 240 |
response_text = str(response)
|
| 241 |
+
log.error("Gemini generate_content returned no image. Full response (first 1000 chars): %s", response_text[:1000])
|
| 242 |
# Try to extract any error messages
|
| 243 |
if hasattr(response, "prompt_feedback"):
|
| 244 |
feedback = response.prompt_feedback
|
| 245 |
log.error("Prompt feedback: %s", feedback)
|
| 246 |
+
# Check candidates for finish reasons
|
| 247 |
+
for idx, candidate in enumerate(candidates):
|
| 248 |
+
finish_reason = getattr(candidate, "finish_reason", None)
|
| 249 |
+
log.error("Candidate %d finish_reason: %s", idx, finish_reason)
|
| 250 |
except Exception:
|
| 251 |
pass
|
| 252 |
raise RuntimeError("Gemini generate_content returned no image. Check logs for details.")
|