node:vm, sandboxed - no network, no filesystem, a 3-second timeout) against each result’s input/output/expected - plus toolCalls (the linked trace’s recorded tool calls) when the result carries a trace_id, so a scorer can assert on tool behavior. They run after the judge and similarity metrics, so a scorer also receives scores - the result’s other verdicts - and can combine them (see below). Live traffic has a counterpart - the code scorer kind on the Scorers page - but the shapes differ: an offline code scorer is a synchronous JavaScript function body like the ones below, while a live code scorer defines an async handler() and may also be Python.
From the dashboard: attach them in the dataset editor (opened from the sidebar’s Manage → Datasets) or in a judge scorer’s Offline runs step. Each one gets a name and a JavaScript function body.
From the SDK (dataset builder, so the scorer is versioned with the dataset it guards):
{ score, reasoning }. A scorer that throws, times out, or returns something unexpected degrades to { score: null, error } for that one result - it never blocks the judge rating or any other score from being computed.
Combining judges and metrics into one final score
Because code scorers run after the judges and metrics, each one receives ascores object with the result’s other verdicts:
1 - value / 10). The dashboard template has a higherIsBetter flag per entry for exactly this.
That makes a custom weighted final score a few lines - your own blend of judge verdicts and deterministic metrics:
scores.judges carries each additional scorer’s rating by name, so per-judge weights work too. scores is undefined on surfaces that don’t score judges first - always null-check it.
From the SDK, attach scorers when building a grading config:
Reading them back
Code scorers are fully retrievable through the SDK - both the stored configuration and the per-result outputs:datasets.get() also round-trips them through import_dataset(), so copying a dataset carries its scorers along. Remember which home applies at run time: a run graded by an LLM judge scorer uses that scorer’s code scorers; only a run with no scorer selected falls back to the dataset’s own.
Each result’s scorer rows come back via run.results() as codeScorerResults. The runnable versions are sample-scripts/selfhost_demo/03_evaluate_with_a_dataset.py and sample-scripts/eval_deep_dive/08_weighted_final_score.py.
Expected-trajectory matching reports
through the same per-result scorer rows: a case with
expected_tools shows a
“Trajectory match (mode)” row next to your own code scorers, pass/fail with the
expected-vs-actual call lists in its reasoning.
