(MOT-4184) feat(harness): add prompt evaluation support - #595
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 122 files, which is 22 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (122)
You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
skill-check — worker0 verified, 49 skipped (no docs/).
Four for four. Nicely done. |
* (MOT-4184) feat(eval): add prompt comparison worker * (MOT-4184) fix(eval): satisfy clippy and refresh schemas
…er set one This PR introduced both the `ChatParams.max_output_tokens` field and the `Some(generation_max_output_tokens)` that fills it, so every harness turn started carrying an explicit output cap equal to the model's own ceiling — a request-shape change for callers that never asked for one, which lets a provider apply a different policy than its default. `generation_max_output_tokens` stays the internal reservation that context assembly and `budget::reserve` budget against. Send it only when the caller configured a cap, and send the reservation rather than the raw request so the provider is never told it may emit more than we reserved. Callers that want a cap enforced still get one: `eval` sets it from `limits.execution.max_output_tokens_per_call`, as does agent-quality. Fixes the E2E floor failures where the scripted provider rejected generation 1 with `max_output_tokens: expected absent, got 4096`.
Native exposure now publishes the harness-intercepted subscription controls (`subscribe::native_control_tools`) ahead of the registry functions a turn's policy allows — correct, since the engine registry never listed them and an agent allowed to call them previously saw no tool for it. Every fixture send compiles to `expose: Native`, so all six scenarios that `allow_id(engine::register_trigger)` now receive that extra leading tool. Only E2E-004 surfaced in CI; the other five died at generation 1 on the `max_output_tokens` floor first and would have failed on the next run. The control tools' descriptions and schemas live in the harness crate, which the e2e crate does not depend on, so `tools_exact_after_controls` pins them by name while the registry tools that follow stay byte-exact. The list as a whole stays an exact match — an unexpected extra tool keeps its prose and fails, which is what caught this change to begin with.
Resolves against the prompt-evaluation work that landed in #595, which edited the same sections this branch moves out of the identity prompt. - `default.txt` keeps this branch's trimmed identity. The `curl` prohibition is dropped with it: the web worker has injected that rule through its own pre-generate hook since #385, so the line was a stale duplicate that also fired on rigs without the web worker installed. - #595's dispatch doctrine (audit every resolved resource selector before dispatching; derive a per-run namespace suffix and confirm the namespace is absent) moves into `harness/skills/orchestration.md`, which now owns spawn and reaction mechanics. - #595 also corrected the reaction session semantics: an ordinary event reaction MUST omit `metadata.session_id`, and every event then creates a fresh distinct child under the resolved console root. The playbook still carried the older rule, that omitting it delivers back into the registering chat, so it was describing behaviour the runtime no longer has. Same for `once`, which is a top-level registration field and is standing by default on join predecessor edges. - The three tests #595 added assert that doctrine against the identity prompt. They now assert it against the orchestration playbook, which is where the text lives after this branch.
What changed
Adds the harness-side surface prompt evaluation needs, and — since #596 was merged into this branch — the
evalworker that consumes it.New harness functions
harness::metrics(src/functions/metrics.rs) — consolidates usage, cost, function-call, trace, and span metrics for a session behind one call, so callers stop stitching them together per collector.harness::session-tree(src/functions/session_tree.rs) — inspect a session and its descendants.harness::send/harness::spawngain execution budgets:max_output_tokens,max_total_tokens,max_cost_usd,max_turns. Enforcement and reservation live insrc/budget.rs; teardown of an evaluation's sessions insrc/functions/teardown.rs.Behavior changes worth a reviewer's attention — these are easy to miss in a 19k-line diff:
default_functionsis nowallow: ["*"](src/config.rs, −65 lines). It was a curated read-only baseline. Per the doc comment this PR rewrites, that value doubles as the ask-mode ceiling — so this widens what every ask-mode turn may call, not just parentless spawns. It now admitsstate::set,harness::spawn,router::chat,shell::exec, andengine::register_trigger, each of which the removed test asserted was excluded. Deliberate (discovery-denied policies break models), but it is a security-posture change and should be reviewed as one.src/functions/subscribe.rs).engine::register_triggerandengine::unregister_triggerare harness-intercepted, so the engine registry never listed them and an agent whose policy allowed them saw no tool.native_control_toolspublishes their real contracts, ahead of registry functions.max_output_tokensis forwarded only when the caller set one (src/turn_loop.rs). The first pass sent it on every request, filled with the model's own ceiling — a request-shape change for callers that never asked, which lets a provider apply a different policy than its default. The internal reservation still budgets against the effective limit; when a caller does set a cap we send the reservation, so the provider is never told it may emit more than we reserved.Also included
harness/tests/agent-quality/— a package that runs two harness subjects against the same real-model scenarios (same prompt / two models, or same model / two prompts) on one path: build prompt →harness::send→ awaitharness::turn-completed→ readharness::metrics→ evaluate.evalworker and its console page, merged from (MOT-4184) feat(eval): add prompt comparison worker #596. See that PR's description for its surface.tools_exact_after_controlsmatcher — the control tools' prose lives in the harness crate, which the e2e crate cannot import, so they are pinned by name while registry tools stay byte-exact.Size
19k additions reads alarming; roughly a third is generated and another third is the eval worker merged in from #596.
evalworkerevalconsole UIsrc/The harness change under review is the 1,960-line row, concentrated in
metrics.rs(567),budget.rs(519),session_tree.rs(193),clients/session.rs(173), andturn_loop.rs(116).Why
The eval worker needs a durable, queryable harness API with bounded execution and reliable cleanup. Keeping the flow at
send -> metrics -> resultreuses the existing harness and engine primitives instead of growing a parallel evaluation stack.Validation
cargo test --manifest-path harness/Cargo.toml -p harness-integration— 84 passed, 0 failed (includes every scenario'sfixture_is_validand the compiled-scenario round-trip).cargo check --manifest-path harness/Cargo.toml --all-targets --offline— clean.cargo test --manifest-path eval/Cargo.toml --offline— 36 passed, 0 failed, run against theevaltree as merged.Not verified locally: the full
make integration-e2ewas not run to completion, so the 9 E2E scenarios rest on CI. The two fixture-affecting behavior changes above (native control tools,max_output_tokens) were derived from the failing run's artifacts and fromturn_loop.rs's tool-assembly order rather than from a passing local run.