feat(custom-model): generate Run-menu entries from saved endpoint profiles - #430
opticon454 wants to merge 3 commits into
Conversation
…files Follow-up to Ark0N#393, picking up the work Ark0N invited in his merge comment: "generate those entries from the saved profiles rather than a fixed duplicate per harness, and put it in a follow-up PR so this one stays the backend... The Run-menu picker is yours if you want it." Adds the frontend surface the backend has been waiting on: - Run menu: a "Custom Endpoints" section lists one entry per (harness that supports customModelInjection, saved endpoint) pair, e.g. "Claude Code (llama.cpp)". The harness list comes from window.__codemanCustomModelClis, injected at page render straight off the CLI registry's own capabilities (never a hardcoded id list in the frontend), so a CLI whose injection recipe lands later appears with no frontend change. Picking an entry runs that harness's own existing run*() function unmodified (case creation, env overrides, everything, forced to a single instance) and then applies the endpoint's default model to the session it creates via the existing POST /api/sessions/:id/custom-model route. Entries are hidden for a remote/docker active case, since that route already refuses both. - Settings: App Settings -> Models gets a "Custom model endpoints" group wiring up the customModelEndpointsEnabled toggle (declared since Ark0N#393, read by nothing until now) plus CRUD against the existing /api/model-endpoints routes: list, add/edit (inline form), delete, discover models. - Backend: CustomModelHost gains an optional defaultModelId, the model the picker applies with no further choice per endpoint (one generated menu entry per CLI+endpoint pair, not per CLI+endpoint+model). The route refuses a value that isn't one of the endpoint's own discovered models, and a fresh discovery drops a default that no longer appears rather than carrying an invalid one forward. Docs: docs/custom-model-endpoints.md describes the new picker and settings panel; CLAUDE.md's Custom Model Endpoint Profiles entry drops the "backend-only" status note and documents the picker's generation mechanism. Tests: four new route tests cover defaultModelId validation, acceptance, and the drop/keep behaviour across a re-discovery; a new render-index-html test pins the __codemanCustomModelClis injection (present, agent CLIs supporting the capability, antigravity and shell excluded) and its solo-window skip. No browser test was added for the Run-menu picker itself or the settings CRUD panel (this box has no tmux, so the live server used by test:browser/test:mobile could not be exercised here) -- worth a Playwright pass before merge, same as any other frontend PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG
New docs/wiki/Custom-Model-Endpoints.md (auto-synced to the live GitHub wiki on push to master, per docs/wiki/Contributing.md) covers turning the feature on, adding an endpoint, the Run-menu picker's one-off-run behaviour, the per-harness confidence table, and what it deliberately does not do yet (remote/Docker sessions, live hot-swap). Linked from the sidebar, from Agent-CLIs.md's "Read next" list plus a short pointer section, and from Settings-Reference.md's Models section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG
CI on PR Ark0N#430 failed test/server-index-title.test.ts's byte-identity check: renderIndexHtml now injects a second unconditional <script> before </head> (window.__codemanCustomModelClis, added alongside the existing __codemanCliAvailable one), and the test only knew to strip the older one before comparing the rendered HTML against the raw template. Strip both. Unlike __codemanCliAvailable (an object, historically injected only where something resolved), the new one is a plain array injected unconditionally, possibly empty, so it needs stripping on every machine, not just one with CLIs installed. Verified the two replace() calls compose correctly against the exact strings server.ts actually produces (simulated in isolation; this box has no tmux, so the real WebServer-backed test file cannot run here at all -- same environment gap noted throughout this PR's review). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG
|
Thanks for picking this up, and for reading the merge comment on #393 as an actual invitation rather than a pleasantry. The shape is right: generating the entries off It is still a draft so I am reviewing it as one. The backend, the docs and the architecture are sound. The frontend half does not currently work, and I want to be specific rather than vague about it, because none of it needs a redesign. Every item below is small and local. You were honest up front that no browser test was possible on your box ("this box has no tmux"). That is exactly where the damage landed, so it is worth saying plainly: three of these would have shown up on a single page load. First, credit where it is due: your last commit ( 1. Every generated inline onclick="app.runCustomModelEntry(${JSON.stringify(cli.id)}, ...)"
The repo already has the right idiom four lines away in the same file: Fix it that way rather than by reordering quotes, because there is a second reason: 2. The endpoint list is read as a bare array, but the wire carries the envelope (
Worth knowing why no test caught it, since it is not your fault: 3. A failed launch applies the endpoint to whatever session was already open, and restarts it ( Either have the runner hand back the id it created, or snapshot before and require it to have changed: const before = this.activeSessionId;
await runner();
const sessionId = this.activeSessionId;
if (!sessionId || sessionId === before) return;The snapshot form is a heuristic (it also declines if a run legitimately re-selects the same session), but declining to apply is the safe side of that trade. Two majors: 4. It bypasses the Run launch in-flight lock. 5. No test for any of the new frontend behaviour. Three of the four blockers are DOM-level facts that need no Playwright and no tmux. Minors, worth doing while you are in here:
Nits: the The backend piece ( Items 1, 2 and 3 are what I need before this comes out of draft. It is not going into the release I am assembling now, which is fine for a draft. Ping me when it is ready and I will take another pass. |
… it never had The wiki was written for seven run modes and never received Grok Build, DeepSeek Harness or OMP. They now appear everywhere the others do: the modes table and per-CLI notes, install commands, environment prefixes, the Quick Start table, the requirements rows, the vocabulary, and every "seven modes" count. The 1.27 to 1.29.0 changes land on the pages that own them: attaching a case to an existing container, multi-case adoption and the copy-a-case picker (Docker Cases); file reads over ssh in remote cases and what stays unavailable (Remote SSH Sessions, Working With Files, Security); single-page app routing, frame recovery, localhost links as tabs and the egress guard (Web Tabs); DeepSeek as the one non-Claude mode with real stop/blocked signals and Approvals items, Codex's own work detection, last-response, the model-endpoint routes and refreshed counts (HTTP API, Driving From An Agent, Hooks, Notifications, Keeping Agents Running, Core Concepts); Shift+drag, right-click copy, Auto Copy, the Ctrl+Z guard, font weight, the vertical rail and its activity sort (Keyboard Shortcuts, Input And Voice, The Dashboard, Settings Reference); the 600px phone cutoff, Codex shift arrows and iPhone Duo (Mobile Guide); the Docker Compose route and its update rule (Installation, Running As A Service); four new symptom entries and a "which CLIs" question (Troubleshooting, FAQ). Custom model endpoints are deliberately left to #430, which adds that page and edits Agent CLIs, Settings Reference and the sidebar; these edits stay out of the regions #430, #428 and #376 touch, and all three still merge cleanly on top. Both READMEs: the web-tab menu entry is labelled "Add URL" in the UI, not "Add dashboard". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up to #393. Picks up exactly what @Ark0N invited in the merge comment there:
What this adds
Run menu: a "Custom Endpoints" section generates one entry per (harness that supports
customModelInjection, saved endpoint) pair, e.g. "Claude Code (llama.cpp)". The harness list comes fromwindow.__codemanCustomModelClis, injected at page render straight off the CLI registry's owncapabilities.customModelInjection(never a hardcoded id list in the frontend), so a CLI whose injection recipe lands later shows up with no frontend change. Picking an entry runs that harness's existingrun*()function unmodified (case creation, env overrides, everything — forced to a single instance) and then applies the endpoint's default model to the session it creates, via the existingPOST /api/sessions/:id/custom-modelroute. Entries are hidden entirely for a remote/docker active case, since that route already refuses both.Settings: App Settings -> Models gets a "Custom model endpoints" group wiring up the
customModelEndpointsEnabledtoggle (declared in #393, read by nothing until now) plus CRUD against the existing/api/model-endpointsroutes: list, add/edit (inline form), delete, discover models.Backend:
CustomModelHostgains an optionaldefaultModelId, the model the picker applies with no further choice per endpoint (one generated menu entry per CLI+endpoint pair, not per CLI+endpoint+model). The route refuses a value that isn't one of the endpoint's own discovered models, and a fresh discovery drops a default that no longer appears rather than carrying an invalid one forward.Docs
docs/custom-model-endpoints.mddescribes the new picker and settings panel.CLAUDE.md's Custom Model Endpoint Profiles entry drops the "backend-only" status note and documents the picker's generation mechanism.Tests
Four new route tests cover
defaultModelIdvalidation, acceptance, and the drop/keep behaviour across a re-discovery. A newrender-index-htmltest pins the__codemanCustomModelClisinjection (present, agent CLIs supporting the capability, antigravity and shell excluded) and its solo-window skip.npm run typecheck,npm run lint, andnode scripts/check-frontend-syntax.mjsare all clean.npm testshows no regressions versus master (every failure on my machine is pre-existing Windows-environment noise — missingnpx/tmux,EPERMonfs.watch, HEIC tooling — unrelated to this diff; confirmed by diffing the fail list against a cleanmastercheckout).Known gap: no browser test for the picker or the settings CRUD panel — this box has no tmux, so
test:browser/test:mobilecouldn't be exercised here. Worth a Playwright pass before merge, same as any other frontend PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG
Wiki
docs/wiki/Custom-Model-Endpoints.mdis a new page (auto-synced to the live GitHub wiki on push to master, perdocs/wiki/Contributing.md) covering how the feature works end-to-end: turning it on, adding/discovering an endpoint, what picking a Run-menu entry actually does, the per-harness confidence table, and what it deliberately doesn't do yet (remote/Docker sessions, live hot-swap). Linked from the sidebar,Agent-CLIs.md, andSettings-Reference.md.