Skip to content
OpenSmartRoute
Documentation
Agent skills

osr-evaluation

Agent skills 4 min read

Evaluate and benchmark an OpenSmartRoute router - author JSONL evaluation datasets, run `osr eval` with cost/quality frontiers, baselines, robustness, calibration (ECE, Brier, conformal) and ablation reports, load RouterBench-style presets, run off-policy evaluation of logged decisions, and gate CI on minimum accuracy. Use when measuring routing accuracy, comparing strategies, or building a regression suite for routing quality.

Package
.claude/skills/osr-evaluation
Compatibility
OpenSmartRoute >= 0.4, Python >= 3.10
License
Apache-2.0
Domains
data_analysis coding general
Quality prior
0.85
Tags
opensmartroute evaluation benchmark calibration

Install by copying .claude/skills/osr-evaluation/ into the skills folder of your coding assistant. To load every package as a routing target: osr --skills .claude/skills route "..." --plan.

Dataset format (JSONL, one row per request)

{"text": "Hi there! How's it going?", "acceptable": ["llm-small"]}
{"text": "Prove sqrt(2) is irrational", "expected": "llm-frontier", "context": {"tier": "pro"}}
{"prompt": "Summarise this PDF", "scores": {"llm-small": 0.4, "llm-mid": 0.8}, "task_type": "summarize"}

Fields: text (or prompt), expected (single id), acceptable (list of ids), context, profile, task_type, scores {id: quality} (RouterBench style), samples {id: [floats]}, tokens {id: tokens spent} (effort / elastic siblings). Unknown keys are ignored. A row is correct when the decision is expected or in acceptable; with scores, mean_quality reports the quality of the chosen target.

Agentic tasks (--agentic) use a different JSONL: one multi-step task per line with a per-agent, per-step success / latency table (eval.agentic.AgentTask):

{"task_id": "t1", "steps": ["look up the order", "reason through the refund conflict step by step"],
 "success": {"fast-agent": [0.97, 0.45], "strong-agent": [0.94, 0.90]},
 "latency_ms": {"fast-agent": [400, 400], "strong-agent": [2000, 2000]}, "domain": "customer_support"}

Presets: --preset routerbench|llmrouterbench|routereval|xroutebench|routerxbench maps public benchmark files onto your catalogue (eval.datasets.load_benchmark(path, preset, model_map, limit)).

CLI

osr -t targets.yaml -r rules.yaml eval data.jsonl [--limit N] [--json]
    --frontier       2-D cost/quality frontier + area under it
    --frontier3      quality/cost/latency Pareto frontier + hypervolume
    --baselines      random / cheapest / best-prior / task-table / oracle (+ noise floor); --quality-floor 0.7
                     held out: the rows are split 50/50 by --seed (default 0), the task table is fitted on the
                     train half, every baseline AND the router are scored on the test half, and `held_out.comparison`
                     gives router-minus-baseline with a paired bootstrap 95 % interval and a verdict; every
                     accuracy carries a Wilson 95 % interval (`accuracy_ci`)
    --robustness     repeat flip-rate, paraphrase stability, profile-swap fairness (first 200 rows)
    --calibration    ECE, Brier, reliability diagram, conformal set coverage / size; plus held-out ECE after
                     fitting a temperature / isotonic calibrator on the even rows (>= 20 rows)
    --ablation       leave-one-strategy-out and leave-one-extractor-out deltas
    --multi-round    route-execute-judge-refine loop vs best single target (rows with scores); --threshold --max-rounds
    --effort         effort routing vs always-highest-effort (rows with scores + tokens; targets declare effort)
    --agentic        DATASET is agentic tasks; per-step routing vs best single agent on accuracy-latency; --retries N
    --min-accuracy X exit status 2 when accuracy < X (CI gate)
osr -t targets.yaml ope logs.jsonl [--max-weight 20]     # IPS / SNIPS / DR off-policy evaluation
osr -t targets.yaml safety [--learned-guard] [--json]    # red-team safety-routing suite
osr collect --cache-dir data --tier frontier=llm-frontier --tier mid=llm-mid --tier small=llm-small
    # fifteen public sources (routellm-battles, arena-55k/100k/140k, arena-expert-5k, search-arena-7k, repochat-arena,
    # llama3-70b-battles, ppe-human, webdev-arena, mt-bench-human, reward-bench, ultrafeedback, routellm-gpt4,
    # routellm-mmlu), resumable after a 429; --source NAME picks, --refresh refetches
osr -t targets.yaml slm train --out slm.json --cache-dir data --report   # every *-tiers cache; numpy when installed
    # judge --report against the constant baseline: "always mid" is acceptable ~64 % of the time on the arena corpora
OSR_SLM_ENCODER=attention osr ... slm train ... --report  # compare the transformer-block encoder on the holdout
osr slm eval slm.json data.jsonl --min-accuracy 0.8       # CI gate for the routing SLM itself

Python

from opensmartroute.eval import evaluate, load_dataset, cost_quality_frontier, area_under_frontier, calibration_report
rows = load_dataset("data.jsonl")
r = evaluate(router, rows)             # EvalResult
print(r.summary())                     # n, accuracy [wilson 95 %], mean_cost_per_1k, mean_latency_ms, mean_confidence,
                                       # mean_quality, ece, brier, abstain_rate, set_coverage, mean_set_size
r.confusion                            # {expected: {chosen: count}}
r.errors                               # rows that missed, with the decision trace
r.hits                                 # per-row correctness in dataset order (for paired comparisons)

from opensmartroute.eval.baselines import baseline_suite, compare_policies, held_out_split
train, test = held_out_split(rows, seed=0)                    # seeded, reproducible 50/50 partition
suite = baseline_suite(test, router.registry.all(), train_rows=train)   # table fitted on train only
for c in compare_policies(evaluate(router, test).hits, suite):          # same rows, paired bootstrap
    print(c["baseline"], c["delta"], c["delta_ci"], c["verdict"])   # "router better" only when the CI excludes 0

Also: eval.robustness.{repeat_flip_rate, paraphrase_robustness, profile_swap_fairness}, eval.frontier.hypervolume, eval.ope.off_policy_evaluate. Exit criteria as runnable measurements: eval.criteria (cold_start_ratio, effort_token_savings, multi_round_vs_best_single, match_at_1_at_scale, conformal_coverage, knapsack_never_exceeds_cap, ope_within_live_ci, run_all) and eval.agentic.task_routing_frontier.

Interpreting results

  • Accuracy below baselines' best-prior means the catalogue (examples / complexity bands) is wrong, not the weights. Fix data first.
  • ece > 0.1 -> confidence is miscalibrated: run router.calibrate() on logged outcomes or lower OSR_ROUTING_SOFTMAX_TEMPERATURE. calibration_report()["held_out"] shows what a fitted TemperatureScaler / IsotonicCalibrator reaches out of sample (on the public human-preference suites raw ECE 0.25-0.32 drops to 0.04-0.06 isotonic); ship the fitted calibrator rather than tuning weights.
  • --ablation deltas near zero identify strategies you can drop to cut latency.
  • --robustness flip-rate > 0 with identical inputs means a stochastic strategy (bandit) is dominating; seed it or lower its weight.
  • Use --frontier when the goal is "same quality, less cost": pick the objective weights on the knee.

CI gate pattern

- run: osr -t examples/targets.yaml -r examples/rules.yaml eval examples/eval_dataset.jsonl --min-accuracy 0.9 --json

Keep the dataset in the repo, deterministic (seed=0 strategies), and add a row for every routing bug you fix.

On the hosted platform

A workspace evaluates the same JSONL without running the router itself: POST /api/v1/evaluations (route scope) routes every row on the workspace's own pool - policy, tenants, allow / deny lists, installed listings and providers apply - executes nothing and meters nothing, and keeps a report (accuracy with Wilson interval, realised quality, price of the mix against the dearest admitted target, ECE / Brier / reliability bins, the mix, per-domain accuracy, confusion pairs, misroutes, the random / cheapest / best-prior / dearest baselines on the same rows, an optional cost-weight frontier). POST /api/v1/evaluations/replay re-routes the prompts kept under the log_payloads policy through today's router and compares with what answered then (moves, projected cost, agreement with reported outcomes). The dashboard page is /platform/dashboard/evaluation; osr eval rows.jsonl --remote (signed in with osr login) sends the same file there and gates on --min-accuracy like the local run; the TypeScript client has client.evaluations.run / replay / get. The implementation is platform/api/osr_platform/evaluations.py, which reuses EvalRow, the baselines and the calibration helpers of this package and wraps the routing in opensmartroute.observability.untraced().

Something wrong or out of date on this page? Tell us - the page is named for you, so a one-line note is enough.