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>=2.6.0",
"pydantic-settings>=2.2.0", "pydantic-settings>=2.2.0",
"pyyaml>=6.0.1", "pyyaml>=6.0.1",
"google-generativeai>=0.8.0", "google-genai>=0.8.0",
"anthropic>=0.40.0", "anthropic>=0.40.0",
"openai>=1.50.0", "openai>=1.50.0",
"requests>=2.32.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 from __future__ import annotations
import json import json
import re import re
import time import time
import google.generativeai as genai from google import genai
from google.api_core.exceptions import ResourceExhausted from google.genai import errors as genai_errors
from google.genai import types
from ..config import Settings from ..config import Settings
from ..logging_setup import get_logger from ..logging_setup import get_logger
@ -17,10 +18,17 @@ log = get_logger(__name__)
def _extract_retry_seconds(err: Exception, fallback: float) -> float: 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 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: class GeminiClient:
provider = "gemini" provider = "gemini"
@ -29,45 +37,53 @@ class GeminiClient:
) -> None: ) -> None:
if not settings.gemini_api_key: if not settings.gemini_api_key:
raise RuntimeError("GEMINI_API_KEY nicht gesetzt") raise RuntimeError("GEMINI_API_KEY nicht gesetzt")
genai.configure(api_key=settings.gemini_api_key) self.model = model or "gemini-2.5-flash-lite"
self.model = model or "gemini-2.0-flash"
self.temperature = temperature self.temperature = temperature
self._model = genai.GenerativeModel( self.client = genai.Client(api_key=settings.gemini_api_key)
self.model, self._config = types.GenerateContentConfig(
system_instruction=SYSTEM_PROMPT, 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: def decide(self, user_prompt: str) -> TradeDecision:
attempts = 0 attempts = 0
max_attempts = 3 max_attempts = 3
while True: while True:
try: try:
resp = self._model.generate_content( resp = self.client.models.generate_content(
user_prompt, model=self.model,
generation_config={ contents=user_prompt,
"response_mime_type": "application/json", config=self._config,
"response_schema": JSON_SCHEMA,
"temperature": self.temperature,
},
request_options={"timeout": self.timeout},
) )
break break
except ResourceExhausted as e: except genai_errors.APIError as e:
attempts += 1 if _is_rate_limit(e):
if attempts >= max_attempts: attempts += 1
log.warning("gemini.rate_limit_giveup", attempts=attempts) if attempts >= max_attempts:
return TradeDecision( log.warning("gemini.rate_limit_giveup", attempts=attempts)
action="HOLD", confidence=0.0, suggested_size_pct=0.0, return TradeDecision(
reasoning="rate_limit_exhausted", action="HOLD", confidence=0.0, suggested_size_pct=0.0,
) reasoning="rate_limit_exhausted",
wait = min(_extract_retry_seconds(e, 30.0) + 2, 60.0) )
log.warning("gemini.rate_limit", attempt=attempts, wait_s=wait) wait = min(_extract_retry_seconds(e, 30.0) + 2, 60.0)
time.sleep(wait) log.warning("gemini.rate_limit", attempt=attempts, wait_s=wait)
text = resp.text or "{}" time.sleep(wait)
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: try:
data = json.loads(text) data = json.loads(text)
return TradeDecision.model_validate(data) return TradeDecision.model_validate(data)
except Exception as e: except Exception as e:
log.warning("gemini.parse_failed", error=str(e), raw=text[:300]) 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}",
)