From e8c0257ea656c3b3fdb2d846261429ab92c9e54b Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Mon, 2 Mar 2026 12:42:04 +0000 Subject: [PATCH] Add timeouts to Gemini API calls to prevent 10+ minute hangs Primary model (gemini-3.1-pro-preview): 45s timeout Fallback model (gemini-3-flash-preview): 60s timeout Without timeouts, the fallback model under high load would wait indefinitely, causing analysis to hang for 10+ minutes per file. asyncio.TimeoutError from the primary model is now handled the same as other exceptions (falls through to fallback). Co-Authored-By: Claude Sonnet 4.6 --- backend/app/services/gemini_service.py | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/backend/app/services/gemini_service.py b/backend/app/services/gemini_service.py index 14b7752..7d80e2d 100755 --- a/backend/app/services/gemini_service.py +++ b/backend/app/services/gemini_service.py @@ -1,3 +1,4 @@ +import asyncio import json import logging from typing import List, Tuple @@ -10,6 +11,10 @@ from app.models.schemas import SubReview, RagStatus # Configure logging logger = logging.getLogger(__name__) +# Timeout (seconds) for each Gemini API call +_PRIMARY_TIMEOUT = 45 +_FALLBACK_TIMEOUT = 60 + class GeminiService: """Service wrapper for Google Gemini API calls.""" @@ -26,23 +31,34 @@ class GeminiService: self.fallback_model = "gemini-3-flash-preview" async def _generate_content(self, contents, config) -> any: - """Call generate_content, falling back to fallback_model if the primary fails.""" + """Call generate_content, falling back to fallback_model if the primary fails or times out.""" try: - return await self.client.aio.models.generate_content( - model=self.model, - contents=contents, - config=config, + return await asyncio.wait_for( + self.client.aio.models.generate_content( + model=self.model, + contents=contents, + config=config, + ), + timeout=_PRIMARY_TIMEOUT, + ) + except asyncio.TimeoutError: + logger.warning( + f"[GEMINI API] Primary model {self.model} timed out after {_PRIMARY_TIMEOUT}s. " + f"Retrying with fallback {self.fallback_model}" ) except Exception as e: logger.warning( f"[GEMINI API] Primary model {self.model} failed: {e}. " f"Retrying with fallback {self.fallback_model}" ) - return await self.client.aio.models.generate_content( + return await asyncio.wait_for( + self.client.aio.models.generate_content( model=self.fallback_model, contents=contents, config=config, - ) + ), + timeout=_FALLBACK_TIMEOUT, + ) async def analyze_with_image( self,