Files
awesome-kubernetes/src/gemini_utils.py

96 lines
4.4 KiB
Python

import httpx
import asyncio
import random
import json
import re
from typing import Dict, Any, List, Optional
from src.config import GEMINI_API_KEYS, GEMINI_API_VERSION, GEMINI_MODELS
from src.logger import log_event
# Global para mantener el índice de la API Key actual
CURRENT_KEY_INDEX = 0
class GeminiDiagnostics:
def __init__(self):
self.attempts = []
def add_attempt(self, model: str, status: int, error: str = None, response_text: str = None):
self.attempts.append({
"model": model,
"status": status,
"error": error,
"response_preview": response_text[:200] if response_text else None
})
def get_report(self) -> str:
report = "DIAGNÓSTICO GEMINI:\n"
for i, a in enumerate(self.attempts):
report += f" {i+1}. [{a['model']}] Status: {a['status']}"
if a['error']: report += f" | Error: {a['error']}"
if a['response_preview']: report += f" | Resp: {a['response_preview']}"
report += "\n"
return report
async def call_gemini_with_retry(prompt: str, response_format: str = "json", max_retries: int = 3):
"""
Llama a la API de Gemini con rotación exhaustiva y REINTENTO REAL en 429.
"""
global CURRENT_KEY_INDEX
if not GEMINI_API_KEYS:
raise ValueError("No hay GEMINI_API_KEYS configuradas.")
diagnostics = GeminiDiagnostics()
async with httpx.AsyncClient() as client:
for key_attempt in range(len(GEMINI_API_KEYS)):
api_key = GEMINI_API_KEYS[CURRENT_KEY_INDEX]
for model in GEMINI_MODELS:
full_model_name = f"models/{model}"
api_url = f"https://generativelanguage.googleapis.com/{GEMINI_API_VERSION}/{full_model_name}:generateContent?key={api_key}"
# Reintentos por modelo (incluyendo 429)
for attempt in range(max_retries + 2):
try:
payload = {"contents": [{"parts": [{"text": prompt}]}]}
response = await client.post(api_url, json=payload, timeout=45)
if response.status_code == 200:
resp_json = response.json()
if 'candidates' in resp_json and resp_json['candidates']:
text_resp = resp_json['candidates'][0]['content']['parts'][0]['text']
if response_format == "json":
match = re.search(r'\{.*\}|\[.*\]', text_resp, re.DOTALL)
if match:
data = json.loads(match.group(0))
return data[0] if isinstance(data, list) and len(data) > 0 else data
diagnostics.add_attempt(model, 200, "JSON no encontrado", text_resp)
break
return text_resp
diagnostics.add_attempt(model, 200, "Sin candidates")
break
elif response.status_code == 429:
wait_time = (10 * (attempt + 1)) + random.random() * 5
log_event(f" [!] API 429 (Límite): Reintentando {model} en {wait_time:.1f}s... (Intento {attempt+1})")
await asyncio.sleep(wait_time)
continue # Reintentar el MISMO modelo
elif response.status_code in [500, 503, 504]:
diagnostics.add_attempt(model, response.status_code, "Server Error")
await asyncio.sleep(5)
continue
else:
diagnostics.add_attempt(model, response.status_code, "API Error", response.text)
break
except Exception as e:
diagnostics.add_attempt(model, 0, f"Excepción: {str(e)}")
break
CURRENT_KEY_INDEX = (CURRENT_KEY_INDEX + 1) % len(GEMINI_API_KEYS)
await asyncio.sleep(2)
raise Exception(f"Fallo crítico Gemini tras rotación exhaustiva.\n{diagnostics.get_report()}")