Model router for agentic systems. Routes every prompt to the right model in <50ms. Cut costs 40-70% with just an endpoint change.
$ npx -y skills add workweave/router --agent claude-code
Run the curl in your terminal, the rest in Claude Code.
Repo: workweave/router
What's inside
One endpoint. Every model. Always the right one.
A drop-in proxy for Anthropic, OpenAI, and Gemini that picks the best model for every request: using a tiny on-box embedder, not a vibes-based prompt.
Built by Weave: The #1 engineering intelligence platform, loved by Robinhood, PostHog, Reducto, and hundreds of others.
Point Claude Code, Codex, Cursor, or your own app at localhost:8080. The router:
The fastest way: point Claude Code, Codex, opencode, or pi at the hosted Weave Router with one command. No clone, no Docker, no Postgres.
npx @workweave/router
That's it. The installer asks which tool (Claude Code, Codex, opencode, or pi), walks you through scope (user vs. project), grabs a router key, and wires the right config file. Other flavors:
npx @workweave/router --claude # skip the picker, Claude Code
npx @workweave/router --codex # skip the picker, OpenAI Codex CLI
npx @workweave/router --opencode # skip the picker, opencode
npx @workweave/router --pi # skip the picker, pi + Loom UI
npx @workweave/router --scope project # per-repo, commits settings.json (or .codex/ / opencode.json)
npx @workweave/router --local # self-hosted localhost:8080
npx @workweave/router --base-url https://router.acme.internal
npx @workweave/router@0.1.0 # pin a version
Requires Node β₯ 18 (Claude Code, opencode, and pi paths also need jq). Full
flag reference: install/npm/README.md.
If you want the router (and dashboard) running on your own box:
# 1. Drop a provider key in. OpenRouter is the recommended baseline.
echo "OPENROUTER_API_KEY=sk-or-v1-..." >> .env.local
# 2. Boot Postgres + router on :8080 and seed an rk_ key.
make full-setup
The router is up at http://localhost:8080, the dashboard at
http://localhost:8080/ui/ (password: admin), and your rk_... key
prints in the logs.
# Call it like Anthropic
curl -sS http://localhost:8080/v1/messages \
-H "Authorization: Bearer rk_..." \
-d '{"model":"claude-sonnet-4-5","max_tokens":256,
"messages":[{"role":"user","content":"hi"}]}'
# ...or like OpenAI
curl -sS http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer rk_..." \
-d '{"model":"gpt-4o-mini",
"messages":[{"role":"user","content":"hi"}]}'
# Peek at the routing decision without proxying
curl -sS http://localhost:8080/v1/route -H "Authorization: Bearer rk_..." -d '...'
Only the grey boxes are off your machine. The router, the scorer, Postgres, and your provider keys all stay local; prompts go from the router straight to the provider you configured, never to Weave.
flowchart LR
client["Claude Code, Codex, opencode,<br/>pi, Cursor, your own app"]
router["Router :8080<br/>/v1/messages Β· /v1/chat/completions<br/>/v1beta/models Β· /v1/route"]
scorer["Cluster scorer<br/>in-process ONNX embedder"]
hmm["HMM policy sidecar :8093<br/>optional, make up-hmm"]
pg[("Postgres<br/>installations, rk_ keys,<br/>encrypted BYOK keys, usage")]
ui["Dashboard /ui<br/>selfhosted mode only"]
providers["Anthropic Β· OpenAI Β· Gemini<br/>OpenRouter and any<br/>OpenAI-compatible endpoint"]
otel["Your OTLP collector<br/>Honeycomb, Datadog, Grafana"]
client -->|"rk_β¦ bearer token,<br/>streamed response back"| router
router -->|"embed and score the action"| scorer
router -.->|"ROUTER_DEFAULT_STRATEGY=hmm"| hmm
router -->|"auth, config, usage"| pg
pg --> ui
router -->|"provider key from env or BYOK"| providers
router -.->|"spans and usage logs"| otel
classDef external fill:#f4f4f5,stroke:#a1a1aa,color:#3f3f46
class providers,otel external
Multi-replica deployments also need Pub/Sub (PUBSUB_*) for cache
invalidation; docker compose runs the emulator for you.
The default stack uses the in-process cluster scorer. To run the frozen HMM policy as a companion container, add a Google API key and use the opt-in target:
echo 'GOOGLE_API_KEY=...' >> .env.local
make up-hmm
This does not change the default strategy. See
sidecars/hmm/README.md for artifact verification,
embedding compatibility, and explicit HMM selection.
Claude Code. Run make install-cc to wire Claude Code at the local
self-hosted router (it's also invoked automatically at the end of
make full-setup). For the hosted router, use npx @workweave/router
above.
Codex (OpenAI CLI). npx @workweave/router --codex patches
~/.codex/config.toml (or <repo>/.codex/config.toml with --scope project)
with a managed [model_providers.weave] block and sets model_provider = "weave".
Codex's existing OPENAI_API_KEY flows through to api.openai.com for the
plan-based passthrough; the router key rides in an X-Weave-Router-Key HTTP
header. Re-install and --uninstall --codex rewrite/remove only the managed
block, leaving the rest of your Codex config untouched.
opencode. npx @workweave/router --opencode merges a provider.weave
entry into ~/.config/opencode/opencode.json (or <repo>/opencode.json
with --scope project). It uses opencode's bundled @ai-sdk/anthropic
provider pointed at the router's /v1 endpoint β the router speaks the
Anthropic Messages API natively, so opencode works unmodified. The router
key and identity headers ride alongside the provider config; re-install
rewrites only the managed block and --uninstall --opencode strips it.
pi. npx @workweave/router --pi keeps stock pi as the runtime and installs
the router's pi extension. It adds the Loom header, Wooly's animated terminal
mascot, a persistent WEAVE ROUTER route/savings line, /fm + /ufm
model-pin commands with a [forced] status, and context-isolated subagents
without shipping or maintaining a forked pi binary.
Cursor (early beta, performance may not be the best). Settings β
Models β Override OpenAI Base URL β http://localhost:8080/v1, paste
rk_... as the API key.
Switching on/off. After installing, npx @workweave/router off --claude
(or --codex / --opencode) routes that client straight to its provider
again without discarding the router config; on flips it back, and status
reports which way it's pointing. Claude Code also gets /router-off,
/router-on, and /router-status slash commands. Cursor toggles via the same
Settings β Models override above. See install/README.md.
Two keys, don't mix them up:
sk-or-.../sk-ant-.../sk-...= your upstream provider key. Lives in.env.local.rk_...= your router key. Clients send this as a Bearer token.
| Endpoint | Format |
|---|---|
POST /v1/messages | Anthropic Messages, routed |
POST /v1/chat/completions | OpenAI Chat Completions, routed |
POST /v1beta/models/:action | Gemini generateContent, routed |
POST /v1/route | Returns the decision, no upstream call |
GET /v1/models Β Β·Β POST /v1/messages/count_tokens | Anthropic passthrough |
GET /health Β Β·Β GET /readyz Β Β·Β GET /validate | liveness + dependency readiness + key check |
GET /v1/analytics/routing-decisions | Raw routing decisions as cursor-paginated NDJSON (docs) |
GET /v1/analytics/schema Β Β·Β GET /v1/analytics/models | Export field dictionary + price book |
Keep liveness probes on /health. Point startup or readiness probes at
/readyz when configured policy sidecars must be ready before traffic arrives.
Zhang, Y. et al. Beyond GPT-5: Making LLMs Cheaper and Better via PerformanceβEfficiency Optimized Routing (Avengers-Pro). arXiv:2508.12631, 2025. https://arxiv.org/abs/2508.12631 β©
.claude/
skills/
debug-claude-session/
.deployment.json.example
SKILL.md
fix-pr-reviews/
SKILL.md
test-claude-locally/
scripts/
mock_openai_upstream.py
SKILL.md
.dockerignore
.env.example
.gitattributes
.github/
workflows/
check_cluster_routing.yml
check_migrations.yml
cluster_routing_ack.yml
comment_length_review.yml
publish_cluster_routing.yml
publish_npm.yml
smoke.yml
test.yml
.gitignore
AGENTS.md
CLAUDE.md
cmd/
AGENTS.md
CLAUDE.md
genprices/
main_test.go
main.go
initdb/
main.go
router/
baseline_test.go
hmm_capabilities_test.go
hmm_capabilities.go
hmm_roster_source_test.go
hmm_roster_source.go
main.go
policy_sidecar_auth_test.go
policy_sidecar_auth.go
policy_sidecars_test.go
policy_sidecars.go
routing-report/
main_test.go
main.go
seed/
main.go
CONTRIBUTING.md
db/
AGENTS.md
CLAUDE.md
init/
00-create-schema.sql
migrations/
0001_init.down.sql
0001_init.up.sql
0002_installation-excluded-models.down.sql
0002_installation-excluded-models.up.sql
0003_session_pin_cache_stats.down.sql
0003_session_pin_cache_stats.up.sql
0004_multiple-api-keys-per-installation.down.sql
0004_multiple-api-keys-per-installation.up.sql
0005_telemetry-costs-to-micros.down.sql
0005_telemetry-costs-to-micros.up.sql
0006_credit_billing.down.sql
0006_credit_billing.up.sql
0007_user_display_name.down.sql
0007_user_display_name.up.sql
0008_ledger_memo.down.sql
0008_ledger_memo.up.sql
0009_session_pin_consecutive_upstream_errors.down.sql
0009_session_pin_consecutive_upstream_errors.up.sql
0010_session_pin_last_served_model.down.sql
0010_session_pin_last_served_model.up.sql
0011_session_pin_has_ever_switched.down.sql
0011_session_pin_has_ever_switched.up.sql
0012_telemetry_candidate_scores_propensity.down.sql
0012_telemetry_candidate_scores_propensity.up.sql
0013_installation-excluded-providers.down.sql
0013_installation-excluded-providers.up.sql
0014_telemetry-identity-columns.down.sql
0014_telemetry-identity-columns.up.sql
0015_loop-escalation-events.down.sql
0015_loop-escalation-events.up.sql
0016_telemetry-turn-type.down.sql
0016_telemetry-turn-type.up.sql
0017_telemetry_rollout_id.down.sql
0017_telemetry_rollout_id.up.sql
0018_spiral-shadow-events.down.sql
0018_spiral-shadow-events.up.sql
0019_telemetry-response-outcome.down.sql
0019_telemetry-response-outcome.up.sql
0020_router-feedback.down.sql
0020_router-feedback.up.sql
0021_telemetry-session-key-join.down.sql
0021_telemetry-session-key-join.up.sql
0022_telemetry-fresh-scorer-shadow.down.sql
0022_telemetry-fresh-scorer-shadow.up.sql
0023_telemetry-tool-result-bytes.down.sql
0023_telemetry-tool-result-bytes.up.sql
0024_request-feedback.down.sql
0024_request-feedback.up.sql
0025_installation-routing-preference.down.sql
0025_installation-routing-preference.up.sql
0026_installation-usage-bypass.down.sql
0026_installation-usage-bypass.up.sql
0027_installation-preferred-models.down.sql
0027_installation-preferred-models.up.sql
0028_telemetry-credential-key-parts.down.sql
0028_telemetry-credential-key-parts.up.sql
0029_installation-subscription-routing-disabled.down.sql
0029_installation-subscription-routing-disabled.up.sql
0030_session_pin_paired_model.down.sql
0030_session_pin_paired_model.up.sql
0031_api-key-spend-cap.down.sql
0031_api-key-spend-cap.up.sql
0032_autopay-config.down.sql
0032_autopay-config.up.sql
0033_telemetry-strategy-route-id.down.sql
0033_telemetry-strategy-route-id.up.sql
0034_installation-policy-routing.down.sql
0034_installation-policy-routing.up.sql
0035_telemetry-policy-router.down.sql
0035_telemetry-policy-router.up.sql
0036_policy-shadow-decisions.down.sql
0036_policy-shadow-decisions.up.sql
0037_monthly-spend-limits.down.sql
0037_monthly-spend-limits.up.sql
0038_autopay-monthly-cap.down.sql
0038_autopay-monthly-cap.up.sql
0039_unified-limit-headers.down.sql
0039_unified-limit-headers.up.sql
0040_auto-feedback-columns.down.sql
0040_auto-feedback-columns.up.sql
0041_router-feedback-turn-id-metadata.down.sql
0041_router-feedback-turn-id-metadata.up.sql
0042_cluster-model-lists.down.sql
0042_cluster-model-lists.up.sql
0043_session-pin-overload-tracking.down.sql
0043_session-pin-overload-tracking.up.sql
0044_managed-byok.down.sql
0044_managed-byok.up.sql
0045_anthropic-gateway-and-capture-ceiling.down.sql
0045_anthropic-gateway-and-capture-ceiling.up.sql
0046_api-key-scope.down.sql
0046_api-key-scope.up.sql
0047_telemetry-export-cursor-index.down.sql
0047_telemetry-export-cursor-index.up.sql
pubsub/
entrypoint.sh
queries/
autopay.sql
billing.sql
cluster_model_lists.sql
loop_escalation_events.sql
model_router_api_keys.sql
model_router_external_api_keys.sql
model_router_installations.sql
model_router_request_telemetry.sql
model_router_users.sql
policy_shadow_decisions.sql
request_feedback.sql
router_feedback.sql
session_pins.sql
spend_limits.sql
spiral_shadow_events.sql
sqlc.yml
docker-compose.yml
Dockerfile
docs/
ANALYTICS_EXPORT.md
CONFIGURATION.md
POLICY_ROUTER_HARNESS.md
README.md
SEMANTICS.md
SMOKE.md
TRANSLATION_COMPATIBILITY.md
frontend/
next-env.d.ts
next.config.ts
package-lock.json
package.json
postcss.config.js
public/
android-chrome-192x192.png
android-chrome-512x512.png
favicon-16x16.png
favicon-32x32.png
weave.svg
src/
app/
(app)/
dashboard/
page.tsx
layout.tsx
settings/
page.tsx
(auth)/
layout.tsx
login/
page.tsx
favicon.ico
globals.css
layout.tsx
page.tsx
components/
atoms/
Kbd/
index.ts
Kbd.tsx
Skeleton/
index.ts
Skeleton.tsx
Text/
index.ts
Text.tsx
Chart/
Chart.tsx
ChartContainer.tsx
ChartContext.ts
ChartLegend.tsx
ChartMessageCard.tsx
ChartReferenceLineLabel.tsx
ChartTick.tsx
ChartTooltip.tsx
color.ts
constants.ts
helpers/
getDefaultSeriesColor.ts
getRechartsDomain.ts
index.ts
types.ts
useToggleSeriesVisibility.ts
ChartCard/
ChartCard.tsx
index.ts
charts/
CostBreakdownChart.tsx
CumulativeSavingsChart.tsx
DrillDownModal.tsx
ModelBreakdownChart.tsx
RouterCostSavingsChart.tsx
SavingsRateChart.tsx
useChartDrillDown.ts
DashboardPageFilters/
DashboardPageFilters.tsx
FilterPill.tsx
index.ts
useDashboardFilters.ts
Input.tsx
Logo.tsx
molecules/
Button/
Button.tsx
index.ts
Card/
Card.tsx
index.ts
Command/
Command.tsx
index.ts
Modal/
index.ts
Modal.tsx
Popover/
index.ts
Popover.tsx
Tooltip/
index.ts
Tooltip.tsx
Page/
index.ts
Page.tsx
PageHeader/
index.ts
PageHeader.tsx
ResponsiveGrid/
index.ts
ResponsiveGrid.tsx
Sidebar.tsx
SidebarLayout.tsx
Statistic/
index.ts
Statistic.tsx
types.ts
lib/
api.ts
cn.ts
objects/
scalars/
DateTime.ts
TimeGranularity/
index.ts
TimeGranularity.ts
tools/
cn.ts
LoadState.ts
map/
groupBy.ts
index.ts
once.ts
types/
index.ts
SafeOmit.ts
unreachable.ts
tailwind.config.ts
tsconfig.json
go.mod
go.sum
install/
cc-statusline.sh
commands/
fm.md
force-model.md
rf.md
router-feedback.md
router-off.md
router-on.md
router-session.md
router-status.md
ufm.md
unforce-model.md
install.sh
npm/
.gitignore
bin.js
package.json
README.md
scripts/
copy-installer.js
opencode-weave/
package.json
README.md
src/
index.ts
test/
capture.test.ts
pi-router/
package.json
README.md
src/
compaction.ts
config.ts
dispatch.ts
force-model.ts
index.ts
metadata.ts
pricing.generated.ts
provider.ts
routed-model.ts
safety.ts
savings.ts
ui.ts
wooly.ts
test/
compaction.test.ts
e2e.sh
force-model.test.ts
mock_router.py
opencode_smoke.sh
savings.test.ts
ui.test.ts
unit-suite.ts
README.md
spin
tests/
cc-statusline_test.sh
uninstall.sh
internal/
analytics/
analytics.go
cursor_test.go
cursor.go
pricebook.go
schema_test.go
schema.go
service_test.go
service.go
api/
admin/
auth.go
catalog_test.go
catalog.go
config_test.go
config.go
content_capture_internal_test.go
content_capture.go
excluded_models.go
excluded_providers.go
health_test.go
health.go
hmm_roster.go
installation.go
keys_test.go
keys.go
metrics.go
... 1023 moreFAQ
router is a Claude Code plugin with 3 hand-picked skills for development work, indexed on Flowy. Install it with the command on its page. It includes debug-claude-session, fix-pr-reviews, test-claude-locally. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.