From d2398b3624a9960e8b7daac23d5a0cfa6353d899 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Wed, 15 Jul 2026 15:30:36 +0200 Subject: [PATCH] Bild-Weiche v2: frischer Client + Retry fuer den Vision-Unteraufruf Befund :9010 (Journal): httpx.ReadTimeout nach Sekunden trotz timeout=240 - der gepoolte Keep-Alive-Client reicht Sockets wieder aus, die llama-swap beim Modell-Swap gekappt hat. Der Beschreibungs-Unteraufruf nutzt jetzt einen eigenen Kurzzeit-Client und versucht es einmal erneut. Co-Authored-By: Claude Fable 5 --- backend/routers/gateway_proxy.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/routers/gateway_proxy.py b/backend/routers/gateway_proxy.py index 1183d82..25d4ed8 100644 --- a/backend/routers/gateway_proxy.py +++ b/backend/routers/gateway_proxy.py @@ -1,6 +1,8 @@ import logging import os +import httpx + from fastapi import APIRouter, Request from fastapi.responses import JSONResponse, StreamingResponse @@ -61,18 +63,23 @@ async def _bilder_fuer_coder_beschreiben(body: dict, client, vision_alias: str) "messages": [{"role": "user", "content": [part, {"type": "text", "text": _BILD_BESCHREIB_PROMPT}]}], } - try: - # Grosszuegiger Timeout: VL-30B ist on-demand (Kaltladen ~30 s) + Beschreibung ~25 s. - r = await client.post(f"{LLAMA_SWAP_URL}/v1/chat/completions", json=frage, timeout=240.0) - text = "" - if r.status_code == 200: - text = ((r.json().get("choices") or [{}])[0].get("message") or {}).get("content") or "" - if not text.strip(): - log.warning("Bild-Weiche v2: Beschreibung leer/fehlgeschlagen (HTTP %s) — Fallback Umleitung. Body: %.200s", - r.status_code, r.text) - return False - except Exception: - log.warning("Bild-Weiche v2: Vision-Aufruf scheiterte — Fallback Umleitung", exc_info=True) + # Eigener Kurzzeit-Client statt des gepoolten (Befund 15.07.: ReadTimeout nach + # Sekunden trotz timeout=240 — llama-swap kappt beim Modell-Swap gern alte + # Keep-Alive-Sockets, der Pool reicht sie trotzdem wieder aus) + 1 Retry. + text = "" + for versuch in (1, 2): + try: + async with httpx.AsyncClient(timeout=240.0) as c: + r = await c.post(f"{LLAMA_SWAP_URL}/v1/chat/completions", json=frage) + if r.status_code == 200: + text = ((r.json().get("choices") or [{}])[0].get("message") or {}).get("content") or "" + if text.strip(): + break + log.warning("Bild-Weiche v2 (Versuch %s): Beschreibung leer/HTTP %s — %.200s", + versuch, r.status_code, r.text) + except Exception: + log.warning("Bild-Weiche v2 (Versuch %s): Vision-Aufruf scheiterte", versuch, exc_info=True) + if not text.strip(): return False msg["content"][idx] = {"type": "text", "text": ( f"[Bild {nr}: dem Request lag ein Bild bei — {vision_alias} beschreibt es so:\n"