Skip to content

Commit 3b1642c

Browse files
committed
fix(core): use attempt wording for repeat output
1 parent 46e9e61 commit 3b1642c

5 files changed

Lines changed: 91 additions & 31 deletions

File tree

‎apps/dashboard/src/components/EvalDetail.tsx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -618,9 +618,9 @@ function RepeatAggregateChecksTab({
618618
<div className="space-y-6">
619619
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
620620
<div className="grid gap-3 md:grid-cols-4">
621-
<RunMetricRow label="Run success" value={formatPercent(group.passRate)} />
621+
<RunMetricRow label="Attempt success" value={formatPercent(group.passRate)} />
622622
<RunMetricRow label="Mean score" value={formatPercent(group.meanScore)} />
623-
<RunMetricRow label="Passed runs" value={`${group.passedTrials}/${group.trialCount}`} />
623+
<RunMetricRow label="Passed attempts" value={`${group.passedTrials}/${group.trialCount}`} />
624624
<RunMetricRow label="Assertions" value={formatPercent(group.assertionPassRate)} />
625625
</div>
626626
</div>
@@ -644,7 +644,7 @@ function RepeatAggregateChecksTab({
644644
) : null}
645645

646646
<section className="space-y-3">
647-
<h4 className="text-xs font-semibold uppercase tracking-wider text-gray-300">Runs</h4>
647+
<h4 className="text-xs font-semibold uppercase tracking-wider text-gray-300">Attempts</h4>
648648
{group.trials.map((trial, index) => (
649649
<TrialActionRow
650650
key={caseTrialPath(trial, index)}
@@ -804,7 +804,7 @@ function RepeatAggregateTranscriptTab({
804804
return (
805805
<section className="space-y-3">
806806
<h4 className="text-xs font-semibold uppercase tracking-wider text-gray-300">
807-
Run transcripts
807+
Attempt transcripts
808808
</h4>
809809
{group.trials.map((trial, index) => {
810810
const runLabel = caseTrialPath(trial, index);

‎apps/dashboard/src/components/ResultTable.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,15 +711,15 @@ function RepeatStatusCell({
711711
return (
712712
<ResultStatusSymbol
713713
status={status}
714-
label={`${group.passedTrials}/${group.trialCount} runs passed`}
714+
label={`${group.passedTrials}/${group.trialCount} attempts passed`}
715715
/>
716716
);
717717
}
718718

719719
function RepeatSummaryText({ group }: { group: RepeatRunGroup }) {
720720
const parts = [
721-
`${group.trialCount} runs`,
722-
`${formatPercent(group.passRate)} run success`,
721+
`${group.trialCount} attempts`,
722+
`${formatPercent(group.passRate)} attempt success`,
723723
`${formatPercent(group.meanScore)} mean score`,
724724
group.assertionPassRate != null
725725
? `${formatPercent(group.assertionPassRate)} assertions (${group.passedAssertions}/${group.assertionCount})`

‎packages/core/src/evaluation/orchestrator.ts‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -809,11 +809,11 @@ export async function runEvaluation(
809809
} = options;
810810
const repoRootPath = pathFromRoot(repoRoot);
811811

812-
// Disable cache when trials > 1 (cache makes trials deterministic = pointless)
812+
// Disable cache when repeat count > 1 (cached responses would make attempts deterministic).
813813
let useCache = options.useCache;
814814
if (trials && trials.count > 1 && useCache) {
815815
console.warn(
816-
'Warning: Caching is disabled when trials.count > 1 (cached responses would make trials deterministic).',
816+
'Warning: Caching is disabled when evaluate_options.repeat.count > 1 (cached responses would make attempts deterministic).',
817817
);
818818
useCache = false;
819819
}
@@ -875,9 +875,11 @@ export async function runEvaluation(
875875
typeof primaryProvider.invokeBatch === 'function';
876876
let batchingDisabledByRuntimePolicy = false;
877877

878-
// Disable batch mode when trials > 1 (batch processes all cases at once, incompatible with per-case retries)
878+
// Disable batch mode when repeat count > 1 (batching is incompatible with separate attempts).
879879
if (trials && trials.count > 1 && providerSupportsBatch) {
880-
console.warn('Warning: Batch mode is disabled when trials.count > 1. Using per-case dispatch.');
880+
console.warn(
881+
'Warning: Batch mode is disabled when evaluate_options.repeat.count > 1. Using per-case dispatch for attempts.',
882+
);
881883
providerSupportsBatch = false;
882884
batchingDisabledByRuntimePolicy = true;
883885
}
@@ -2449,7 +2451,7 @@ async function runEvalCaseWithTrials(
24492451
cumulativeCost += trialCost;
24502452
} else if (trialsConfig.costLimitUsd && !costWarningEmitted) {
24512453
console.warn(
2452-
'Warning: cost_limit_usd is set but provider does not report cost. All trials will run.',
2454+
'Warning: cost_limit_usd is set but provider does not report cost. All attempts will run.',
24532455
);
24542456
costWarningEmitted = true;
24552457
}

‎packages/core/src/evaluation/types.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,11 +1250,11 @@ export interface EvaluationResult {
12501250
readonly afterEachOutput?: string;
12511251
/** Unified diff of workspace file changes */
12521252
readonly fileChanges?: string;
1253-
/** Individual trial results (only present when trials.count > 1) */
1253+
/** Individual attempt results (only present when evaluate_options.repeat.count > 1) */
12541254
readonly trials?: readonly TrialResult[];
1255-
/** Aggregation metadata describing how the final score was computed from trials */
1255+
/** Aggregation metadata describing how the final score was computed from attempts */
12561256
readonly aggregation?: TrialAggregation;
1257-
/** Whether the trial loop was terminated early due to cost limit */
1257+
/** Whether the attempt loop was terminated early due to cost limit */
12581258
readonly costLimited?: boolean;
12591259
/** Whether the evaluation was skipped due to suite-level budget exhaustion */
12601260
readonly budgetExceeded?: boolean;

‎packages/core/test/evaluation/orchestrator.test.ts‎

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, describe, expect, it, mock } from 'bun:test';
1+
import { afterEach, describe, expect, it, mock, spyOn } from 'bun:test';
22
import {
33
existsSync,
44
mkdirSync,
@@ -905,6 +905,56 @@ console.log('spreadsheet: revenue,total\\nQ1,42');`,
905905
}
906906
});
907907

908+
it('disables provider batching when repeat attempts are configured', async () => {
909+
class BatchCapableProvider implements Provider {
910+
readonly id = 'batch:repeat';
911+
readonly kind = 'mock' as const;
912+
readonly targetName = 'repeat';
913+
readonly supportsBatch = true;
914+
invokeCalls = 0;
915+
batchCalls = 0;
916+
917+
async invoke(): Promise<ProviderResponse> {
918+
this.invokeCalls += 1;
919+
return {
920+
output: [{ role: 'assistant', content: 'OK' }],
921+
};
922+
}
923+
924+
async invokeBatch(): Promise<readonly ProviderResponse[]> {
925+
this.batchCalls += 1;
926+
throw new Error('batch should not be used for repeat attempts');
927+
}
928+
}
929+
930+
const provider = new BatchCapableProvider();
931+
const warnSpy = spyOn(console, 'warn').mockImplementation(() => {});
932+
try {
933+
const results = await runEvaluation({
934+
testFilePath: 'in-memory.yaml',
935+
repoRoot: 'in-memory',
936+
target: {
937+
...baseTarget,
938+
providerBatching: true,
939+
workers: 1,
940+
},
941+
providerFactory: () => provider,
942+
evaluators: evaluatorRegistry,
943+
evalCases: [baseTestCase],
944+
trials: { count: 2, strategy: 'pass_any' },
945+
});
946+
947+
expect(results).toHaveLength(1);
948+
expect(provider.batchCalls).toBe(0);
949+
expect(provider.invokeCalls).toBe(2);
950+
expect(warnSpy).toHaveBeenCalledWith(
951+
'Warning: Batch mode is disabled when evaluate_options.repeat.count > 1. Using per-case dispatch for attempts.',
952+
);
953+
} finally {
954+
warnSpy.mockRestore();
955+
}
956+
});
957+
908958
it('uses a custom evaluator prompt when provided', async () => {
909959
const directory = mkdtempSync(path.join(tmpdir(), 'agentv-custom-grader-'));
910960
const promptPath = path.join(directory, 'grader-prompt.md');
@@ -1936,7 +1986,7 @@ describe('runEvaluation with trials', () => {
19361986
expect(result.costLimited).toBeUndefined();
19371987
});
19381988

1939-
it('disables cache when trials > 1', async () => {
1989+
it('disables cache when repeat attempts are configured', async () => {
19401990
const provider = new MultiCallProvider();
19411991
const evalRegistry = createScoringEvaluator([0.5, 0.9]);
19421992
const trials: TrialsConfig = { count: 2, strategy: 'pass_any' };
@@ -1948,21 +1998,29 @@ describe('runEvaluation with trials', () => {
19481998
async set() {},
19491999
};
19502000

1951-
const results = await runEvaluation({
1952-
testFilePath: 'in-memory.yaml',
1953-
repoRoot: 'in-memory',
1954-
target: baseTarget,
1955-
providerFactory: () => provider,
1956-
evaluators: evalRegistry,
1957-
evalCases: [baseTestCase],
1958-
trials,
1959-
cache,
1960-
useCache: true, // Should be overridden to false
1961-
});
2001+
const warnSpy = spyOn(console, 'warn').mockImplementation(() => {});
2002+
try {
2003+
const results = await runEvaluation({
2004+
testFilePath: 'in-memory.yaml',
2005+
repoRoot: 'in-memory',
2006+
target: baseTarget,
2007+
providerFactory: () => provider,
2008+
evaluators: evalRegistry,
2009+
evalCases: [baseTestCase],
2010+
trials,
2011+
cache,
2012+
useCache: true, // Should be overridden to false
2013+
});
19622014

1963-
// Provider should have been called for each trial (cache disabled)
1964-
expect(provider.callCount).toBe(2);
1965-
expect(results[0].trials).toHaveLength(2);
2015+
// Provider should have been called for each attempt (cache disabled)
2016+
expect(provider.callCount).toBe(2);
2017+
expect(results[0].trials).toHaveLength(2);
2018+
expect(warnSpy).toHaveBeenCalledWith(
2019+
'Warning: Caching is disabled when evaluate_options.repeat.count > 1 (cached responses would make attempts deterministic).',
2020+
);
2021+
} finally {
2022+
warnSpy.mockRestore();
2023+
}
19662024
});
19672025
});
19682026

0 commit comments

Comments
 (0)