Files
windmill/ai_evals/modes/app.ts
centdix 3f5f211a22 add final context size metric to ai_evals harness (#9660)
Record finalContextTokens per attempt: the input-token total of the last
model request (input + cache-creation + cache-read), i.e. how full the
context window ended up. Complements the cumulative tokenUsage.prompt,
which conflates context size with loop-iteration count.

Captured generically in the shared frontend runEval via the chat loop's
lastIterationUsage, so it covers all frontend modes (global/flow/script/
app), plus CLI mode via the last assistant turn's usage. Aggregated as
average and max over passed attempts and printed in the run summary.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:21:28 +02:00

68 lines
2.4 KiB
TypeScript

import { loadAppFixture } from "../adapters/frontend/core/app/appFixtureLoader";
import { buildAppArtifacts } from "../core/appArtifacts";
import type { AppValidationSpec } from "../core/types";
import type { FrontendEvalModelConfig } from "../core/models";
import { validateAppState, type AppFilesState } from "../core/validators";
import type { BenchmarkArtifactFile, ModeRunner } from "../core/types";
import { runAppEval } from "../adapters/frontend/core/app/appEvalRunner";
import { getFrontendApiKey } from "./frontendCommon";
import type { WindmillBackendSettings } from "../core/windmillBackendSettings";
export function createAppModeRunner(
modelConfig: FrontendEvalModelConfig,
backendSettings: WindmillBackendSettings,
): ModeRunner<AppFilesState, AppFilesState, AppFilesState> {
return {
mode: "app",
concurrency: 5,
judgeThreshold: 80,
async loadInitial(path) {
return path ? await loadAppFixture(path) : undefined;
},
async loadExpected(path) {
return path ? await loadAppFixture(path) : undefined;
},
async run(prompt, initial, context) {
const result = await runAppEval(
prompt,
getFrontendApiKey(modelConfig.provider),
{
initialFrontend: initial?.frontend,
initialBackend: initial?.backend,
initialDatatables: initial?.datatables,
maxIterations: context.evalCase?.runtime?.maxTurns,
appContext: context.evalCase?.runtime?.appContext,
provider: modelConfig.provider,
model: modelConfig.model,
backend: backendSettings,
runContext: context,
},
);
return {
success: result.success,
actual: result.files as AppFilesState,
error: result.error,
assistantMessageCount: result.assistantMessageCount,
toolCallCount: result.toolCallCount,
toolsUsed: result.toolsUsed,
skillsInvoked: [],
tokenUsage: result.tokenUsage,
finalContextTokens: result.finalContextTokens,
};
},
validate({ evalCase, actual, initial, expected, run }) {
return validateAppState({
actual,
initial,
expected,
validate: evalCase.validate as AppValidationSpec | undefined,
toolsUsed: run.toolsUsed,
});
},
buildArtifacts(actual): BenchmarkArtifactFile[] {
return buildAppArtifacts(actual);
},
};
}