osr-evaluation
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 itselfPython
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 0Also: 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-priormeans the catalogue (examples / complexity bands) is wrong, not the weights. Fix data first. ece > 0.1-> confidence is miscalibrated: runrouter.calibrate()on logged outcomes or lowerOSR_ROUTING_SOFTMAX_TEMPERATURE.calibration_report()["held_out"]shows what a fittedTemperatureScaler/IsotonicCalibratorreaches 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.--ablationdeltas near zero identify strategies you can drop to cut latency.--robustnessflip-rate > 0 with identical inputs means a stochastic strategy (bandit) is dominating; seed it or lower its weight.- Use
--frontierwhen 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 --jsonKeep 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.