diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py
index a2d816c..95b95de 100644
--- a/bridge/aria_bridge.py
+++ b/bridge/aria_bridge.py
@@ -3544,12 +3544,17 @@ class ARIABridge:
iid = (payload.get("instanceId") or "").strip()
if iid:
prev = self._workers.get(iid, {})
+ _models = payload.get("models")
self._workers[iid] = {
"instanceId": iid,
"service": payload.get("service") or "",
"node": payload.get("node") or "",
"gpus": payload.get("gpus") or "",
"model": payload.get("model") or "",
+ # models: welche Modelle die Box fahren kann (llm/llama-swap).
+ # Fallback auf [model] fuer alte Adapter ohne models-Feld.
+ "models": [m for m in _models if m] if isinstance(_models, list)
+ else ([payload.get("model")] if payload.get("model") else []),
"busy": bool(prev.get("busy", False)),
"last_seen": time.time(),
}
@@ -4006,9 +4011,10 @@ class ARIABridge:
req_payload["tools"] = tools
if model:
req_payload["model"] = model
- # Redundanz/Multitasking: freie llm-Instanz gezielt adressieren; None
- # → Broadcast wie bisher. Mehrere Instanzen → parallele Turns.
- llm_target = self._pick_worker("llm")
+ # Redundanz/Multitasking: freie llm-Instanz gezielt adressieren, die
+ # das gewaehlte Modell fahren kann; None → Broadcast wie bisher.
+ # Mehrere Boxen mit demselben Modell → Round-Robin (pro Projekt verteilt).
+ llm_target = self._pick_worker("llm", model=model or None)
if llm_target:
req_payload["targetInstance"] = llm_target
logger.info("[rvs] llm_request → llm-adapter (id=%s, msgs=%d, max_tokens=%d, tools=%d, model=%s, target=%s)",
@@ -4767,29 +4773,39 @@ class ARIABridge:
out.append({
"instanceId": w["instanceId"], "service": w.get("service") or "",
"node": w.get("node") or "", "gpus": w.get("gpus") or "",
- "model": w.get("model") or "", "busy": bool(w.get("busy")),
+ "model": w.get("model") or "", "models": w.get("models") or [],
+ "busy": bool(w.get("busy")),
"online": (now - w.get("last_seen", 0)) < self.WORKER_OFFLINE_S,
})
return out
- def _pick_worker(self, service: str) -> Optional[str]:
+ def _pick_worker(self, service: str, model: Optional[str] = None) -> Optional[str]:
"""Waehlt eine online, moeglichst freie Instanz des Diensts (Round-Robin
- ueber die freien). Gibt die instanceId oder None (keine online). Fuer
- Stage-3-Routing (targetInstance)."""
+ ueber die freien). Gibt die instanceId oder None. Fuer Stage-3-Routing
+ (targetInstance).
+
+ model: wenn gesetzt (nur llm sinnvoll), kommen nur Boxen in Frage, die das
+ Modell fahren koennen (models-Liste oder legacy model-Feld). Meldet KEINE
+ Box das Modell → None (nachsichtig: Aufrufer faellt auf Broadcast zurueck)."""
now = time.time()
online = [w for w in self._workers.values()
if w.get("service") == service
and (now - w.get("last_seen", 0)) < self.WORKER_OFFLINE_S]
+ if model:
+ online = [w for w in online
+ if model in (w.get("models") or [])
+ or w.get("model") == model]
if not online:
return None
free = [w for w in online if not w.get("busy")]
pool = free or online # alle busy → trotzdem eine nehmen (least-bad)
- # Round-Robin: rotierender Zeiger pro Dienst.
+ # Round-Robin: rotierender Zeiger pro Dienst(+Modell).
+ rr_key = f"{service}:{model}" if model else service
rr = getattr(self, "_worker_rr", None)
if rr is None:
rr = self._worker_rr = {}
- idx = rr.get(service, 0) % len(pool)
- rr[service] = idx + 1
+ idx = rr.get(rr_key, 0) % len(pool)
+ rr[rr_key] = idx + 1
chosen = pool[idx]
chosen["busy"] = True # optimistisch, bis der naechste Ping korrigiert
return chosen["instanceId"]
diff --git a/diagnostic/index.html b/diagnostic/index.html
index 81c374e..61d269c 100644
--- a/diagnostic/index.html
+++ b/diagnostic/index.html
@@ -602,9 +602,13 @@
Beim ersten Wechsel zu einem Modell lädt die AI-Box das GGUF (mehrere GB) —
die erste Antwort dauert dann länger, danach ist es gecacht.
- Liste kommt aus /shared/config/local_models.json (Keys = xtts/llama-swap/config.yaml).
+ Verfügbare Modelle kommen live von den angemeldeten LLM-Boxen;
+ Namen aus /shared/config/local_models.json.