3 Commits
Author SHA1 Message Date
Palash Debnath 3008f89919 feat(catalogue): engine list + detail, weights under their engine
The engine matrix (five columns, three-line rows, every chip on every row)
becomes a shadcn table with three columns — Engine · Runs on · Status — and
one primary action per row (Use / Install). Everything else lives in a
detail panel for the selected row: GPU compatibility chips, isolation,
hints and reasons, health and self-test probes, one-click install
progress, setup snippet, disk usage, docs, license, the curated-model
picker, and now the engine's downloadable WEIGHTS.

Weights belong to their engine: every models.yaml entry names the backend
ids that load it (`engines:`), the detail panel lists and installs them
(EngineWeights, on the model store's install/cancel/remove flow via the
extracted useModelDownloads hook), and the sherpa-onnx engine shows its
dictation-model picker there. The page's "Downloaded weights" list and
recommendation card are gone; only weights no engine owns (speaker
diarisation) remain in a small "Other weights" list. A backend test pins
the mapping: every entry has an `engines` list and every id is a real
backend.

- useEngineInventory: the matrix's state machines extracted verbatim
  (shared/local fetch, residency, health/self-test cooldowns, install
  poller with overlap guard + epoch, disk-usage generations, license).
- Row status phrases: GPU active / CPU fallback / CPU / Available /
  Needs setup / Installing… / failed; routing "unavailable" never reads
  Ready. Group captions keep "Ready to use" / "Add more engines".
- Engine titles read "Engines" (each locale's own word); backend
  "Model Catalogue → Engines/Models" messages and docs updated to the
  new structure.
- Dead matrix CSS (phone-tier grid) removed; scopeReco and RecoBanner gone.
2026-09-10 07:59:47 -07:00
Palash DebnathandClaude Opus 5 075ab7b68b fix(engines): say why an engine is unavailable instead of reporting a failed check
Closes #1866.

Model Catalogue → Engines showed "Engine unavailable. Check installation and
configuration." and "Last error: A previous engine check failed." for engines
the user had simply never installed. Neither names a missing package, a missing
step, or a next action, and the second reads like a crash or a poisoned cache
rather than "you have not installed this yet" — so a normal, expected state
looked like a fault.

The probe's own sentence still cannot cross the boundary: it carries exception
text, local paths and sometimes credentials, which is why it was replaced in
the first place. What changed is that the private diagnostic is now CLASSIFIED
into a VoiceStudio-owned category — package not installed, needs configuring,
file missing or unreadable — exactly the shape _public_routing_reason already
uses for routing. Anything unrecognised keeps the old generic sentence rather
than asserting a cause the probe never gave.

test_docs_url_survives_the_public_metadata_scrub pinned the generic wording
while testing something else; it now asserts what it is actually about, that no
private text survives.

Also skips the exec-bit placeholder test on Windows, where os.access(X_OK) is
true for any existing file so the assertion cannot fail — it errored the whole
module on a Windows checkout. Pre-existing, unrelated to this change, and in
the way of running these tests at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ypcgSsh5j2PEonSJiAU1S
2026-09-09 14:37:27 -07:00
Chang-Jin-LeeandClaude Opus 5 adfe7b8588 fix(engines): give an unavailable engine row somewhere to send the user
Model Catalogue -> Engines can only ever say "Engine unavailable. Check
installation and configuration." and "Last error: A previous engine check
failed." for an engine whose package is not importable. That is by design:
public_backends() replaces reason and last_error because an availability probe
can carry exception text, a local path or a credential, and two of the shipping
is_available() implementations do interpolate an exception into their message.

So the row cannot explain itself. docs/engines/<engine>.md can -- accurate,
and for cosyvoice CI-guarded against the installer registry -- but nothing in
frontend/src referenced docs/engines at all, so the point of failure was a dead
end. #1746 is that dead end reaching the tracker.

Add a registry-authored docs_url next to install_hint and setup_snippet. It is
a VoiceStudio-owned constant keyed on the engine id, not probe output, so the
public scrub leaves it intact by construction rather than by classification --
which is what keeps the security boundary where the maintainer put it. The row
renders it with the same "Learn more" affordance MCPBindingsPanel and
RemoteBackendPanel already use, so no new i18n key is needed.

The health log line said "Engine health check failed; details withheld" and
named neither the engine nor the kind of failure, while the response tells the
user to check the backend log and docs/engines asks them to copy that engine's
lines. Log the registry id and a stable exception class -- the same class=
shape core.public_errors.public_failure() already logs. The diagnostic text
stays out and the id is flattened to one token, so
tests/test_response_safety.py's existing log-injection test passes unchanged.

Not touched: publishing the computed reason itself. That is the product
decision the reporter flagged, and the log line is deliberate, not an
oversight -- test_engine_health_route_logs_but_does_not_return_private_
diagnostic pins it.

Refs #1866

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chang-Jin-Lee <ckdwls525@gmail.com>
2026-09-08 13:29:57 +09:00