PolyLLM — repository guide
PolyLLM (www.polyllm.io) is a BYOK multi-provider AI workspace: OpenAI, Anthropic, Gemini, xAI behind one normalized
adapter layer, real streaming, capability-driven configuration, Arena comparisons, usage/cost tracking.
The original product brief lives in docs/SPEC-original.md; the UI conventions in docs/UI-BRIEF.md.
Non-negotiables
- Research before provider changes. Read
docs/provider-research/<provider>.md, re-fetch the official docs, probe the real API (pnpm providers:matrix), then updatesrc/lib/ai/providers/<provider>/catalog*.ts. Never assume model ids, parameters, event names or limits from memory. BumpLast documentation audit. - Provider quirks stay inside
src/lib/ai/providers/*. UI and services only seePolyModel,UnifiedChatRequest,UnifiedStreamEvent,PolyProviderError. - Capabilities drive the UI. Never branch on a model id in components; use
model.capabilities/model.parameters. Never send a parameter the model does not accept (filterSettings()+ adapter translation). - Keys. User keys are encrypted (
src/lib/crypto/keys.ts) and decrypted only ingetDecryptedKey()right before a request. Never log, return or serialize them;PublicConnectionexposeskeyHintonly. Owner keys in.envare for the registry sync, admin diagnostics and tests — never for user requests. - No fake integrations. A capability is ✅ only after a real request passed (
docs/provider-test-matrix.md). - Secrets only in
.env(gitignored) and the mld manifest on M1M32;.env.exampledocuments the variables.
Commands
pnpm dev · pnpm typecheck · pnpm lint · pnpm test (vitest) · pnpm test:e2e (Playwright; dev server with
EMAIL_DRY_RUN=1 EMAIL_DRY_RUN_PRINT=1) · pnpm db:generate / pnpm db:migrate · pnpm models:sync [provider] ·
pnpm providers:matrix [provider].
Deploy
mld from the laptop (see docs/deployment-cluster.md): mld stage . polyllm && mld deploy polyllm --node M3U96a.
Manifest with secrets: M1M32:~/dispatch/apps/polyllm.json (local gitignored copy deploy/polyllm.mld.json).
Gotchas
- React Compiler lint: no
Date.now()/impure calls in render, no setState synchronously in effects without an explicit eslint-disable, memoize derived lists. import "server-only"modules are used by CLI scripts throughscripts/hooks.mjs(tsx--import) and by vitest via an alias.- Anthropic: adaptive thinking may legitimately produce zero thinking tokens on easy prompts; Haiku 4.5 needs
budget_tokens. - Gemini: free-tier keys have tiny per-model daily quotas (429
limit: 20) and some models are paid-tier only (429limit: 0); thought signatures must be replayed on function-call parts. - xAI: bad keys return HTTP 400 (
Incorrect API key),frequency/presence_penaltyalways 400, web search only via/v1/responses. - OpenAI: Responses API has no
seed/stop; sampling params only whenreasoning.effort === "none"on reasoning models.