mission-control-v2 / core /llm_client.py
vfven's picture
Update core/llm_client.py
f7c8bab verified
Raw
History Blame
6.64 kB
# core/llm_client.py
import asyncio
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from agents.registry import PROVIDERS, AGENT_REGISTRY
class LLMClient:
def __init__(self):
self.semaphore = asyncio.Semaphore(6) # mΓ‘ximo 6 llamadas simultΓ‘neas
async def call(self, agent_key: str, messages: list, temperature: float = 0.7):
agent = AGENT_REGISTRY.get(agent_key)
if not agent:
raise ValueError(f"Agente {agent_key} no encontrado")
provider = PROVIDERS[agent["provider"]]
system_prompt = agent["role"]
full_messages = [{"role": "system", "content": system_prompt}] + messages
if provider["type"] == "gemini":
# ── Soporte para Google Gemini ───────────────────────────────────────
try:
import google.generativeai as genai
genai.configure(api_key=provider["key"])
model_name = agent["models"][0]
print(f"Intentando Gemini con modelo: {model_name}")
try:
model = genai.GenerativeModel(model_name)
except Exception as e:
print(f"Modelo {model_name} fallΓ³: {str(e)}")
model_name = "gemini-2.5-flash" # fallback seguro
print(f"Usando fallback: {model_name}")
model = genai.GenerativeModel(model_name)
# Convertir formato OpenAI β†’ Gemini
gemini_messages = []
for msg in full_messages:
role = "user" if msg["role"] in ("system", "user") else "model"
gemini_messages.append({
"role": role,
"parts": [{"text": msg["content"]}]
})
response = await asyncio.to_thread(
model.generate_content,
gemini_messages,
generation_config={"temperature": temperature, "max_output_tokens": 4096}
)
print(f"Gemini respondiΓ³ exitosamente con {model_name}")
return response.text
except ImportError:
raise ImportError("Instala google-generativeai: pip install google-generativeai")
except Exception as e:
print(f"Error completo en Gemini ({model_name}): {str(e)}")
if hasattr(e, 'response'):
print(f"Status: {e.response.status_code}")
print(f"Body: {e.response.text[:500]}")
raise
elif provider["type"] == "openai_compat":
# ── Soporte para OpenRouter, Groq, etc. ───────────────────────────────
@retry(
stop=stop_after_attempt(6), # un intento mΓ‘s
wait=wait_exponential(multiplier=2, min=5, max=120), # espera progresiva: 5s β†’ 10s β†’ 20s β†’ 40s β†’ 80s β†’ 120s
retry=retry_if_exception_type((httpx.HTTPStatusError, httpx.TimeoutException)),
before_sleep=lambda retry_state: print(f"Reintento {retry_state.attempt_number} despuΓ©s de {retry_state.outcome.exception()}")
)
async def try_provider(base_url, model, api_key, headers):
async with self.semaphore:
payload = {
"model": model,
"messages": full_messages,
"temperature": temperature,
"max_tokens": 4096,
"top_p": 0.92
}
print(f"Intentando {provider['name']} con modelo {model}")
async with httpx.AsyncClient(timeout=120) as client: # timeout mΓ‘s largo
url = f"{base_url}/chat/completions" # ← Β‘esto es lo que falta!
print(f"URL completa: {url}")
resp = await client.post(
url,
headers={"Authorization": f"Bearer {api_key}", **headers},
json=payload
)
try:
resp.raise_for_status()
except httpx.HTTPStatusError as exc:
print(f"HTTP Error {resp.status_code} con {model}")
print(f"Response body: {resp.text[:500]}")
raise exc
content = resp.json()["choices"][0]["message"]["content"]
print(f"Γ‰xito con {model}")
return content
last_err = None
for model in agent.get("models", []):
try:
return await try_provider(
provider["base_url"],
model,
provider["key"],
provider.get("headers", {})
)
except Exception as e:
print(f"Error completo con {model}: {str(e)}")
if hasattr(e, 'response') and e.response is not None:
print(f" β†’ Status: {e.response.status_code}")
print(f" β†’ Body: {e.response.text[:500]}")
print(f" β†’ Headers: {dict(e.response.headers)}")
last_err = e
# Fallback a OpenRouter
if agent["provider"] != "openrouter" and PROVIDERS["openrouter"]["key"]:
or_p = PROVIDERS["openrouter"]
print("Probando fallback a OpenRouter...")
for model in or_p["models"][:3]: # solo los primeros 3 para no saturar
try:
return await try_provider(
or_p["base_url"],
model,
or_p["key"],
or_p.get("headers", {})
)
except Exception as e:
print(f"Fallback fallΓ³ con {model}: {str(e)}")
last_err = e
error_msg = f"Todos los proveedores fallaron. Último error: {last_err}"
print(error_msg)
raise Exception(error_msg)
else:
raise ValueError(f"Tipo de proveedor desconocido: {provider['type']}")
# Instancia global
llm = LLMClient()