feat(gemini): migrate to google-genai SDK (no more deprecation warning)

This commit is contained in:
sylyx 2026-05-07 15:09:43 +02:00
parent 97ccecb82b
commit b8ccb2f250
2 changed files with 47 additions and 31 deletions

View File

@ -11,7 +11,7 @@ dependencies = [
"pydantic>=2.6.0",
"pydantic-settings>=2.2.0",
"pyyaml>=6.0.1",
"google-generativeai>=0.8.0",
"google-genai>=0.8.0",
"anthropic>=0.40.0",
"openai>=1.50.0",
"requests>=2.32.0",

View File

@ -1,12 +1,13 @@
"""Google-Gemini-Client mit JSON-Output."""
"""Google-Gemini-Client mit JSON-Output (neues google-genai SDK)."""
from __future__ import annotations
import json
import re
import time
import google.generativeai as genai
from google.api_core.exceptions import ResourceExhausted
from google import genai
from google.genai import errors as genai_errors
from google.genai import types
from ..config import Settings
from ..logging_setup import get_logger
@ -17,10 +18,17 @@ log = get_logger(__name__)
def _extract_retry_seconds(err: Exception, fallback: float) -> float:
m = re.search(r"retry in (\d+(?:\.\d+)?)", str(err))
m = re.search(r"retry in (\d+(?:\.\d+)?)", str(err), re.IGNORECASE)
return float(m.group(1)) if m else fallback
def _is_rate_limit(err: Exception) -> bool:
code = getattr(err, "code", None) or getattr(err, "status_code", None)
if code == 429:
return True
return "429" in str(err) or "RESOURCE_EXHAUSTED" in str(err).upper()
class GeminiClient:
provider = "gemini"
@ -29,31 +37,29 @@ class GeminiClient:
) -> None:
if not settings.gemini_api_key:
raise RuntimeError("GEMINI_API_KEY nicht gesetzt")
genai.configure(api_key=settings.gemini_api_key)
self.model = model or "gemini-2.0-flash"
self.model = model or "gemini-2.5-flash-lite"
self.temperature = temperature
self._model = genai.GenerativeModel(
self.model,
self.client = genai.Client(api_key=settings.gemini_api_key)
self._config = types.GenerateContentConfig(
system_instruction=SYSTEM_PROMPT,
temperature=self.temperature,
response_mime_type="application/json",
response_schema=JSON_SCHEMA,
)
self.timeout = settings.ai.timeout_seconds
def decide(self, user_prompt: str) -> TradeDecision:
attempts = 0
max_attempts = 3
while True:
try:
resp = self._model.generate_content(
user_prompt,
generation_config={
"response_mime_type": "application/json",
"response_schema": JSON_SCHEMA,
"temperature": self.temperature,
},
request_options={"timeout": self.timeout},
resp = self.client.models.generate_content(
model=self.model,
contents=user_prompt,
config=self._config,
)
break
except ResourceExhausted as e:
except genai_errors.APIError as e:
if _is_rate_limit(e):
attempts += 1
if attempts >= max_attempts:
log.warning("gemini.rate_limit_giveup", attempts=attempts)
@ -64,10 +70,20 @@ class GeminiClient:
wait = min(_extract_retry_seconds(e, 30.0) + 2, 60.0)
log.warning("gemini.rate_limit", attempt=attempts, wait_s=wait)
time.sleep(wait)
text = resp.text or "{}"
continue
log.warning("gemini.api_error", error=str(e)[:300])
return TradeDecision(
action="HOLD", confidence=0.0, suggested_size_pct=0.0,
reasoning=f"api_error: {e}",
)
text = (resp.text or "{}").strip()
try:
data = json.loads(text)
return TradeDecision.model_validate(data)
except Exception as e:
log.warning("gemini.parse_failed", error=str(e), raw=text[:300])
return TradeDecision(action="HOLD", confidence=0.0, suggested_size_pct=0.0, reasoning=f"parse_error: {e}")
return TradeDecision(
action="HOLD", confidence=0.0, suggested_size_pct=0.0,
reasoning=f"parse_error: {e}",
)