@aigne/afs-llm-bench 1.12.0-beta.5
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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/bench.cjs +944 -0
- package/dist/bench.d.cts +89 -0
- package/dist/bench.d.cts.map +1 -0
- package/dist/bench.d.mts +89 -0
- package/dist/bench.d.mts.map +1 -0
- package/dist/bench.mjs +944 -0
- package/dist/bench.mjs.map +1 -0
- package/dist/errors.cjs +14 -0
- package/dist/errors.d.cts +13 -0
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.mts +13 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +14 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.cjs +38 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +10 -0
- package/dist/judge.cjs +189 -0
- package/dist/judge.d.cts +67 -0
- package/dist/judge.d.cts.map +1 -0
- package/dist/judge.d.mts +67 -0
- package/dist/judge.d.mts.map +1 -0
- package/dist/judge.mjs +185 -0
- package/dist/judge.mjs.map +1 -0
- package/dist/outlier.cjs +73 -0
- package/dist/outlier.d.cts +30 -0
- package/dist/outlier.d.cts.map +1 -0
- package/dist/outlier.d.mts +30 -0
- package/dist/outlier.d.mts.map +1 -0
- package/dist/outlier.mjs +73 -0
- package/dist/outlier.mjs.map +1 -0
- package/dist/patcher.cjs +296 -0
- package/dist/patcher.d.cts +119 -0
- package/dist/patcher.d.cts.map +1 -0
- package/dist/patcher.d.mts +119 -0
- package/dist/patcher.d.mts.map +1 -0
- package/dist/patcher.mjs +291 -0
- package/dist/patcher.mjs.map +1 -0
- package/dist/prompts.cjs +123 -0
- package/dist/prompts.d.cts +30 -0
- package/dist/prompts.d.cts.map +1 -0
- package/dist/prompts.d.mts +30 -0
- package/dist/prompts.d.mts.map +1 -0
- package/dist/prompts.mjs +121 -0
- package/dist/prompts.mjs.map +1 -0
- package/dist/reporter.cjs +322 -0
- package/dist/reporter.d.cts +27 -0
- package/dist/reporter.d.cts.map +1 -0
- package/dist/reporter.d.mts +27 -0
- package/dist/reporter.d.mts.map +1 -0
- package/dist/reporter.mjs +322 -0
- package/dist/reporter.mjs.map +1 -0
- package/dist/runner.cjs +710 -0
- package/dist/runner.d.cts +345 -0
- package/dist/runner.d.cts.map +1 -0
- package/dist/runner.d.mts +345 -0
- package/dist/runner.d.mts.map +1 -0
- package/dist/runner.mjs +697 -0
- package/dist/runner.mjs.map +1 -0
- package/dist/scoring.cjs +47 -0
- package/dist/scoring.d.cts +28 -0
- package/dist/scoring.d.cts.map +1 -0
- package/dist/scoring.d.mts +28 -0
- package/dist/scoring.d.mts.map +1 -0
- package/dist/scoring.mjs +46 -0
- package/dist/scoring.mjs.map +1 -0
- package/manifest.json +39 -0
- package/package.json +62 -0
package/dist/bench.mjs
ADDED
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
import { ValidationError } from "./errors.mjs";
|
|
2
|
+
import { runMatrix, runMatrixFull, validateRunArgs, validateRunFullArgs } from "./runner.mjs";
|
|
3
|
+
import { patchCatalog } from "./patcher.mjs";
|
|
4
|
+
import { BadPromptError, loadPromptsFromDir } from "./prompts.mjs";
|
|
5
|
+
import { generateReport } from "./reporter.mjs";
|
|
6
|
+
import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { AFSBaseProvider, Actions, Explain, List, Meta, Read, Stat, getPlatform, makeNsLog } from "@aigne/afs";
|
|
9
|
+
import { joinURL } from "ufo";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
//#region src/bench.ts
|
|
13
|
+
/**
|
|
14
|
+
* AFSBench — LLM AFS benchmark provider (Phase 0 scaffold).
|
|
15
|
+
*
|
|
16
|
+
* Routes:
|
|
17
|
+
* / — directory (list → prompts, results, .actions)
|
|
18
|
+
* /prompts — directory (list → prompt entries)
|
|
19
|
+
* /prompts/:id — file (parsed prompt content)
|
|
20
|
+
* /results — directory (list → result JSON files)
|
|
21
|
+
* /results/:date — file (raw result JSON)
|
|
22
|
+
* /.actions/run — Phase 1: matrix runner. Phase 0: NOT_IMPLEMENTED.
|
|
23
|
+
* /.actions/patchCatalog — Phase 1: catalog patcher. Phase 0: NOT_IMPLEMENTED.
|
|
24
|
+
*
|
|
25
|
+
* Phase 1 will plug `runner.ts` and `patcher.ts` into the action handlers; for
|
|
26
|
+
* now they always return `{ success: false, error: { code: "NOT_IMPLEMENTED" } }`.
|
|
27
|
+
*/
|
|
28
|
+
const log = makeNsLog("provider:llm-bench");
|
|
29
|
+
const DEFAULT_PROMPTS_DIR = "benchmarks/llm-afs/prompts";
|
|
30
|
+
const DEFAULT_RESULTS_DIR = "benchmarks/llm-afs/results";
|
|
31
|
+
const DEFAULT_CATALOG_PATH = "providers/ai/device/catalog.json";
|
|
32
|
+
const optionsSchema = z.object({
|
|
33
|
+
name: z.string().optional(),
|
|
34
|
+
description: z.string().optional(),
|
|
35
|
+
promptsDir: z.string().optional().describe("Absolute path to the prompt set on disk (defaults to benchmarks/llm-afs/prompts)."),
|
|
36
|
+
resultsDir: z.string().optional().describe("Absolute path to the results dir (defaults to benchmarks/llm-afs/results)."),
|
|
37
|
+
catalogPath: z.string().optional().describe("Absolute path to providers/ai/device/catalog.json (maintainer dev artifact). Default: workspace-root-relative.")
|
|
38
|
+
});
|
|
39
|
+
const ROOT_ACTIONS = [
|
|
40
|
+
{
|
|
41
|
+
name: "run",
|
|
42
|
+
description: "Run a (models × prompts × samples) benchmark matrix (Phase 1).",
|
|
43
|
+
schema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
description: "Run (models × prompts × samples) matrix and write results JSON to disk. Phase 1.",
|
|
46
|
+
properties: {
|
|
47
|
+
models: {
|
|
48
|
+
description: "Canonical model ids to test. Phase 1: required explicit array, length 1-5; no 'all' shortcut.",
|
|
49
|
+
type: "array",
|
|
50
|
+
items: { type: "string" }
|
|
51
|
+
},
|
|
52
|
+
prompts: {
|
|
53
|
+
description: "Prompt ids to run, or 'all' (Phase 1: array length 1-5).",
|
|
54
|
+
oneOf: [{
|
|
55
|
+
type: "string",
|
|
56
|
+
enum: ["all"]
|
|
57
|
+
}, {
|
|
58
|
+
type: "array",
|
|
59
|
+
items: { type: "string" }
|
|
60
|
+
}]
|
|
61
|
+
},
|
|
62
|
+
samples: {
|
|
63
|
+
type: "number",
|
|
64
|
+
description: "Samples per (model, prompt) pair, clamped to [1, 100]. Default 3."
|
|
65
|
+
},
|
|
66
|
+
parallel: {
|
|
67
|
+
type: "number",
|
|
68
|
+
description: "Concurrent samples within one (model, prompt). Clamped to [1, 4]. Default 1."
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
required: ["models"]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "runFull",
|
|
76
|
+
description: "Headless full-matrix runner with incremental flush + rate-limit backoff (Phase 2.1).",
|
|
77
|
+
schema: {
|
|
78
|
+
type: "object",
|
|
79
|
+
description: "Headless full-matrix benchmark runner. Larger limits than `run` (≤200 models / ≤200 prompts), incremental flush after every (model, prompt), exponential backoff for rate-limit errors, fast-fail for invalid api-key.",
|
|
80
|
+
properties: {
|
|
81
|
+
models: {
|
|
82
|
+
description: "Canonical model ids to test. Required array, length 1-200.",
|
|
83
|
+
type: "array",
|
|
84
|
+
items: { type: "string" }
|
|
85
|
+
},
|
|
86
|
+
prompts: {
|
|
87
|
+
description: "Prompt ids to run, or 'all' (array length 1-200).",
|
|
88
|
+
oneOf: [{
|
|
89
|
+
type: "string",
|
|
90
|
+
enum: ["all"]
|
|
91
|
+
}, {
|
|
92
|
+
type: "array",
|
|
93
|
+
items: { type: "string" }
|
|
94
|
+
}]
|
|
95
|
+
},
|
|
96
|
+
samples: {
|
|
97
|
+
type: "number",
|
|
98
|
+
description: "Samples per (model, prompt) pair, clamped to [1, 100]. Default 3."
|
|
99
|
+
},
|
|
100
|
+
parallel: {
|
|
101
|
+
type: "number",
|
|
102
|
+
description: "Concurrent samples within one (model, prompt). Clamped to [1, 4]. Default 1."
|
|
103
|
+
},
|
|
104
|
+
judgeFor: {
|
|
105
|
+
type: "array",
|
|
106
|
+
description: "Dimensions to evaluate via LLM-as-judge. Phase 0 of Run 3 only validates this arg; Phase 1 wires the judge.",
|
|
107
|
+
items: {
|
|
108
|
+
type: "string",
|
|
109
|
+
enum: ["instruction_following"]
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
judges: {
|
|
113
|
+
type: "array",
|
|
114
|
+
description: "Multi-judge ensemble (Phase 0 of Run 4). N judge model ids; takes precedence over `judgeModel`. N=1 is single-judge passthrough; N≥2 aggregates per spec (succ=2 mean, succ=3 median, succ≥4 trimmed mean). Length 1-10. Shares the same judgeHub.",
|
|
115
|
+
items: { type: "string" },
|
|
116
|
+
minItems: 1,
|
|
117
|
+
maxItems: 10
|
|
118
|
+
},
|
|
119
|
+
includeRaw: {
|
|
120
|
+
type: "boolean",
|
|
121
|
+
description: "When true, write a sidecar `<stamp>-raw.json` next to the main results file containing AgentRunResult.trace[] for every successful sample. Default false to keep main results small and avoid leaking trace details."
|
|
122
|
+
},
|
|
123
|
+
outlierThreshold: {
|
|
124
|
+
type: "number",
|
|
125
|
+
description: "Per-dimension stddev cap for outlier flagging (Phase 3 of Run 4). Defaults to 0.15. Clamped to [0.01, 1.0]."
|
|
126
|
+
},
|
|
127
|
+
outlierRerun: {
|
|
128
|
+
type: "boolean",
|
|
129
|
+
description: "When true, after the main matrix the runner schedules one extra batch (≤ 3 samples) for each (model, prompt) flagged as an outlier and recomputes the aggregate + outliers. Total samples per (model, prompt) bounded by 2 × N. Default false."
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
required: ["models"]
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "patchCatalog",
|
|
137
|
+
description: "Merge a results JSON into the ai-device catalog `afs` subtree (Phase 1).",
|
|
138
|
+
schema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
description: "Merge a results JSON into providers/ai/device/catalog.json under each model's `afs` subtree. Phase 1.",
|
|
141
|
+
properties: { results: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "Path to the results JSON to merge (default: latest under resultsDir)."
|
|
144
|
+
} }
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "report",
|
|
149
|
+
description: "Render a results JSON as a markdown or HTML benchmark report (Phase 2 of Run 4).",
|
|
150
|
+
schema: {
|
|
151
|
+
type: "object",
|
|
152
|
+
description: "Render a results JSON as a human-readable benchmark report (markdown or HTML). Phase 2 of Run 4.",
|
|
153
|
+
properties: {
|
|
154
|
+
results: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "Path to the results JSON to render (must resolve inside the results dir)."
|
|
157
|
+
},
|
|
158
|
+
format: {
|
|
159
|
+
type: "string",
|
|
160
|
+
enum: ["markdown", "html"],
|
|
161
|
+
description: "Output format. Defaults to markdown."
|
|
162
|
+
},
|
|
163
|
+
previous: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Optional path to a prior results JSON; when supplied the report adds a delta column."
|
|
166
|
+
},
|
|
167
|
+
includeRaw: {
|
|
168
|
+
type: "boolean",
|
|
169
|
+
description: "When true, embed the raw results JSON inside the report. Default false to avoid leaking trace data into shared reports."
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
required: ["results"]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
function findWorkspaceRoot(start) {
|
|
177
|
+
const path = getPlatform().path;
|
|
178
|
+
let dir = start;
|
|
179
|
+
for (let i = 0; i < 12; i++) {
|
|
180
|
+
if (existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
|
|
181
|
+
const parent = path.dirname(dir);
|
|
182
|
+
if (parent === dir) return void 0;
|
|
183
|
+
dir = parent;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function resolveAbsolute(p, fallback) {
|
|
187
|
+
const path = getPlatform().path;
|
|
188
|
+
const value = p ?? fallback;
|
|
189
|
+
if (path.isAbsolute(value)) return value;
|
|
190
|
+
const cwd = getPlatform().process?.cwd?.() ?? process.cwd();
|
|
191
|
+
const base = p === void 0 ? findWorkspaceRoot(cwd) ?? cwd : cwd;
|
|
192
|
+
return path.resolve(base, value);
|
|
193
|
+
}
|
|
194
|
+
async function safeReaddir(dir) {
|
|
195
|
+
const fs = getPlatform().fs;
|
|
196
|
+
if (!fs || !await fs.exists(dir)) return [];
|
|
197
|
+
return fs.readdir(dir);
|
|
198
|
+
}
|
|
199
|
+
async function readResultFile(resultsDir, date) {
|
|
200
|
+
const fs = getPlatform().fs;
|
|
201
|
+
const path = getPlatform().path;
|
|
202
|
+
if (!fs) return void 0;
|
|
203
|
+
const filename = date.endsWith(".json") ? date : `${date}.json`;
|
|
204
|
+
const target = path.join(resultsDir, filename);
|
|
205
|
+
if (!await fs.exists(target)) return void 0;
|
|
206
|
+
const raw = await fs.readTextFile(target);
|
|
207
|
+
let parsed;
|
|
208
|
+
try {
|
|
209
|
+
parsed = JSON.parse(raw);
|
|
210
|
+
} catch {
|
|
211
|
+
parsed = raw;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
raw,
|
|
215
|
+
parsed
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
var AFSBench = class extends AFSBaseProvider {
|
|
219
|
+
name;
|
|
220
|
+
description;
|
|
221
|
+
accessMode = "readwrite";
|
|
222
|
+
promptsDir;
|
|
223
|
+
resultsDir;
|
|
224
|
+
catalogPath;
|
|
225
|
+
static schema() {
|
|
226
|
+
return optionsSchema;
|
|
227
|
+
}
|
|
228
|
+
static manifest() {
|
|
229
|
+
return {
|
|
230
|
+
name: "llm-bench",
|
|
231
|
+
description: "LLM AFS benchmark suite — score model AFS-friendliness, patch ai-device catalog.\n- list /prompts to see available benchmark prompts\n- list /results to see prior runs\n- exec /.actions/run to score a (models × prompts × samples) matrix (Phase 1)\n- exec /.actions/patchCatalog to merge a result file into the ai-device catalog (Phase 1)",
|
|
232
|
+
uriTemplate: "llm-bench://",
|
|
233
|
+
category: "ai",
|
|
234
|
+
schema: optionsSchema,
|
|
235
|
+
tags: [
|
|
236
|
+
"ai",
|
|
237
|
+
"benchmark",
|
|
238
|
+
"llm",
|
|
239
|
+
"evaluation",
|
|
240
|
+
"catalog"
|
|
241
|
+
],
|
|
242
|
+
capabilityTags: [
|
|
243
|
+
"read-only",
|
|
244
|
+
"auth:none",
|
|
245
|
+
"local"
|
|
246
|
+
],
|
|
247
|
+
security: {
|
|
248
|
+
riskLevel: "local",
|
|
249
|
+
resourceAccess: ["local-filesystem"],
|
|
250
|
+
dataSensitivity: [],
|
|
251
|
+
notes: ["Phase 0 is scaffold-only (NOT_IMPLEMENTED actions). Phase 1 will run benchmarks via agent-run and write the ai-device catalog (maintainer tool boundary, see decisions.md #24)."]
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
static treeSchema() {
|
|
256
|
+
return {
|
|
257
|
+
operations: [
|
|
258
|
+
"list",
|
|
259
|
+
"read",
|
|
260
|
+
"exec",
|
|
261
|
+
"stat",
|
|
262
|
+
"explain"
|
|
263
|
+
],
|
|
264
|
+
tree: {
|
|
265
|
+
"/": {
|
|
266
|
+
kind: "llm-bench:root",
|
|
267
|
+
operations: [
|
|
268
|
+
"list",
|
|
269
|
+
"read",
|
|
270
|
+
"exec"
|
|
271
|
+
],
|
|
272
|
+
actions: [
|
|
273
|
+
"run",
|
|
274
|
+
"runFull",
|
|
275
|
+
"patchCatalog",
|
|
276
|
+
"report"
|
|
277
|
+
]
|
|
278
|
+
},
|
|
279
|
+
"/prompts": {
|
|
280
|
+
kind: "llm-bench:directory",
|
|
281
|
+
operations: ["list", "read"]
|
|
282
|
+
},
|
|
283
|
+
"/prompts/{id}": {
|
|
284
|
+
kind: "llm-bench:prompt",
|
|
285
|
+
operations: ["read"]
|
|
286
|
+
},
|
|
287
|
+
"/results": {
|
|
288
|
+
kind: "llm-bench:directory",
|
|
289
|
+
operations: ["list", "read"]
|
|
290
|
+
},
|
|
291
|
+
"/results/{date}": {
|
|
292
|
+
kind: "llm-bench:result",
|
|
293
|
+
operations: ["read"]
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
bestFor: ["LLM AFS-friendliness benchmark", "catalog scoring"],
|
|
297
|
+
notFor: ["runtime telemetry — see ai-device BenchmarkStore for that"]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
constructor(options = {}) {
|
|
301
|
+
super();
|
|
302
|
+
const parsed = optionsSchema.parse(options);
|
|
303
|
+
this.name = parsed.name ?? "llm-bench";
|
|
304
|
+
this.description = parsed.description ?? "LLM AFS benchmark suite";
|
|
305
|
+
this.promptsDir = resolveAbsolute(parsed.promptsDir, DEFAULT_PROMPTS_DIR);
|
|
306
|
+
this.resultsDir = resolveAbsolute(parsed.resultsDir, DEFAULT_RESULTS_DIR);
|
|
307
|
+
this.catalogPath = resolveAbsolute(parsed.catalogPath, DEFAULT_CATALOG_PATH);
|
|
308
|
+
}
|
|
309
|
+
/** Test/debug helpers. */
|
|
310
|
+
getPromptsDir() {
|
|
311
|
+
return this.promptsDir;
|
|
312
|
+
}
|
|
313
|
+
getResultsDir() {
|
|
314
|
+
return this.resultsDir;
|
|
315
|
+
}
|
|
316
|
+
getCatalogPath() {
|
|
317
|
+
return this.catalogPath;
|
|
318
|
+
}
|
|
319
|
+
async loadPrompts() {
|
|
320
|
+
const { prompts } = await loadPromptsFromDir(this.promptsDir);
|
|
321
|
+
return prompts;
|
|
322
|
+
}
|
|
323
|
+
async listResultFiles() {
|
|
324
|
+
const path = getPlatform().path;
|
|
325
|
+
return (await safeReaddir(this.resultsDir)).filter((f) => path.extname(f).toLowerCase() === ".json").sort();
|
|
326
|
+
}
|
|
327
|
+
async listRoot(_ctx) {
|
|
328
|
+
const promptCount = (await this.loadPrompts()).length;
|
|
329
|
+
const resultCount = (await this.listResultFiles()).length;
|
|
330
|
+
return { data: [this.buildEntry("/prompts", { meta: {
|
|
331
|
+
kind: "llm-bench:directory",
|
|
332
|
+
childrenCount: promptCount
|
|
333
|
+
} }), this.buildEntry("/results", { meta: {
|
|
334
|
+
kind: "llm-bench:directory",
|
|
335
|
+
childrenCount: resultCount
|
|
336
|
+
} })] };
|
|
337
|
+
}
|
|
338
|
+
async readRoot(_ctx) {
|
|
339
|
+
return this.buildEntry("/", {
|
|
340
|
+
content: {
|
|
341
|
+
provider: "llm-bench",
|
|
342
|
+
promptsDir: this.promptsDir,
|
|
343
|
+
resultsDir: this.resultsDir
|
|
344
|
+
},
|
|
345
|
+
meta: {
|
|
346
|
+
kind: "llm-bench:root",
|
|
347
|
+
childrenCount: 2
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async statRoot(_ctx) {
|
|
352
|
+
return { data: this.buildEntry("/", { meta: {
|
|
353
|
+
kind: "llm-bench:root",
|
|
354
|
+
childrenCount: 2
|
|
355
|
+
} }) };
|
|
356
|
+
}
|
|
357
|
+
async metaRoot(_ctx) {
|
|
358
|
+
const prompts = await this.loadPrompts();
|
|
359
|
+
const results = await this.listResultFiles();
|
|
360
|
+
return this.buildEntry("/.meta", {
|
|
361
|
+
content: {
|
|
362
|
+
provider: "llm-bench",
|
|
363
|
+
phase: "0-scaffold",
|
|
364
|
+
promptsDir: this.promptsDir,
|
|
365
|
+
resultsDir: this.resultsDir,
|
|
366
|
+
promptCount: prompts.length,
|
|
367
|
+
resultCount: results.length
|
|
368
|
+
},
|
|
369
|
+
meta: {
|
|
370
|
+
kind: "llm-bench:root",
|
|
371
|
+
childrenCount: 2
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
async explainRoot(_ctx) {
|
|
376
|
+
return {
|
|
377
|
+
content: [
|
|
378
|
+
"# LLM AFS Benchmark",
|
|
379
|
+
"",
|
|
380
|
+
"Score how well an LLM uses AFS tools (exploration, tool reliability,",
|
|
381
|
+
"self-stop, parallelism, efficiency, instruction-following). Scores get",
|
|
382
|
+
"patched into the ai-device catalog so policies can rank by real performance.",
|
|
383
|
+
"",
|
|
384
|
+
"## Phase 0 (scaffold)",
|
|
385
|
+
"",
|
|
386
|
+
"- `list /prompts` — benchmark prompt set (parsed from markdown)",
|
|
387
|
+
"- `list /results` — historical run JSON files",
|
|
388
|
+
"- `exec /.actions/run` / `/.actions/patchCatalog` — placeholders, return NOT_IMPLEMENTED until Phase 1 lands",
|
|
389
|
+
"",
|
|
390
|
+
"See `intent/llm-afs-benchmark/INTENT.md` for the full design."
|
|
391
|
+
].join("\n"),
|
|
392
|
+
format: "markdown"
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
async readCapabilities(_ctx) {
|
|
396
|
+
const operations = this.getOperationsDeclaration();
|
|
397
|
+
const manifest = {
|
|
398
|
+
schemaVersion: 1,
|
|
399
|
+
provider: this.name,
|
|
400
|
+
description: this.description,
|
|
401
|
+
tools: [],
|
|
402
|
+
operations,
|
|
403
|
+
actions: [{
|
|
404
|
+
description: "Root actions",
|
|
405
|
+
catalog: ROOT_ACTIONS.map((a) => ({
|
|
406
|
+
name: a.name,
|
|
407
|
+
description: a.description,
|
|
408
|
+
inputSchema: a.schema
|
|
409
|
+
})),
|
|
410
|
+
discovery: {
|
|
411
|
+
pathTemplate: "/.actions",
|
|
412
|
+
note: "Phase 0 returns NOT_IMPLEMENTED for both actions; full behaviour lands in Phase 1."
|
|
413
|
+
}
|
|
414
|
+
}]
|
|
415
|
+
};
|
|
416
|
+
return this.buildEntry("/.meta/.capabilities", {
|
|
417
|
+
content: manifest,
|
|
418
|
+
meta: {
|
|
419
|
+
kind: "afs:capabilities",
|
|
420
|
+
...operations
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async listPrompts(_ctx) {
|
|
425
|
+
return { data: (await this.loadPrompts()).map((p) => this.buildEntry(joinURL("/prompts", p.id), {
|
|
426
|
+
id: p.id,
|
|
427
|
+
meta: {
|
|
428
|
+
kind: "llm-bench:prompt",
|
|
429
|
+
childrenCount: 0,
|
|
430
|
+
title: p.frontmatter.title,
|
|
431
|
+
domain: p.frontmatter.domain ?? "english",
|
|
432
|
+
maxRounds: p.frontmatter.max_rounds
|
|
433
|
+
}
|
|
434
|
+
})) };
|
|
435
|
+
}
|
|
436
|
+
async readPrompts(_ctx) {
|
|
437
|
+
const prompts = await this.loadPrompts();
|
|
438
|
+
return this.buildEntry("/prompts", {
|
|
439
|
+
content: { promptIds: prompts.map((p) => p.id) },
|
|
440
|
+
meta: {
|
|
441
|
+
kind: "llm-bench:directory",
|
|
442
|
+
childrenCount: prompts.length
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
async statPrompts(_ctx) {
|
|
447
|
+
const prompts = await this.loadPrompts();
|
|
448
|
+
return { data: this.buildEntry("/prompts", { meta: {
|
|
449
|
+
kind: "llm-bench:directory",
|
|
450
|
+
childrenCount: prompts.length
|
|
451
|
+
} }) };
|
|
452
|
+
}
|
|
453
|
+
async metaPrompts(_ctx) {
|
|
454
|
+
const prompts = await this.loadPrompts();
|
|
455
|
+
return this.buildEntry("/prompts/.meta", {
|
|
456
|
+
content: {
|
|
457
|
+
promptIds: prompts.map((p) => p.id),
|
|
458
|
+
promptsDir: this.promptsDir
|
|
459
|
+
},
|
|
460
|
+
meta: {
|
|
461
|
+
kind: "llm-bench:directory",
|
|
462
|
+
childrenCount: prompts.length
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
async explainPrompts(_ctx) {
|
|
467
|
+
const prompts = await this.loadPrompts();
|
|
468
|
+
return {
|
|
469
|
+
content: [
|
|
470
|
+
"# Prompts",
|
|
471
|
+
"",
|
|
472
|
+
`Loaded from ${this.promptsDir}. ${prompts.length} prompt(s) parsed.`,
|
|
473
|
+
"",
|
|
474
|
+
...prompts.map((p) => `- \`${p.id}\` — ${p.frontmatter.title}`)
|
|
475
|
+
].join("\n"),
|
|
476
|
+
format: "markdown"
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
async findPromptOrUndefined(id) {
|
|
480
|
+
return (await this.loadPrompts()).find((p) => p.id === id);
|
|
481
|
+
}
|
|
482
|
+
async listPrompt(_ctx) {
|
|
483
|
+
return { data: [] };
|
|
484
|
+
}
|
|
485
|
+
async readPrompt(ctx) {
|
|
486
|
+
const prompt = await this.findPromptOrUndefined(ctx.params.id);
|
|
487
|
+
if (!prompt) return void 0;
|
|
488
|
+
return this.buildEntry(joinURL("/prompts", prompt.id), {
|
|
489
|
+
id: prompt.id,
|
|
490
|
+
content: prompt.body,
|
|
491
|
+
meta: {
|
|
492
|
+
kind: "llm-bench:prompt",
|
|
493
|
+
childrenCount: 0,
|
|
494
|
+
title: prompt.frontmatter.title,
|
|
495
|
+
domain: prompt.frontmatter.domain ?? "english",
|
|
496
|
+
maxRounds: prompt.frontmatter.max_rounds,
|
|
497
|
+
sections: Object.keys(prompt.sections),
|
|
498
|
+
filename: prompt.filename
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
async statPrompt(ctx) {
|
|
503
|
+
const prompt = await this.findPromptOrUndefined(ctx.params.id);
|
|
504
|
+
if (!prompt) return { data: void 0 };
|
|
505
|
+
return { data: this.buildEntry(joinURL("/prompts", prompt.id), {
|
|
506
|
+
id: prompt.id,
|
|
507
|
+
meta: {
|
|
508
|
+
kind: "llm-bench:prompt",
|
|
509
|
+
childrenCount: 0
|
|
510
|
+
}
|
|
511
|
+
}) };
|
|
512
|
+
}
|
|
513
|
+
async metaPrompt(ctx) {
|
|
514
|
+
const prompt = await this.findPromptOrUndefined(ctx.params.id);
|
|
515
|
+
if (!prompt) return void 0;
|
|
516
|
+
return this.buildEntry(joinURL("/prompts", prompt.id, ".meta"), {
|
|
517
|
+
content: {
|
|
518
|
+
id: prompt.id,
|
|
519
|
+
title: prompt.frontmatter.title,
|
|
520
|
+
domain: prompt.frontmatter.domain ?? "english",
|
|
521
|
+
maxRounds: prompt.frontmatter.max_rounds,
|
|
522
|
+
weightDim: prompt.frontmatter.weight_dim,
|
|
523
|
+
sections: Object.keys(prompt.sections)
|
|
524
|
+
},
|
|
525
|
+
meta: {
|
|
526
|
+
kind: "llm-bench:prompt",
|
|
527
|
+
childrenCount: 0,
|
|
528
|
+
title: prompt.frontmatter.title,
|
|
529
|
+
domain: prompt.frontmatter.domain ?? "english",
|
|
530
|
+
maxRounds: prompt.frontmatter.max_rounds
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
async explainPrompt(ctx) {
|
|
535
|
+
const prompt = await this.findPromptOrUndefined(ctx.params.id);
|
|
536
|
+
if (!prompt) return {
|
|
537
|
+
content: `# Prompt not found\n\nNo prompt registered with id \`${ctx.params.id}\`.`,
|
|
538
|
+
format: "markdown"
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
content: [
|
|
542
|
+
`# ${prompt.frontmatter.title}`,
|
|
543
|
+
"",
|
|
544
|
+
`Id: \`${prompt.id}\` · Domain: ${prompt.frontmatter.domain ?? "english"} · Max rounds: ${prompt.frontmatter.max_rounds}`,
|
|
545
|
+
"",
|
|
546
|
+
"## Sections",
|
|
547
|
+
...Object.keys(prompt.sections).map((s) => `- ${s}`)
|
|
548
|
+
].join("\n"),
|
|
549
|
+
format: "markdown"
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async listResults(_ctx) {
|
|
553
|
+
return { data: (await this.listResultFiles()).map((filename) => {
|
|
554
|
+
const id = filename.replace(/\.json$/i, "");
|
|
555
|
+
return this.buildEntry(joinURL("/results", id), {
|
|
556
|
+
id,
|
|
557
|
+
meta: {
|
|
558
|
+
kind: "llm-bench:result",
|
|
559
|
+
childrenCount: 0,
|
|
560
|
+
filename
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}) };
|
|
564
|
+
}
|
|
565
|
+
async readResults(_ctx) {
|
|
566
|
+
const files = await this.listResultFiles();
|
|
567
|
+
return this.buildEntry("/results", {
|
|
568
|
+
content: { runs: files },
|
|
569
|
+
meta: {
|
|
570
|
+
kind: "llm-bench:directory",
|
|
571
|
+
childrenCount: files.length
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
async statResults(_ctx) {
|
|
576
|
+
const files = await this.listResultFiles();
|
|
577
|
+
return { data: this.buildEntry("/results", { meta: {
|
|
578
|
+
kind: "llm-bench:directory",
|
|
579
|
+
childrenCount: files.length
|
|
580
|
+
} }) };
|
|
581
|
+
}
|
|
582
|
+
async metaResults(_ctx) {
|
|
583
|
+
const files = await this.listResultFiles();
|
|
584
|
+
return this.buildEntry("/results/.meta", {
|
|
585
|
+
content: {
|
|
586
|
+
runs: files,
|
|
587
|
+
resultsDir: this.resultsDir
|
|
588
|
+
},
|
|
589
|
+
meta: {
|
|
590
|
+
kind: "llm-bench:directory",
|
|
591
|
+
childrenCount: files.length
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
async explainResults(_ctx) {
|
|
596
|
+
const files = await this.listResultFiles();
|
|
597
|
+
return {
|
|
598
|
+
content: [
|
|
599
|
+
"# Results",
|
|
600
|
+
"",
|
|
601
|
+
`Historical runs under ${this.resultsDir}. ${files.length} file(s).`,
|
|
602
|
+
"",
|
|
603
|
+
"Each `.json` file is a snapshot from `arc afs exec /bench/.actions/run` (Phase 1)."
|
|
604
|
+
].join("\n"),
|
|
605
|
+
format: "markdown"
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
async listResult(_ctx) {
|
|
609
|
+
return { data: [] };
|
|
610
|
+
}
|
|
611
|
+
async readResult(ctx) {
|
|
612
|
+
const file = await readResultFile(this.resultsDir, ctx.params.date);
|
|
613
|
+
if (!file) return void 0;
|
|
614
|
+
const id = ctx.params.date.replace(/\.json$/i, "");
|
|
615
|
+
return this.buildEntry(joinURL("/results", id), {
|
|
616
|
+
id,
|
|
617
|
+
content: file.parsed,
|
|
618
|
+
meta: {
|
|
619
|
+
kind: "llm-bench:result",
|
|
620
|
+
childrenCount: 0
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
async statResult(ctx) {
|
|
625
|
+
if (!await readResultFile(this.resultsDir, ctx.params.date)) return { data: void 0 };
|
|
626
|
+
const id = ctx.params.date.replace(/\.json$/i, "");
|
|
627
|
+
return { data: this.buildEntry(joinURL("/results", id), {
|
|
628
|
+
id,
|
|
629
|
+
meta: {
|
|
630
|
+
kind: "llm-bench:result",
|
|
631
|
+
childrenCount: 0
|
|
632
|
+
}
|
|
633
|
+
}) };
|
|
634
|
+
}
|
|
635
|
+
async metaResult(ctx) {
|
|
636
|
+
const file = await readResultFile(this.resultsDir, ctx.params.date);
|
|
637
|
+
if (!file) return void 0;
|
|
638
|
+
const id = ctx.params.date.replace(/\.json$/i, "");
|
|
639
|
+
return this.buildEntry(joinURL("/results", id, ".meta"), {
|
|
640
|
+
content: {
|
|
641
|
+
id,
|
|
642
|
+
sizeBytes: file.raw.length
|
|
643
|
+
},
|
|
644
|
+
meta: {
|
|
645
|
+
kind: "llm-bench:result",
|
|
646
|
+
childrenCount: 0
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
async listRootActions(_ctx) {
|
|
651
|
+
return { data: ROOT_ACTIONS.map((a) => this.buildEntry(joinURL("/.actions", a.name), {
|
|
652
|
+
id: a.name,
|
|
653
|
+
content: {
|
|
654
|
+
name: a.name,
|
|
655
|
+
description: a.description,
|
|
656
|
+
inputSchema: a.schema
|
|
657
|
+
},
|
|
658
|
+
meta: { kind: "action" }
|
|
659
|
+
})) };
|
|
660
|
+
}
|
|
661
|
+
async execRun(ctx, args) {
|
|
662
|
+
try {
|
|
663
|
+
const validated = validateRunArgs(args);
|
|
664
|
+
const allPrompts = await this.loadPrompts();
|
|
665
|
+
const requestedIds = validated.prompts === "all" ? allPrompts.map((p) => p.id) : validated.prompts;
|
|
666
|
+
const selected = [];
|
|
667
|
+
for (const id of requestedIds) {
|
|
668
|
+
const found = allPrompts.find((p) => p.id === id);
|
|
669
|
+
if (!found) throw new BadPromptError(id, `unknown prompt id: ${id}`);
|
|
670
|
+
selected.push(found);
|
|
671
|
+
}
|
|
672
|
+
const afs = ctx.context?.afs;
|
|
673
|
+
const afsExec = afs?.exec?.bind(afs);
|
|
674
|
+
if (!afsExec) return {
|
|
675
|
+
success: false,
|
|
676
|
+
error: {
|
|
677
|
+
code: "NO_AFS_CONTEXT",
|
|
678
|
+
message: "AFS root unavailable in route context — cannot dispatch /dev/agent/.actions/run"
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
const out = await runMatrix({
|
|
682
|
+
deps: { exec: (path, dispatchArgs) => afsExec(path, dispatchArgs, {}) },
|
|
683
|
+
prompts: selected,
|
|
684
|
+
models: validated.models,
|
|
685
|
+
samples: validated.samples,
|
|
686
|
+
parallel: validated.parallel,
|
|
687
|
+
resultsDir: this.resultsDir,
|
|
688
|
+
...validated.hub !== void 0 ? { hub: validated.hub } : {}
|
|
689
|
+
});
|
|
690
|
+
return {
|
|
691
|
+
success: true,
|
|
692
|
+
data: {
|
|
693
|
+
results: out.results,
|
|
694
|
+
file: out.file,
|
|
695
|
+
ranBatch: out.ranBatch
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
} catch (err) {
|
|
699
|
+
if (err instanceof ValidationError) return {
|
|
700
|
+
success: false,
|
|
701
|
+
error: {
|
|
702
|
+
code: "VALIDATION",
|
|
703
|
+
message: err.message
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
if (err instanceof BadPromptError) return {
|
|
707
|
+
success: false,
|
|
708
|
+
error: {
|
|
709
|
+
code: "BAD_PROMPT",
|
|
710
|
+
message: err.message
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
return {
|
|
714
|
+
success: false,
|
|
715
|
+
error: {
|
|
716
|
+
code: "RUNNER_ERROR",
|
|
717
|
+
message: err instanceof Error ? err.message : String(err)
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async execRunFull(ctx, args) {
|
|
723
|
+
try {
|
|
724
|
+
const validated = validateRunFullArgs(args);
|
|
725
|
+
const allPrompts = await this.loadPrompts();
|
|
726
|
+
const requestedIds = validated.prompts === "all" ? allPrompts.map((p) => p.id) : validated.prompts;
|
|
727
|
+
const selected = [];
|
|
728
|
+
for (const id of requestedIds) {
|
|
729
|
+
const found = allPrompts.find((p) => p.id === id);
|
|
730
|
+
if (!found) throw new BadPromptError(id, `unknown prompt id: ${id}`);
|
|
731
|
+
selected.push(found);
|
|
732
|
+
}
|
|
733
|
+
const afs = ctx.context?.afs;
|
|
734
|
+
const afsExec = afs?.exec?.bind(afs);
|
|
735
|
+
if (!afsExec) return {
|
|
736
|
+
success: false,
|
|
737
|
+
error: {
|
|
738
|
+
code: "NO_AFS_CONTEXT",
|
|
739
|
+
message: "AFS root unavailable in route context — cannot dispatch /dev/agent/.actions/run"
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
const out = await runMatrixFull({
|
|
743
|
+
deps: { exec: (path, dispatchArgs) => afsExec(path, dispatchArgs, {}) },
|
|
744
|
+
prompts: selected,
|
|
745
|
+
models: validated.models,
|
|
746
|
+
samples: validated.samples,
|
|
747
|
+
parallel: validated.parallel,
|
|
748
|
+
resultsDir: this.resultsDir,
|
|
749
|
+
...validated.hub !== void 0 ? { hub: validated.hub } : {},
|
|
750
|
+
judgeFor: validated.judgeFor,
|
|
751
|
+
judges: validated.judges,
|
|
752
|
+
includeRaw: validated.includeRaw,
|
|
753
|
+
outlierThreshold: validated.outlierThreshold,
|
|
754
|
+
outlierRerun: validated.outlierRerun,
|
|
755
|
+
onProgress: progressLogger
|
|
756
|
+
});
|
|
757
|
+
return {
|
|
758
|
+
success: true,
|
|
759
|
+
data: {
|
|
760
|
+
results: out.results,
|
|
761
|
+
file: out.file,
|
|
762
|
+
rawFile: out.rawFile,
|
|
763
|
+
ranBatch: out.ranBatch,
|
|
764
|
+
judgeFor: validated.judgeFor ?? null
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
} catch (err) {
|
|
768
|
+
if (err instanceof ValidationError) return {
|
|
769
|
+
success: false,
|
|
770
|
+
error: {
|
|
771
|
+
code: "VALIDATION",
|
|
772
|
+
message: err.message
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
if (err instanceof BadPromptError) return {
|
|
776
|
+
success: false,
|
|
777
|
+
error: {
|
|
778
|
+
code: "BAD_PROMPT",
|
|
779
|
+
message: err.message
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
return {
|
|
783
|
+
success: false,
|
|
784
|
+
error: {
|
|
785
|
+
code: "RUNNER_ERROR",
|
|
786
|
+
message: err instanceof Error ? err.message : String(err)
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
async execPatchCatalog(ctx, args) {
|
|
792
|
+
try {
|
|
793
|
+
const resultsArg = args.results;
|
|
794
|
+
if (typeof resultsArg !== "string" || resultsArg.length === 0) throw new ValidationError("'results' must be a non-empty string path");
|
|
795
|
+
const afs = ctx.context?.afs;
|
|
796
|
+
const afsExec = afs?.exec?.bind(afs);
|
|
797
|
+
const afsRead = afs?.read?.bind(afs);
|
|
798
|
+
const afsList = afs?.list?.bind(afs);
|
|
799
|
+
if (!afsRead || !afsList) return {
|
|
800
|
+
success: false,
|
|
801
|
+
error: {
|
|
802
|
+
code: "NO_AFS_CONTEXT",
|
|
803
|
+
message: "AFS unavailable in route context — cannot fetch /dev/ai/score-weights. Ensure llm-bench is mounted alongside ai-device."
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
return {
|
|
807
|
+
success: true,
|
|
808
|
+
data: await patchCatalog({
|
|
809
|
+
resultsPath: resultsArg,
|
|
810
|
+
catalogPath: this.catalogPath,
|
|
811
|
+
resultsRoot: this.resultsDir,
|
|
812
|
+
loadPolicyWeights: () => loadWeightsViaAFS(afsList, afsRead),
|
|
813
|
+
refreshRegistry: afsExec ? async () => {
|
|
814
|
+
const result = await afsExec("/dev/ai/.actions/refreshRegistry", {}, {});
|
|
815
|
+
if (!result.success) throw new Error(`refreshRegistry failed: ${result.error?.message ?? "(no message)"}`);
|
|
816
|
+
} : void 0
|
|
817
|
+
})
|
|
818
|
+
};
|
|
819
|
+
} catch (err) {
|
|
820
|
+
if (err instanceof ValidationError) return {
|
|
821
|
+
success: false,
|
|
822
|
+
error: {
|
|
823
|
+
code: "VALIDATION",
|
|
824
|
+
message: err.message
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
success: false,
|
|
829
|
+
error: {
|
|
830
|
+
code: "PATCH_ERROR",
|
|
831
|
+
message: err instanceof Error ? err.message : String(err)
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
async execReport(ctx, args) {
|
|
837
|
+
try {
|
|
838
|
+
const resultsArg = args.results;
|
|
839
|
+
if (typeof resultsArg !== "string" || resultsArg.length === 0) throw new ValidationError("'results' must be a non-empty string path");
|
|
840
|
+
const previousArg = args.previous;
|
|
841
|
+
if (previousArg !== void 0 && (typeof previousArg !== "string" || previousArg.length === 0)) throw new ValidationError("'previous' must be a non-empty string path when provided");
|
|
842
|
+
const formatArg = args.format;
|
|
843
|
+
if (formatArg !== void 0 && formatArg !== "markdown" && formatArg !== "html") throw new ValidationError("'format' must be 'markdown' or 'html'");
|
|
844
|
+
const includeRawArg = args.includeRaw;
|
|
845
|
+
if (includeRawArg !== void 0 && typeof includeRawArg !== "boolean") throw new ValidationError("'includeRaw' must be a boolean when provided");
|
|
846
|
+
const afs = ctx.context?.afs;
|
|
847
|
+
const afsRead = afs?.read?.bind(afs);
|
|
848
|
+
const afsList = afs?.list?.bind(afs);
|
|
849
|
+
const report = await generateReport({
|
|
850
|
+
resultsPath: resultsArg,
|
|
851
|
+
resultsRoot: this.resultsDir,
|
|
852
|
+
previousPath: previousArg,
|
|
853
|
+
format: formatArg,
|
|
854
|
+
includeRaw: includeRawArg === true,
|
|
855
|
+
loadPolicyWeights: afsRead && afsList ? () => loadWeightsViaAFS(afsList, afsRead) : () => Promise.resolve({})
|
|
856
|
+
});
|
|
857
|
+
return {
|
|
858
|
+
success: true,
|
|
859
|
+
data: {
|
|
860
|
+
content: report.content,
|
|
861
|
+
format: report.format,
|
|
862
|
+
generatedAt: report.generatedAt
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
} catch (err) {
|
|
866
|
+
if (err instanceof ValidationError) return {
|
|
867
|
+
success: false,
|
|
868
|
+
error: {
|
|
869
|
+
code: "VALIDATION",
|
|
870
|
+
message: err.message
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
return {
|
|
874
|
+
success: false,
|
|
875
|
+
error: {
|
|
876
|
+
code: "REPORT_ERROR",
|
|
877
|
+
message: err instanceof Error ? err.message : String(err)
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
__decorate([List("/")], AFSBench.prototype, "listRoot", null);
|
|
884
|
+
__decorate([Read("/")], AFSBench.prototype, "readRoot", null);
|
|
885
|
+
__decorate([Stat("/")], AFSBench.prototype, "statRoot", null);
|
|
886
|
+
__decorate([Meta("/")], AFSBench.prototype, "metaRoot", null);
|
|
887
|
+
__decorate([Explain("/")], AFSBench.prototype, "explainRoot", null);
|
|
888
|
+
__decorate([Read("/.meta/.capabilities")], AFSBench.prototype, "readCapabilities", null);
|
|
889
|
+
__decorate([List("/prompts")], AFSBench.prototype, "listPrompts", null);
|
|
890
|
+
__decorate([Read("/prompts")], AFSBench.prototype, "readPrompts", null);
|
|
891
|
+
__decorate([Stat("/prompts")], AFSBench.prototype, "statPrompts", null);
|
|
892
|
+
__decorate([Meta("/prompts")], AFSBench.prototype, "metaPrompts", null);
|
|
893
|
+
__decorate([Explain("/prompts")], AFSBench.prototype, "explainPrompts", null);
|
|
894
|
+
__decorate([List("/prompts/:id")], AFSBench.prototype, "listPrompt", null);
|
|
895
|
+
__decorate([Read("/prompts/:id")], AFSBench.prototype, "readPrompt", null);
|
|
896
|
+
__decorate([Stat("/prompts/:id")], AFSBench.prototype, "statPrompt", null);
|
|
897
|
+
__decorate([Meta("/prompts/:id")], AFSBench.prototype, "metaPrompt", null);
|
|
898
|
+
__decorate([Explain("/prompts/:id")], AFSBench.prototype, "explainPrompt", null);
|
|
899
|
+
__decorate([List("/results")], AFSBench.prototype, "listResults", null);
|
|
900
|
+
__decorate([Read("/results")], AFSBench.prototype, "readResults", null);
|
|
901
|
+
__decorate([Stat("/results")], AFSBench.prototype, "statResults", null);
|
|
902
|
+
__decorate([Meta("/results")], AFSBench.prototype, "metaResults", null);
|
|
903
|
+
__decorate([Explain("/results")], AFSBench.prototype, "explainResults", null);
|
|
904
|
+
__decorate([List("/results/:date")], AFSBench.prototype, "listResult", null);
|
|
905
|
+
__decorate([Read("/results/:date")], AFSBench.prototype, "readResult", null);
|
|
906
|
+
__decorate([Stat("/results/:date")], AFSBench.prototype, "statResult", null);
|
|
907
|
+
__decorate([Meta("/results/:date")], AFSBench.prototype, "metaResult", null);
|
|
908
|
+
__decorate([Actions("/")], AFSBench.prototype, "listRootActions", null);
|
|
909
|
+
__decorate([Actions.Exec("/", "run", void 0, { effect: "write" })], AFSBench.prototype, "execRun", null);
|
|
910
|
+
__decorate([Actions.Exec("/", "runFull", void 0, { effect: "write" })], AFSBench.prototype, "execRunFull", null);
|
|
911
|
+
__decorate([Actions.Exec("/", "patchCatalog", void 0, { effect: "write" })], AFSBench.prototype, "execPatchCatalog", null);
|
|
912
|
+
__decorate([Actions.Exec("/", "report", void 0, { effect: "read" })], AFSBench.prototype, "execReport", null);
|
|
913
|
+
function progressLogger(event) {
|
|
914
|
+
const tag = `${event.model} × ${event.prompt} sample ${event.sampleIndex}/${event.totalSamples}`;
|
|
915
|
+
if (event.status === "ok" && event.scores) {
|
|
916
|
+
const s = event.scores;
|
|
917
|
+
const parts = [
|
|
918
|
+
`expl=${s.exploration.toFixed(2)}`,
|
|
919
|
+
`tool=${s.tool_reliability.toFixed(2)}`,
|
|
920
|
+
`stop=${s.self_stop.toFixed(2)}`,
|
|
921
|
+
`par=${s.parallelism.toFixed(2)}`,
|
|
922
|
+
`eff=${s.efficiency.toFixed(2)}`
|
|
923
|
+
];
|
|
924
|
+
if (typeof s.instruction_following === "number") parts.push(`inst=${s.instruction_following.toFixed(2)}`);
|
|
925
|
+
log.info(`▸ ${tag}: ✓ scores={${parts.join(" ")}}`);
|
|
926
|
+
} else if (event.status === "error" && event.error) log.info(`▸ ${tag}: ✗ ${event.error.kind} after ${event.error.attempts} attempt(s) — ${event.error.message}`);
|
|
927
|
+
}
|
|
928
|
+
async function loadWeightsViaAFS(list, read) {
|
|
929
|
+
const entries = (await list("/dev/ai/score-weights")).data ?? [];
|
|
930
|
+
const pairs = await Promise.all(entries.map(async (entry) => {
|
|
931
|
+
const policy = entry.id ?? entry.path.split("/").pop();
|
|
932
|
+
if (!policy) return null;
|
|
933
|
+
const content = (await read(`/dev/ai/score-weights/${policy}`)).data?.content;
|
|
934
|
+
if (!content?.weights || typeof content.weights !== "object") return null;
|
|
935
|
+
return [policy, content.weights];
|
|
936
|
+
}));
|
|
937
|
+
const out = {};
|
|
938
|
+
for (const pair of pairs) if (pair) out[pair[0]] = pair[1];
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
//#endregion
|
|
943
|
+
export { AFSBench };
|
|
944
|
+
//# sourceMappingURL=bench.mjs.map
|