"""Envía una solicitud de chat completion a Ollama (API local compatible con OpenAI).""" import json import urllib.request import urllib.error from typing import Any def ollama_chat( messages: list[dict], model: str = "llama3.1:8b", base_url: str = "http://localhost:11434", temperature: float = 0.7, max_tokens: int = 1024, ) -> dict: """Envía una solicitud de chat completion a Ollama. Args: messages: Lista de mensajes con formato {"role": "system"|"user"|"assistant", "content": str}. model: Nombre del modelo Ollama a usar. base_url: URL base de la instancia Ollama. temperature: Temperatura de generación (0.0 - 1.0). max_tokens: Número máximo de tokens a generar. Returns: Dict con content, model, total_duration_ms y eval_count. Raises: RuntimeError: Si Ollama no está corriendo o retorna error HTTP. """ url = f"{base_url}/api/chat" payload = { "model": model, "messages": messages, "stream": False, "options": { "temperature": temperature, "num_predict": max_tokens, }, } body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url, data=body, headers={"Content-Type": "application/json"}, method="POST", ) try: with urllib.request.urlopen(req, timeout=60) as resp: raw = resp.read() except urllib.error.URLError as exc: reason = str(exc.reason) if hasattr(exc, "reason") else str(exc) if "connection refused" in reason.lower() or "111" in reason: raise RuntimeError(f"Ollama no está corriendo en {base_url}") from exc raise RuntimeError(f"Error de conexión con Ollama: {reason}") from exc except urllib.error.HTTPError as exc: raise RuntimeError(f"Ollama retornó HTTP {exc.code}: {exc.reason}") from exc response: dict[str, Any] = json.loads(raw) content: str = response["message"]["content"] total_duration_ns: int = response.get("total_duration", 0) total_duration_ms: int = int(total_duration_ns / 1_000_000) eval_count: int = response.get("eval_count", 0) return { "content": content, "model": response.get("model", model), "total_duration_ms": total_duration_ms, "eval_count": eval_count, }