A minimal, streaming chat interface built on Cloudflare Pages with OpenRouter and LangChain. Features a clean UI with light/dark mode, model selection, markdown streaming, and quick-action prompts.
- Streaming Responses — Server-Sent Events (SSE) for real-time token streaming
- Multiple Free Models — Pre-configured with 5 free OpenRouter models
- Markdown Rendering — Streaming markdown via
streaming-markdown - Light/Dark Theme — Persisted preference with system detection
- Quick Actions — One-click prompt buttons for common tasks
- Edge Runtime — Deployed on Cloudflare Pages Functions (zero cold start)
- No Build Step — Pure HTML/CSS/JS + TypeScript API handler
├── public/
│ └── index.html # Single-file SPA (HTML + CSS + JS module)
├── functions/
│ └── api/
│ └── chat.ts # Cloudflare Pages Function (SSE streaming)
├── docs/ # SDLC artifacts (intent, design, plan, test)
├── wrangler.toml # Cloudflare Pages config
├── package.json # Dependencies (wrangler, langchain, streaming-markdown)
└── .dev.vars # Local environment variables (gitignored)
- User sends message →
fetch("/api/chat", { method: "POST", body: JSON.stringify({ messages, model }) }) functions/api/chat.tsreceives request, initializesChatOpenAIwith OpenRouter base URL- LangChain streams tokens → converted to SSE format (
data: {...}\n\n) - Frontend consumes SSE via
ReadableStream, feeds chunks tostreaming-markdownparser - Markdown renders incrementally in the chat bubble
- Node.js 18+
- Cloudflare account (for deployment)
- OpenRouter API key
# Install dependencies
npm install
# Create .dev.vars with your OpenRouter API key
echo 'OPENROUTER_API_KEY="sk-or-v1-..."' > .dev.vars
# Start dev server (Cloudflare Pages emulator)
npm run devVisit http://localhost:8788 (or the port shown in terminal).
| Variable | Description | Default |
|---|---|---|
OPENROUTER_API_KEY |
Your OpenRouter API key | Required |
DEFAULT_MODEL |
Default model ID | nvidia/nemotron-3-ultra-550b-a55b:free |
Set in .dev.vars (local) or Cloudflare Pages dashboard (production).
# Deploy to Cloudflare Pages
npm run deployOr connect your Git repo in the Cloudflare Pages dashboard for automatic deployments on push.
Add in Cloudflare Pages → Settings → Environment Variables:
| Name | Value |
|---|---|
OPENROUTER_API_KEY |
Your OpenRouter API key |
DEFAULT_MODEL |
(Optional) Override default model |
Edit the <select id="model"> in public/index.html:
<option value="provider/model-name">Display Name</option>Add buttons inside #quick-actions-container:
<button data-suggestion="Your prompt here"
class="w-full text-left text-sm px-3 py-2.5 rounded-xl hover:bg-[var(--surface-hover)] transition">
Button Label
</button>CSS custom properties in :root (light) and .dark (dark) control all colors. Modify in <style> block of index.html.
| Layer | Technology |
|---|---|
| Hosting | Cloudflare Pages |
| Runtime | Cloudflare Pages Functions (Workers) |
| Language | TypeScript (API), Vanilla JS (Frontend) |
| LLM SDK | LangChain (@langchain/openai, @langchain/core) |
| LLM Provider | OpenRouter (OpenAI-compatible API) |
| Markdown | streaming-markdown (incremental parsing) |
| Styling | Tailwind CSS (via CDN) + CSS Variables |
Project artifacts, traced from intent → design → plan → verification:
| Document | Purpose |
|---|---|
| docs/intent.md | Why this exists: problem, outcome, constraints, open questions |
| docs/design.md | Architecture, API contract, data flow, decisions, known gaps |
| docs/plan.md | Milestones, AI-native workflow, metrics, risks |
| docs/test.md | Verification path, test layers, evals, governance |
Note:
docs/test.mdis currently a proposal — there is no test runner in the repo yet.npm run dev+ manual browser/curlchecks are the baseline today.
MIT — see LICENSE for details.