opencode-speed
v1.3.0
Published
Benchmark all OpenCode Zen / Go models in one shot — TTFT (time to first token) and TPS (tokens per second).
Maintainers
Readme
OpencodeSpeed
Benchmark every OpenCode Go (or Zen) model's speed in a single command — measuring TTFT (time to first token) and TPS (tokens per second) using the smallest possible prompt and token budget.
Velocity TUI — built with
Ink 7 + React 19 + Chalk 5(Box/Textflexbox,borderStyle="round"panels,HBargradient bars,getLayoutresponsive columns, alternate-screen) and OpencodeSpeed's velocity theme (cyan#00E5FF/ violet#A78BFA/ pink#F472B6/ sky#22D3EE/ emerald#34D399gradientcyan→violet→pink).
Zero dependencies for plain output; ink + react for the interactive TUI (Node.js 18+).
Install
# anywhere, run without installing
npx opencode-speed
# or install globally
npm install -g opencode-speed
opencode-speed
# or run from this repo
./bench.shQuick start
./bench.shThe app opens as a TUI window (Ink alternate-screen, centered Box with borderStyle="round") so it doesn't pollute scrollback. The approval window shows the active configuration (provider, prompt, runs, concurrency, API-key source) and waits for approval — press Enter / y / Space to start, q / Esc to cancel. Pass --yes / -y to skip (CI). --json and non-TTY pipes auto-skip.
It uses your OpenCode Go API key from ~/.local/share/opencode/auth.json, $OPENCODE_GO_API_KEY, or a prompt. After approval it shows a live benchmark progress panel with a velocity-gradient bar (cyan→violet→pink via gradientSpeed) plus a Threads panel listing every parallel slot (#01…#NN) with its live status (⧗ queued at provider, ▶ streaming + elapsed, ✔ ok + tok/s, ✖ failed, · idle), then a dashboard:
- Overview (full-width, cyan
#00E5FFborder) — provider, prompt, model count, elapsed, sort - Fastest TPS (sky
#22D3EE) & Lowest TTFT (violet#A78BFA) — side-by-side,HBargradient - By Model — sorted by TPS/TTFT/NAME (hot pink
#F472B6) —DataRowtable withTPSgold#FFD700,TTFTviolet,HBarper-row TPS bar - By Endpoint (emerald
#34D399) & Status (amber#FBBF24) — breakdowns - Footer —
[1] TPS [2] TTFT [3] Name [a] Avg [c] Conc [r] Refresh [q] Quit(recolored;ccycles1→3→5→10→15→30)
Velocity palette: overview cyan, model pink, speed sky, latency violet,
gradientSpeedcyan→violet→pink.
In plain/JSON mode it still prints:
MODEL TTFT TOKENS TOTAL TPS LIMITS STATUS
glm-5.2 (chat) 2.93s 65 3.85s 75.0 880/2.2k/4.3k ok
...The LIMITS column shows the documented Go plan usage caps per model — estimated requests per 5-hour / weekly / monthly window ($12/$30/$60), from opencode.ai/docs/go. Shown for the go provider only; — for unknown/free models (e.g. ox-alpha-free). Also included per model in --json output as limits: {h5, weekly, monthly}.
and 1/2/3/c/r/a/q remain live keys in the TUI (a cycles avg off→3x→5x, c cycles concurrency 1→3→5→10→15→30 — both stage the new value and wait for R to refresh; r cycles auto-refresh interval).
Options
| Flag | Description |
| --- | --- |
| -p, --provider <go\|zen> | Provider to benchmark (default go) |
| -k, --api-key <key> | API key override |
| --runs <1\|3\|5> / --avg / --average | Runs per model, averaged (default 1/off). Use 3 or 5 for stable averaging. In interactive mode press a to stage off → 3x → 5x (then R to refresh). |
| -c, --concurrency <1-30> | Parallel models (default 5; interactive: press c to stage 1→3→5→10→15→30 (then R to refresh)). Use 30 for fastest, 1 for safest rate limits. Respects 429 with backoff. |
| -s, --sort <tps\|ttft\|name> | Table sort order (default tps) |
| --timeout <seconds> | Per-request timeout (default 120) |
| --prompt <text> | Prompt sent to each model (default "Say OK") |
| --max-tokens <n> | Max output tokens (default 64; reasoning models need a small budget to finish) |
| --only <id,...> | Only benchmark specific model ids |
| --refresh <1\|5\|15\|30> | Periodic refresh every N minutes (or 1m/5m/etc, off). Disabled by default. In interactive mode press r to cycle off → 1m → 5m → 15m → 30m (R for immediate) |
| --json | Machine-readable JSON output (also skips the approval window) |
| --no-color | Disable ANSI colors |
| -y, --yes | Skip the approval window and start immediately |
How it works
- Fetches the model list from
https://opencode.ai/zen/go/v1/models(.../zen/v1/modelsfor Zen). - Routes each model to its correct endpoint:
/responses— GPT/Grok/Muse (OpenAI Responses API)/messages— Qwen/MiniMax (Anthropic API)/chat/completions— GLM/Kimi/DeepSeek/MiMo/etc.
- Streams a tiny request (
"Say OK", capped output) and measures:- TTFT — time until the first token arrives
- TPS — output tokens ÷ total stream time (TTFT included), using the
same definition as opencode's built-in per-message
tok/sfooter: non-reasoning output tokens from the provider'susage, over request-start → last-token wall time. With--runs, runs are pooled exactly like opencode pools a turn: Σtokens ÷ Σtime.
- Uses exact output token counts from the provider's
usage(reasoning tokens excluded, matching opencode'stokens.output), otherwise estimates from the streamed text.
Notes
- Parallel by default:
bench.shnow runs5models in parallel (was sequential). On a typical 30-model Go catalog that cuts wall time from ~60s → ~12s. Presscin the TUI to stage1→3→5→10→15→30(thenRto refresh); or pass-c 1/-c 30at launch. ChangingAvg(a) also stages and waits forR. The runner backs off on429automatically.15and30are available for high-parallel benchmarking. - Provider-side serialization: the OpenCode Go gateway processes generation requests roughly one-at-a-time per API key. All slots do fire simultaneously (verified: 10 concurrent control requests get headers in parallel, while generation response headers arrive strictly sequentially, each right after the previous stream ends), but with
-c 15/-c 30most threads will sit showing⧗ queued at provideruntil their turn — results will still finish one by one and wall time won't improve much past a few concurrent streams. This is a server-side limit; higher concurrency mostly pre-warms the queue. - Reasoning-heavy models (e.g. Kimi K3, Grok 4.5) may still take several seconds even for a 2-token reply — that's reflected in their TTFT.
Opencode Plugin (v2) — NEW in 1.3.0
opencode-speed now ships as an opencode v2 plugin — benchmark any Go/Zen model's TTFT + TPS without leaving opencode.
The plugin exposes the same engine (src/bench.js:596 tps = Σtokens/Σtime, TTFT included, matching opencode's footer) via:
- Tool
opencode_speed_benchmark— callable by the agent (provider,prompt,maxTokens,concurrency1-30,runs1/3/5,only,sort,timeout) - Slash commands
/opencode-speedand alias/speed— e.g./opencode-speed -p zen --runs 3 -c 10 --only glm-5.2 - TUI route
opencode-speed—Ctrl+Shift+Spalette command,sidebar.footerslot, full velocity dashboard (sky/violet/pinkHBar, Threads panel, sort controls)
Install as plugin
# from npm (once published)
npm install -g opencode-speed # CLI still works
# or as project pluginAdd to opencode.jsonc (project or ~/.config/opencode/opencode.jsonc):
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
"opencode-speed", // from npm
// or local dev:
// "/absolute/path/OpencodeSpeed/src/plugin.ts",
// "[email protected]",
{
"package": "opencode-speed",
"options": {
"provider": "go",
"concurrency": 5,
"runs": 1,
"prompt": "Say OK",
// "apiKey": "sk-..." // or use $OPENCODE_GO_API_KEY / auth.json
}
}
]
}For remote servers / TUI-only (so the TUI works even when the server is remote), also add to ~/.config/opencode/cli.json:
{ "$schema": "https://opencode.ai/v2/cli.json", "plugins": ["opencode-speed"] }Restart the service:
opencode2 service restart
opencode2 plugin list # should show opencode-speed (active)
opencode2 api get /api/pluginUse inside opencode
- Agent tool: ask “benchmark Go models for TPS” — the model will call
opencode_speed_benchmark - Slash:
/opencode-speed --runs 3 -c 10or/speed -p zen - TUI:
Ctrl+P→OpencodeSpeed: Benchmark model speedorCtrl+Shift+S, or click⚡ OpencodeSpeedin the sidebar →▶ Run benchmark
API key resolution (same as CLI src/bench.js:229): options.apiKey → $OPENCODE_GO_API_KEY / $OPENCODE_ZEN_API_KEY → ~/.local/share/opencode/auth.json (keys opencode-go) → ~/.config/opencode/auth.json.
The plugin is
tui:true(src/plugin.ts:54+package.json:exports["./tui"]), so the server plugin auto-loadssrc/tui.tsx. No separate install needed.
Local dev
npm run typecheck # tsc --noEmit --skipLibCheck
node --import tsx test-plugin.mjs # mock ctx → registers tool+commands
npm pack --dry-run # shows src/ + README included, node_modules excludedSee src/plugin.ts:17 (formatMarkdownTable) and src/tui.tsx:1 (SpeedRoute) for the implementation.
Publishing
npm login
npm publish # ships src/ + README via `files` (≈186 kB with Ink deps optional)
npm view opencode-speed versionsprepack runs tsc + node --check. npm pack is wired up. The
published package ships src/ + README (CLI via bin:opencode-speed, plugin via exports:"." → src/plugin.ts and exports:"./tui" → src/tui.tsx).
