promptCompare
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v163View latest version
A Val Town app for comparing LLM model outputs side-by-side. Users enter a prompt, select 1-4 models from the OpenCode Zen catalog, and see responses stream in parallel columns. Supports multi-turn conversations where each model maintains its own conversation history.
backend/index.ts— Hono app entry point, serves frontend, mounts route modulesbackend/routes/auth.ts— Password auth viaPROMPTCOMPARE_PASSWORDenv var, sets HttpOnly session cookiebackend/routes/api.ts— REST endpoints for conversations + streaming chat proxy to OpenCode Zenbackend/database/migrations.ts— SQLite schema (val-scoped viastd/sqlite/main.ts)backend/database/queries.ts— Typed query functions
frontend/index.html— Shell with CDN imports (React 18.2.0 via esm.sh, Tailwind via twind)frontend/index.tsx— App entry, routing (login vs main)frontend/components/— Chat UI components inspired by ai-elements patterns
shared/types.ts— TypeScript interfaces shared between frontend and backend
conversations (
id TEXT PRIMARY KEY,
title TEXT,
model_ids TEXT NOT NULL, -- JSON array of model IDs
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
)
messages (
id TEXT PRIMARY KEY,
conversation_id TEXT NOT NULL,
role TEXT NOT NULL, -- 'user'
content TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (conversation_id) REFERENCES conversations(id)
)
responses (
id TEXT PRIMARY KEY,
message_id TEXT NOT NULL,
model_id TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (message_id) REFERENCES messages(id)
)
POST /api/auth— Login with password, returns session cookieGET /api/auth/check— Check if session is validPOST /api/auth/logout— Clear session cookieGET /api/conversations— List conversationsGET /api/conversations/:id— Get conversation with messages and responsesPOST /api/conversations— Create new conversation (title, model_ids)DELETE /api/conversations/:id— Delete conversationPOST /api/chat— Send a message, streams responses from selected models via SSEGET /api/models— Return non-deprecated models from models.json
When a user sends a message:
- Backend creates a
messagerow and aresponserow per model - Backend opens parallel requests to OpenCode Zen for each model
- Backend multiplexes responses into a single SSE stream, tagging each chunk with its model_id
- Frontend demuxes the SSE stream and updates each column independently
- When all models finish, backend updates the response rows with final content
SSE event format:
event: chunk
data: {"model_id": "claude-sonnet-4-6", "content": "Hello"}
event: done
data: {"model_id": "claude-sonnet-4-6"}
event: error
data: {"model_id": "claude-sonnet-4-6", "error": "..."}
- Env var:
PROMPTCOMPARE_PASSWORD POST /api/authwith{ password }body- Backend validates, sets HttpOnly cookie with a signed token (or simple hash)
- All
/api/*routes (except auth) check cookie via middleware
+--sidebar--+------------------main-------------------+
| [New Chat] | Model A | Model B | Model C |
| | +-response--+ +-response-+ +-------+ |
| Conv 1 | | streaming | | streaming| | ... | |
| Conv 2 | | markdown | | markdown | | | |
| Conv 3 | +-----------+ +----------+ +-------+ |
| | |
| | +--prompt input bar------------------+ |
| | | Type a message... [Send] | |
| | +------------------------------------+ |
+------------+------------------------------------------+
- On new conversation, show a model picker (checkboxes, max 4)
- Models sourced from
models.json, filtered to excludestatus: "deprecated" - Show model name, family, cost per 1M tokens
- Selected models persist for the conversation
- Single SSE stream (multiplexed) rather than multiple parallel connections — simpler client code
- Each model maintains its own response history for multi-turn context
- Val-scoped SQLite (
std/sqlite/main.ts) for persistence - No build step — all frontend served as raw files via Val Town utilities