Local OpenAI-compatible proxy for OpenCode Go. OpenCode uses one model id — go-router/auto — and this router picks the real upstream model per request (task type, complexity, risk, quota, optional semantic second stage).
Repository: https://github.com/shashankanil/opencode-go-router
flowchart LR
OC[OpenCode client] -->|POST /v1/chat/completions model=auto| R[Router :8787]
R --> C[classify_request]
C --> D[choose_model / resolve_routing_decision]
D -->|optional cheap LLM| A[routing agent]
D --> U[OpenCode Go upstream]
R --> L[JSONL decision log]
- OpenCode sends chat requests to
http://127.0.0.1:8787/v1withmodel: "auto"(providergo-router). - Classifier reads the latest user message (and paths in the text): task type (e.g.
documentation,debugging,ui_frontend), complexity, risk, token estimate,@overrides. - Deterministic routing maps task → default model from
config/router.yaml, then applies policy (repo-wide → strong model, high/dangerous risk → strong model, quota downgrade on unprotected routes). - Optional semantic routing (
routing_mode: hybrid | semantic | agent+routing_agent.enabled): a cheap upstream model returns JSON with a suggested model; Python gates it (valid model id, risk floor, quota). Default isdeterministic— no extra LLM call. - Upstream request is forwarded to
OPENCODE_GO_BASE_URLwith the chosen literal model id (neverauto). Streaming is preserved. - Response headers include
x-router-model,x-router-task,x-router-complexity,x-router-risk(and agent skip metadata when relevant). - Decisions append to
ROUTER_LOG_PATH(defaultlogs/router-decisions.jsonlor~/.opencode-go-router/router-decisions.jsonlvia npx).
This is not oh-my-openagent / Sisyphus — a small standalone router you run beside OpenCode.
Requirements: Node 18+, uv (Python 3.11+). The launcher uses uv run --project only — it does not pip install into your user site-packages.
# One-time templates in your project directory
npx opencode-go-router init
# Set upstream key (OpenCode Go)
export OPENCODE_GO_API_KEY="your-key"
export ROUTER_API_KEY="local-router-key" # optional; must match OpenCode provider apiKey
# Run server (first run may sync deps via uv)
npx opencode-go-router- API: http://127.0.0.1:8787/v1
- Console: http://127.0.0.1:8787/console (stub or built SPA)
- Diagnostics:
npx opencode-go-router doctor
Config and logs for npx default to ~/.opencode-go-router/ (router.yaml, router-decisions.jsonl). Override with ROUTER_CONFIG_PATH / ROUTER_LOG_PATH.
Copy or merge opencode.example.jsonc into your OpenCode config:
Use the same apiKey as ROUTER_API_KEY when the router enforces auth.
git clone https://github.com/shashankanil/opencode-go-router.git
cd opencode-go-router
cp .env.example .env # set OPENCODE_GO_API_KEY
uv sync --extra dev
export $(grep -v '^#' .env | xargs)
uv run python -m router.main
# or: uv run opencode-go-routerPoint OpenCode at http://127.0.0.1:8787/v1 as above.
export ROUTER_API_KEY=local-router-key
uv run python -m router.mainOpen http://127.0.0.1:8787/console. Rebuild SPA after frontend changes:
cd console && npm install && npm run buildWithout console/dist, the server serves console/stub.html.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness |
GET |
/v1/models |
Lists auto |
POST |
/v1/chat/completions |
OpenAI-compatible chat (streaming supported) |
GET |
/console |
Admin UI |
POST |
/api/admin/classify |
Deterministic classify + route (Bearer ROUTER_API_KEY) |
POST |
/api/admin/route-preview |
Deterministic + agent eligibility / optional agent |
GET |
/api/admin/config |
Read merged config |
Prefix the latest user message:
| Prefix | Effect |
|---|---|
@glm |
glm-5.2 |
@kimi |
kimi-k2.7-code |
@cheap |
deepseek-v4-flash |
@strong |
glm-5.2 |
@ui |
task ui_frontend + glm-5.2 |
@debug |
task debugging + kimi-k2.7-code |
@auto |
ignore override routing |
Defined in config/router.yaml → overrides.
Primary file: config/router.yaml (models, routes, classifier rules, quota modes, overrides).
Environment:
| Variable | Default | Purpose |
|---|---|---|
OPENCODE_GO_API_KEY |
— | Upstream auth |
OPENCODE_GO_BASE_URL |
https://opencode.ai/zen/go/v1 |
Upstream base |
ROUTER_API_KEY |
unset | Bearer for local /v1 and admin |
ROUTER_CONFIG_PATH |
config/router.yaml |
Config file |
ROUTER_LOG_PATH |
logs/router-decisions.jsonl |
Decision log |
ROUTER_QUOTA_MODE |
normal |
normal / conserve / emergency |
ROUTER_ROUTING_MODE |
from yaml | deterministic / semantic / hybrid / agent |
ROUTER_HOST |
127.0.0.1 |
Bind host |
ROUTER_PORT |
8787 |
Bind port |
Quota modes: normal — route defaults; conserve — downgrade unprotected routes; emergency — cheap models on unprotected routes.
Semantic routing (optional): default routing_mode: deterministic adds no extra upstream calls. Example (commented in README only; enable in your local yaml if needed):
# routing_mode: hybrid
# routing_agent:
# enabled: true
# model: deepseek-v4-flashModes: deterministic, semantic (hybrid eligibility), hybrid, agent. Preview: POST /api/admin/route-preview.
- npm:
npm publishfrom repo root (packageopencode-go-router, binopencode-go-router). - Python:
pip install/uv pip installfrom git or PyPI nameopencode-go-router(seepyproject.toml).
uv run python -m pytest tests/ -qMIT — see LICENSE.
{ "provider": { "go-router": { "npm": "@ai-sdk/openai-compatible", "name": "OpenCode Go Router", "options": { "baseURL": "http://127.0.0.1:8787/v1", "apiKey": "local-router-key" }, "models": { "auto": { "name": "Auto Router" } } } }, "model": "go-router/auto", "small_model": "go-router/auto" }