@aipack-ai/eval 1.1.6
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 +24 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/src/cli.d.ts +12 -0
- package/dist/src/cli.js +149 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/core/loader.d.ts +21 -0
- package/dist/src/core/loader.js +91 -0
- package/dist/src/core/loader.js.map +1 -0
- package/dist/src/core/mock-stream.d.ts +16 -0
- package/dist/src/core/mock-stream.js +71 -0
- package/dist/src/core/mock-stream.js.map +1 -0
- package/dist/src/core/mock-tools.d.ts +24 -0
- package/dist/src/core/mock-tools.js +161 -0
- package/dist/src/core/mock-tools.js.map +1 -0
- package/dist/src/core/report.d.ts +26 -0
- package/dist/src/core/report.js +149 -0
- package/dist/src/core/report.js.map +1 -0
- package/dist/src/core/runner.d.ts +18 -0
- package/dist/src/core/runner.js +213 -0
- package/dist/src/core/runner.js.map +1 -0
- package/dist/src/core/scorer/index.d.ts +19 -0
- package/dist/src/core/scorer/index.js +56 -0
- package/dist/src/core/scorer/index.js.map +1 -0
- package/dist/src/core/scorer/rule.d.ts +17 -0
- package/dist/src/core/scorer/rule.js +231 -0
- package/dist/src/core/scorer/rule.js.map +1 -0
- package/dist/src/core/trajectory.d.ts +13 -0
- package/dist/src/core/trajectory.js +41 -0
- package/dist/src/core/trajectory.js.map +1 -0
- package/dist/src/core/types.d.ts +251 -0
- package/dist/src/core/types.js +16 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/core/validate.d.ts +7 -0
- package/dist/src/core/validate.js +71 -0
- package/dist/src/core/validate.js.map +1 -0
- package/package.json +53 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aipack-ai/eval —— aipack Eval 体系(M1:mock 模式 + 规则评分器)
|
|
3
|
+
*
|
|
4
|
+
* 快速上手:
|
|
5
|
+
* import { loadCases, runEval, renderMarkdown } from '@aipack-ai/eval';
|
|
6
|
+
* const { cases } = await loadCases(); // 默认 eval/cases
|
|
7
|
+
* const report = await runEval(cases, { mode: 'mock' });
|
|
8
|
+
* console.log(renderMarkdown(report));
|
|
9
|
+
*
|
|
10
|
+
* CLI:
|
|
11
|
+
* pnpm --filter @aipack-ai/eval eval -- --suite tool-calling
|
|
12
|
+
*/
|
|
13
|
+
export type { CaseOrigin, MockToolCall, MockAssistantTurn, MockScript, AgentInput, ExpectedToolCall, ExpectedResult, RuleScorerType, ScorerType, ScorerConfig, EvalCase, ToolCallRecord, RunTrace, ScoreResult, CaseResult, SuiteSummary, EvalReport, RunConfig, BaselineFile, BaselineComparison, } from './src/core/types';
|
|
14
|
+
export { createMockStreamFn } from './src/core/mock-stream';
|
|
15
|
+
export { createMockTools, STANDARD_MOCK_TOOLS, } from './src/core/mock-tools';
|
|
16
|
+
export type { StandardMockToolName } from './src/core/mock-tools';
|
|
17
|
+
export { extractTrajectory, countTurns } from './src/core/trajectory';
|
|
18
|
+
export { expectedToScorers, resolveScorers, scoreTrace, RULE_SCORER_TYPES, runRuleScorer, partialMatch, } from './src/core/scorer';
|
|
19
|
+
export type { CaseScore } from './src/core/scorer';
|
|
20
|
+
export { validateEvalCase } from './src/core/validate';
|
|
21
|
+
export { loadCases, defaultCasesDir, packageRoot, } from './src/core/loader';
|
|
22
|
+
export type { LoadCasesResult } from './src/core/loader';
|
|
23
|
+
export { runEval } from './src/core/runner';
|
|
24
|
+
export { renderMarkdown, writeReport, writeBaseline, readBaseline, reportToBaseline, compareBaseline, finalizeReport, } from './src/core/report';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aipack-ai/eval —— aipack Eval 体系(M1:mock 模式 + 规则评分器)
|
|
3
|
+
*
|
|
4
|
+
* 快速上手:
|
|
5
|
+
* import { loadCases, runEval, renderMarkdown } from '@aipack-ai/eval';
|
|
6
|
+
* const { cases } = await loadCases(); // 默认 eval/cases
|
|
7
|
+
* const report = await runEval(cases, { mode: 'mock' });
|
|
8
|
+
* console.log(renderMarkdown(report));
|
|
9
|
+
*
|
|
10
|
+
* CLI:
|
|
11
|
+
* pnpm --filter @aipack-ai/eval eval -- --suite tool-calling
|
|
12
|
+
*/
|
|
13
|
+
// ─── Mock 层(fixture replay)─────────────────────────────────────
|
|
14
|
+
export { createMockStreamFn } from './src/core/mock-stream.js';
|
|
15
|
+
export { createMockTools, STANDARD_MOCK_TOOLS, } from './src/core/mock-tools.js';
|
|
16
|
+
// ─── 轨迹提取 ─────────────────────────────────────────────────────
|
|
17
|
+
export { extractTrajectory, countTurns } from './src/core/trajectory.js';
|
|
18
|
+
// ─── 评分器 ───────────────────────────────────────────────────────
|
|
19
|
+
export { expectedToScorers, resolveScorers, scoreTrace, RULE_SCORER_TYPES, runRuleScorer, partialMatch, } from './src/core/scorer/index.js';
|
|
20
|
+
// ─── 校验 / 加载 ──────────────────────────────────────────────────
|
|
21
|
+
export { validateEvalCase } from './src/core/validate.js';
|
|
22
|
+
export { loadCases, defaultCasesDir, packageRoot, } from './src/core/loader.js';
|
|
23
|
+
// ─── Runner 与报告 ────────────────────────────────────────────────
|
|
24
|
+
export { runEval } from './src/core/runner.js';
|
|
25
|
+
export { renderMarkdown, writeReport, writeBaseline, readBaseline, reportToBaseline, compareBaseline, finalizeReport, } from './src/core/report.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA0BH,kEAAkE;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAG/B,iEAAiE;AACjE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEtE,kEAAkE;AAClE,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,iEAAiE;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAG3B,kEAAkE;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* packages/eval/src/cli.ts - aipack-eval 命令行入口
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* aipack-eval run [--suite <name>]... [--cases-dir <dir>] [--repeats <n>]
|
|
7
|
+
* [--report-dir <dir>] [--baseline <path>]
|
|
8
|
+
* [--update-baseline] [--threshold <0.02>]
|
|
9
|
+
*
|
|
10
|
+
* 退出码:0 = 全过且无回归;1 = 有失败或回归;2 = 用法错误。
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* packages/eval/src/cli.ts - aipack-eval 命令行入口
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* aipack-eval run [--suite <name>]... [--cases-dir <dir>] [--repeats <n>]
|
|
7
|
+
* [--report-dir <dir>] [--baseline <path>]
|
|
8
|
+
* [--update-baseline] [--threshold <0.02>]
|
|
9
|
+
*
|
|
10
|
+
* 退出码:0 = 全过且无回归;1 = 有失败或回归;2 = 用法错误。
|
|
11
|
+
*/
|
|
12
|
+
import { loadCases } from './core/loader.js';
|
|
13
|
+
import { runEval } from './core/runner.js';
|
|
14
|
+
import { finalizeReport, renderMarkdown } from './core/report.js';
|
|
15
|
+
const USAGE = `用法: aipack-eval run [options]
|
|
16
|
+
|
|
17
|
+
选项:
|
|
18
|
+
--suite <name> 只跑指定套件(可多次)
|
|
19
|
+
--cases-dir <dir> 用例目录(缺省 <包根>/eval/cases)
|
|
20
|
+
--report-dir <dir> 报告输出目录(缺省 ./eval-results;'none' 不写盘)
|
|
21
|
+
--baseline <path> baseline 文件(门禁对比)
|
|
22
|
+
--update-baseline 用本次结果更新 baseline
|
|
23
|
+
--threshold <x> 通过率回归阈值(缺省 0.02)
|
|
24
|
+
--repeats <n> 每用例重复次数(缺省 1)
|
|
25
|
+
-h, --help 显示帮助`;
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const args = {
|
|
28
|
+
suites: [],
|
|
29
|
+
updateBaseline: false,
|
|
30
|
+
threshold: 0.02,
|
|
31
|
+
};
|
|
32
|
+
let i = 0;
|
|
33
|
+
while (i < argv.length) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
const next = () => {
|
|
36
|
+
const v = argv[++i];
|
|
37
|
+
if (v === undefined)
|
|
38
|
+
throw new Error(`选项 ${a} 缺少参数值`);
|
|
39
|
+
return v;
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
switch (a) {
|
|
43
|
+
case 'run':
|
|
44
|
+
args.command = 'run';
|
|
45
|
+
break;
|
|
46
|
+
case '--suite':
|
|
47
|
+
args.suites.push(next());
|
|
48
|
+
break;
|
|
49
|
+
case '--cases-dir':
|
|
50
|
+
args.casesDir = next();
|
|
51
|
+
break;
|
|
52
|
+
case '--report-dir':
|
|
53
|
+
args.reportDir = next();
|
|
54
|
+
break;
|
|
55
|
+
case '--baseline':
|
|
56
|
+
args.baselinePath = next();
|
|
57
|
+
break;
|
|
58
|
+
case '--update-baseline':
|
|
59
|
+
args.updateBaseline = true;
|
|
60
|
+
break;
|
|
61
|
+
case '--threshold': {
|
|
62
|
+
const v = Number(next());
|
|
63
|
+
if (!Number.isFinite(v) || v < 0 || v > 1)
|
|
64
|
+
throw new Error('阈值须为 0~1');
|
|
65
|
+
args.threshold = v;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case '--repeats': {
|
|
69
|
+
const v = Number(next());
|
|
70
|
+
if (!Number.isInteger(v) || v < 1)
|
|
71
|
+
throw new Error('repeats 须为正整数');
|
|
72
|
+
args.repeats = v;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case '-h':
|
|
76
|
+
case '--help':
|
|
77
|
+
return { error: 'HELP' };
|
|
78
|
+
default:
|
|
79
|
+
return { error: `未知参数: ${a}\n${USAGE}` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
return { error: `${e.message}\n${USAGE}` };
|
|
84
|
+
}
|
|
85
|
+
i += 1;
|
|
86
|
+
}
|
|
87
|
+
if (args.command !== 'run')
|
|
88
|
+
return { error: `缺少子命令 'run'\n${USAGE}` };
|
|
89
|
+
return args;
|
|
90
|
+
}
|
|
91
|
+
async function main() {
|
|
92
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
93
|
+
if ('error' in parsed) {
|
|
94
|
+
if (parsed.error === 'HELP') {
|
|
95
|
+
console.log(USAGE);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
console.error(parsed.error);
|
|
99
|
+
return 2;
|
|
100
|
+
}
|
|
101
|
+
const { cases, errors } = await loadCases(parsed.casesDir);
|
|
102
|
+
if (errors.length > 0) {
|
|
103
|
+
console.error('用例加载错误:');
|
|
104
|
+
for (const e of errors)
|
|
105
|
+
console.error(` - ${e}`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
if (cases.length === 0) {
|
|
109
|
+
console.error('没有可运行的用例');
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
const config = {
|
|
113
|
+
mode: 'mock',
|
|
114
|
+
suites: parsed.suites,
|
|
115
|
+
repeats: parsed.repeats,
|
|
116
|
+
casesDir: parsed.casesDir,
|
|
117
|
+
reportDir: parsed.reportDir === 'none' ? undefined : parsed.reportDir,
|
|
118
|
+
baselinePath: parsed.baselinePath,
|
|
119
|
+
updateBaseline: parsed.updateBaseline,
|
|
120
|
+
regressionThreshold: parsed.threshold,
|
|
121
|
+
};
|
|
122
|
+
console.error(`加载 ${cases.length} 个用例${parsed.suites.length ? `(套件: ${parsed.suites.join(', ')})` : ''},mode=mock`);
|
|
123
|
+
const report = await runEval(cases, config);
|
|
124
|
+
const finalized = await finalizeReport(report, config);
|
|
125
|
+
console.log(renderMarkdown(report));
|
|
126
|
+
if (finalized.jsonPath)
|
|
127
|
+
console.error(`\n报告已写入: ${finalized.jsonPath}`);
|
|
128
|
+
if (config.updateBaseline && finalized.baseline === undefined) {
|
|
129
|
+
console.error(`baseline 已更新: ${config.baselinePath}`);
|
|
130
|
+
}
|
|
131
|
+
let exitCode = report.totals.passed === report.totals.cases ? 0 : 1;
|
|
132
|
+
if (finalized.comparison && !finalized.comparison.ok) {
|
|
133
|
+
exitCode = 1;
|
|
134
|
+
const { overallDelta, regressions } = finalized.comparison;
|
|
135
|
+
console.error(`\n⚠️ baseline 回归(整体 ${overallDelta >= 0 ? '+' : ''}${(overallDelta * 100).toFixed(1)}%):`);
|
|
136
|
+
for (const r of regressions) {
|
|
137
|
+
console.error(` - ${r.suite}: ${(r.baseline * 100).toFixed(1)}% → ${(r.current * 100).toFixed(1)}%(${(r.delta * 100).toFixed(1)}%)`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (finalized.comparison) {
|
|
141
|
+
console.error(`\nbaseline 门禁通过(${(finalized.comparison.overallDelta * 100).toFixed(1)}%)`);
|
|
142
|
+
}
|
|
143
|
+
return exitCode;
|
|
144
|
+
}
|
|
145
|
+
main().then((code) => process.exit(code), (e) => {
|
|
146
|
+
console.error(e);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/loader.ts - 用例加载器
|
|
3
|
+
*
|
|
4
|
+
* 递归扫描用例目录(缺省 <包根>/eval/cases),解析 JSON 为 EvalCase,
|
|
5
|
+
* 加载即校验(fail fast),重复 id 报错。
|
|
6
|
+
*/
|
|
7
|
+
import type { EvalCase } from './types';
|
|
8
|
+
/** 包根目录:从当前文件向上查找最近的 package.json(源码与 dist 产物通用) */
|
|
9
|
+
export declare function packageRoot(): string;
|
|
10
|
+
export declare function defaultCasesDir(): string;
|
|
11
|
+
export interface LoadCasesResult {
|
|
12
|
+
cases: EvalCase[];
|
|
13
|
+
/** 相对用例目录的文件路径(诊断用) */
|
|
14
|
+
sources: Map<string, string>;
|
|
15
|
+
errors: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 加载用例目录。校验失败的文件进入 errors(不中断整体加载),
|
|
19
|
+
* 但重复 id 一律报错(防套件意外覆盖)。
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadCases(casesDir?: string): Promise<LoadCasesResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/loader.ts - 用例加载器
|
|
3
|
+
*
|
|
4
|
+
* 递归扫描用例目录(缺省 <包根>/eval/cases),解析 JSON 为 EvalCase,
|
|
5
|
+
* 加载即校验(fail fast),重复 id 报错。
|
|
6
|
+
*/
|
|
7
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, join, resolve } from 'node:path';
|
|
11
|
+
import { validateEvalCase } from './validate.js';
|
|
12
|
+
/** 包根目录:从当前文件向上查找最近的 package.json(源码与 dist 产物通用) */
|
|
13
|
+
export function packageRoot() {
|
|
14
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
for (let i = 0; i < 10; i += 1) {
|
|
16
|
+
if (existsSync(join(dir, 'package.json')))
|
|
17
|
+
return dir;
|
|
18
|
+
const parent = dirname(dir);
|
|
19
|
+
if (parent === dir)
|
|
20
|
+
break;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
// 兜底:回到源码相对位置(src/core → 上两级)
|
|
24
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
25
|
+
}
|
|
26
|
+
export function defaultCasesDir() {
|
|
27
|
+
return join(packageRoot(), 'eval', 'cases');
|
|
28
|
+
}
|
|
29
|
+
async function* walk(dir) {
|
|
30
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
31
|
+
for (const e of entries) {
|
|
32
|
+
const full = join(dir, e.name);
|
|
33
|
+
if (e.isDirectory())
|
|
34
|
+
yield* walk(full);
|
|
35
|
+
else if (e.isFile() && (e.name.endsWith('.json') || e.name.endsWith('.jsonc'))) {
|
|
36
|
+
yield full;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 加载用例目录。校验失败的文件进入 errors(不中断整体加载),
|
|
42
|
+
* 但重复 id 一律报错(防套件意外覆盖)。
|
|
43
|
+
*/
|
|
44
|
+
export async function loadCases(casesDir) {
|
|
45
|
+
const dir = resolve(casesDir ?? defaultCasesDir());
|
|
46
|
+
const cases = [];
|
|
47
|
+
const sources = new Map();
|
|
48
|
+
const errors = [];
|
|
49
|
+
const seenIds = new Map();
|
|
50
|
+
let dirExists = true;
|
|
51
|
+
try {
|
|
52
|
+
await stat(dir);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
dirExists = false;
|
|
56
|
+
}
|
|
57
|
+
if (!dirExists) {
|
|
58
|
+
return { cases, sources, errors: [`用例目录不存在: ${dir}`] };
|
|
59
|
+
}
|
|
60
|
+
for await (const file of walk(dir)) {
|
|
61
|
+
const rel = file.slice(dir.length + 1);
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(await readFile(file, 'utf-8'));
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
errors.push(`${rel}: JSON 解析失败 - ${e.message}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// 支持单文件单用例或数组
|
|
71
|
+
const list = Array.isArray(parsed) ? parsed : [parsed];
|
|
72
|
+
for (const item of list) {
|
|
73
|
+
const c = item;
|
|
74
|
+
const caseErrors = validateEvalCase(c);
|
|
75
|
+
if (caseErrors.length > 0) {
|
|
76
|
+
errors.push(`${rel}: ${caseErrors.join('; ')}`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const dup = seenIds.get(c.id);
|
|
80
|
+
if (dup) {
|
|
81
|
+
errors.push(`${rel}: id '${c.id}' 与 ${dup} 重复`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
seenIds.set(c.id, rel);
|
|
85
|
+
cases.push(c);
|
|
86
|
+
sources.set(c.id, rel);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { cases, sources, errors };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/core/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/mock-stream.ts - Mock StreamFn(fixture replay)
|
|
3
|
+
*
|
|
4
|
+
* 通过 RuntimeOptions.streamFn 注入脚本化的 LLM 响应,零改动 Runtime。
|
|
5
|
+
* 每次 streamFn 调用消费 MockScript.turns 中的一条;轮次耗尽后按
|
|
6
|
+
* fallbackText / infiniteTool 兜底。
|
|
7
|
+
*/
|
|
8
|
+
import type { StreamFn } from '@aipack-ai/agent';
|
|
9
|
+
import type { MockScript } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* 创建脚本式 StreamFn:第 n 次调用返回第 n 轮脚本。
|
|
12
|
+
*
|
|
13
|
+
* - 轮次耗尽且未配置 infiniteTool:返回 stopReason 'stop' 的兜底文本轮
|
|
14
|
+
* - 配置 infiniteTool:持续返回该工具调用(模拟死循环,配合 maxTurns 断言)
|
|
15
|
+
*/
|
|
16
|
+
export declare function createMockStreamFn(script: MockScript): StreamFn;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/mock-stream.ts - Mock StreamFn(fixture replay)
|
|
3
|
+
*
|
|
4
|
+
* 通过 RuntimeOptions.streamFn 注入脚本化的 LLM 响应,零改动 Runtime。
|
|
5
|
+
* 每次 streamFn 调用消费 MockScript.turns 中的一条;轮次耗尽后按
|
|
6
|
+
* fallbackText / infiniteTool 兜底。
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_USAGE = { input: 10, output: 5, total: 15 };
|
|
9
|
+
/** 把脚本轮次转成框架层 AssistantMessage */
|
|
10
|
+
function buildTurnMessage(turn, idBase) {
|
|
11
|
+
const content = [];
|
|
12
|
+
if (turn.text)
|
|
13
|
+
content.push({ type: 'text', text: turn.text });
|
|
14
|
+
for (const [i, tc] of (turn.toolCalls ?? []).entries()) {
|
|
15
|
+
content.push({
|
|
16
|
+
type: 'toolCall',
|
|
17
|
+
id: tc.id ?? `mock_tc_${idBase}_${i + 1}`,
|
|
18
|
+
name: tc.name,
|
|
19
|
+
arguments: tc.args ?? {},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const hasToolCalls = (turn.toolCalls ?? []).length > 0;
|
|
23
|
+
return {
|
|
24
|
+
role: 'assistant',
|
|
25
|
+
content,
|
|
26
|
+
stopReason: turn.stopReason ?? (hasToolCalls ? 'toolUse' : 'stop'),
|
|
27
|
+
usage: {
|
|
28
|
+
input: turn.usage?.input ?? DEFAULT_USAGE.input,
|
|
29
|
+
output: turn.usage?.output ?? DEFAULT_USAGE.output,
|
|
30
|
+
total: turn.usage?.total ?? DEFAULT_USAGE.total,
|
|
31
|
+
},
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 创建脚本式 StreamFn:第 n 次调用返回第 n 轮脚本。
|
|
37
|
+
*
|
|
38
|
+
* - 轮次耗尽且未配置 infiniteTool:返回 stopReason 'stop' 的兜底文本轮
|
|
39
|
+
* - 配置 infiniteTool:持续返回该工具调用(模拟死循环,配合 maxTurns 断言)
|
|
40
|
+
*/
|
|
41
|
+
export function createMockStreamFn(script) {
|
|
42
|
+
let callIndex = 0;
|
|
43
|
+
return async function* () {
|
|
44
|
+
// 每次调用让出一次事件循环(模拟网络延迟):
|
|
45
|
+
// 保证墙钟超时 / 并发调度有执行机会,行为更接近真实 provider
|
|
46
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
47
|
+
const turn = script.turns[callIndex];
|
|
48
|
+
if (turn) {
|
|
49
|
+
callIndex += 1;
|
|
50
|
+
yield { type: 'done', message: buildTurnMessage(turn, callIndex) };
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (script.infiniteTool) {
|
|
54
|
+
callIndex += 1;
|
|
55
|
+
yield {
|
|
56
|
+
type: 'done',
|
|
57
|
+
message: buildTurnMessage({
|
|
58
|
+
toolCalls: [
|
|
59
|
+
{ name: script.infiniteTool, args: script.infiniteToolArgs ?? {} },
|
|
60
|
+
],
|
|
61
|
+
}, callIndex),
|
|
62
|
+
};
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
yield {
|
|
66
|
+
type: 'done',
|
|
67
|
+
message: buildTurnMessage({ text: script.fallbackText ?? 'done' }, callIndex + 1),
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=mock-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-stream.js","sourceRoot":"","sources":["../../../src/core/mock-stream.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAE1D,kCAAkC;AAClC,SAAS,gBAAgB,CACvB,IAKC,EACD,MAAc;IAEd,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,WAAW,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE;YACzC,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,SAAS,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACvD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO;QACP,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,KAAK,EAAE;YACL,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,aAAa,CAAC,KAAK;YAC/C,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,aAAa,CAAC,MAAM;YAClD,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,aAAa,CAAC,KAAK;SAChD;QACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,OAAO,KAAK,SAAS,CAAC;QACpB,wBAAwB;QACxB,sCAAsC;QACtC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,IAAI,CAAC,CAAC;YACf,MAAM,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,SAAS,IAAI,CAAC,CAAC;YACf,MAAM;gBACJ,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE,gBAAgB,CACvB;oBACE,SAAS,EAAE;wBACT,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE,EAAE;qBACnE;iBACF,EACD,SAAS,CACV;aACF,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM;YACJ,IAAI,EAAE,MAAe;YACrB,OAAO,EAAE,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,EAAE,SAAS,GAAG,CAAC,CAAC;SAClF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/mock-tools.ts - 标准 mock 工具集
|
|
3
|
+
*
|
|
4
|
+
* 提供一组确定性的内存工具,供 golden 用例构造真实的工作流:
|
|
5
|
+
* echo - 回显参数
|
|
6
|
+
* readFile - 读 case 预置的内存文件系统
|
|
7
|
+
* writeFile - 写内存文件系统
|
|
8
|
+
* listDir - 列出前缀匹配的文件
|
|
9
|
+
* search - 跨文件全文检索
|
|
10
|
+
* fail - 恒定失败(测错误恢复轨迹)
|
|
11
|
+
*
|
|
12
|
+
* 工具按 case 隔离:每次 createMockTools 都返回独立 fs 副本。
|
|
13
|
+
*/
|
|
14
|
+
import type { Tool } from '@aipack-ai/agent';
|
|
15
|
+
/** 标准工具名列表(用例 input.tools 缺省时的注册范围) */
|
|
16
|
+
export declare const STANDARD_MOCK_TOOLS: readonly ["echo", "readFile", "writeFile", "listDir", "search", "fail"];
|
|
17
|
+
export type StandardMockToolName = (typeof STANDARD_MOCK_TOOLS)[number];
|
|
18
|
+
/**
|
|
19
|
+
* 创建一组隔离的 mock 工具。
|
|
20
|
+
*
|
|
21
|
+
* @param fs 预置文件系统(会被深拷贝,case 间互不影响)
|
|
22
|
+
* @param names 需要的工具名列表;缺省全部标准工具
|
|
23
|
+
*/
|
|
24
|
+
export declare function createMockTools(fs?: Record<string, string>, names?: string[]): Tool[];
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/eval/src/core/mock-tools.ts - 标准 mock 工具集
|
|
3
|
+
*
|
|
4
|
+
* 提供一组确定性的内存工具,供 golden 用例构造真实的工作流:
|
|
5
|
+
* echo - 回显参数
|
|
6
|
+
* readFile - 读 case 预置的内存文件系统
|
|
7
|
+
* writeFile - 写内存文件系统
|
|
8
|
+
* listDir - 列出前缀匹配的文件
|
|
9
|
+
* search - 跨文件全文检索
|
|
10
|
+
* fail - 恒定失败(测错误恢复轨迹)
|
|
11
|
+
*
|
|
12
|
+
* 工具按 case 隔离:每次 createMockTools 都返回独立 fs 副本。
|
|
13
|
+
*/
|
|
14
|
+
const STRING_SCHEMA = {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
message: { type: 'string', description: '要回显的消息' },
|
|
18
|
+
},
|
|
19
|
+
required: ['message'],
|
|
20
|
+
};
|
|
21
|
+
const READ_FILE_SCHEMA = {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
path: { type: 'string', description: '文件路径' },
|
|
25
|
+
},
|
|
26
|
+
required: ['path'],
|
|
27
|
+
};
|
|
28
|
+
const WRITE_FILE_SCHEMA = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
path: { type: 'string', description: '文件路径' },
|
|
32
|
+
content: { type: 'string', description: '写入内容' },
|
|
33
|
+
},
|
|
34
|
+
required: ['path', 'content'],
|
|
35
|
+
};
|
|
36
|
+
const LIST_DIR_SCHEMA = {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
path: { type: 'string', description: '目录前缀' },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const SEARCH_SCHEMA = {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
query: { type: 'string', description: '检索关键词' },
|
|
46
|
+
},
|
|
47
|
+
required: ['query'],
|
|
48
|
+
};
|
|
49
|
+
const FAIL_SCHEMA = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {},
|
|
52
|
+
};
|
|
53
|
+
function textResult(text, details = null) {
|
|
54
|
+
const content = [{ type: 'text', text }];
|
|
55
|
+
return { content, details };
|
|
56
|
+
}
|
|
57
|
+
function errorResult(message) {
|
|
58
|
+
const content = [{ type: 'text', text: `error: ${message}` }];
|
|
59
|
+
return { content, details: { error: message } };
|
|
60
|
+
}
|
|
61
|
+
/** 标准工具名列表(用例 input.tools 缺省时的注册范围) */
|
|
62
|
+
export const STANDARD_MOCK_TOOLS = [
|
|
63
|
+
'echo',
|
|
64
|
+
'readFile',
|
|
65
|
+
'writeFile',
|
|
66
|
+
'listDir',
|
|
67
|
+
'search',
|
|
68
|
+
'fail',
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* 创建一组隔离的 mock 工具。
|
|
72
|
+
*
|
|
73
|
+
* @param fs 预置文件系统(会被深拷贝,case 间互不影响)
|
|
74
|
+
* @param names 需要的工具名列表;缺省全部标准工具
|
|
75
|
+
*/
|
|
76
|
+
export function createMockTools(fs = {}, names) {
|
|
77
|
+
const files = new Map(Object.entries(structuredClone(fs)));
|
|
78
|
+
const all = {
|
|
79
|
+
echo: {
|
|
80
|
+
name: 'echo',
|
|
81
|
+
description: '回显输入消息(测试用)',
|
|
82
|
+
parameters: STRING_SCHEMA,
|
|
83
|
+
execute: async (_id, args) => {
|
|
84
|
+
const message = args?.message;
|
|
85
|
+
return textResult(String(message ?? ''));
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
readFile: {
|
|
89
|
+
name: 'readFile',
|
|
90
|
+
description: '读取内存文件系统中的文件内容',
|
|
91
|
+
parameters: READ_FILE_SCHEMA,
|
|
92
|
+
execute: async (_id, args) => {
|
|
93
|
+
const { path } = (args ?? {});
|
|
94
|
+
if (typeof path !== 'string')
|
|
95
|
+
return errorResult('path 必须为字符串');
|
|
96
|
+
const content = files.get(path);
|
|
97
|
+
if (content === undefined)
|
|
98
|
+
return errorResult(`文件不存在: ${path}`);
|
|
99
|
+
return textResult(content, { path, bytes: content.length });
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
writeFile: {
|
|
103
|
+
name: 'writeFile',
|
|
104
|
+
description: '写入内存文件系统(新建或覆盖)',
|
|
105
|
+
parameters: WRITE_FILE_SCHEMA,
|
|
106
|
+
execute: async (_id, args) => {
|
|
107
|
+
const { path, content } = (args ?? {});
|
|
108
|
+
if (typeof path !== 'string' || typeof content !== 'string') {
|
|
109
|
+
return errorResult('path 与 content 必须为字符串');
|
|
110
|
+
}
|
|
111
|
+
files.set(path, content);
|
|
112
|
+
return textResult(`ok: wrote ${content.length} bytes to ${path}`, {
|
|
113
|
+
path,
|
|
114
|
+
bytes: content.length,
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
listDir: {
|
|
119
|
+
name: 'listDir',
|
|
120
|
+
description: '列出内存文件系统中指定前缀下的文件',
|
|
121
|
+
parameters: LIST_DIR_SCHEMA,
|
|
122
|
+
execute: async (_id, args) => {
|
|
123
|
+
const { path } = (args ?? {});
|
|
124
|
+
const prefix = typeof path === 'string' ? path : '';
|
|
125
|
+
const entries = [...files.keys()]
|
|
126
|
+
.filter((k) => k.startsWith(prefix))
|
|
127
|
+
.sort();
|
|
128
|
+
return textResult(entries.join('\n') || '(empty)', { entries });
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
search: {
|
|
132
|
+
name: 'search',
|
|
133
|
+
description: '跨内存文件全文检索,返回 file:line:text 命中行',
|
|
134
|
+
parameters: SEARCH_SCHEMA,
|
|
135
|
+
execute: async (_id, args) => {
|
|
136
|
+
const { query } = (args ?? {});
|
|
137
|
+
if (typeof query !== 'string' || query.length === 0) {
|
|
138
|
+
return errorResult('query 必须为非空字符串');
|
|
139
|
+
}
|
|
140
|
+
const hits = [];
|
|
141
|
+
for (const [file, content] of files) {
|
|
142
|
+
content.split('\n').forEach((line, i) => {
|
|
143
|
+
if (line.includes(query))
|
|
144
|
+
hits.push(`${file}:${i + 1}:${line.trim()}`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return textResult(hits.join('\n') || '(no matches)', { hits: hits.length });
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
fail: {
|
|
151
|
+
name: 'fail',
|
|
152
|
+
description: '恒定返回错误的工具(测错误恢复)',
|
|
153
|
+
parameters: FAIL_SCHEMA,
|
|
154
|
+
execute: async () => errorResult('intentional failure'),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
if (!names || names.length === 0)
|
|
158
|
+
return [...STANDARD_MOCK_TOOLS].map((n) => all[n]);
|
|
159
|
+
return names.map((n) => all[n]).filter((t) => t !== undefined);
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=mock-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-tools.js","sourceRoot":"","sources":["../../../src/core/mock-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE;KACnD;IACD,QAAQ,EAAE,CAAC,SAAS,CAAC;CACtB,CAAC;AAEF,MAAM,gBAAgB,GAAG;IACvB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE;KAC9C;IACD,QAAQ,EAAE,CAAC,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE;QAC7C,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE;KACjD;IACD,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;CAC9B,CAAC;AAEF,MAAM,eAAe,GAAG;IACtB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE;KAC9C;CACF,CAAC;AAEF,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE;KAChD;IACD,QAAQ,EAAE,CAAC,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY,EAAE,UAAmB,IAAI;IACvD,MAAM,OAAO,GAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,OAAO,GAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,CAAC;IAC9E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM;IACN,UAAU;IACV,WAAW;IACX,SAAS;IACT,QAAQ;IACR,MAAM;CACE,CAAC;AAIX;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,KAA6B,EAAE,EAC/B,KAAgB;IAEhB,MAAM,KAAK,GAAwB,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEhF,MAAM,GAAG,GAAyB;QAChC,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM;YACZ,WAAW,EAAE,aAAa;YAC1B,UAAU,EAAE,aAAa;YACzB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC3B,MAAM,OAAO,GAAI,IAA8B,EAAE,OAAO,CAAC;gBACzD,OAAO,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3C,CAAC;SACF;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,gBAAgB;YAC7B,UAAU,EAAE,gBAAgB;YAC5B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAsB,CAAC;gBACnD,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,OAAO,WAAW,CAAC,aAAa,CAAC,CAAC;gBAChE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChC,IAAI,OAAO,KAAK,SAAS;oBAAE,OAAO,WAAW,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;gBAChE,OAAO,UAAU,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9D,CAAC;SACF;QACD,SAAS,EAAE;YACT,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,iBAAiB;YAC9B,UAAU,EAAE,iBAAiB;YAC7B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC3B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAGpC,CAAC;gBACF,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC5D,OAAO,WAAW,CAAC,uBAAuB,CAAC,CAAC;gBAC9C,CAAC;gBACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACzB,OAAO,UAAU,CAAC,aAAa,OAAO,CAAC,MAAM,aAAa,IAAI,EAAE,EAAE;oBAChE,IAAI;oBACJ,KAAK,EAAE,OAAO,CAAC,MAAM;iBACtB,CAAC,CAAC;YACL,CAAC;SACF;QACD,OAAO,EAAE;YACP,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,mBAAmB;YAChC,UAAU,EAAE,eAAe;YAC3B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAsB,CAAC;gBACnD,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;qBAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;qBACnC,IAAI,EAAE,CAAC;gBACV,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAClE,CAAC;SACF;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,iCAAiC;YAC9C,UAAU,EAAE,aAAa;YACzB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC3B,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAuB,CAAC;gBACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACpD,OAAO,WAAW,CAAC,gBAAgB,CAAC,CAAC;gBACvC,CAAC;gBACD,MAAM,IAAI,GAAa,EAAE,CAAC;gBAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;oBACpC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;wBACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;4BAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBACzE,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,cAAc,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9E,CAAC;SACF;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM;YACZ,WAAW,EAAE,kBAAkB;YAC/B,UAAU,EAAE,WAAW;YACvB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,qBAAqB,CAAC;SACxD;KACF,CAAC;IAEF,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,GAAG,mBAAmB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAa,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AAC5E,CAAC"}
|