CLI and interactive workspace
Run, follow, compare and export evaluations from your terminal. Every resource command has the shape evalrouter <noun> <verb> and maps to one API operation.
On this page
uv tool install evalrouter
evalrouter login
evalrouterRun the command alone for the interactive workspace, or add --help to any command for its flags. If your shell can't find the command, run uv tool update-shell and restart the terminal. The upgrade command installs new releases. Start with the quickstart.
Command shape#
evalrouter benchmarks list
evalrouter models list
evalrouter quotes create gsm8k --model gpt-5
evalrouter runs create --quote QUOTE_ID --yes --wait
evalrouter runs list
evalrouter runs get RUN_ID
evalrouter runs results RUN_ID
evalrouter runs export RUN_ID --file-format html,json| Shortcut | What it does |
|---|---|
run | Quote, confirm, start and follow a run in one step |
agents list | List the ACP Registry agents you can run |
login, logout, whoami | Sign in, sign out, show the account |
open | Open a run's report in the browser |
upgrade | Install the latest release |
completion | Print a shell completion script |
api PATH | Send one raw request (-X METHOD, --body FILE) |
A command from an earlier release, such as status or wait, prints its replacement and exits 2. The command reference lists every resource command.
Start a run#
The run command quotes the run, shows the price, asks you to confirm, starts it and follows it:
evalrouter run gsm8k --model gpt-5
evalrouter run gsm8k --model gpt-5 --dry-run # the quote, nothing is charged
evalrouter run gsm8k --model gpt-5 --yes --json
evalrouter run gsm8k --model gpt-5 --max-cost 5 # with a spending cap
evalrouter run --from RUN_ID # the same setup again, as a new run- Names, not IDs. Benchmarks and models take a slug or an unambiguous name, and
gsm8k@2pins a version. Exact IDs work too. At a terminal, a missing or ambiguous name becomes a choice. --max-cost USDis the most the run may charge. The run starts only if the quote's maximum fits. Without it, your wallet balance bounds the run and the confirmation says so. Your API key's spend ceiling bounds everything.--dry-runprints the quote (maximum charge and plan), starts nothing and needs no confirmation.- Idempotent. Repeating the same start after a lost connection returns the same run, never a second one.
To review the quote on its own, create it first with quotes create, then start it with run --quote QUOTE_ID --yes.
Several models in one run#
Repeat --model to run two to eight models on the same tasks in one run, under one spending cap or your wallet:
evalrouter run gsm8k --model gpt-5 --model claude-sonnet-5 --dry-run # one quote for every model
evalrouter run gsm8k --model gpt-5 --model claude-sonnet-5 --benchmark gpqa-diamond
evalrouter runs compare RUN_ID # rank, model, score, 95% range
evalrouter runs continue RUN_ID # finish an interrupted runThe quote lists every model and, when the cap is split, each one's share. The reasoning setting applies to every model. The compare command ranks the models on the tasks every model finished:
gsm8k@6 · Accuracy · 1,319 tasks
claude-sonnet-5-5 92.7% vs gpt-4.1-mini 84.2% on 1,319 tasks · difference 8.5 percentage points (6.9–10.1)
1 claude-sonnet-5-5 92.7% (91.3–94.0)
2 gpt-4.1-mini 84.2% (82.2–86.1)
Difference 8.5 percentage points (6.9–10.1)- The second line summarises the top two, adding "(N models)" when there are more.
- Each row is rank (
1=for a tie), model, score and 95% range. - The difference row (top model minus the other) appears with exactly two models.
- A run compared on fewer tasks than planned says "300 of 1,319 tasks".
- With
--json, the output adds every pair.
A run stopped by its cap, your credit or a cancellation shows as interrupted, with the reason. The continue command quotes only its unfinished tasks, asks you to confirm and starts the run that completes it, reusing finished tasks at no charge. The original then shows as continued, and comparing it compares the completed run.
Evaluate an agent#
The --agent flag takes one value, and --model is the model the agent uses through the run's metered gateway:
evalrouter agents list # ACP Registry agents and their integrity
evalrouter run gsm8k --model gpt-5 --agent opencode # the pinned snapshot's version
evalrouter run gsm8k --model gpt-5 --agent opencode@1.18.34
evalrouter run gsm8k --model gpt-5 --agent "local:my-agent --acp"
evalrouter run gsm8k --model gpt-5 --agent remote:my-agent # a remote a2a agent, by connection name
evalrouter run gsm8k --model gpt-5 --agent opencode --hosted # hosted by EvalRouter (or --agent hosted:opencode)Registry and local agents run on this machine: EvalRouter delivers each task, meters every model call and grades the submission. A remote agent calls the model through EvalRouter. The agent list comes from the ACP Registry snapshot bundled with the CLI, with no API call, and labels each agent's integrity:
| Label | Meaning |
|---|---|
pinned | A binary with a published sha256 |
package-registry | An exact npm or PyPI package |
unverified-upstream | Runs only with --allow-unverified, and its observed hash is recorded |
The agent contract#
EvalRouter evaluates any agent that meets its agent contract:
| Part | What the agent does |
|---|---|
| Transport | It speaks ACP over stdio, and the CLI starts it. A remote agent speaks A2A with EvalRouter's evaluation extension. |
| Model calls | It sends every model call to the run's gateway with the run's token. The gateway serves OpenAI Chat Completions, and an Anthropic Messages subset on routes that offer one. |
| Task tools | It reads the task, submits its answer and finishes with the tools of the MCP server named evalrouter. EvalRouter grades only the last submission before done. |
On your machine, the agent starts with an empty private home directory. It gets only the locale, path, proxy and certificate variables from your environment. Your EvalRouter and provider keys don't reach it.
Some agents ask permission before they act, as Qwen Code and Claude Code do. The CLI then approves only calls to the evalrouter tools and reads or edits inside the task's workspace. The policy trusts what the agent reports, so it isn't an isolation boundary. OpenCode and Goose run their own shell and file tools without asking.
Agent status#
| Agent | Status | How the CLI points it at the gateway |
|---|---|---|
opencode | conformant (1.18.34) | Its own config file names the gateway as its only provider. It ignores a workspace opencode.json. The models.dev download and auto-update are off. ACP mode build. |
qwen-code | conformant (0.25.0) | System settings select the gateway and the run's model. They outrank a workspace .qwen/settings.json. Qwen Code finds the task tools before the chat starts. Telemetry, suggestions, managed memory and the models.dev refresh are off. ACP mode default. |
goose | conformant (1.53.0) | Environment settings, which Goose reads before any config file, select its openai provider at the gateway. Workspace files don't change the provider. Session naming and telemetry are off. ACP mode auto. |
claude-acp | conformant (0.86.0) | Claude Code's gateway mode points every model it picks at the run's model, so it needs a managed Anthropic Messages model. It reads no user, project or local settings. ACP mode default. |
| Status | Meaning |
|---|---|
conformant | The agent meets the contract with a reviewed launch profile. This doesn't mean it is isolated. |
compatible | The agent meets the contract with the generic gateway settings. |
unsupported | The agent needs a model API the gateway doesn't serve, or a vendor-hosted account. |
broken:<reason> | The agent fails a contract check, for the stated reason. |
A status holds for the pinned version. EvalRouter checks a new version again before it lists that version. These statuses are separate from the integrity labels above, which say how an agent is published.
Check an agent yourself with evalrouter . The check needs no account and makes no model calls. It sees only connections that use the proxy settings, so it reports checks-passed, never a contract status, and then exits 0. An interactive evalrouter run --agent runs the same check on a registry agent's first use. It stops the run only when the check sees a model bypass.
Only a model call that bypasses the run's gateway fails the contract. That is a call to a known model provider, such as api.openai.com, or an agent that ignores the gateway settings. Other network activity doesn't fail it. At startup, OpenCode installs its plugin package from registry.npmjs.org, and Goose downloads the public models.dev catalog. Neither carries the task or a credential.
Any other agent, including a local command, gets the gateway in the standard OpenAI environment variables, such as OPENAI_BASE_URL. The run plan warns that EvalRouter can't stop that agent from calling another provider. The run neither meters nor caps those calls.
Scripts and agents#
| Setting | Effect |
|---|---|
--format human|json|ids (EVALROUTER_FORMAT) | json: one JSON document on stdout. ids: one ID per line. |
--json | Same as --format json. |
--no-input | Never prompt. Where a choice is needed, fail with exit status 5. |
--profile NAME (EVALROUTER_PROFILE) | A named sign-in and API origin, kept separately (evalrouter ). |
--base-url, --workspace-id, --timeout | API origin, workspace and request deadline for this command. |
A flag wins over its environment variable, which wins over the profile. The options command lists them all. With JSON output, stdout carries exactly one JSON document. Progress goes to stderr, only on a terminal. An error is one object on stderr, with stdout empty:
{"error": {"code": "confirmation_required", "message": "…", "hint": "…", "retryable": false, "request_id": null, "details": null}}JSON output, CI=true and known agent environments never prompt. Colour is off without a terminal, and with NO_COLOR or a dumb terminal.
The API serves clients from 0.5.0 on. An older CLI or SDK gets HTTP 426 on every command. Nothing runs, and EvalRouter charges nothing. The CLI prints the upgrade command for its installation. An interactive CLI offers to upgrade when a newer release exists. The background check never installs anything or delays startup. Scripts, JSON output and pinned installations skip it, and EVALROUTER_NO_UPDATE_CHECK=1 turns it off.
Exit status#
| Status | Meaning |
|---|---|
| 0 | Success. |
| 1 | The API refused the request, or another error. |
| 2 | Invalid command line or input. Nothing was sent. |
| 3 | The run ended without completing (failed, partial or cancelled). |
| 4 | Not signed in, or the credential was rejected. |
| 5 | A confirmation or choice was needed and not given, or the quote exceeds the cap. Nothing started. |
| 6 | The API could not be reached. A run you were watching keeps going on the server. |
| 124 | The wait timeout passed. The server work continues. |
| 130 | Interrupted (Ctrl-C). Submitted server work is not cancelled. |
Lists and pages#
Paging
| Flag | What it does |
|---|---|
--limit N | Read up to N rows (default 50) |
--all | Read every page |
--cursor | Continue after an earlier page |
Output
| Flag | What it does |
|---|---|
--json | One document that includes the next page cursor, empty on the last page |
--columns, --sort | Choose and order the table's columns |
--filter KEY=VALUE | Keep matching rows |
--wide | Show every column |
--jq EXPR | Shape the JSON with a jq expression |
Browse interactively
benchmarks list -imodels list -iruns list -i
Cache
| Flag | What it does |
|---|---|
--refresh | Read now, ignoring a recent local copy |
--offline | Make no request |
Public catalog reads may reuse a recent local copy when the server allows it, and say so. Quotes always check live availability and prices.
Follow, read and export a run#
evalrouter runs wait RUN_ID --progress auto
evalrouter runs events list RUN_ID --follow
evalrouter runs results RUN_ID
evalrouter runs export RUN_ID --file-format html,json --open
evalrouter runs cancel RUN_ID
evalrouter open RUN_ID| Command or flag | What it does |
|---|---|
runs wait | Retries temporary API failures with growing delays. Stops watching with exit status 6 after --disconnect-timeout (default 90 seconds) without an answer. |
--progress json | One evalrouter.progress.v1 record per line on stderr |
runs events list | One JSON event per line. With --follow, reads until the run ends and exits like runs wait. |
runs export | Prints the full path of every file saved. Without --output, files are named evalrouter-<id>.<format> in the current directory, and an existing name gets a -2 suffix. |
--file-format bundle | Saves the evidence ZIP |
--include-resumed | Saves the chain result of a run and its completed successors |
comparisons | Downloads one descriptive comparison from saved results, without running inference. --evidence also downloads each full export. |
comparisons create | Compares exact result versions under a release policy on the server |
result-groups | Saves a group of runs for one report |
open RUN_ID | Opens the run's live report and prints its address. Never opens a browser in CI, over SSH, without a display, or with --no-browser. |
Reasoning and output format#
Reasoning defaults belong to the exact model route. The --reasoning flag sets an explicit effort. Without it the route's default applies, and the quote shows the effective choice. The API refuses a route that can't run a requested effort, before any charge.
Benchmark versions that grade a JSON answer may offer a choice of output format with --output-format. Prompts and grading are the same in every format. Results record the format, and scores from different formats are never ranked together. The API refuses a route that can't run the chosen format. Neither setting can change on an existing quote.
| Format | Use |
|---|---|
default | The benchmark version's default format |
leaderboard | The version's leaderboard policy. Never falls back to prompt_json. |
prompt_json | The author prompt only |
native_json | Adds the provider's JSON object response format |
json_schema | Adds the row's author schema as a non-strict JSON schema response format |
Connections and remote agents#
The connections create command registers your own model endpoint. The CLI never takes a key as an argument. At a terminal it asks with hidden input, and scripts pass the key on stdin or name an environment variable.
printf '%s' "$PROVIDER_KEY" | evalrouter connections create --name NAME --endpoint URL --model MODEL --key-stdin
evalrouter connections check CONNECTION_IDUse a checked connection as a quote's model, or as --model connection:ID on run. Your provider may bill model or agent requests separately.
A provider account holds one key and base URL for several models. Add it once, then attach each model to it:
evalrouter providers add --name munito --endpoint URL --key-ref op://vault/item/key
evalrouter connections create --name qwen --provider munito --model MODELChange with providers update NAME | Effect |
|---|---|
A new key (--rotate-key, --key-stdin, --key-env or --key-ref) | Running evaluations continue and their next call uses it. A call already being sent when the change commits may use the previous key once. |
A new --endpoint | Running evaluations on the account stop. Check each model again before quoting it. |
A lower --max-concurrency | Running calls keep their slots. New calls wait for the new limit. |
Disabling the account with providers disable NAME stops every model on it.
A remote agent is a connection with --protocol a2a. Its credentials come from the environment or stdin, never the command line:
evalrouter connections create --protocol a2a --name my-agent --endpoint https://agent.example.com \
--header-env Authorization=AGENT_TOKEN
evalrouter run BENCHMARK --model MODEL --agent remote:my-agent --yesRemote agent options
| Flag | What it does |
|---|---|
--header-env NAME=ENVVAR | A header from an environment variable (repeatable, up to four) |
--header-stdin NAME | A header from stdin |
--oauth2-token-url, --oauth2-client-id | OAuth2 client credentials |
--oauth2-client-secret-env | The OAuth2 secret from an environment variable |
--oauth2-scope, --oauth2-audience | Optional OAuth2 scope and audience |
--card-path | The agent card path (default /.well-known/agent-card.json) |
--agent-parameters | Agent parameters from a JSON object file, on run and quotes create |
--agent-gateway-api | A model API the agent may call through the run's gateway (repeatable, omit for none) |
--agent-max-concurrent-tasks | Tasks the agent runs at once, 1 to 4 (default 4) |
API keys and workspaces#
evalrouter api-keys create --label ci --scope eval:read --scope eval:write --spend-ceiling 20
evalrouter api-keys update KEY_ID --spend-ceiling none
evalrouter api-keys roll KEY_ID
evalrouter workspaces switch NAME_OR_IDA key's spend ceiling bounds what that key can spend in total, and none removes it. Switching workspaces registers this device for another workspace without a browser.
Tab completion
| Shell | Setup |
|---|---|
| zsh | Add eval to ~/.zshrc, after compinit |
| Bash | Add eval to your startup file |
| fish | Run evalrouter |
Open a new terminal afterwards. Tab completes commands, flags and their choices, benchmark names (type @ for versions), model names, registry agents, remote connection names, profiles and recent run IDs.
Commands, flags and agents come from the installed CLI. The other names come from a local cache, kept per profile, API origin and workspace. The CLI clears the cache when you sign in, sign out or switch workspace. A background process refreshes it with read-only list requests when it is more than five minutes old. Pressing Tab never waits for the API and works offline.
Preview commands#
These commands are preview. evalrouter --help marks them. They sit outside the stability promise, and any release may change or remove them. Scripts should rely only on the commands in the reference below.
recommend,releases,tracesdatasets,tune
Command reference#
Every resource command and what it does. Run any of them with --help for its flags.
All resource commands
| Command | What it does |
|---|---|
evalrouter api-keys create | Create an API key; its secret is shown once. |
evalrouter | Revoke an API key. |
evalrouter api-keys list | List this workspace's API keys. |
evalrouter | Replace an API key; the old one keeps working for the overlap. |
evalrouter | Change an API key's spend ceiling. |
evalrouter | Show one environment family. |
evalrouter | List environment families. Pages with --limit, --all and --cursor. |
evalrouter | Show one evaluation reference. |
evalrouter | List evaluation references. Pages with --limit, --all and --cursor. |
evalrouter | Show one benchmark and its versions. |
evalrouter | Show which imports this workspace can use. |
evalrouter | Import a file from a GitHub repository as a private source. |
evalrouter | Preview the files of a GitHub repository revision. |
evalrouter | List the branches and tags of a GitHub repository. |
evalrouter | Import a Hugging Face dataset as a private source. |
evalrouter benchmarks list | List maintained benchmarks. Pages with --limit, --all and --cursor. |
evalrouter | Stop new runs of a private benchmark. |
evalrouter | Create a private benchmark from a data file and a small manifest. |
evalrouter | Show one private benchmark. |
evalrouter | List this workspace's private benchmarks. |
evalrouter | Archive an uploaded source to free a slot. |
evalrouter | Upload local data files as a source private to this workspace. |
evalrouter | Show one uploaded source. |
evalrouter | List uploaded sources and the slots they hold. |
evalrouter | Compare exact result versions under a release policy. |
evalrouter | Show a comparison. |
evalrouter comparisons list | List comparisons. Pages with --limit, --all and --cursor. |
evalrouter | Test a connection with one small request. |
evalrouter | Connect an OpenAI-compatible endpoint. |
evalrouter | Stop using a connection. |
evalrouter connections list | List endpoint connections. Pages with --limit, --all and --cursor. |
evalrouter | Change a connection or rotate its key. |
evalrouter | Approve pending examples. |
evalrouter | Build a private benchmark from a dataset version. |
evalrouter | Add recent production traces as held-out examples; running it confirms the workspace may evaluate on them. |
evalrouter | Freeze the approved examples as a new version. |
evalrouter | List a dataset's versions. |
evalrouter models list | List models and their prices. Pages with --limit, --all and --cursor. |
evalrouter providers add | Add a provider's key and address once. |
evalrouter | Stop using a provider account and its models. |
evalrouter | Show a provider account. |
evalrouter providers list | List provider accounts. Pages with --limit, --all and --cursor. |
evalrouter | Rotate a provider account's key or change its address. |
evalrouter quotes create | Price a run and freeze its setup without starting it. |
evalrouter | Show a quote. |
evalrouter | Group existing runs' results for reuse. |
evalrouter | Save a result group export to a file. |
evalrouter | Show a result group. |
evalrouter | List result groups. Pages with --limit, --all and --cursor. |
evalrouter | List a run's attempts and their outcomes. Pages with --limit, --all and --cursor. |
evalrouter | Request cancellation of a run. |
evalrouter | Compare a multi-model run's models on the tasks every model finished. |
evalrouter runs create | Start a run from a quote under a spending cap (--max-cost). |
evalrouter | Print a run's events as JSON lines. |
evalrouter | Save a run's versioned export to a file. |
evalrouter runs get RUN_ID | Show one run. |
evalrouter runs list | List recent runs. Pages with --limit, --all and --cursor. |
evalrouter | Show a run's frozen receipt. |
evalrouter | Show a run's scores, coverage and uncertainty. |
evalrouter | List a run's per-task samples (--all pages through every one). Pages with --limit, --all and --cursor. |
evalrouter runs wait RUN_ID | Wait until it ends; interrupting never cancels the server work. |
evalrouter traces connect | Create a trace connection and print its ingest key once. |
evalrouter | List trace connections. |
evalrouter traces list | List recent complete traces. Pages with --limit, --all and --cursor. |
evalrouter workspaces create | Create a workspace. |
evalrouter | Delete a workspace (confirm with its name). |
evalrouter | Show a workspace. |
evalrouter workspaces list | List workspaces you belong to. |
evalrouter | Rename a workspace. |