🔄 Google client 加入 SSL/连接错误自动重试(指数退避,最多5次)
This commit is contained in:
parent
2c87c33c69
commit
a125f5c906
|
|
@ -1,3 +1,5 @@
|
||||||
|
import time
|
||||||
|
import random
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||||
|
|
@ -7,11 +9,7 @@ from .validators import validate_model
|
||||||
|
|
||||||
|
|
||||||
class NormalizedChatGoogleGenerativeAI(ChatGoogleGenerativeAI):
|
class NormalizedChatGoogleGenerativeAI(ChatGoogleGenerativeAI):
|
||||||
"""ChatGoogleGenerativeAI with normalized content output.
|
"""ChatGoogleGenerativeAI with normalized content output and auto-retry on SSL/connection errors."""
|
||||||
|
|
||||||
Gemini 3 models return content as list: [{'type': 'text', 'text': '...'}]
|
|
||||||
This normalizes to string for consistent downstream handling.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _normalize_content(self, response):
|
def _normalize_content(self, response):
|
||||||
content = response.content
|
content = response.content
|
||||||
|
|
@ -25,7 +23,24 @@ class NormalizedChatGoogleGenerativeAI(ChatGoogleGenerativeAI):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def invoke(self, input, config=None, **kwargs):
|
def invoke(self, input, config=None, **kwargs):
|
||||||
return self._normalize_content(super().invoke(input, config, **kwargs))
|
max_retries = 5
|
||||||
|
for attempt in range(1, max_retries + 1):
|
||||||
|
try:
|
||||||
|
return self._normalize_content(super().invoke(input, config, **kwargs))
|
||||||
|
except Exception as e:
|
||||||
|
err = str(e)
|
||||||
|
# 判断是否为可重试的网络错误
|
||||||
|
retryable = any(kw in err for kw in [
|
||||||
|
"SSL", "EOF", "RemoteProtocolError", "ConnectError",
|
||||||
|
"Server disconnected", "ConnectionError", "timeout",
|
||||||
|
"503", "502", "500",
|
||||||
|
])
|
||||||
|
if retryable and attempt < max_retries:
|
||||||
|
wait = 2 ** attempt + random.uniform(0, 2)
|
||||||
|
print(f"\n⚠️ Gemini 连接失败(第 {attempt}/{max_retries} 次),{wait:.1f}s 后重试... [{type(e).__name__}]")
|
||||||
|
time.sleep(wait)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
class GoogleClient(BaseLLMClient):
|
class GoogleClient(BaseLLMClient):
|
||||||
|
|
@ -42,20 +57,14 @@ class GoogleClient(BaseLLMClient):
|
||||||
if key in self.kwargs:
|
if key in self.kwargs:
|
||||||
llm_kwargs[key] = self.kwargs[key]
|
llm_kwargs[key] = self.kwargs[key]
|
||||||
|
|
||||||
# Map thinking_level to appropriate API param based on model
|
|
||||||
# Gemini 3 Pro: low, high
|
|
||||||
# Gemini 3 Flash: minimal, low, medium, high
|
|
||||||
# Gemini 2.5: thinking_budget (0=disable, -1=dynamic)
|
|
||||||
thinking_level = self.kwargs.get("thinking_level")
|
thinking_level = self.kwargs.get("thinking_level")
|
||||||
if thinking_level:
|
if thinking_level:
|
||||||
model_lower = self.model.lower()
|
model_lower = self.model.lower()
|
||||||
if "gemini-3" in model_lower:
|
if "gemini-3" in model_lower:
|
||||||
# Gemini 3 Pro doesn't support "minimal", use "low" instead
|
|
||||||
if "pro" in model_lower and thinking_level == "minimal":
|
if "pro" in model_lower and thinking_level == "minimal":
|
||||||
thinking_level = "low"
|
thinking_level = "low"
|
||||||
llm_kwargs["thinking_level"] = thinking_level
|
llm_kwargs["thinking_level"] = thinking_level
|
||||||
else:
|
else:
|
||||||
# Gemini 2.5: map to thinking_budget
|
|
||||||
llm_kwargs["thinking_budget"] = -1 if thinking_level == "high" else 0
|
llm_kwargs["thinking_budget"] = -1 if thinking_level == "high" else 0
|
||||||
|
|
||||||
return NormalizedChatGoogleGenerativeAI(**llm_kwargs)
|
return NormalizedChatGoogleGenerativeAI(**llm_kwargs)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue