Find the right OpenCode Go model — without burning through your quota.
ZenPick compares every OpenCode Go model side-by-side with live benchmarks, algorithmic fit scores, and quota burn estimates. One $10/month subscription, 13+ models — pick the one that matches your task and your budget.
👉 zp.svelte-apps.me · github.com/Michael-Obele/zenpick
OpenCode Go gives you a single API key to 12+ curated coding models. But which one do you actually use? Some burn through your $60 monthly quota in hours; others last all month. Some are built for frontend UI work, others for long-context agentic work.
ZenPick answers three questions:
- Which model fits my task? — Scenario scores rank every model for Brainstorming, Coding, Agentic, Budget, and Frontend UI use cases.
- How fast will it burn my quota? — Thermal burn badges and request-per-window estimates show you the economics at a glance.
- What closed-source model does it replace? — Migration hints map each Go model to its frontier equivalent (e.g. "if you used Claude Sonnet 4.6, try DeepSeek V4 Pro").
- 🎯 Scenario filters — Every model gets a 0–100 fit score per scenario. No empty tables, no brittle tag matching.
- 📊 Sortable table — Compare by benchmark scores, pricing, quota requests, or scenario fit.
- 🔥 Thermal burn badges — ❄️ Quota-friendly (cyan) · 🌡️ Moderate (amber) · 🔥 Burns fast (red)
- 🖥️ Detail drawer — Per-model deep dive: pricing, quota estimates, benchmark bars, migration hints, one-tap copy model ID.
- 🧮 Quota calculator — Slider to estimate token usage and cost-per-request against your Go subscription.
- ⚡ Live data — Fetches from modelgrep (OpenRouter pricing + Artificial Analysis benchmarks) and OpenCode Go. Cached for 6 hours with stale-while-revalidate.
| Layer | Technology |
|---|---|
| Framework | Svelte 5 + SvelteKit |
| Styling | Tailwind CSS v4 |
| Components | shadcn-svelte + Bits UI |
| Icons | Lucide |
| Data | modelgrep API + OpenCode Go /models endpoint |
| Runtime | Bun |
| Deployment | Netlify |
# Clone
git clone https://github.com/Michael-Obele/zenpick.git
cd zenpick
# Install dependencies
bun install
# Set up environment
cp .env.example .env
# No API keys needed — modelgrep is free and open
# Start dev server
bun run devOpen http://localhost:5173 in your browser.
src/
lib/
types/models.ts — Shared type definitions
server/
modelgrep.ts — modelgrep API client
opencode-go.ts — OpenCode Go /models endpoint
inference.ts — Algorithmic tag & scenario scoring
cache.ts — In-memory TTL cache (6h)
burn.ts — Burn rate tier calculations
remote/
models.remote.ts — getModels() query
components/
FilterBar.svelte — Scenario pills + search
ModelTable.svelte — Sortable/filterable model table
ModelDrawer.svelte — Detail panel with migration hints
QuotaCalculator.svelte — Token → quota estimator
ui/ — shadcn-svelte primitives
routes/
+page.svelte — Single-page app
layout.css — Global styles
static/
screenshot.png — README screenshot
docs/
plans/ — Design docs
Because the catalog is rendered client-side, LLM crawlers that cannot run JavaScript would see an empty page. ZenPick therefore serves a dynamically generated /llms.txt (the llmstxt.org convention): the live model catalog as plain markdown, generated on request from the same server-side cache the app uses.
- Route:
src/routes/llms.txt/+server.ts(a+server.tsroute, the same pattern as a/sitemap.xml). - Content: summary, the full model table (pricing, burn, quota, fit scores, migration hints, per-model llm-stats link), key pages, and data sources.
- Caching:
Cache-Control: public, max-age=600on top of the 6h in-memory model cache.
Model data is cached in-process (src/lib/cache.ts) with a 6h TTL and stale-while-revalidate: a stale request returns the old data immediately and refreshes in the background. That means new upstream models appear automatically within ~6h + one request — but sometimes you want them now.
For that there is a secret-protected endpoint:
curl -X POST https://zp.svelte-apps.me/api/revalidate \
-H "Authorization: Bearer $REVALIDATE_SECRET"
# → { "ok": true, "models": 32, "durationMs": 1234, "at": "..." }POST /api/revalidate forces a fresh upstream fetch (bypassing the TTL) and rebuilds both the model cache and the frontier snapshot. Point a Netlify build hook, a scheduled GitHub Action, or a manual curl at it.
- Setup: set
REVALIDATE_SECRET(any long random string) in your local.envand in the Netlify environment. If it is unset the endpoint returns500; a wrong/missing token returns401. - Effect: only the instance that handles the request is invalidated (the cache is per-instance memory). On a single warm serverless instance that is the whole app; with multiple instances the rest converge as their own TTLs lapse. Making it global would require a shared cache (Netlify Blobs / KV / Redis).
- Client pages: an already-open tab keeps its current data until reload or navigation. A fresh page load (or
getModels().refresh()) picks up the rebuilt cache.
MIT
