From 2dfe6fd9c3d3697419ef6144ff8d32c3df10c8bd Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 11 Jul 2026 14:29:18 +0200 Subject: [PATCH] =?UTF-8?q?feat(local-llm):=20B0.5-2=20=E2=80=94=20Live-La?= =?UTF-8?q?de-Status=20des=20lokalen=20Modells=20in=20Diagnostic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapter meldet bei Modellwechsel/Erst-Load service_status (service=llm): loading -> ready (mit loadSeconds; freshlyDownloaded 🎉 bei langem Erst-Load) oder error. Laeuft ueber den vorhandenen Pfad: Adapter -> RVS -> Diagnostic (RVS-Client) -> Browser -> updateServiceStatus (generisch; nur Label 'Lokales LLM' ergaenzt). Kein Bridge-/server.js-Change noetig. Download-% gibt llama-swap nicht her — daher Zustands-Status (laedt/bereit/ Fehler), im gemeinsamen Service-Banner wie whisper/flux. Co-Authored-By: Claude Opus 4.8 --- diagnostic/index.html | 2 +- xtts/llm-adapter/adapter.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/diagnostic/index.html b/diagnostic/index.html index d7a576c..c79b869 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -2641,7 +2641,7 @@ // Liste neu aufbauen list.innerHTML = ''; let anyLoading = false, anyError = false; - const labels = { f5tts: 'F5-TTS', whisper: 'Whisper STT', flux: 'FLUX Image-Gen' }; + const labels = { f5tts: 'F5-TTS', whisper: 'Whisper STT', flux: 'FLUX Image-Gen', llm: 'Lokales LLM' }; for (const [s, info] of Object.entries(_serviceState)) { const row = document.createElement('div'); row.style.cssText = 'display:flex;align-items:center;gap:6px;'; diff --git a/xtts/llm-adapter/adapter.py b/xtts/llm-adapter/adapter.py index 1da8fc7..db049bb 100644 --- a/xtts/llm-adapter/adapter.py +++ b/xtts/llm-adapter/adapter.py @@ -110,7 +110,22 @@ async def _call_llama(messages: list, *, max_tokens: int, temperature: float, return {"ok": False, "content": "", "error": str(e)[:300]} +# B0.5-2: Lade-Status ans Diagnostic (service_status, service="llm"). Wir kennen +# den Download-Fortschritt nicht (llama-swap gibt ihn nicht her), aber wir melden +# den Zustand bei Modellwechsel: loading -> ready/error. _last_model = aktuell +# geladenes; _ready_models = in dieser Session schon einmal bereit gewesene +# (fuer den "frisch geladen"-Hinweis 🎉 bei langem Erst-Load). +_last_model = None +_ready_models: set = set() + + +async def _emit_llm_status(ws, state: str, model: str, **extra) -> None: + await _send(ws, "service_status", + {"service": "llm", "state": state, "model": model, **extra}) + + async def _handle_llm_request(ws, payload: dict) -> None: + global _last_model req_id = payload.get("requestId", "") messages = payload.get("messages") or [] if not isinstance(messages, list) or not messages: @@ -123,11 +138,30 @@ async def _handle_llm_request(ws, payload: dict) -> None: stop = payload.get("stop") tools = payload.get("tools") or None model = (payload.get("model") or "").strip() or None + eff_model = model or LLM_MODEL + + # Modellwechsel (oder erster Request) → llama-swap laedt/swappt: Status melden. + switching = eff_model != _last_model + if switching: + await _emit_llm_status(ws, "loading", eff_model) + t0 = time.time() res = await _call_llama(messages, max_tokens=max_tokens, temperature=temperature, stop=stop, tools=tools, model=model) dt = time.time() - t0 + + if switching: + if res.get("ok"): + fresh = (eff_model not in _ready_models) and dt > 25 + _ready_models.add(eff_model) + _last_model = eff_model + await _emit_llm_status(ws, "ready", eff_model, + loadSeconds=round(dt, 1), freshlyDownloaded=fresh) + else: + # bei Fehler _last_model NICHT setzen → naechster Versuch meldet erneut loading + await _emit_llm_status(ws, "error", eff_model, + error=(res.get("error") or "")[:120]) tc = res.get("tool_calls") logger.info("llm_request id=%s model=%s -> ok=%s %.2fs content_len=%d tool_calls=%d", (req_id[:8] if req_id else "?"), model or LLM_MODEL, res.get("ok"), dt,