# Discovery Notes: Multi-Provider Local Models Single source of truth for implementation context. Read this first before touching the plan or code. ## Tech stack - Monorepo with pnpm workspaces. - `apps/server`: Fastify + Postgres, native inference, local-model routing, BooChat APIs. - `apps/web`: React + Vite SPA, shared chat and coder UI. - `apps/coder`: host-side BooCoder service, provider probing, native and external-agent dispatch, Arena, MCP. - `packages/contracts`: shared cross-app schemas and types, built before consumers. - TypeScript strict mode. Server and coder use NodeNext and `.js` import suffixes. - Tests: `pnpm -C apps/server test`, `pnpm -C apps/coder test`. No dedicated web test harness. ## ADRs found - `docs/adr/0001-arena-two-lane-scheduling.md` Summary: local llama-backed contestants run serially in one lane, cloud contestants run in parallel in another lane; multi-provider work must preserve this lane model. - `docs/adr/0002-arena-dedicated-tables-not-flow-runner.md` Summary: Arena owns its own storage and runtime shape; reuse dispatcher machinery but do not fold Arena back into flow-runner abstractions. ## Coding standards found - `docs/coding-standards/cross-app-contract-parity.md` Summary: when a cross-app contract changes, update the canonical package source plus app-side secondary representations in the same batch; missing one side silently drops behavior at runtime. - `CLAUDE.md` Summary: `packages/contracts` is the single source for provider-snapshot and message-metadata contracts, deploy-by-surface rules matter, and contract changes must respect app-local secondary unions and renderers where they still exist. ## Relevant architecture notes - `apps/server/CLAUDE.md` Summary: `services/inference/provider.ts` is the current llama-swap provider seam; `model-context.ts` and `compaction.ts` currently assume one upstream. - `apps/coder/CLAUDE.md` Summary: provider snapshot and `opencode` integration are the main local-model seams; `llama-swap/*` is currently the local namespace assumption. - `apps/web/CLAUDE.md` Summary: `ModelPicker` and `AgentComposerBar` are separate UI surfaces with different constraints; any provider snapshot loading-state change can make providers disappear from the coder UI. ## Code touch points ### Shared contracts and config patterns - `packages/contracts/src/provider-config.ts` Existing coder ACP provider config schema; useful precedent, but not the right place to overload with local host inventory semantics. - `apps/coder/src/services/provider-config-registry.ts` Existing pattern for schema-in-package plus app-local load/build cache. - `packages/contracts/src/provider-snapshot.ts` Shared snapshot contract used by coder and web. ### Server: catalog, routing, and downstream local-model consumers - `apps/server/src/config.ts` Current env config includes `LLAMA_SWAP_URL`, `LLAMA_SIDECAR_URL`, and `DEFAULT_MODEL`; multi-provider config must enter here. - `apps/server/src/routes/models.ts` Current `/api/models` route fetches one llama-swap and optionally DeepSeek. - `apps/server/src/services/inference/provider.ts` Current route selection and AI SDK provider seam; central place to remove heuristic provider detection. - `apps/server/src/services/model-context.ts` Current context cache keys by bare model string and assumes one `LLAMA_SWAP_URL`. - `apps/server/src/services/compaction.ts` Uses `resolveModelEndpoint()` today, but still contains one-provider assumptions and a DeepSeek prefix special case. - `apps/server/src/services/task-model.ts` Returns one resolved `{url, model}` pair today. - `apps/server/src/index.ts` Calls `configureModelContext({ llamaSwapUrl })`; this wiring must change when context lookup becomes provider-aware. - `apps/server/src/routes/settings.ts` Existing shared settings persistence surface; right place for `favorite_models`. ### Web: BooChat and coder selection UI - `apps/web/src/components/ModelPicker.tsx` Shared BooChat model picker component; currently assumes a flat `/api/models` list. - `apps/web/src/components/AgentComposerBar.tsx` Native BooCoder provider/mode/model picker surface. - `apps/web/src/lib/model-label.ts` Display-only model prettifier used by both pickers. - `apps/web/src/api/client.ts` `models()` currently expects `ModelInfo[]`. - `apps/web/src/api/types.ts` Holds the web-side API contract for `/api/models` and other cross-app payloads. ### Coder: native, snapshot, arena, and external-agent bridge - `apps/coder/src/config.ts` Current coder config still exposes `LLAMA_SWAP_URL`; multi-provider config must enter here too. - `apps/coder/src/services/provider-snapshot.ts` Current snapshot fetches one `LLAMA_SWAP_URL`, prefixes local models as `llama-swap/*`, and merges them into `opencode`. - `apps/coder/src/services/dispatcher.ts` Current native and external-agent dispatch logic still assumes local bare ids or `llama-swap/*` for local routing. - `apps/coder/src/services/backends/opencode-server.ts` `parseModel()` splits only once at `/`; this is good news because a stable outer provider namespace can carry an inner composite model id. - `apps/coder/src/services/arena-model-call.ts` Direct one-shot local model call against `LLAMA_SWAP_URL`. - `apps/coder/src/services/arena-analyzer.ts` Local-vs-cloud checks rely on one local model set and one upstream. - `apps/coder/src/index.ts` Builds the local-model set for Arena from one fetched llama-swap list. ## Recent activity and churn High-churn files in the last 90 days: - `apps/web/src/api/types.ts` - `apps/web/src/api/client.ts` - `apps/server/src/index.ts` - `apps/server/src/types/api.ts` - `apps/coder/src/services/dispatcher.ts` - `apps/coder/src/index.ts` - `apps/coder/src/services/provider-snapshot.ts` - `apps/web/src/components/AgentComposerBar.tsx` - `apps/server/src/services/compaction.ts` Implication: keep work units narrow and avoid combining unrelated refactors in these files. ## Constraints and load-bearing facts - `packages/contracts` already owns provider-snapshot types; if the snapshot contract changes, rebuild the package before touching consumers. - `apps/web` has no dedicated test harness, so web verification will rely on typecheck plus smoke testing. - Arena’s local lane semantics are intentional; multi-provider support must not collapse local models into parallel execution. - `opencode` local parity is not a small rename. The current host config and snapshot behavior collapse identity to one `llama-swap` namespace. ## Gaps and unknowns - No existing shared local-provider config file or schema exists in-repo yet. - `/api/models` shape change is not yet specified in app-local types; W2 must settle the contract before W4 starts. - The final `opencode` gateway path is not implemented anywhere yet; W7 is net-new code, not just adaptation. - No dedicated docs for “add a machine” exist yet; W8 must create them.