fix(eval): run replay harness in verify

This commit is contained in:
ZacharyZhang-NY committed 2026-04-22 14:47:31 -04:00
1 parent f90fb037f9
commit cb315f308d
7 files changed
+612 -79

No files matched your search

+24 -20
View File
@@ -1,23 +1,20 @@
/**
* Eval runner for B5 harness.
* Eval runner for the recorded-fixture harness.
*
* Given a parsed `FixtureFile`, produces an `EvalResult` by:
* 1. Summing the token ledger across all recorded assistant turns.
* 2. Running the assertion judge against the recorded turns.
* 3. Measuring wall-clock duration of steps (1)–(2).
*
* Tonight's scope deliberately evaluates assertions against the RECORDED
* trace, not a live replay-driven agent loop. That keeps the harness
* useful as a snapshot-regression tool the moment the first fixture lands,
* without wiring in the full `pi-agent-core` Agent loop (which requires
* tools + hooks and is better done incrementally once we have fixtures).
*
* The replay-client + fixture-loader are written now so the live-replay
* tier can be added in B5.1 without reshaping types.
* A fixture now runs through the real `pi-agent-core` Agent loop with a
* replay stream and recorded tool-result executors. Assertions are judged
* against the replayed transcript that the Agent produced, so tool-call
* ordering, tool-result handoff, token ledger aggregation, and assertion
* evaluation share the same path the worker uses in production.
*/
import { judgeAssertions } from "./assertion-judge.js";
import type { EvalResult, FixtureFile, TokenLedger } from "./types.js";
import { replayFixtureThroughAgent } from "./tool-loop-replay.js";
import type { EvalResult, FixtureFile, TokenLedger, TraceTurn } from "./types.js";
export interface RunEvalOptions {
systemPrompt?: string;
}
const ZERO_LEDGER: TokenLedger = {
input: 0,
@@ -26,8 +23,8 @@ const ZERO_LEDGER: TokenLedger = {
output: 0,
};
function sumLedger(fixture: FixtureFile): TokenLedger {
return fixture.turns.reduce<TokenLedger>((acc, turn) => {
function sumLedger(turns: TraceTurn[]): TokenLedger {
return turns.reduce<TokenLedger>((acc, turn) => {
if (turn.role !== "assistant") return acc;
return {
input: acc.input + turn.tokenLedger.input,
@@ -38,10 +35,17 @@ function sumLedger(fixture: FixtureFile): TokenLedger {
}, ZERO_LEDGER);
}
export function runEval(fixture: FixtureFile): EvalResult {
export async function runEval(
fixture: FixtureFile,
options: RunEvalOptions = {},
): Promise<EvalResult> {
const start = Date.now();
const results = judgeAssertions(fixture.assertions, fixture.turns);
const tokenLedger = sumLedger(fixture);
const replayedTurns = await replayFixtureThroughAgent(
fixture,
options.systemPrompt ?? "",
);
const results = judgeAssertions(fixture.assertions, replayedTurns);
const tokenLedger = sumLedger(replayedTurns);
const durationMs = Date.now() - start;
const passed = results.every((result) => result.passed);