Compare LLM judges in the LLM Scorer, with support for TypeSafe's Jev - #461
Merged
Merged
Conversation
…ment - Categorical scorers can list their categories (with optional descriptions) and numeric scorers their scale levels. Judges are told exactly what to answer, and answers outside those bounds are kept as written and counted as invalid, instead of turning the whole run's scores into strings. - An LLM Scorer can have several judges. Each response is scored once per judge, with scores keyed by judge name (Multi-Eval keeps one judge). - "Compare to" picks an input column as the ground-truth label. A new "Judges" tab in the Inspector shows each judge's agreement with the label and with the other judges (descriptive only, with n), the responses where they disagree, and the answers that didn't fit the format. - The node is compact: format and options share one row, with categories, scale and "Reason before scoring" in a popover. - Table View: long scores wrap in score columns instead of being cut off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jev is a "decision" model: it answers typed questions about a text rather than writing text. OpenRouter serves it at /api/alpha/decisions. - Jev (~typesafe/jev-latest) is in the OpenRouter menu, and any typesafe/ model ID is recognized as a decision model. - As a judge, Jev gets the response as its state and one question built from the scorer: the rubric as instructions, and the format as the type (true/false -> noul, categorical -> choice with the categories and their descriptions, numeric -> score with the scale's levels). Its score is shifted from 0-based levels to the scorer's 1-to-N scale. - Jev and LLM judges can score side by side; LLM judges still get the full grader prompt. Formats Jev can't answer (open-ended, too few categories, a scale outside 2-10 levels, an empty rubric) fail with a clear message before any request. In a Prompt node, Jev says to use it as a judge. - Fix: queryLLM's cache index dropped the cache files of earlier runs (an inner variable shadowed the one it saved), so changing a model's settings and changing them back re-queried everything. Jev hit this on every rubric edit, since its question lives in its settings. Checked live against OpenRouter: 36 decisions, all 200s, in the expected shape, at about $0.00002 each. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Hovering the Run button says what a run will do, as the Prompt Node's does: e.g. "Will load scores from cache", "Will send 36 requests to Jev and load others from cache". If the scorer can't run as set up (e.g. Jev with an open-ended format), it says why instead. - Right-clicking an LLM Scorer offers "Clear cached scores" first, which clears its saved scores and every judge's cached answers. - The counting uses the same judge setup and inputs as a run, now shared helpers in backend.ts, so the tooltip can't disagree with the run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Response stats record cost where the provider reports it (OpenRouter's usage.cost, for any model it serves), also as the stat_cost_usd metavar. - The scorer totals each judge's stats over a run: answers, cost, median time per answer, and tokens. The Judges tab shows them in a table, with bars in each judge's model color in the cost and time cells. - Jev's probability for each answer is kept with its score (eval_res.probs), shown beside its answers in the disagreements, and, with a label set, in a table of how often Jev is right at each confidence it states. - The Run button's tooltip is cleared when a run starts, so it isn't stale. Checked live: Jev and GPT-5.4 Nano both scored 36 responses at 100% agreement with the label; OpenRouter reported both judges' costs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Repeating the label as its description cost about 10 input tokens per category and told Jev nothing new. Measured live on 36 responses with 9 categories: input tokens fell from 508 to 419 per call (18%), with the same 100% agreement with the ground-truth label and the same confidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each judge's agreement with the ground-truth label gets a bar in its model's color, scaled to 100% (or, for numeric scores, to the largest mean difference), matching the cost and speed table. Judge colors are now worked out once for the whole tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… example
- Data nodes (Tabular Data, Text Fields, Items, Media) can connect straight
to an LLM Scorer: each value is a response to score, and a table row's
other columns become its variables, e.g. a ground-truth label. Values keep
stable ids, so their scores stay cached; "Clear cached scores" clears them.
- Table View: with scores in their own columns, each response gets its own
row. Responses with the same vars (e.g. from a Split Node) shared a row,
and all but the first lost their scores.
- "Compare to" shows a saved metavariable by name, e.g. "scam (metavariable)".
- New example flow, jev-judges: TypeSafe's Jev, GPT-5.4 Mini and Claude
Sonnet 5 judge two tasks against human labels, run live:
- Triage 120 bank support questions into 8 card topics (Banking77, CC BY
4.0): Jev 99%, Sonnet 98%, Mini 93%; Jev $0.0025 vs. Sonnet $0.08.
- Flag scams in 100 text messages (SMS Spam Collection, CC BY 4.0), each
lightly paraphrased first in case judges memorised the public dataset:
all three 98%; Jev $0.00135 vs. Sonnet $0.03, 253 ms vs. 1.8 s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…SON answers
- Jev reads text only. Asked to score an image (e.g. from a Media Node, now
a scorer input), it answered about an empty string; it now fails with a
message saying to use a judge that takes images, before any request.
- Keeping earlier runs' cache files listed (so switching settings back
reuses responses) also put them in exports, e.g. responses of models since
removed. The index lists them apart, as stale_cache_files: lookups and the
Run tooltip's count still use them, clearing removes them, and exports
leave them out.
- Only decision judges' answers are read as {"answer", "p"} JSON. A text
judge that happened to answer in JSON had its answer cut down and a
made-up probability shown in the Judges tab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A long description pushed the button past the bottom of its fixed-height card, out of view. The description now takes the space between the title and the button and scrolls when it doesn't fit; while there's more below, its bottom edge fades out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the LLM Scorer a way to compare LLM judges, and adds TypeSafe's Jev (a "decision" model on OpenRouter) as one of them. Also adds an example flow for the Jev launch.
What it does
Several judges in one LLM Scorer. "Add judge +" adds a model; each response is scored once per judge, with scores keyed by judge name. Multi-Eval keeps one judge.
Explicit answer formats. Categorical scorers list their categories (optionally with descriptions), and numeric ones their scale's levels. Judges are told exactly what to answer. Answers outside the format are kept as written and counted as invalid, instead of turning the whole run's scores into strings.
Ground truth and a Judges tab. "Compare to" picks a column as the true label. A new Judges tab in the Inspector shows:
Jev support. Jev (
~typesafe/jev-latest) is in the OpenRouter menu. As a judge it gets the response as its state, and one typed question built from the rubric and format:It goes through OpenRouter's
/api/alpha/decisions. Its probability for each answer is kept with the score. Formats Jev can't answer fail with a clear message before any request, and using it in a Prompt Node says to use it as a judge.Cost in response stats. Builds on Record each response's latency, token counts and speed #459: cost is read from OpenRouter's
usage.cost, for any model it serves, and exposed asstat_cost_usd.Data straight into the scorer. Tabular Data, Text Fields, Items and Media nodes can connect directly to an LLM Scorer. A table row's other columns become variables.
Smaller scorer improvements:
Example flow: "🧑⚖️ Compare LLM judges, with Jev" (
jev-judges.cforge), run live against human labels:Both datasets are CC BY 4.0 and credited in the flow. Samples were hand-picked to leave out crude or sensitive items.
Fixes found along the way
SCORE:line. Before, only numbers were extracted.Behavior changes for existing users
trigger()can open on a named tab. Other nodes' Inspectors are unchanged; the Judges tab only appears for LLM Scorers.Testing
with_stats_metavarsgainedcost_usd).Not verified
/api/alpha/decisions), so its path may change. It's one constant inutils.ts.🤖 Generated with Claude Code