Conversation
When ChainForge runs locally with `pip install chainforge[stats]`, a Stats switch in the Vis Node header shows evalstats' analysis under the plot: per-group means with confidence intervals, rank bands with verdicts (tied for best / significant drop-off), collapsible pairwise differences, and the methods evalstats ran. Intervals are drawn on accuracy bars and box plots. - chainforge/stats.py: always evalstats' paired design. Items without a score for every group and run are excluded up front and reported; no statistics below 15 items. - flask_app.py: /app/compareEvalStats and /app/checkEvalStatsAvailable, gated on the extra like RAG; window.__EVALSTATS_AVAILABLE. - evalStats.ts: pairs results by input (vars, metavars, LLM), leaving out values that only describe one group, such as upstream responses. - VisNode: stacked true/false charts become grouped bars, so each bar can carry an interval; box plots show means when intervals are drawn; the dark-mode grid is dimmer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- The toolbar no longer sets the node's width: it wraps, and its selects are smaller and capped, so the node can be resized much narrower. - The graph type menu moves into the node header beside the Stats switch, both aligned with the close button. The choice is saved with the node. - With Stats on, numeric bar charts show means (not sums), so each bar can carry its confidence interval; errors are left out of the means. - Legend text matches the axis labels in dark mode. - Plots redraw when resized in development: StrictMode reran the effect that disconnected the resize observer without rerunning the ref that created it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
evalstats 0.3.2 exposes what the Vis Node shows, so ChainForge no longer imports its private helpers or copies its rules: - Incomplete items are dropped with evalstats.complete_items(), and its typed errors (InsufficientItemsError, MissingCellsError, TooFewGroupsError, AmbiguousLabelsError) become messages instead of matching on error text. The 15-item floor is evalstats.MIN_ITEMS. - Rank bands, verdicts, per-pair significance and p-values come from to_dict(); the panel shows evalstats' verdicts and bolds pairs it marks significant. - The Methods list is formatted from methods(), now including resampling. - Notes come from result.notes. evalstats gets the real group names, since two-factor levels are now structured, so the id mapping and the rewriting of note text are gone. - Requires evalstats>=0.3.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sults
Battle-testing the statistics against a wide range of eval-like data turned
up two problems:
- Pairing results by input kept any value that ever occurred in more than
one group. An upstream LLM's answer passed on to a judge prompt is often
the same across LLMs ("Paris", "yes"), so those answers split items apart
and most were left out; and a design where each LLM answered different
questions merged into one item, reported as "identical inputs". Items now
keep only the dimensions needed to tell the (input, group) pairs apart:
dropping an answer or a response id leaves them all distinct, dropping a
real input does not.
- When a group, or a combination such as one LLM never run on one value of a
variable, has no scores at all, the panel said only that 0 items had
results for every group. It now names the groups with none.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
In the "Compare prompt across models" example, each prompt's samples are stored as separate responses with identical inputs. Stats rejected them with a red "identical inputs" error, which read like a failure rather than a limit of the data. - Responses for the same input in the same group now count as further runs of that input. - The too-few message counts inputs, and says repeated responses to one input count once. - When prompt variables are compared, the panel explains that their values aren't counted as inputs, and how to use them as inputs instead. - Messages that don't produce statistics carry a reason code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Likely best" assumes a higher score is better, which doesn't hold for metrics like a bias rate. Verdicts now read "Highest", "Tied for highest" and "Significantly lower". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Numeric bar charts summed scores unless Stats was on, so on the web, or without chainforge[stats], a per-response metric like response length read as the total across all responses. Bars now always show each group's mean, under a "Mean of scores" axis, with the mean and n on hover. Results that aren't scores are left out of both. True/false accuracy bars (a proportion) and categorical counts are unchanged. Also moves the new stats UI onto the type scale tokens (--fs-xs, --fs-sm). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ianarawjo
force-pushed
the
claude/evalstats-vis-node-integration-0c75d8
branch
from
September 17, 2026 15:52
82d1f00 to
26c7acb
Compare
Categorical scores were drawn as counts under the axis title "Number of 'true' values", which only fit binary data (and binary data never reached that chart). They are now 100%-stacked bars: each group, or each value of a variable and LLM on a two-level axis, split into the share of its responses in each category, under "% of responses", with the count and n on hover. Percentages keep groups with different numbers of responses comparable, as percent-true bars already do. The graph type menu is now disabled for true/false and categorical scores, which are always plotted as bars; before, categorical scores left it enabled with no effect. 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.
What it does
When ChainForge runs locally with
pip install chainforge[stats](evalstats 0.3.2 or newer), a Stats switch in the Vis Node's header shows evalstats results under the plot:methods(): design, score type, CI methods, simultaneous CIs, the p-value test and correction, the Friedman test, the rank-band rule, resampling, and the evalstats version.It always uses evalstats' paired design. Items without a score for every group and run are dropped with
evalstats.complete_items()and listed, with a prompt to rerun those queries. Belowevalstats.MIN_ITEMS(15), no statistics are shown.Changes
chainforge/stats.py: builds the evalstats input, then relies on evalstats' public API:complete_items()drops incomplete items;to_dict()supplies rank bands, verdicts, significance and p-values;methods()supplies the Methods list, andresult.notesthe notes;InsufficientItemsError,MissingCellsError,TooFewGroupsError,AmbiguousLabelsError) become messages.No private evalstats helpers are imported.
flask_app.py:/app/compareEvalStatsand/app/checkEvalStatsAvailable, gated on the extra like RAG, pluswindow.__EVALSTATS_AVAILABLE.setup.py/ CI / README: the[stats]extra (evalstats>=0.3.2).backend/evalStats.ts: pairs results by input (vars, metavars, LLM). It leaves out values that only describe one group, such as an upstream LLM's answer text.VisStatsPanel.tsx: the panel.VisNode.tsx:Testing
tests/test_stats.py, run against evalstats 0.3.2.methods()lines." / "and names that collide.tscpasses.compare_eval_results, a few also through the Flask route.None, NaN, inf), 2–10 groups, 15–1000 items, 1–5 runs, model × prompt grids, random and structured missing results, unicode, 500-character," / ", numeric-looking and empty names, and duplicates.significantagreeing with the CIs, and each difference equalling the gap between means. None failed." / "names, and LLM × variable with one combination never run. Checked in each:Remaining
🤖 Generated with Claude Code