Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,12 @@ export {
export type { LlmConfig, ProviderName } from "./config/types.js";

export { getAdapter } from "./telemetry/adapter.js";

export { renderReport } from "./report/render.js";
export type {
ReportViewModel,
EvaluatorViewModel,
ResultViewModel,
TurnViewModel,
DetailCard,
} from "./report/types.js";
3 changes: 3 additions & 0 deletions core/src/execute/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export interface ReportMeta {
generatedAt: string;
targetName: string;
targetKind: "agent" | "mcp";
/** See {@link UnifiedRunReport.suiteId}. */
suiteId: string;
effort: Effort;
attackModel: string;
judgeModel: string;
Expand Down Expand Up @@ -130,6 +132,7 @@ export function buildUnifiedReport(
generatedAt: meta.generatedAt,
targetName: meta.targetName,
targetKind: meta.targetKind,
suiteId: meta.suiteId,
effort: meta.effort,
attackModel: meta.attackModel,
judgeModel: meta.judgeModel,
Expand Down
16 changes: 15 additions & 1 deletion core/src/execute/runAll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export async function runAll(
config: RunConfig,
options?: RunAllOptions
): Promise<UnifiedRunReport> {
const runStartedAt = Date.now();

// Fan every progress event to the legacy onProgress callback (backward-compat)
// and to any registered lifecycle listeners.
const listeners = options?.listeners ?? [];
Expand Down Expand Up @@ -137,12 +139,13 @@ export async function runAll(
evaluatorResults.push(...loop.evaluatorResults);
const stopReason = loop.stopReason;

// Build report (partial or complete) with stop reason and token usage.
// Build report (partial or complete) with stop reason, token usage, and duration.
const report = buildReport(config, evaluatorResults);
const usage = tokenTracker.totals;
if (usage.totalTokens > 0) {
report.summary.tokenUsage = usage;
}
report.summary.durationMs = Date.now() - runStartedAt;
if (stopReason) {
(report as UnifiedRunReport & { stopReason?: string }).stopReason = stopReason;
}
Expand Down Expand Up @@ -307,6 +310,16 @@ async function curateTracesIfConfigured(
}
}

/**
* Label for the "Evaluation Suite" report field. Only a `mode: "suite"` selection
* runs a whole named suite verbatim — explicit evaluator lists and preloaded specs
* are, by definition, a hand-picked subset, so they're labeled "Custom Suite"
* rather than borrowing a suite name that would overstate what actually ran.
*/
function suiteLabel(selection: RunConfig["selection"]): string {
return selection.mode === "suite" ? selection.suite : "Custom Suite";
}

/** Assemble a {@link UnifiedRunReport} from Node-side run config and evaluator results. */
function buildReport(config: RunConfig, evaluators: EvaluatorResult[]): UnifiedRunReport {
const { attackModel, judgeModel } = modelLabel(config.attackerLlm, config.judgeLlm);
Expand All @@ -316,6 +329,7 @@ function buildReport(config: RunConfig, evaluators: EvaluatorResult[]): UnifiedR
generatedAt: new Date().toISOString(),
targetName: config.target.name,
targetKind: config.target.kind,
suiteId: suiteLabel(config.selection),
effort: config.effort,
attackModel,
judgeModel,
Expand Down
6 changes: 6 additions & 0 deletions core/src/execute/runAllBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export async function runAllBrowser(
agentTarget: AgentTarget,
options?: BrowserRunAllOptions
): Promise<UnifiedRunReport> {
const runStartedAt = Date.now();
const notify = options?.onProgress ?? (() => {});
const attackModel = createModel(config.attackerLlm);
const judgeModel = createModel(config.judgeLlm ?? config.attackerLlm);
Expand Down Expand Up @@ -237,6 +238,7 @@ export async function runAllBrowser(
if (usage.totalTokens > 0) {
report.summary.tokenUsage = usage;
}
report.summary.durationMs = Date.now() - runStartedAt;
return report;
}

Expand All @@ -254,6 +256,10 @@ function buildBrowserReport(
generatedAt: new Date().toISOString(),
targetName: config.targetName ?? "target",
targetKind: "agent",
// The browser path takes preloaded evaluator specs with no suite concept —
// the extension builds its own report/suite label independently of this
// return value (see popup.js's buildFinalReport).
suiteId: "Custom Suite",
effort: config.effort,
attackModel,
judgeModel,
Expand Down
8 changes: 8 additions & 0 deletions core/src/execute/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ export interface UnifiedRunReport {
generatedAt: string;
targetName: string;
targetKind: "agent" | "mcp";
/**
* The evaluation suite that was run, or "Custom Suite" when the selection
* doesn't correspond to one whole named suite (an explicit evaluator list,
* preloaded specs, or a hand-picked subset of a suite's evaluators).
*/
suiteId: string;
effort: Effort;
attackModel: string;
judgeModel: string;
Expand All @@ -267,6 +273,8 @@ export interface UnifiedRunReport {
safetyScore: number;
attackSuccessRate: number;
tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number };
/** Wall-clock time for the whole run, from the first evaluator to the last. */
durationMs?: number;
};
evaluators: EvaluatorResult[];
/** Set when the run was stopped early due to a non-retryable LLM error. */
Expand Down
2 changes: 1 addition & 1 deletion core/src/report/buildReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function toReportViewModel(report: UnifiedRunReport): ReportViewModel {
generatedAt: report.generatedAt,
generatorModel: report.attackModel,
judgeModel: report.judgeModel,
target: { name: report.targetName },
target: { name: report.targetName, suiteId: report.suiteId },
summary: report.summary,
evaluators: report.evaluators.map(toEvaluatorViewModel),
stopReason: report.stopReason,
Expand Down
Loading
Loading