@aipack-ai/eval 1.1.6 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/src/cli.d.ts +9 -3
- package/dist/src/cli.js +361 -26
- package/dist/src/cli.js.map +1 -1
- package/dist/src/core/compare.d.ts +28 -0
- package/dist/src/core/compare.js +101 -0
- package/dist/src/core/compare.js.map +1 -0
- package/dist/src/core/embedding.d.ts +33 -0
- package/dist/src/core/embedding.js +68 -0
- package/dist/src/core/embedding.js.map +1 -0
- package/dist/src/core/history.d.ts +39 -0
- package/dist/src/core/history.js +134 -0
- package/dist/src/core/history.js.map +1 -0
- package/dist/src/core/import.d.ts +30 -0
- package/dist/src/core/import.js +111 -0
- package/dist/src/core/import.js.map +1 -0
- package/dist/src/core/judge-llm.d.ts +13 -0
- package/dist/src/core/judge-llm.js +47 -0
- package/dist/src/core/judge-llm.js.map +1 -0
- package/dist/src/core/live.d.ts +111 -0
- package/dist/src/core/live.js +187 -0
- package/dist/src/core/live.js.map +1 -0
- package/dist/src/core/report.d.ts +9 -2
- package/dist/src/core/report.js +30 -5
- package/dist/src/core/report.js.map +1 -1
- package/dist/src/core/runner.d.ts +10 -5
- package/dist/src/core/runner.js +174 -41
- package/dist/src/core/runner.js.map +1 -1
- package/dist/src/core/scorer/index.d.ts +20 -2
- package/dist/src/core/scorer/index.js +36 -9
- package/dist/src/core/scorer/index.js.map +1 -1
- package/dist/src/core/scorer/llm-judge.d.ts +43 -0
- package/dist/src/core/scorer/llm-judge.js +111 -0
- package/dist/src/core/scorer/llm-judge.js.map +1 -0
- package/dist/src/core/scorer/semantic.d.ts +27 -0
- package/dist/src/core/scorer/semantic.js +74 -0
- package/dist/src/core/scorer/semantic.js.map +1 -0
- package/dist/src/core/types.d.ts +65 -4
- package/dist/src/core/validate.js +9 -3
- package/dist/src/core/validate.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/compare.ts - L4 模型 / Provider 对比(M5)
|
|
3
|
+
*
|
|
4
|
+
* 同一组用例在多个被测模型上各跑一遍 runEval(顺序执行,各算各的
|
|
5
|
+
* 预算与 repeats),汇总出通过率 / 平均分 / token 成本 / 延迟 /
|
|
6
|
+
* 分套件通过率矩阵。judge / embed 装配沿用 base config(judge 异源
|
|
7
|
+
* 校验对每个被测模型独立生效)。
|
|
8
|
+
*/
|
|
9
|
+
import { runEval } from './runner.js';
|
|
10
|
+
/** 逐个模型跑同一组用例(顺序执行,避免跨模型并发互扰限流) */
|
|
11
|
+
export async function compareModels(cases, base, models) {
|
|
12
|
+
if (models.length === 0)
|
|
13
|
+
throw new Error('compareModels 需要至少一个模型');
|
|
14
|
+
const startedAt = Date.now();
|
|
15
|
+
const entries = [];
|
|
16
|
+
for (const model of models) {
|
|
17
|
+
const report = await runEval(cases, { ...base, model });
|
|
18
|
+
entries.push({ model, report });
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
22
|
+
durationMs: Date.now() - startedAt,
|
|
23
|
+
mode: base.mode,
|
|
24
|
+
suites: base.suites ?? [],
|
|
25
|
+
entries,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// ─── Markdown 渲染 ────────────────────────────────────────────────
|
|
29
|
+
function pct(x) {
|
|
30
|
+
return `${(x * 100).toFixed(1)}%`;
|
|
31
|
+
}
|
|
32
|
+
/** 单模型平均每 case 耗时(含 repeats) */
|
|
33
|
+
function avgDurationMs(entry) {
|
|
34
|
+
const rs = entry.report.results;
|
|
35
|
+
if (rs.length === 0)
|
|
36
|
+
return 0;
|
|
37
|
+
return rs.reduce((a, r) => a + r.durationMs, 0) / rs.length;
|
|
38
|
+
}
|
|
39
|
+
function allSuites(entries) {
|
|
40
|
+
const set = new Set();
|
|
41
|
+
for (const e of entries)
|
|
42
|
+
for (const s of Object.keys(e.report.bySuite))
|
|
43
|
+
set.add(s);
|
|
44
|
+
return [...set].sort();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 对比表(报告按被测模型逐列对比;passRate 用 runEval 同一口径:
|
|
48
|
+
* 跳过用例不计入分母)。
|
|
49
|
+
*/
|
|
50
|
+
export function renderComparisonMarkdown(cmp) {
|
|
51
|
+
const lines = [];
|
|
52
|
+
lines.push('# L4 模型 / Provider 对比');
|
|
53
|
+
lines.push('');
|
|
54
|
+
lines.push(`- 模式: ${cmp.mode}`);
|
|
55
|
+
lines.push(`- 开始: ${cmp.startedAt}`);
|
|
56
|
+
lines.push(`- 总耗时: ${cmp.durationMs}ms`);
|
|
57
|
+
lines.push('');
|
|
58
|
+
// 总览
|
|
59
|
+
lines.push('## 总览');
|
|
60
|
+
lines.push('');
|
|
61
|
+
lines.push('| 模型 | 用例 | 通过率 | 平均分 | token 总耗 | 平均耗时/case |');
|
|
62
|
+
lines.push('|---|---|---|---|---|---|');
|
|
63
|
+
for (const e of cmp.entries) {
|
|
64
|
+
const t = e.report.totals;
|
|
65
|
+
lines.push(`| ${e.model} | ${t.cases} | ${pct(t.passRate)} | ${t.avgScore.toFixed(3)} | ${t.usageTotal} | ${Math.round(avgDurationMs(e))}ms |`);
|
|
66
|
+
}
|
|
67
|
+
lines.push('');
|
|
68
|
+
// 分套件矩阵
|
|
69
|
+
const suites = allSuites(cmp.entries);
|
|
70
|
+
if (suites.length > 0) {
|
|
71
|
+
lines.push('## 分套件通过率');
|
|
72
|
+
lines.push('');
|
|
73
|
+
lines.push(`| 套件 | ${cmp.entries.map((e) => e.model).join(' | ')} |`);
|
|
74
|
+
lines.push(`|---|${cmp.entries.map(() => '---').join('|')}|`);
|
|
75
|
+
for (const suite of suites) {
|
|
76
|
+
const cells = cmp.entries.map((e) => {
|
|
77
|
+
const s = e.report.bySuite[suite];
|
|
78
|
+
return s ? `${pct(s.passRate)} (${s.passed}/${s.total})` : '—';
|
|
79
|
+
});
|
|
80
|
+
lines.push(`| ${suite} | ${cells.join(' | ')} |`);
|
|
81
|
+
}
|
|
82
|
+
lines.push('');
|
|
83
|
+
}
|
|
84
|
+
// 失败明细(每模型最多列 5 条,防报告爆炸)
|
|
85
|
+
for (const e of cmp.entries) {
|
|
86
|
+
const failed = e.report.results.filter((r) => !r.passed);
|
|
87
|
+
if (failed.length === 0)
|
|
88
|
+
continue;
|
|
89
|
+
lines.push(`## 失败用例 — ${e.model}`);
|
|
90
|
+
lines.push('');
|
|
91
|
+
for (const r of failed.slice(0, 5)) {
|
|
92
|
+
const reason = r.error ?? r.scores.find((s) => !s.passed)?.reason ?? '';
|
|
93
|
+
lines.push(`- ${r.caseId}(score ${r.score.toFixed(3)}): ${reason}`);
|
|
94
|
+
}
|
|
95
|
+
if (failed.length > 5)
|
|
96
|
+
lines.push(`- …共 ${failed.length} 条失败`);
|
|
97
|
+
lines.push('');
|
|
98
|
+
}
|
|
99
|
+
return lines.join('\n');
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/core/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/embedding.ts - OpenAI 兼容 embedding 客户端(M5)
|
|
3
|
+
*
|
|
4
|
+
* semantic 评分器的真实装配:POST {baseUrl}/embeddings(OpenAI 兼容协议,
|
|
5
|
+
* DeepSeek / OpenAI / Moonshot / 各兼容网关通用)。
|
|
6
|
+
*
|
|
7
|
+
* env 优先级低于显式参数(与 live.ts 的 spec 解析对称):
|
|
8
|
+
* AIPACK_EVAL_EMBEDDING_MODEL / AIPACK_EVAL_EMBEDDING_API_KEY /
|
|
9
|
+
* AIPACK_EVAL_EMBEDDING_BASE_URL
|
|
10
|
+
*/
|
|
11
|
+
import type { EmbedFn } from './scorer';
|
|
12
|
+
export interface EmbeddingConfig {
|
|
13
|
+
/** embedding 模型 id(如 text-embedding-3-small) */
|
|
14
|
+
model?: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/** 缺省 https://api.openai.com/v1 */
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/** 单请求超时 ms(缺省 30000) */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const EMBEDDING_ENV_KEYS: {
|
|
22
|
+
readonly model: "AIPACK_EVAL_EMBEDDING_MODEL";
|
|
23
|
+
readonly apiKey: "AIPACK_EVAL_EMBEDDING_API_KEY";
|
|
24
|
+
readonly baseUrl: "AIPACK_EVAL_EMBEDDING_BASE_URL";
|
|
25
|
+
};
|
|
26
|
+
/** 合并显式参数与环境变量,缺 model 抛可操作错误 */
|
|
27
|
+
export declare function resolveEmbeddingConfig(opts?: EmbeddingConfig): EmbeddingConfig & {
|
|
28
|
+
model: string;
|
|
29
|
+
apiKey: string;
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
};
|
|
32
|
+
/** 装配 OpenAI 兼容 embedding 函数 */
|
|
33
|
+
export declare function createOpenAiCompatibleEmbedFn(opts?: EmbeddingConfig): EmbedFn;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/embedding.ts - OpenAI 兼容 embedding 客户端(M5)
|
|
3
|
+
*
|
|
4
|
+
* semantic 评分器的真实装配:POST {baseUrl}/embeddings(OpenAI 兼容协议,
|
|
5
|
+
* DeepSeek / OpenAI / Moonshot / 各兼容网关通用)。
|
|
6
|
+
*
|
|
7
|
+
* env 优先级低于显式参数(与 live.ts 的 spec 解析对称):
|
|
8
|
+
* AIPACK_EVAL_EMBEDDING_MODEL / AIPACK_EVAL_EMBEDDING_API_KEY /
|
|
9
|
+
* AIPACK_EVAL_EMBEDDING_BASE_URL
|
|
10
|
+
*/
|
|
11
|
+
export const EMBEDDING_ENV_KEYS = {
|
|
12
|
+
model: 'AIPACK_EVAL_EMBEDDING_MODEL',
|
|
13
|
+
apiKey: 'AIPACK_EVAL_EMBEDDING_API_KEY',
|
|
14
|
+
baseUrl: 'AIPACK_EVAL_EMBEDDING_BASE_URL',
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_BASE_URL = 'https://api.openai.com/v1';
|
|
17
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
18
|
+
/** 合并显式参数与环境变量,缺 model 抛可操作错误 */
|
|
19
|
+
export function resolveEmbeddingConfig(opts = {}) {
|
|
20
|
+
const env = process.env;
|
|
21
|
+
const model = opts.model ?? env[EMBEDDING_ENV_KEYS.model];
|
|
22
|
+
if (!model) {
|
|
23
|
+
throw new Error(`semantic 评分器需要 embedding 模型:--embed-model <id> 或 ${EMBEDDING_ENV_KEYS.model}`);
|
|
24
|
+
}
|
|
25
|
+
const apiKey = opts.apiKey ?? env[EMBEDDING_ENV_KEYS.apiKey] ?? env.OPENAI_API_KEY;
|
|
26
|
+
if (!apiKey) {
|
|
27
|
+
throw new Error(`embedding 缺少 API Key:--embed-api-key <key>、${EMBEDDING_ENV_KEYS.apiKey} 或 OPENAI_API_KEY`);
|
|
28
|
+
}
|
|
29
|
+
const baseUrl = (opts.baseUrl ?? env[EMBEDDING_ENV_KEYS.baseUrl] ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
30
|
+
return {
|
|
31
|
+
model,
|
|
32
|
+
apiKey,
|
|
33
|
+
baseUrl,
|
|
34
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** 装配 OpenAI 兼容 embedding 函数 */
|
|
38
|
+
export function createOpenAiCompatibleEmbedFn(opts = {}) {
|
|
39
|
+
const cfg = resolveEmbeddingConfig(opts);
|
|
40
|
+
return async (text) => {
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(`${cfg.baseUrl}/embeddings`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
Authorization: `Bearer ${cfg.apiKey}`,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({ model: cfg.model, input: text }),
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const body = await res.text().catch(() => '');
|
|
55
|
+
throw new Error(`embedding HTTP ${res.status}: ${body.slice(0, 200)}`);
|
|
56
|
+
}
|
|
57
|
+
const json = (await res.json());
|
|
58
|
+
const vec = json.data?.[0]?.embedding;
|
|
59
|
+
if (!Array.isArray(vec))
|
|
60
|
+
throw new Error('embedding 响应缺少 data[0].embedding');
|
|
61
|
+
return vec;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../../src/core/embedding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/history.ts - 历史趋势(日环比可视化)
|
|
3
|
+
*
|
|
4
|
+
* 每次运行向 JSONL 追加一行(append-only,天然按时间序),
|
|
5
|
+
* 读回后渲染 Markdown:趋势 sparkline + 逐次通过率 + 环比 Δ。
|
|
6
|
+
*
|
|
7
|
+
* 为什么用 JSONL 而不是单文件 JSON:cron 场景下并发/追加写更安全,
|
|
8
|
+
* 且 diff 友好(一行一次运行)。
|
|
9
|
+
*/
|
|
10
|
+
import type { EvalReport, SuiteSummary } from './types';
|
|
11
|
+
/** 每次运行落一行(JSONL) */
|
|
12
|
+
export interface HistoryEntry {
|
|
13
|
+
runId: string;
|
|
14
|
+
/** ISO 时间 */
|
|
15
|
+
at: string;
|
|
16
|
+
mode: 'mock' | 'live';
|
|
17
|
+
model?: string;
|
|
18
|
+
cases: number;
|
|
19
|
+
passed: number;
|
|
20
|
+
skipped: number;
|
|
21
|
+
passRate: number;
|
|
22
|
+
avgScore: number;
|
|
23
|
+
durationMs: number;
|
|
24
|
+
usageTotal: number;
|
|
25
|
+
bySuite: Record<string, Pick<SuiteSummary, 'total' | 'passed' | 'passRate'>>;
|
|
26
|
+
}
|
|
27
|
+
/** 报告 → 历史条目 */
|
|
28
|
+
export declare function reportToHistoryEntry(report: EvalReport): HistoryEntry;
|
|
29
|
+
/** 追加一次运行记录(JSONL,一行一条) */
|
|
30
|
+
export declare function appendHistory(report: EvalReport, historyPath: string): Promise<string>;
|
|
31
|
+
/** 读取历史(最新在最后);文件不存在返回空数组 */
|
|
32
|
+
export declare function readHistory(historyPath: string, limit?: number): Promise<HistoryEntry[]>;
|
|
33
|
+
/** 0~1 序列 → sparkline 字符串 */
|
|
34
|
+
export declare function sparkline(values: number[], width?: number): string;
|
|
35
|
+
/**
|
|
36
|
+
* 渲染通过率趋势(Markdown)。
|
|
37
|
+
* 环比定义:与上一次运行对比(cron 每日跑 → 即日环比)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function renderHistoryTrend(entries: HistoryEntry[], limit?: number): string;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/history.ts - 历史趋势(日环比可视化)
|
|
3
|
+
*
|
|
4
|
+
* 每次运行向 JSONL 追加一行(append-only,天然按时间序),
|
|
5
|
+
* 读回后渲染 Markdown:趋势 sparkline + 逐次通过率 + 环比 Δ。
|
|
6
|
+
*
|
|
7
|
+
* 为什么用 JSONL 而不是单文件 JSON:cron 场景下并发/追加写更安全,
|
|
8
|
+
* 且 diff 友好(一行一次运行)。
|
|
9
|
+
*/
|
|
10
|
+
import { appendFile, mkdir, readFile } from 'node:fs/promises';
|
|
11
|
+
import { dirname, resolve } from 'node:path';
|
|
12
|
+
/** 报告 → 历史条目 */
|
|
13
|
+
export function reportToHistoryEntry(report) {
|
|
14
|
+
return {
|
|
15
|
+
runId: report.runId,
|
|
16
|
+
at: report.startedAt,
|
|
17
|
+
mode: report.mode,
|
|
18
|
+
model: report.model,
|
|
19
|
+
cases: report.totals.cases,
|
|
20
|
+
passed: report.totals.passed,
|
|
21
|
+
skipped: report.totals.skipped,
|
|
22
|
+
passRate: report.totals.passRate,
|
|
23
|
+
avgScore: report.totals.avgScore,
|
|
24
|
+
durationMs: report.durationMs,
|
|
25
|
+
usageTotal: report.totals.usageTotal,
|
|
26
|
+
bySuite: Object.fromEntries(Object.entries(report.bySuite).map(([k, s]) => [
|
|
27
|
+
k,
|
|
28
|
+
{ total: s.total, passed: s.passed, passRate: s.passRate },
|
|
29
|
+
])),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** 追加一次运行记录(JSONL,一行一条) */
|
|
33
|
+
export async function appendHistory(report, historyPath) {
|
|
34
|
+
const path = resolve(historyPath);
|
|
35
|
+
await mkdir(dirname(path), { recursive: true });
|
|
36
|
+
await appendFile(path, `${JSON.stringify(reportToHistoryEntry(report))}\n`, 'utf-8');
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
/** 读取历史(最新在最后);文件不存在返回空数组 */
|
|
40
|
+
export async function readHistory(historyPath, limit = 30) {
|
|
41
|
+
const path = resolve(historyPath);
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = await readFile(path, 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const entries = [];
|
|
50
|
+
for (const line of raw.split('\n')) {
|
|
51
|
+
const s = line.trim();
|
|
52
|
+
if (!s)
|
|
53
|
+
continue;
|
|
54
|
+
try {
|
|
55
|
+
entries.push(JSON.parse(s));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// 坏行跳过(不因一行损坏丢掉整份历史)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return limit > 0 ? entries.slice(-limit) : entries;
|
|
62
|
+
}
|
|
63
|
+
// ─── 可视化 ───────────────────────────────────────────────────────
|
|
64
|
+
const SPARK_CHARS = '▁▂▃▄▅▆▇█';
|
|
65
|
+
/** 0~1 序列 → sparkline 字符串 */
|
|
66
|
+
export function sparkline(values, width = 20) {
|
|
67
|
+
if (values.length === 0)
|
|
68
|
+
return '';
|
|
69
|
+
const seq = values.slice(-width);
|
|
70
|
+
return seq
|
|
71
|
+
.map((v) => {
|
|
72
|
+
const clamped = Math.min(1, Math.max(0, Number.isFinite(v) ? v : 0));
|
|
73
|
+
const idx = Math.round(clamped * (SPARK_CHARS.length - 1));
|
|
74
|
+
return SPARK_CHARS[idx];
|
|
75
|
+
})
|
|
76
|
+
.join('');
|
|
77
|
+
}
|
|
78
|
+
function pct(x) {
|
|
79
|
+
return `${(x * 100).toFixed(1)}%`;
|
|
80
|
+
}
|
|
81
|
+
function deltaCell(cur, prev) {
|
|
82
|
+
if (prev === undefined)
|
|
83
|
+
return '—';
|
|
84
|
+
const d = (cur - prev) * 100;
|
|
85
|
+
if (Math.abs(d) < 0.05)
|
|
86
|
+
return '±0.0%';
|
|
87
|
+
const sign = d > 0 ? '+' : '';
|
|
88
|
+
const icon = d < 0 ? '🔻' : '🔺';
|
|
89
|
+
return `${icon}${sign}${d.toFixed(1)}%`;
|
|
90
|
+
}
|
|
91
|
+
function shortAt(iso) {
|
|
92
|
+
// 2026-09-29T03:12:44.123Z → 09-29 03:12
|
|
93
|
+
return `${iso.slice(5, 10)} ${iso.slice(11, 16)}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 渲染通过率趋势(Markdown)。
|
|
97
|
+
* 环比定义:与上一次运行对比(cron 每日跑 → 即日环比)。
|
|
98
|
+
*/
|
|
99
|
+
export function renderHistoryTrend(entries, limit = 15) {
|
|
100
|
+
if (entries.length === 0)
|
|
101
|
+
return '(暂无历史记录)';
|
|
102
|
+
const rows = entries.slice(-limit);
|
|
103
|
+
const lines = [];
|
|
104
|
+
const rates = rows.map((e) => e.passRate);
|
|
105
|
+
const latest = rows[rows.length - 1];
|
|
106
|
+
const prev = rows.length > 1 ? rows[rows.length - 2] : undefined;
|
|
107
|
+
lines.push(`\`${sparkline(rates)}\` 最近 ${rows.length} 次:${pct(latest.passRate)}(环比 ${deltaCell(latest.passRate, prev?.passRate)})`);
|
|
108
|
+
lines.push('');
|
|
109
|
+
lines.push(`| 时间(UTC) | 模式 | 模型 | 通过 | 通过率 | 环比 Δ | 平均分 | 耗时 |`);
|
|
110
|
+
lines.push(`|---|---|---|---|---|---|---|---|`);
|
|
111
|
+
rows.forEach((e, i) => {
|
|
112
|
+
lines.push(`| ${shortAt(e.at)} | ${e.mode} | ${e.model ?? '—'} | ${e.passed}/${e.cases} | ${pct(e.passRate)} | ${deltaCell(e.passRate, i > 0 ? rows[i - 1].passRate : undefined)} | ${e.avgScore.toFixed(2)} | ${(e.durationMs / 1000).toFixed(1)}s |`);
|
|
113
|
+
});
|
|
114
|
+
// 分套件环比:定位是哪一组掉了
|
|
115
|
+
const suiteDeltas = [];
|
|
116
|
+
if (prev) {
|
|
117
|
+
for (const [suite, cur] of Object.entries(latest.bySuite)) {
|
|
118
|
+
const before = prev.bySuite[suite];
|
|
119
|
+
if (!before)
|
|
120
|
+
continue;
|
|
121
|
+
const d = (cur.passRate - before.passRate) * 100;
|
|
122
|
+
if (Math.abs(d) >= 0.05) {
|
|
123
|
+
suiteDeltas.push(` - ${suite}: ${pct(before.passRate)} → ${pct(cur.passRate)}(${d > 0 ? '+' : ''}${d.toFixed(1)}%)`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (suiteDeltas.length > 0) {
|
|
128
|
+
lines.push('');
|
|
129
|
+
lines.push(`分套件环比:`);
|
|
130
|
+
lines.push(...suiteDeltas);
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../../src/core/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/import.ts - trace 回流用例入库(M4)
|
|
3
|
+
*
|
|
4
|
+
* 把 observability-server `POST /api/v1/export-eval` 导出的 EvalCase JSON
|
|
5
|
+
* 写入用例目录(入库 = 纳入日常 eval 回归)。入库前走 validateEvalCase
|
|
6
|
+
* (fail fast),支持单用例 / 数组 / { cases: [...] } 三种文件形态,
|
|
7
|
+
* 以及 suite / origin / id 前缀的人工修正(bad case 固化纪律)。
|
|
8
|
+
*/
|
|
9
|
+
import type { CaseOrigin } from './types';
|
|
10
|
+
export interface ImportOptions {
|
|
11
|
+
/** 导出文件路径(单 case / 数组 / { cases: [...] }) */
|
|
12
|
+
file: string;
|
|
13
|
+
/** 用例目录(缺省 defaultCasesDir) */
|
|
14
|
+
outDir?: string;
|
|
15
|
+
/** 覆盖 suite */
|
|
16
|
+
suite?: string;
|
|
17
|
+
/** 覆盖 origin('trace' | 'bugfix') */
|
|
18
|
+
origin?: CaseOrigin;
|
|
19
|
+
/** case id 加前缀(如 'bugfix'),避免与既有 id 冲突 */
|
|
20
|
+
prefix?: string;
|
|
21
|
+
/** 只校验不写盘 */
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ImportResult {
|
|
25
|
+
/** 写入的文件路径(相对 outDir) */
|
|
26
|
+
written: string[];
|
|
27
|
+
/** 校验/写入错误(带 case 上下文) */
|
|
28
|
+
errors: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare function importCases(opts: ImportOptions): Promise<ImportResult>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/import.ts - trace 回流用例入库(M4)
|
|
3
|
+
*
|
|
4
|
+
* 把 observability-server `POST /api/v1/export-eval` 导出的 EvalCase JSON
|
|
5
|
+
* 写入用例目录(入库 = 纳入日常 eval 回归)。入库前走 validateEvalCase
|
|
6
|
+
* (fail fast),支持单用例 / 数组 / { cases: [...] } 三种文件形态,
|
|
7
|
+
* 以及 suite / origin / id 前缀的人工修正(bad case 固化纪律)。
|
|
8
|
+
*/
|
|
9
|
+
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
11
|
+
import { validateEvalCase } from './validate.js';
|
|
12
|
+
import { defaultCasesDir } from './loader.js';
|
|
13
|
+
function normalizeId(id) {
|
|
14
|
+
// 文件名 = id 的最后一段('trace-export/t-1' → 't-1.json'),非法字符收敛
|
|
15
|
+
const last = id.split('/').pop() ?? id;
|
|
16
|
+
return last.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
17
|
+
}
|
|
18
|
+
export async function importCases(opts) {
|
|
19
|
+
const outDir = resolve(opts.outDir ?? defaultCasesDir());
|
|
20
|
+
const raw = await readFile(opts.file, 'utf-8');
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return { written: [], errors: [`JSON 解析失败: ${e.message}`] };
|
|
27
|
+
}
|
|
28
|
+
let list;
|
|
29
|
+
if (Array.isArray(parsed))
|
|
30
|
+
list = parsed;
|
|
31
|
+
else if (parsed && typeof parsed === 'object' && Array.isArray(parsed.cases)) {
|
|
32
|
+
list = parsed.cases;
|
|
33
|
+
}
|
|
34
|
+
else
|
|
35
|
+
list = [parsed];
|
|
36
|
+
if (list.length === 0)
|
|
37
|
+
return { written: [], errors: ['文件中没有用例'] };
|
|
38
|
+
const written = [];
|
|
39
|
+
const errors = [];
|
|
40
|
+
/** 已占用文件名:suite → Set<fileName> */
|
|
41
|
+
const taken = new Map();
|
|
42
|
+
const takeFor = (suiteName) => {
|
|
43
|
+
let set = taken.get(suiteName);
|
|
44
|
+
if (!set) {
|
|
45
|
+
set = new Set();
|
|
46
|
+
taken.set(suiteName, set);
|
|
47
|
+
}
|
|
48
|
+
return set;
|
|
49
|
+
};
|
|
50
|
+
if (!opts.dryRun) {
|
|
51
|
+
// 同目录已存在的文件也占位,避免覆盖人工已修正的用例
|
|
52
|
+
try {
|
|
53
|
+
const suiteDirGuess = opts.suite ?? guessSuite(list);
|
|
54
|
+
if (suiteDirGuess) {
|
|
55
|
+
const existing = await readdir(resolve(outDir, suiteDirGuess)).catch(() => []);
|
|
56
|
+
for (const f of existing)
|
|
57
|
+
takeFor(suiteDirGuess).add(f);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// outDir 不存在时忽略
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const [i, item] of list.entries()) {
|
|
65
|
+
const c = item;
|
|
66
|
+
const label = `#${i} ${c.id ?? '<missing id>'}`;
|
|
67
|
+
if (!c || typeof c !== 'object') {
|
|
68
|
+
errors.push(`${label}: 不是对象`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (opts.suite)
|
|
72
|
+
c.suite = opts.suite;
|
|
73
|
+
if (opts.origin)
|
|
74
|
+
c.origin = opts.origin;
|
|
75
|
+
if (opts.prefix)
|
|
76
|
+
c.id = `${opts.prefix}/${String(c.id ?? `case-${i}`)}`;
|
|
77
|
+
const validation = validateEvalCase(c);
|
|
78
|
+
if (validation.length > 0) {
|
|
79
|
+
errors.push(`${label}: ${validation.join('; ')}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const suite = String(c.suite);
|
|
83
|
+
const takenSet = takeFor(suite);
|
|
84
|
+
let fileName = `${normalizeId(String(c.id))}.json`;
|
|
85
|
+
let n = 1;
|
|
86
|
+
while (takenSet.has(fileName)) {
|
|
87
|
+
fileName = `${normalizeId(String(c.id))}-${n}.json`;
|
|
88
|
+
n += 1;
|
|
89
|
+
}
|
|
90
|
+
takenSet.add(fileName);
|
|
91
|
+
if (opts.dryRun) {
|
|
92
|
+
written.push(`${suite}/${fileName}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const target = resolve(outDir, suite, fileName);
|
|
96
|
+
try {
|
|
97
|
+
await mkdir(resolve(outDir, suite), { recursive: true });
|
|
98
|
+
await writeFile(target, `${JSON.stringify(c, null, 2)}\n`, 'utf-8');
|
|
99
|
+
written.push(`${suite}/${fileName}`);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
errors.push(`${label}: 写入失败 - ${e.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return { written, errors };
|
|
106
|
+
}
|
|
107
|
+
function guessSuite(list) {
|
|
108
|
+
const first = list[0];
|
|
109
|
+
return typeof first?.suite === 'string' && first.suite ? first.suite : undefined;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/core/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/judge-llm.ts - judge 模型装配(M5)
|
|
3
|
+
*
|
|
4
|
+
* 把 StreamFn + 框架 Model 装配成 llm-judge 需要的 complete(prompt)。
|
|
5
|
+
* 与 runner 的 live 装配对称:judge 模型也走 createLiveLlm / 注入 StreamFn,
|
|
6
|
+
* 单次非流式调用(内部仍消费事件流,聚合 text delta)。
|
|
7
|
+
*/
|
|
8
|
+
import type { Model, StreamFn } from '@aipack-ai/agent';
|
|
9
|
+
import type { JudgeDeps } from './scorer';
|
|
10
|
+
/** 单条消息 → complete(prompt) 的适配(temperature 0,judge 要确定性) */
|
|
11
|
+
export declare function createCompleteFromStreamFn(streamFn: StreamFn, model: Model | undefined): (prompt: string) => Promise<string>;
|
|
12
|
+
/** JudgeDeps 快捷装配 */
|
|
13
|
+
export declare function createJudgeDeps(streamFn: StreamFn, model: Model | undefined, modelLabel?: string): JudgeDeps;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/judge-llm.ts - judge 模型装配(M5)
|
|
3
|
+
*
|
|
4
|
+
* 把 StreamFn + 框架 Model 装配成 llm-judge 需要的 complete(prompt)。
|
|
5
|
+
* 与 runner 的 live 装配对称:judge 模型也走 createLiveLlm / 注入 StreamFn,
|
|
6
|
+
* 单次非流式调用(内部仍消费事件流,聚合 text delta)。
|
|
7
|
+
*/
|
|
8
|
+
/** 单条消息 → complete(prompt) 的适配(temperature 0,judge 要确定性) */
|
|
9
|
+
export function createCompleteFromStreamFn(streamFn, model) {
|
|
10
|
+
return async (prompt) => {
|
|
11
|
+
const context = {
|
|
12
|
+
systemPrompt: '',
|
|
13
|
+
messages: [{ role: 'user', content: prompt, timestamp: Date.now() }],
|
|
14
|
+
};
|
|
15
|
+
let text = '';
|
|
16
|
+
const result = streamFn(model, context);
|
|
17
|
+
for await (const event of result) {
|
|
18
|
+
if (event.type === 'text_delta')
|
|
19
|
+
text += event.delta;
|
|
20
|
+
else if (event.type === 'done') {
|
|
21
|
+
// 兜底:done 消息里的 text(个别 provider 不发 text_delta)
|
|
22
|
+
if (!text) {
|
|
23
|
+
const content = event.message.content;
|
|
24
|
+
if (typeof content === 'string')
|
|
25
|
+
text = content;
|
|
26
|
+
else
|
|
27
|
+
for (const block of content)
|
|
28
|
+
if (block.type === 'text')
|
|
29
|
+
text += block.text;
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
else if (event.type === 'error') {
|
|
34
|
+
throw new Error('judge 流式调用错误');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return text;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** JudgeDeps 快捷装配 */
|
|
41
|
+
export function createJudgeDeps(streamFn, model, modelLabel) {
|
|
42
|
+
return {
|
|
43
|
+
complete: createCompleteFromStreamFn(streamFn, model),
|
|
44
|
+
...(modelLabel ? { modelLabel } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=judge-llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-llm.js","sourceRoot":"","sources":["../../../src/core/judge-llm.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,4DAA4D;AAC5D,MAAM,UAAU,0BAA0B,CACxC,QAAkB,EAClB,KAAwB;IAExB,OAAO,KAAK,EAAE,MAAc,EAAmB,EAAE;QAC/C,MAAM,OAAO,GAAG;YACd,YAAY,EAAE,EAAE;YAChB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;SAC9E,CAAC;QACF,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAc,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;gBAAE,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC;iBAChD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC/B,+CAA+C;gBAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;oBACtC,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAAE,IAAI,GAAG,OAAO,CAAC;;wBAC3C,KAAK,MAAM,KAAK,IAAI,OAAO;4BAAE,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gCAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;gBAClF,CAAC;gBACD,MAAM;YACR,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,qBAAqB;AACrB,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,KAAwB,EACxB,UAAmB;IAEnB,OAAO;QACL,QAAQ,EAAE,0BAA0B,CAAC,QAAQ,EAAE,KAAK,CAAC;QACrD,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC"}
|