Add local model backends: per-spawn dropdown pointing claude at alternative endpoints

Operators can register Anthropic-API-compatible endpoints (a local Qwen/Llama
behind LiteLLM or claude-code-router, an LLM gateway) on the dashboard's
Claude -> Models tab and pick one from a Model dropdown when spawning an agent.
The agent still launches as the same claude binary with the same hooks, skills,
connectors, plugins, and gates — only its ANTHROPIC_BASE_URL / ANTHROPIC_MODEL /
ANTHROPIC_AUTH_TOKEN env differs — and it stays pinned to its backend across
resumes. No selection keeps the worker's Claude subscription untouched.

- claude_models table (+ agents.model_id pin), migration 0012
- control.models resolves a row into the launch env (API keys Fernet-encrypted
  at rest, decrypted only in the control container; placeholder key when none is
  stored so the subscription OAuth token never reaches a local endpoint)
- /claude/models CRUD (admin-gated writes, key never returned), spawn route +
  worker + CLI (--model) pass the selection through, fail-fast on missing or
  disabled backends
- dashboard: Models tab, spawn-form dropdown, model badge in the agents table
- docs/local-models.md: why bare OpenAI-compatible servers break tool calling
  with Qwen-Coder, and working vLLM/LiteLLM/llama.cpp stacks

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DzDofD7gP63WpeLG8vEdZu
This commit is contained in:
Claude
2026-07-29 18:36:16 +00:00
parent c4e6ae0faa
commit 2d5c0e34d7
47 changed files with 1219 additions and 60 deletions
+18
View File
@@ -30,6 +30,9 @@ export interface Agent {
working_dir: string;
status: string;
role?: string | null;
/* Model backend the agent is pinned to (see ClaudeModel); null = the Claude
* subscription the worker is logged in to. */
model_id?: number | null;
/* Latest output snapshot from the worker: the tmux pane tail for legacy agents, the
* latest assistant text for headless runs. For a crashed agent this is the evidence
* frame — the last thing the process said. */
@@ -179,6 +182,21 @@ export interface ClaudePlugin {
created_at: string;
}
/* A registered model backend: an Anthropic-API-compatible endpoint (a local model
* behind LiteLLM / claude-code-router, an LLM gateway) the spawn dropdown offers next
* to the Claude subscription. The API key is write-only server-side (has_api_key only). */
export interface ClaudeModel {
id: number;
name: string;
base_url: string;
model: string;
small_fast_model?: string | null;
env?: Record<string, string> | null;
enabled: boolean;
has_api_key: boolean;
created_at: string;
}
/* Stored overrides + the env baseline they merge over at launch (read-only here). */
export interface ClaudePermissions {
default_mode?: string | null;