Skip to content

benchmarking/evaluate: run evaluations, with a run_results source - #3551

Merged
Max Smythe (maxsmythe) merged 3 commits into
agent-substrate:mainfrom
roycaihw:run-evaluations-b
Nov 12, 2026
Merged

Max Smythe (maxsmythe) merged 3 commits into
agent-substrate:mainfrom
roycaihw:run-evaluations-b

Conversation

@roycaihw

@roycaihw Haowei Cai (Roy) (roycaihw) commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Part of #3298 (design). First of four PRs: #3551 (this), #3550, #3552, #3592.

Benchmark pass/fail logic is spread over one-off tools. With this, a test entry declares evaluations, each a set of variables (source queries) and CEL conditions over their values, and the evaluator judges a run into a Report: pass, fail or invalid per evaluation. Nothing calls it yet. #3552 wires it into the runner and writes evaluation.json and the stats.jsonl rows.

Conditions compile with cel-expr-python when the evaluations load. Sources are classes with three hooks (sources/base.py): validate(cfg) when the evaluations load, init(run) when the run starts, and query(cfg) after it. A variable whose source can't judge the run is invalid and is left out of the CEL data, so every condition that reads it is invalid and the evaluation can't pass. run_results reads one stats.jsonl row. The config is pydantic models. source_cfg: null is an error; leave the key out for {}.

AI use: I wrote this with an AI coding agent from the design, then reviewed the diff and checked the goodput numbers against the #2526 prototype on 248 Prow runs.

@roycaihw
Haowei Cai (Roy) (roycaihw) force-pushed the run-evaluations-b branch 2 times, most recently from 4805228 to 7972199 Compare November 3, 2026 17:56
@roycaihw Haowei Cai (Roy) (roycaihw) changed the title benchmarking/evaluate: evaluator, run_results source and CLI benchmarking/evaluate: run evaluations, with a run_results source Nov 3, 2026
@roycaihw
Haowei Cai (Roy) (roycaihw) marked this pull request as ready for review November 4, 2026 04:00
@roycaihw

Copy link
Copy Markdown
Contributor Author

Max Smythe (@maxsmythe) Nishanth Kotla (@Nishanth29) PTAL

@roycaihw
Haowei Cai (Roy) (roycaihw) force-pushed the run-evaluations-b branch 2 times, most recently from d89d6b6 to 5950bb3 Compare November 4, 2026 16:29
Comment thread benchmarking/evaluate/config.py Resolved
Comment thread benchmarking/evaluate/outputs.py Resolved
Comment thread benchmarking/evaluate/checks.py Resolved
Comment thread benchmarking/evaluate/sources/base.py Resolved
Comment thread benchmarking/evaluate/sources/base.py Resolved
Comment thread benchmarking/evaluate/config.py Resolved
Comment thread benchmarking/evaluate/evaluator.py Resolved
Comment thread benchmarking/evaluate/evaluator.py Resolved
Comment thread benchmarking/evaluate/outputs.py Resolved
Comment thread benchmarking/evaluate/outputs.py Resolved
Comment thread benchmarking/evaluate/outputs.py Resolved
@roycaihw

Copy link
Copy Markdown
Contributor Author

Max Smythe (@maxsmythe) I've addressed the comments. PTAL

Comment thread benchmarking/evaluate/sources/base.py Resolved
Comment thread benchmarking/evaluate/sources/base.py Outdated


@dataclass(frozen=True)
class BenchmarkRunContext:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an abstract concept or specific to the RunResults source?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an abstract. It's what every source gets about the run. Reworded the docstring.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I don't get is why every class gets/needs this information. Wont most/many sources be reading from their own data files that the benchmarking framework likely wont be aware of (even if they live in the artifact directory)?

If the source already knows what files they are looking for, why does it need this data?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. It was shaped around the RunResults source. Now it only says which run it is (test, tag, run_ts, uri) and gives access to the run's stored files. Each source reads what it needs, or looks the run up in its own data by that identity.

Comment thread benchmarking/evaluate/cel_env.py Resolved
Comment thread benchmarking/evaluate/cel_env.py Resolved
Comment thread benchmarking/evaluate/cel_env.py Resolved
Comment thread benchmarking/evaluate/cel_env.py Resolved
Comment thread benchmarking/evaluate/config.py Resolved
Comment thread benchmarking/evaluate/config.py Outdated
if bad := [k for k in cfg if not isinstance(k, str)]:
raise ConfigError(f"{where}: source_cfg: non-string keys {sorted(bad, key=repr)}")
try:
SOURCES[source].validate(cfg)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.

We'll need to figure out how to extend this with pluggable sources, but... baby steps

Comment thread benchmarking/evaluate/cel_env.py Resolved
@maxsmythe

Copy link
Copy Markdown
Collaborator

Haowei Cai (Roy) (@roycaihw) Thanks! Took another pass.

Benchmark pass/fail logic is spread over one-off tools. With this, a test entry declares `evaluations`, each a set of variables (source queries) and CEL conditions over their values, and the evaluator judges a run into a `Report`: pass, fail or invalid per evaluation. Nothing calls it yet.

Conditions compile with `cel-expr-python` when the evaluations load. Sources are classes with three hooks (`sources/base.py`): `validate(cfg)` when the evaluations load, `init(run)` when the run starts, and `query(cfg)` after it. A variable whose source can't judge the run is invalid and is left out of the CEL data, so every condition that reads it is invalid and the evaluation can't pass. `run_results` reads one `stats.jsonl` row.

AI use: I wrote this with an AI coding agent from the design, then reviewed the diff and checked the goodput numbers against the tools/goodput prototype on 248 Prow runs.

Signed-off-by: Haowei Cai <haoweic@google.com>
Conditions are now plain CEL: integer literals are no longer rewritten,
reserved names are left to CEL, and the evaluator no longer tracks which
variables a condition reads. An invalid variable is left out of the CEL
data and makes its evaluation invalid. The config shape is pydantic models.

Signed-off-by: Haowei Cai <haoweic@google.com>
@roycaihw

Copy link
Copy Markdown
Contributor Author

Max Smythe (@maxsmythe) I've addressed the comments. PTAL.

@roycaihw

Copy link
Copy Markdown
Contributor Author

/retest

@maxsmythe Max Smythe (maxsmythe) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question about the run() object, and then I threw AI at it

Comment thread benchmarking/evaluate/config.py Resolved
Comment thread benchmarking/evaluate/cel_env.py Resolved
Comment thread benchmarking/evaluate/evaluator.py Resolved
BenchmarkRunContext now carries the run's identity and artifact(name)
instead of a file list, and the Report reads the identity from it. Each
source chooses which artifacts to read, or looks the run up in its own
store by identity.

source_cfg is typed as JSON so non-JSON values or non-string keys fail at
load. RunResults skips blank lines in stats.jsonl and marks the variable
invalid on any malformed line. Conditions accept DYN so bare bool fields
work, and evaluated_at converts to UTC before formatting.

AI use: I wrote this with an AI coding agent from the design, then reviewed the diff.

Signed-off-by: Haowei Cai <haoweic@google.com>
@roycaihw

Copy link
Copy Markdown
Contributor Author

Max Smythe (@maxsmythe) Thanks for reviewing! I addressed the question and the comments. PTAL.

@maxsmythe Max Smythe (maxsmythe) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@maxsmythe

Copy link
Copy Markdown
Collaborator

Thanks for all the work! Merging as a test-only change

@maxsmythe
Max Smythe (maxsmythe) added this pull request to the merge queue Nov 12, 2026
Merged via the queue into agent-substrate:main with commit e472ffb Nov 12, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants