Spaces:
Running
Running
| # 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. βββββββββββββββββββββββββββββββ | |
| 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() |