feat(gemini): migrate to google-genai SDK (no more deprecation warning)
This commit is contained in:
parent
97ccecb82b
commit
b8ccb2f250
@ -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",
|
||||||
|
|||||||
@ -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,31 +37,29 @@ 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:
|
||||||
|
if _is_rate_limit(e):
|
||||||
attempts += 1
|
attempts += 1
|
||||||
if attempts >= max_attempts:
|
if attempts >= max_attempts:
|
||||||
log.warning("gemini.rate_limit_giveup", attempts=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)
|
wait = min(_extract_retry_seconds(e, 30.0) + 2, 60.0)
|
||||||
log.warning("gemini.rate_limit", attempt=attempts, wait_s=wait)
|
log.warning("gemini.rate_limit", attempt=attempts, wait_s=wait)
|
||||||
time.sleep(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:
|
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}",
|
||||||
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user