cb15c5e0d8
Release / Check for new version (push) Has been cancelled
Release / Build macOS ARM64 (push) Has been cancelled
Release / Build macOS x64 (push) Has been cancelled
Release / Build Linux ARM64 (push) Has been cancelled
Release / Build Linux musl ARM64 (push) Has been cancelled
Release / Build Linux musl x64 (push) Has been cancelled
Release / Build Linux x64 (push) Has been cancelled
Release / Build Windows x64 (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish sandbox package to npm (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
CI / Rust (windows-latest - x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Native E2E Tests (push) Has been cancelled
CI / Windows Integration Test (push) Has been cancelled
CI / Global Install (macos-latest) (push) Has been cancelled
CI / Global Install (ubuntu-latest) (push) Has been cancelled
CI / Version Sync Check (push) Has been cancelled
CI / Rust (push) Has been cancelled
CI / Dashboard (push) Has been cancelled
CI / Sandbox Package (push) Has been cancelled
CI / Rust (macos-latest - aarch64-apple-darwin) (push) Has been cancelled
CI / Rust (macos-latest - x86_64-apple-darwin) (push) Has been cancelled
CI / Global Install (windows-latest) (push) Has been cancelled
144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
import type { EvalResult, EvalSummary, Category } from "./types.ts";
|
|
|
|
const PASS = "\x1b[32m\u2713\x1b[0m";
|
|
const FAIL = "\x1b[31m\u2717\x1b[0m";
|
|
const ERR = "\x1b[33m!\x1b[0m";
|
|
const DIM = "\x1b[2m";
|
|
const RESET = "\x1b[0m";
|
|
const BOLD = "\x1b[1m";
|
|
|
|
function padRight(str: string, len: number): string {
|
|
return str + " ".repeat(Math.max(0, len - str.length));
|
|
}
|
|
|
|
export function printResult(result: EvalResult): void {
|
|
const icon = result.error ? ERR : result.pass ? PASS : FAIL;
|
|
const status = result.error ? "ERROR" : result.pass ? "PASS" : "FAIL";
|
|
const duration = `${DIM}${result.durationMs}ms${RESET}`;
|
|
|
|
console.log(` ${icon} ${padRight(result.caseName, 50)} ${status} ${duration}`);
|
|
|
|
if (result.error) {
|
|
console.log(` ${DIM}Error: ${result.error}${RESET}`);
|
|
return;
|
|
}
|
|
|
|
const failedExpected = result.patternResults.filter(
|
|
(p) => p.type === "expected" && !p.matched,
|
|
);
|
|
const matchedForbidden = result.patternResults.filter(
|
|
(p) => p.type === "forbidden" && p.matched,
|
|
);
|
|
|
|
for (const p of failedExpected) {
|
|
console.log(` ${FAIL} Expected pattern not found: ${DIM}${p.pattern}${RESET}`);
|
|
}
|
|
for (const p of matchedForbidden) {
|
|
console.log(` ${FAIL} Forbidden pattern matched: ${DIM}${p.pattern}${RESET}`);
|
|
}
|
|
|
|
if (result.judge) {
|
|
console.log(
|
|
` ${DIM}Judge: ${result.judge.score}/5 - ${result.judge.reasoning}${RESET}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
export function printCategoryHeader(category: string): void {
|
|
console.log(`\n${BOLD}${category}${RESET}`);
|
|
console.log(`${"─".repeat(70)}`);
|
|
}
|
|
|
|
export function computeSummary(
|
|
results: EvalResult[],
|
|
totalDurationMs: number,
|
|
): EvalSummary {
|
|
const byCategory: Record<Category, { total: number; passed: number }> = {
|
|
"skill-loading": { total: 0, passed: 0 },
|
|
"skill-selection": { total: 0, passed: 0 },
|
|
"command-usage": { total: 0, passed: 0 },
|
|
"context-footprint": { total: 0, passed: 0 },
|
|
};
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
let errors = 0;
|
|
|
|
for (const r of results) {
|
|
byCategory[r.category].total++;
|
|
if (r.error) {
|
|
errors++;
|
|
} else if (r.pass) {
|
|
passed++;
|
|
byCategory[r.category].passed++;
|
|
} else {
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
return {
|
|
total: results.length,
|
|
passed,
|
|
failed,
|
|
errors,
|
|
byCategory,
|
|
durationMs: totalDurationMs,
|
|
};
|
|
}
|
|
|
|
export function printSummary(summary: EvalSummary): void {
|
|
console.log(`\n${BOLD}Summary${RESET}`);
|
|
console.log(`${"═".repeat(70)}`);
|
|
|
|
for (const [cat, stats] of Object.entries(summary.byCategory)) {
|
|
if (stats.total === 0) continue;
|
|
const pct = Math.round((stats.passed / stats.total) * 100);
|
|
const bar = stats.passed === stats.total ? PASS : FAIL;
|
|
console.log(` ${bar} ${padRight(cat, 20)} ${stats.passed}/${stats.total} (${pct}%)`);
|
|
}
|
|
|
|
console.log(`${"─".repeat(70)}`);
|
|
const totalPct = summary.total > 0
|
|
? Math.round((summary.passed / summary.total) * 100)
|
|
: 0;
|
|
const icon = summary.failed === 0 && summary.errors === 0 ? PASS : FAIL;
|
|
console.log(
|
|
` ${icon} ${BOLD}Total: ${summary.passed}/${summary.total} passed (${totalPct}%)${RESET}`,
|
|
);
|
|
if (summary.errors > 0) {
|
|
console.log(` ${ERR} ${summary.errors} error(s)`);
|
|
}
|
|
console.log(` ${DIM}Duration: ${(summary.durationMs / 1000).toFixed(1)}s${RESET}\n`);
|
|
}
|
|
|
|
export function printResultsJson(
|
|
results: EvalResult[],
|
|
summary: EvalSummary,
|
|
): void {
|
|
const output = {
|
|
summary: {
|
|
total: summary.total,
|
|
passed: summary.passed,
|
|
failed: summary.failed,
|
|
errors: summary.errors,
|
|
passRate: summary.total > 0
|
|
? Math.round((summary.passed / summary.total) * 100)
|
|
: 0,
|
|
durationMs: summary.durationMs,
|
|
byCategory: summary.byCategory,
|
|
},
|
|
results: results.map((r) => ({
|
|
id: r.caseId,
|
|
name: r.caseName,
|
|
category: r.category,
|
|
pass: r.pass,
|
|
durationMs: r.durationMs,
|
|
error: r.error,
|
|
patterns: r.patternResults,
|
|
judge: r.judge,
|
|
response: r.response,
|
|
})),
|
|
};
|
|
console.log(JSON.stringify(output, null, 2));
|
|
}
|