|
33 | 33 |
|
34 | 34 | import pipmaster as pm |
35 | 35 |
|
36 | | -# Install the Google Gemini client on demand |
| 36 | +# Install the Google Gemini client and its dependencies on demand |
37 | 37 | if not pm.is_installed("google-genai"): |
38 | 38 | pm.install("google-genai") |
| 39 | +if not pm.is_installed("google-api-core"): |
| 40 | + pm.install("google-api-core") |
39 | 41 |
|
40 | 42 | from google import genai # type: ignore |
41 | 43 | from google.genai import types # type: ignore |
| 44 | +from google.api_core import exceptions as google_api_exceptions # type: ignore |
42 | 45 |
|
43 | 46 | DEFAULT_GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com" |
44 | 47 |
|
45 | 48 | LOG = logging.getLogger(__name__) |
46 | 49 |
|
47 | 50 |
|
| 51 | +class InvalidResponseError(Exception): |
| 52 | + """Custom exception class for triggering retry mechanism when Gemini returns empty responses""" |
| 53 | + |
| 54 | + pass |
| 55 | + |
| 56 | + |
48 | 57 | @lru_cache(maxsize=8) |
49 | 58 | def _get_gemini_client( |
50 | 59 | api_key: str, base_url: str | None, timeout: int | None = None |
@@ -176,6 +185,21 @@ def _extract_response_text( |
176 | 185 | return ("\n".join(regular_parts), "\n".join(thought_parts)) |
177 | 186 |
|
178 | 187 |
|
| 188 | +@retry( |
| 189 | + stop=stop_after_attempt(3), |
| 190 | + wait=wait_exponential(multiplier=1, min=4, max=60), |
| 191 | + retry=( |
| 192 | + retry_if_exception_type(google_api_exceptions.InternalServerError) |
| 193 | + | retry_if_exception_type(google_api_exceptions.ServiceUnavailable) |
| 194 | + | retry_if_exception_type(google_api_exceptions.ResourceExhausted) |
| 195 | + | retry_if_exception_type(google_api_exceptions.GatewayTimeout) |
| 196 | + | retry_if_exception_type(google_api_exceptions.BadGateway) |
| 197 | + | retry_if_exception_type(google_api_exceptions.DeadlineExceeded) |
| 198 | + | retry_if_exception_type(google_api_exceptions.Aborted) |
| 199 | + | retry_if_exception_type(google_api_exceptions.Unknown) |
| 200 | + | retry_if_exception_type(InvalidResponseError) |
| 201 | + ), |
| 202 | +) |
179 | 203 | async def gemini_complete_if_cache( |
180 | 204 | model: str, |
181 | 205 | prompt: str, |
@@ -382,7 +406,7 @@ async def _async_stream() -> AsyncIterator[str]: |
382 | 406 | final_text = regular_text or "" |
383 | 407 |
|
384 | 408 | if not final_text: |
385 | | - raise RuntimeError("Gemini response did not contain any text content.") |
| 409 | + raise InvalidResponseError("Gemini response did not contain any text content.") |
386 | 410 |
|
387 | 411 | if "\\u" in final_text: |
388 | 412 | final_text = safe_unicode_decode(final_text.encode("utf-8")) |
@@ -434,7 +458,14 @@ async def gemini_model_complete( |
434 | 458 | stop=stop_after_attempt(3), |
435 | 459 | wait=wait_exponential(multiplier=1, min=4, max=60), |
436 | 460 | retry=( |
437 | | - retry_if_exception_type(Exception) # Gemini uses generic exceptions |
| 461 | + retry_if_exception_type(google_api_exceptions.InternalServerError) |
| 462 | + | retry_if_exception_type(google_api_exceptions.ServiceUnavailable) |
| 463 | + | retry_if_exception_type(google_api_exceptions.ResourceExhausted) |
| 464 | + | retry_if_exception_type(google_api_exceptions.GatewayTimeout) |
| 465 | + | retry_if_exception_type(google_api_exceptions.BadGateway) |
| 466 | + | retry_if_exception_type(google_api_exceptions.DeadlineExceeded) |
| 467 | + | retry_if_exception_type(google_api_exceptions.Aborted) |
| 468 | + | retry_if_exception_type(google_api_exceptions.Unknown) |
438 | 469 | ), |
439 | 470 | ) |
440 | 471 | async def gemini_embed( |
|
0 commit comments