An advanced, provider-agnostic LLM web chat interface. Local-first (Ollama/LM Studio) with support for any OpenAI-compatible cloud provider, full-agentic, secrets held in the proxy.
Status: Phase 0 (foundation) — streaming chat to GLM and any OpenAI-compatible endpoint is working end-to-end. See PLAN.md for what's built and what's next, DESIGN.md for architecture, and RESEARCH.md for the competitive analysis it's based on.
- Provider-agnostic — Ollama, LM Studio, OpenRouter, or any custom OpenAI-compatible URL, all behind one client
- Reasoning stream — model reasoning/thinking output rendered in a collapsible panel, with per-message effort control for reasoning-capable providers
- Conversation branching/forking — tree model; regenerate, edit-and-resubmit, continue (prefill), branch navigation; fork to new chat, branch diff/compare, compact (summarize→new chat), conversation tree graph view; tags + pin + inline rename
- Per-conversation settings — system prompt, model, temperature overrides per chat; global memory recalled across all chats
- Artifacts panel — live preview for HTML/SVG/Mermaid/charts (Recharts) + in-browser Python (Pyodide) execution; auto-opens for previewable blocks
- Multimodal — attach images for vision + text documents (txt/md/code/json/csv → context)
- Multi-model Arena — stream one prompt across 2–4 models side-by-side, pick a 🏆 winner
- Agentic tools —
web_search,web_reader(SSRF-guarded),read_file,write_file,run_bash(sandboxed); custom HTTP tools; multi-round loop; human-in-the-loop approvals for destructive tools - MCP client — connect any stdio Model Context Protocol server; its tools become callable
- Knowledge base (RAG) — ingest docs (chunked + embedded), retrieve via
knowledge_search - Voice — STT dictation + TTS speak (Web Speech API, zero deps)
- ⌨ Power UX — command palette (⌘K), global shortcuts (
?for help), quick message actions (summarize/explain), conversation search/pin/tags/auto-title, prompt snippets, Markdown + JSON backup export/import, token + context display, accent-color theming, error boundary - 📱 PWA — installable + offline app shell (service worker precaches the UI; API is never cached)
- Secrets stay in the proxy — API keys never reach the browser; masked in the UI
- Graceful offline — local runners (Ollama) with live
/modelsdiscovery and preset fallback
| Phase | Theme | Status |
|---|---|---|
| ✅ 0 | Foundation — streaming chat, settings, providers | done |
| ✅ 1 | Power UX — branching, palette, shortcuts, search, fork, tree, compare | done |
| ✅ 2 | Artifacts & rendering — live panels, Pyodide, charts | done |
| ✅ 3 | Agentic — tools, MCP, RAG, HITL approvals | done |
| 🌟 4 | Differentiators — arena, voice, theming, per-conv settings | mostly done |
| 👥 5 | Multi-user (optional) | later |
See PLAN.md for the detailed checklist.
packages/
shared/ types crossing the HTTP boundary (+ provider presets)
providers/ fetch-based OpenAI-compatible streaming client (SSE + reasoning)
apps/
server/ Hono proxy: secrets, /api/chat (NDJSON stream), models, settings, tools
web/ Vite + React + Tailwind SPA
- The proxy is stateless per request; it holds secrets, hosts tools/MCP/RAG, and serves the built SPA in production.
- The SPA talks only to
/api/*; it owns conversation history (localStorage) and never sees API keys.
- Node.js ≥ 20 and pnpm (
npm i -g pnpmorcorepack enable) - A local model runner (recommended): Ollama —
ollama pull llama3.2 - Optionally, an API key for a cloud OpenAI-compatible provider (OpenRouter, etc.)
pnpm installOption A — Dev with hot reload (recommended while building):
pnpm dev # one command (needs a POSIX/Windows shell with `ps`)
# …or two terminals if `pnpm dev` misbehaves on your OS:
# pnpm dev:server # proxy on :8787
# pnpm dev:web # vite on :5173 ← open thisOption B — Production single-port (simplest, most reliable — one server serves UI + API):
pnpm build && pnpm start # → http://localhost:8787Then open the URL. In dev the web app proxies /api/* to :8787. Configure providers and add API keys in Settings (gear icon) — stored in apps/server/data/settings.json (gitignored).
Everything is configurable from the Settings UI:
- Providers — edit built-ins (Ollama, OpenRouter, LM Studio) or + Add a custom OpenAI-compatible endpoint (name, base URL, API key)
- Active provider/model — pick from the composer's dropdowns (live
/modelsdiscovery) - Default system prompt, reasoning effort, tools on/off are per-turn in the composer
You can also pre-seed via env (see .env.example):
OLLAMA_BASE_URL=http://localhost:11434
OPENROUTER_API_KEY=... # optional, for the OpenRouter preset
LLMWEBCHAT_DATA_DIR=./data # where the proxy stores settings.json
PORT=8787
- The proxy binds to 127.0.0.1 by default — no network exposure. Set
LLMWEBCHAT_HOSTto override (then also setLLMWEBCHAT_AUTH_TOKEN). - API keys live only in the proxy; the browser never sees them.
- Server-side tools are sandboxed: web tools block SSRF (private/loopback/metadata IPs), file tools are confined to a workspace root,
run_bashis off by default and runs with a sanitized environment. - It's provider-agnostic by design: pick whatever backend suits you.
pnpm dev:server # proxy only (tsx watch)
pnpm dev:web # web only (vite)
pnpm typecheck # all workspaces
pnpm build # all workspacesConventions when adding features:
- Types that cross HTTP →
packages/shared - Transport/provider logic →
packages/providers - Server-side trust/execution/secrets →
apps/server(+ register route insrc/index.ts) - UI →
apps/web/src/components pnpm typecheck && pnpm buildbefore committing
MIT.