From c5e613d2b1356de7d22981b04a11268b6a6f6947 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 12 Dec 2025 15:31:37 +0100 Subject: [PATCH] verschlanken aus Sicht WP11 --- app/services/llm_service.py | 52 ++++++++++++++----------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/app/services/llm_service.py b/app/services/llm_service.py index e145598..fa7576b 100644 --- a/app/services/llm_service.py +++ b/app/services/llm_service.py @@ -1,6 +1,6 @@ """ app/services/llm_service.py — LLM Client (Ollama) -Version: 0.5.1 (Full: Retry Strategy + Chat Support + JSON Mode) +Version: 0.5.2 (Fix: Removed strict limits, increased Context) """ import httpx @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) class Settings: OLLAMA_URL = os.getenv("MINDNET_OLLAMA_URL", "http://127.0.0.1:11434") - # Timeout für den einzelnen Request (nicht für den gesamten Retry-Zyklus) + # Timeout für die Generierung (lang) LLM_TIMEOUT = float(os.getenv("MINDNET_LLM_TIMEOUT", 300.0)) LLM_MODEL = os.getenv("MINDNET_LLM_MODEL", "phi3:mini") PROMPTS_PATH = os.getenv("MINDNET_PROMPTS_PATH", "./config/prompts.yaml") @@ -28,13 +28,17 @@ class LLMService: self.settings = get_settings() self.prompts = self._load_prompts() - # Connection Limits erhöhen für Parallelität im Import - limits = httpx.Limits(max_keepalive_connections=5, max_connections=10) + # FIX 1: Keine künstlichen Limits mehr. httpx defaults (100) sind besser. + # Wir wollen nicht, dass der Chat wartet, nur weil im Hintergrund Embeddings laufen. + + # Timeout-Konfiguration: + # connect=10.0: Wenn Ollama nicht da ist, failen wir schnell. + # read=LLM_TIMEOUT: Wenn Ollama denkt, geben wir ihm Zeit. + self.timeout = httpx.Timeout(self.settings.LLM_TIMEOUT, connect=10.0) self.client = httpx.AsyncClient( base_url=self.settings.OLLAMA_URL, - timeout=self.settings.LLM_TIMEOUT, - limits=limits + timeout=self.timeout ) def _load_prompts(self) -> dict: @@ -53,24 +57,21 @@ class LLMService: prompt: str, system: str = None, force_json: bool = False, - max_retries: int = 0, # Standard: 0 (Chat failt sofort, Import nutzt >0) - base_delay: float = 5.0 # Start-Wartezeit für Backoff + max_retries: int = 0, + base_delay: float = 2.0 ) -> str: """ Führt einen LLM Call aus. - Features: - - JSON Mode (für Semantic Analyzer) - - System Prompt (für Persona) - - Aggressive Retry (für robusten Import bei Überlast) """ payload: Dict[str, Any] = { "model": self.settings.LLM_MODEL, "prompt": prompt, "stream": False, "options": { - # JSON braucht niedrige Temperature für valide Syntax "temperature": 0.1 if force_json else 0.7, - "num_ctx": 4096 + # FIX 2: Kontext auf 8192 erhöht. + # Wichtig für komplexe Schemas und JSON-Stabilität. + "num_ctx": 8192 } } @@ -82,7 +83,6 @@ class LLMService: attempt = 0 - # RETRY LOOP while True: try: response = await self.client.post("/api/generate", json=payload) @@ -91,43 +91,29 @@ class LLMService: data = response.json() return data.get("response", "").strip() else: - # HTTP Fehler simulieren, um in den except-Block zu springen response.raise_for_status() except Exception as e: - # CATCH-ALL: Wir fangen Timeouts, Connection Errors UND Protokollfehler attempt += 1 - - # Check: Haben wir noch Versuche? if attempt > max_retries: - # Finaler Fehler (wird im Chat oder Log angezeigt) logger.error(f"LLM Final Error (Versuch {attempt}): {e}") - return "Interner LLM Fehler." + # Wir werfen den Fehler weiter, damit der Router nicht "Interner Fehler" als Typ interpretiert + raise e - # Backoff berechnen (5s, 10s, 20s, 40s...) wait_time = base_delay * (2 ** (attempt - 1)) - error_msg = str(e) if str(e) else repr(e) - - logger.warning( - f"⚠️ LLM Fehler ({attempt}/{max_retries}). " - f"Warte {wait_time}s zur Abkühlung... Grund: {error_msg}" - ) - - # Warten und Loop wiederholen + logger.warning(f"⚠️ LLM Retry ({attempt}/{max_retries}) in {wait_time}s: {e}") await asyncio.sleep(wait_time) async def generate_rag_response(self, query: str, context_str: str) -> str: """ WICHTIG FÜR CHAT: - Generiert eine Antwort basierend auf RAG-Kontext. - Nutzt KEINE Retries (User will nicht warten), KEIN JSON. + Kein JSON, keine Retries (User-Latency). """ system_prompt = self.prompts.get("system_prompt", "") rag_template = self.prompts.get("rag_template", "{context_str}\n\n{query}") final_prompt = rag_template.format(context_str=context_str, query=query) - # Chat-Call: force_json=False, max_retries=0 return await self.generate_raw_response( final_prompt, system=system_prompt,