front: cut engine load — front-native loaded-status, config reads to system worker

Fixes the models page's 6-7s stall and reduces engine hits generally:

- loadCachedModels awaited /admin/api/models (engine) before painting the cached-
  models cards, so the fast list waited on the slow engine. /admin/api/models (and
  accel-presets/accel-loras/turboquant-info/model-enable/model-disable) are pure
  config reads/edits, so they now route to the coderai-system worker (it has
  config_manager + models.json) — fast, off the engine.

- model-loaded-status is now served from the FRONT's own state: loaded comes from
  the registry (the front issues every load/unload; the supervisor keeps each
  engine's resident set fresh via the cheap engine-state poll) and running from the
  front's in-flight tracking. Per-model instance detail is synced from the engine
  ONLY when it's idle (inflight==0) and cached otherwise — no engine hit during
  generation. This is the "front maintains its own list, syncs when not under load"
  model.

- The 1s Tasks poll now skips the engine entirely when the primary is mid-
  generation, serving the merged/synthesized list from cache + live in-flight
  tracking instead of burning the timeout.

Verified: model-loaded-status returns front-native loaded/running with no engine
call while busy; compiles.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011DDv7BchtZQWsnPG6Jm49m
parent ececdf83
...@@ -57,6 +57,11 @@ _SYSTEM_PATHS = ( ...@@ -57,6 +57,11 @@ _SYSTEM_PATHS = (
"/admin/api/hf-model-files", "/admin/api/ds4/default-models", "/admin/api/hf-model-files", "/admin/api/ds4/default-models",
"/admin/api/model-add-known", "/admin/api/model-mark-download", "/admin/api/model-add-known", "/admin/api/model-mark-download",
"/admin/api/model-unmark-download", "/admin/api/model-unmark-download",
# Config-based reads/edits (no GPU): the system worker has config_manager +
# models.json, so these stay fast and never touch the busy engine.
"/admin/api/models", "/admin/api/accel-presets", "/admin/api/accel-loras",
"/admin/api/turboquant-info", "/admin/api/model-enable",
"/admin/api/model-disable",
) )
...@@ -83,6 +88,10 @@ class FrontProxy: ...@@ -83,6 +88,10 @@ class FrontProxy:
# without asking the engine. Newest first; bounded. # without asking the engine. Newest first; bounded.
import collections as _collections import collections as _collections
self._recent_activity = _collections.deque(maxlen=25) self._recent_activity = _collections.deque(maxlen=25)
# Last-good per-model instance detail from an engine (synced only when the
# engine is idle), so model-loaded-status can serve loaded/running from the
# front's own state without ever hitting a busy engine.
self._mls_cache: dict = {}
self.registry = EngineRegistry() self.registry = EngineRegistry()
self.supervisor: Optional[EngineSupervisor] = None self.supervisor: Optional[EngineSupervisor] = None
# Per-run secret shared only with the engines (passed via env at spawn). The # Per-run secret shared only with the engines (passed via env at spawn). The
...@@ -436,6 +445,16 @@ class FrontProxy: ...@@ -436,6 +445,16 @@ class FrontProxy:
if prim is None: if prim is None:
return JSONResponse({"engine": "down", "tasks": [], "queue": []}) return JSONResponse({"engine": "down", "tasks": [], "queue": []})
is_tasks = request.url.path.rstrip("/").endswith("/tasks") is_tasks = request.url.path.rstrip("/").endswith("/tasks")
# If the primary is mid-generation, don't poke it (the call would just burn
# the timeout). Serve the merged/synthesized task list from the front's
# last-good cache + live in-flight tracking — no engine hit during work.
if is_tasks and int(getattr(prim, "inflight", 0) or 0) > 0:
ptasks = (self._primary_tasks_cache or []) \
if (time.monotonic() - self._primary_tasks_at) < 120 else []
return JSONResponse({"engine": "busy", "stale": True,
"tasks": self._merge_engine_tasks(prim, ptasks),
"cooling_engines": self._cooling_engines(),
"queue": []})
try: try:
headers = self._filter_headers(request.headers, _DROP_REQ) headers = self._filter_headers(request.headers, _DROP_REQ)
r = await self._short.get(prim.url + request.url.path, headers=headers, r = await self._short.get(prim.url + request.url.path, headers=headers,
...@@ -667,36 +686,45 @@ class FrontProxy: ...@@ -667,36 +686,45 @@ class FrontProxy:
return sorted(running) return sorted(running)
async def model_loaded_status(self, request: Request): async def model_loaded_status(self, request: Request):
"""Proxy /admin/api/model-loaded-status to the primary, then union in the """Loaded-model status, served from the FRONT's own state.
models loaded on every *other* engine. Otherwise the models page only sees
the primary engine's pool and shows models loaded on a secondary (e.g. the The front issues every load/unload and the supervisor keeps each engine's
radeon engine) as idle. Also adds ``running`` — models actively serving a resident set in the registry (refreshed by the cheap engine-state poll that
request — from the front's own in-flight tracking.""" answers even mid-generation), so ``loaded`` + ``running`` come from the front
prim = self.registry.primary() with no engine round-trip. Richer per-model instance detail (instances /
configured_max) is synced from the engine ONLY when it's idle — never adding
load during generation — and cached for use while it's busy."""
running = self._running_models() running = self._running_models()
if prim is None: loaded = set()
return JSONResponse({"loaded": [], "instances": {}, "configured_max": {}, for e in self.registry.all():
"running": running}) if getattr(e, "role", "engine") == "system":
try: continue
headers = self._filter_headers(request.headers, _DROP_REQ) loaded |= set(e.loaded_models or [])
r = await self._short.get(prim.url + request.url.path, headers=headers, data = {
params=request.query_params) "loaded": sorted(self._canonical_loaded(loaded)),
if r.status_code != 200: "running": running,
return Response(content=r.content, status_code=r.status_code, "instances": self._mls_cache.get("instances", {}),
headers=dict(self._filter_headers(r.headers, _DROP_RESP)), "configured_max": self._mls_cache.get("configured_max", {}),
media_type=r.headers.get("content-type")) }
data = r.json() prim = self.registry.primary()
except Exception: # Sync instance detail only when the primary is idle (inflight == 0).
return JSONResponse({"loaded": [], "instances": {}, "configured_max": {}, if prim is not None and int(getattr(prim, "inflight", 0) or 0) == 0:
"running": running}) try:
if isinstance(data, dict): headers = self._filter_headers(request.headers, _DROP_REQ)
loaded = set(data.get("loaded") or []) r = await self._short.get(prim.url + request.url.path, headers=headers,
for e in self.registry.all(): params=request.query_params)
if prim is not None and e.id == prim.id: if r.status_code == 200:
continue ed = r.json()
loaded |= e.loaded_models if isinstance(ed, dict):
data["loaded"] = sorted(loaded) self._mls_cache = {
data["running"] = running "instances": ed.get("instances", {}) or {},
"configured_max": ed.get("configured_max", {}) or {}}
data["instances"] = self._mls_cache["instances"]
data["configured_max"] = self._mls_cache["configured_max"]
merged = loaded | set(ed.get("loaded") or [])
data["loaded"] = sorted(self._canonical_loaded(merged))
except Exception:
pass
return JSONResponse(data) return JSONResponse(data)
async def _forward_to_engine(self, request: Request, engine, body: bytes): async def _forward_to_engine(self, request: Request, engine, body: bytes):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment