# 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/.md`, re-fetch the official docs, probe the real API (`pnpm providers:matrix`), then update `src/lib/ai/providers//catalog*.ts`. Never assume model ids, parameters, event names or limits from memory. Bump `Last documentation audit`. - **Provider quirks stay inside `src/lib/ai/providers/*`.** UI and services only see `PolyModel`, `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 in `getDecryptedKey()` right before a request. Never log, return or serialize them; `PublicConnection` exposes `keyHint` only. Owner keys in `.env` are 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.example` documents 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 through `scripts/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 (429 `limit: 0`); thought signatures must be replayed on function-call parts. - xAI: bad keys return HTTP **400** (`Incorrect API key`), `frequency/presence_penalty` always 400, web search only via `/v1/responses`. - OpenAI: Responses API has no `seed`/`stop`; sampling params only when `reasoning.effort === "none"` on reasoning models.