@pfoundation/ocadvisor 26.9.1 → 26.9.3
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/README.md +444 -46
- package/dist/artificialAnalysis.d.ts +4 -0
- package/dist/artificialAnalysis.d.ts.map +1 -0
- package/dist/artificialAnalysis.js +77 -0
- package/dist/artificialAnalysis.js.map +1 -0
- package/dist/benchmarkConfig.d.ts +21 -0
- package/dist/benchmarkConfig.d.ts.map +1 -0
- package/dist/benchmarkConfig.js +59 -0
- package/dist/benchmarkConfig.js.map +1 -0
- package/dist/benchmarkEvidence.d.ts +37 -0
- package/dist/benchmarkEvidence.d.ts.map +1 -0
- package/dist/benchmarkEvidence.js +81 -0
- package/dist/benchmarkEvidence.js.map +1 -0
- package/dist/benchmarkMatch.d.ts +58 -0
- package/dist/benchmarkMatch.d.ts.map +1 -0
- package/dist/benchmarkMatch.js +259 -0
- package/dist/benchmarkMatch.js.map +1 -0
- package/dist/benchmarkStore.d.ts +45 -0
- package/dist/benchmarkStore.d.ts.map +1 -0
- package/dist/benchmarkStore.js +309 -0
- package/dist/benchmarkStore.js.map +1 -0
- package/dist/benchmarkTypes.d.ts +48 -0
- package/dist/benchmarkTypes.d.ts.map +1 -0
- package/dist/benchmarkTypes.js +298 -0
- package/dist/benchmarkTypes.js.map +1 -0
- package/dist/benchmarkUpdate.d.ts +33 -0
- package/dist/benchmarkUpdate.d.ts.map +1 -0
- package/dist/benchmarkUpdate.js +327 -0
- package/dist/benchmarkUpdate.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +397 -0
- package/dist/cli.js.map +1 -0
- package/dist/data/artificialAnalysis.mappings.json +229 -0
- package/dist/data/artificialAnalysis.snapshot.json +17684 -0
- package/dist/modelProfiles.d.ts +45 -0
- package/dist/modelProfiles.d.ts.map +1 -0
- package/dist/modelProfiles.js +141 -0
- package/dist/modelProfiles.js.map +1 -0
- package/dist/ocAdvisor.d.ts +105 -5
- package/dist/ocAdvisor.d.ts.map +1 -1
- package/dist/ocAdvisor.js +534 -45
- package/dist/ocAdvisor.js.map +1 -1
- package/dist/typesafeGate.d.ts +96 -0
- package/dist/typesafeGate.d.ts.map +1 -0
- package/dist/typesafeGate.js +475 -0
- package/dist/typesafeGate.js.map +1 -0
- package/dist/typesafeState.d.ts +94 -0
- package/dist/typesafeState.d.ts.map +1 -0
- package/dist/typesafeState.js +301 -0
- package/dist/typesafeState.js.map +1 -0
- package/package.json +8 -2
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Versioned Artificial Analysis benchmark snapshot: shared schema,
|
|
2
|
+
// validation, and content hashing for the updater, reader, CLI, and gate.
|
|
3
|
+
//
|
|
4
|
+
// One validation path serves downloads, the bundled seed, user snapshots,
|
|
5
|
+
// and mapping-adjacent reads so every consumer agrees on what is usable.
|
|
6
|
+
// Unknown additive fields are ignored or preserved (never invented), and
|
|
7
|
+
// undocumented metadata stays null instead of guessed.
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
export const BENCHMARK_SCHEMA_VERSION = 1;
|
|
10
|
+
export const BENCHMARK_SOURCE = "artificial-analysis";
|
|
11
|
+
export const BENCHMARK_SOURCE_URL = "https://artificialanalysis.ai/";
|
|
12
|
+
// Recognized metrics in deterministic gate-selection order: the two
|
|
13
|
+
// Artificial Analysis indexes first, then reasoning/math evidence. Scores
|
|
14
|
+
// keep their published labels and units; index points and fractions are
|
|
15
|
+
// never mixed into one aggregate.
|
|
16
|
+
export const RECOGNIZED_METRICS = [
|
|
17
|
+
{
|
|
18
|
+
key: "artificial_analysis_coding_index",
|
|
19
|
+
label: "Artificial Analysis Coding Index",
|
|
20
|
+
category: "coding",
|
|
21
|
+
unit: "index_points",
|
|
22
|
+
higherIsBetter: true,
|
|
23
|
+
benchmarkVersion: null,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: "artificial_analysis_intelligence_index",
|
|
27
|
+
label: "Artificial Analysis Intelligence Index",
|
|
28
|
+
category: "general",
|
|
29
|
+
unit: "index_points",
|
|
30
|
+
higherIsBetter: true,
|
|
31
|
+
benchmarkVersion: null,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "hle",
|
|
35
|
+
label: "HLE (Humanity's Last Exam)",
|
|
36
|
+
category: "reasoning",
|
|
37
|
+
unit: "fraction",
|
|
38
|
+
higherIsBetter: true,
|
|
39
|
+
benchmarkVersion: null,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "gpqa",
|
|
43
|
+
label: "GPQA",
|
|
44
|
+
category: "reasoning",
|
|
45
|
+
unit: "fraction",
|
|
46
|
+
higherIsBetter: true,
|
|
47
|
+
benchmarkVersion: null,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: "artificial_analysis_math_index",
|
|
51
|
+
label: "Artificial Analysis Math Index",
|
|
52
|
+
category: "math",
|
|
53
|
+
unit: "index_points",
|
|
54
|
+
higherIsBetter: true,
|
|
55
|
+
benchmarkVersion: null,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
export const GATE_METRIC_ORDER = RECOGNIZED_METRICS.map((metric) => metric.key);
|
|
59
|
+
const RECOGNIZED_KEYS = new Set(GATE_METRIC_ORDER);
|
|
60
|
+
const HASH_FORMAT = /^[0-9a-f]{64}$/;
|
|
61
|
+
const CATEGORIES = [
|
|
62
|
+
"coding",
|
|
63
|
+
"general",
|
|
64
|
+
"reasoning",
|
|
65
|
+
"math",
|
|
66
|
+
];
|
|
67
|
+
const UNITS = ["index_points", "fraction"];
|
|
68
|
+
function isRecord(value) {
|
|
69
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
+
}
|
|
71
|
+
function isValidDateTime(value) {
|
|
72
|
+
return Number.isFinite(Date.parse(value));
|
|
73
|
+
}
|
|
74
|
+
function isValidationError(value) {
|
|
75
|
+
return typeof value === "object" && value !== null;
|
|
76
|
+
}
|
|
77
|
+
// `in` narrowing cannot discriminate an index-signature Record from the
|
|
78
|
+
// error shape, so evaluations results use this predicate instead.
|
|
79
|
+
function isParseError(value) {
|
|
80
|
+
return typeof value === "object" && value !== null && "error" in value;
|
|
81
|
+
}
|
|
82
|
+
function parseNullableString(value, field) {
|
|
83
|
+
if (value === null || value === undefined)
|
|
84
|
+
return null;
|
|
85
|
+
if (typeof value !== "string")
|
|
86
|
+
return { error: `${field} must be a string or null` };
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
// Recognized scores must be finite numbers or explicit nulls (zero is a
|
|
90
|
+
// real score). Unknown keys keep finite numbers and nulls for forward
|
|
91
|
+
// compatibility; anything else is ignored rather than rejected so additive
|
|
92
|
+
// upstream changes cannot break previously valid snapshots.
|
|
93
|
+
function parseEvaluations(value, label) {
|
|
94
|
+
if (value === null || value === undefined)
|
|
95
|
+
return {};
|
|
96
|
+
if (!isRecord(value)) {
|
|
97
|
+
return { error: `${label} evaluations must be an object` };
|
|
98
|
+
}
|
|
99
|
+
const evaluations = {};
|
|
100
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
101
|
+
if (entry === null) {
|
|
102
|
+
evaluations[key] = null;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (typeof entry !== "number" || !Number.isFinite(entry)) {
|
|
106
|
+
if (RECOGNIZED_KEYS.has(key)) {
|
|
107
|
+
return { error: `${label} has an invalid ${key} score` };
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
evaluations[key] = entry;
|
|
112
|
+
}
|
|
113
|
+
return evaluations;
|
|
114
|
+
}
|
|
115
|
+
export function parseBenchmarkModel(value, label) {
|
|
116
|
+
if (!isRecord(value)) {
|
|
117
|
+
return { error: `${label} must be an object` };
|
|
118
|
+
}
|
|
119
|
+
const id = typeof value.id === "string" ? value.id.trim() : "";
|
|
120
|
+
if (!id)
|
|
121
|
+
return { error: `${label} is missing a stable model id` };
|
|
122
|
+
const creatorID = typeof value.creatorID === "string" ? value.creatorID.trim() : "";
|
|
123
|
+
if (!creatorID)
|
|
124
|
+
return { error: `${label} is missing a creator id` };
|
|
125
|
+
if (typeof value.name !== "string" || typeof value.slug !== "string") {
|
|
126
|
+
return { error: `${label} is missing its name or slug` };
|
|
127
|
+
}
|
|
128
|
+
const evaluatedEffort = parseNullableString(value.evaluatedEffort, `${label} evaluatedEffort`);
|
|
129
|
+
if (isValidationError(evaluatedEffort))
|
|
130
|
+
return evaluatedEffort;
|
|
131
|
+
const evaluatedAt = parseNullableString(value.evaluatedAt, `${label} evaluatedAt`);
|
|
132
|
+
if (isValidationError(evaluatedAt))
|
|
133
|
+
return evaluatedAt;
|
|
134
|
+
if (typeof evaluatedAt === "string" &&
|
|
135
|
+
evaluatedAt.trim() &&
|
|
136
|
+
!isValidDateTime(evaluatedAt)) {
|
|
137
|
+
return { error: `${label} has an invalid evaluatedAt date` };
|
|
138
|
+
}
|
|
139
|
+
const evaluations = parseEvaluations(value.evaluations, label);
|
|
140
|
+
if (isParseError(evaluations))
|
|
141
|
+
return evaluations;
|
|
142
|
+
return {
|
|
143
|
+
id,
|
|
144
|
+
creatorID,
|
|
145
|
+
name: value.name,
|
|
146
|
+
slug: value.slug,
|
|
147
|
+
evaluatedEffort: evaluatedEffort,
|
|
148
|
+
evaluatedAt: evaluatedAt,
|
|
149
|
+
evaluations,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function parseMetricDefinitions(value) {
|
|
153
|
+
if (!Array.isArray(value)) {
|
|
154
|
+
return { error: "metricDefinitions must be an array" };
|
|
155
|
+
}
|
|
156
|
+
const seen = new Set();
|
|
157
|
+
const definitions = [];
|
|
158
|
+
for (let index = 0; index < value.length; index++) {
|
|
159
|
+
const entry = value[index];
|
|
160
|
+
const label = `metric definition ${index}`;
|
|
161
|
+
if (!isRecord(entry))
|
|
162
|
+
return { error: `${label} must be an object` };
|
|
163
|
+
const key = typeof entry.key === "string" ? entry.key.trim() : "";
|
|
164
|
+
if (!key)
|
|
165
|
+
return { error: `${label} is missing its key` };
|
|
166
|
+
if (seen.has(key)) {
|
|
167
|
+
return { error: `duplicate metric definition for ${key}` };
|
|
168
|
+
}
|
|
169
|
+
seen.add(key);
|
|
170
|
+
if (typeof entry.label !== "string") {
|
|
171
|
+
return { error: `${label} is missing its label` };
|
|
172
|
+
}
|
|
173
|
+
if (typeof entry.category !== "string" ||
|
|
174
|
+
!CATEGORIES.includes(entry.category)) {
|
|
175
|
+
return { error: `${label} has an unknown category` };
|
|
176
|
+
}
|
|
177
|
+
if (typeof entry.unit !== "string" || !UNITS.includes(entry.unit)) {
|
|
178
|
+
return { error: `${label} has an unknown unit` };
|
|
179
|
+
}
|
|
180
|
+
if (typeof entry.higherIsBetter !== "boolean") {
|
|
181
|
+
return { error: `${label} is missing higherIsBetter` };
|
|
182
|
+
}
|
|
183
|
+
const benchmarkVersion = parseNullableString(entry.benchmarkVersion, `${label} benchmarkVersion`);
|
|
184
|
+
if (isValidationError(benchmarkVersion))
|
|
185
|
+
return benchmarkVersion;
|
|
186
|
+
definitions.push({
|
|
187
|
+
key,
|
|
188
|
+
label: entry.label,
|
|
189
|
+
category: entry.category,
|
|
190
|
+
unit: entry.unit,
|
|
191
|
+
higherIsBetter: entry.higherIsBetter,
|
|
192
|
+
benchmarkVersion: benchmarkVersion,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return definitions;
|
|
196
|
+
}
|
|
197
|
+
function hasUsableScore(models) {
|
|
198
|
+
return models.some((model) => GATE_METRIC_ORDER.some((key) => typeof model.evaluations[key] === "number" &&
|
|
199
|
+
Number.isFinite(model.evaluations[key])));
|
|
200
|
+
}
|
|
201
|
+
export function parseBenchmarkSnapshot(value) {
|
|
202
|
+
if (!isRecord(value)) {
|
|
203
|
+
return { error: "benchmark snapshot must be an object" };
|
|
204
|
+
}
|
|
205
|
+
if (value.schemaVersion !== BENCHMARK_SCHEMA_VERSION) {
|
|
206
|
+
return {
|
|
207
|
+
error: `unsupported benchmark snapshot schema version ${String(value.schemaVersion)} (expected ${BENCHMARK_SCHEMA_VERSION})`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (value.source !== BENCHMARK_SOURCE) {
|
|
211
|
+
return {
|
|
212
|
+
error: `benchmark snapshot source must be ${BENCHMARK_SOURCE}`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (typeof value.sourceURL !== "string" || !value.sourceURL.trim()) {
|
|
216
|
+
return { error: "benchmark snapshot is missing its sourceURL" };
|
|
217
|
+
}
|
|
218
|
+
if (typeof value.endpoint !== "string" || !value.endpoint.trim()) {
|
|
219
|
+
return { error: "benchmark snapshot is missing its endpoint" };
|
|
220
|
+
}
|
|
221
|
+
if (typeof value.fetchedAt !== "string" ||
|
|
222
|
+
!isValidDateTime(value.fetchedAt)) {
|
|
223
|
+
return { error: "benchmark snapshot has an invalid fetchedAt timestamp" };
|
|
224
|
+
}
|
|
225
|
+
if (typeof value.contentHash !== "string" ||
|
|
226
|
+
!HASH_FORMAT.test(value.contentHash)) {
|
|
227
|
+
return { error: "benchmark snapshot has an invalid contentHash" };
|
|
228
|
+
}
|
|
229
|
+
const methodologyVersion = parseNullableString(value.methodologyVersion, "methodologyVersion");
|
|
230
|
+
if (isValidationError(methodologyVersion))
|
|
231
|
+
return methodologyVersion;
|
|
232
|
+
const metricDefinitions = parseMetricDefinitions(value.metricDefinitions);
|
|
233
|
+
if ("error" in metricDefinitions)
|
|
234
|
+
return metricDefinitions;
|
|
235
|
+
if (!Array.isArray(value.models) || value.models.length === 0) {
|
|
236
|
+
return { error: "benchmark snapshot has an empty model dataset" };
|
|
237
|
+
}
|
|
238
|
+
const seen = new Set();
|
|
239
|
+
const models = [];
|
|
240
|
+
for (let index = 0; index < value.models.length; index++) {
|
|
241
|
+
const parsed = parseBenchmarkModel(value.models[index], `model ${index}`);
|
|
242
|
+
if ("error" in parsed)
|
|
243
|
+
return parsed;
|
|
244
|
+
if (seen.has(parsed.id)) {
|
|
245
|
+
return { error: `duplicate benchmark model id ${parsed.id}` };
|
|
246
|
+
}
|
|
247
|
+
seen.add(parsed.id);
|
|
248
|
+
models.push(parsed);
|
|
249
|
+
}
|
|
250
|
+
if (!hasUsableScore(models)) {
|
|
251
|
+
return { error: "benchmark snapshot has no usable benchmark scores" };
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
schemaVersion: BENCHMARK_SCHEMA_VERSION,
|
|
255
|
+
source: BENCHMARK_SOURCE,
|
|
256
|
+
sourceURL: value.sourceURL,
|
|
257
|
+
endpoint: value.endpoint,
|
|
258
|
+
fetchedAt: value.fetchedAt,
|
|
259
|
+
contentHash: value.contentHash,
|
|
260
|
+
methodologyVersion: methodologyVersion,
|
|
261
|
+
metricDefinitions,
|
|
262
|
+
models,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// Canonical JSON: object keys sorted recursively, arrays in order. Used only
|
|
266
|
+
// for hashing, never for display.
|
|
267
|
+
function stableStringify(value) {
|
|
268
|
+
if (Array.isArray(value)) {
|
|
269
|
+
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
270
|
+
}
|
|
271
|
+
if (isRecord(value)) {
|
|
272
|
+
const body = Object.keys(value)
|
|
273
|
+
.sort()
|
|
274
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
|
|
275
|
+
.join(",");
|
|
276
|
+
return `{${body}}`;
|
|
277
|
+
}
|
|
278
|
+
return JSON.stringify(value) ?? "null";
|
|
279
|
+
}
|
|
280
|
+
export function computeSnapshotHash(input) {
|
|
281
|
+
return createHash("sha256")
|
|
282
|
+
.update(stableStringify({
|
|
283
|
+
source: input.source,
|
|
284
|
+
endpoint: input.endpoint,
|
|
285
|
+
methodologyVersion: input.methodologyVersion,
|
|
286
|
+
models: input.models,
|
|
287
|
+
}), "utf8")
|
|
288
|
+
.digest("hex");
|
|
289
|
+
}
|
|
290
|
+
export function verifySnapshotHash(snapshot) {
|
|
291
|
+
return (computeSnapshotHash({
|
|
292
|
+
source: snapshot.source,
|
|
293
|
+
endpoint: snapshot.endpoint,
|
|
294
|
+
methodologyVersion: snapshot.methodologyVersion,
|
|
295
|
+
models: snapshot.models,
|
|
296
|
+
}) === snapshot.contentHash);
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=benchmarkTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmarkTypes.js","sourceRoot":"","sources":["../src/benchmarkTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const AA_API_KEY_ENV = "ARTIFICIAL_ANALYSIS_API_KEY";
|
|
2
|
+
export declare const BENCHMARK_UPDATE_TIMEOUT_MS = 30000;
|
|
3
|
+
export declare const BENCHMARK_UPDATE_MAX_BYTES: number;
|
|
4
|
+
export type BenchmarkUpdateErrorCode = "missing_key" | "invalid_path" | "timeout" | "too_large" | "http" | "transport" | "invalid_response" | "busy" | "write_failed";
|
|
5
|
+
export interface BenchmarkUpdateSuccess {
|
|
6
|
+
ok: true;
|
|
7
|
+
path: string;
|
|
8
|
+
fetchedAt: string;
|
|
9
|
+
modelCount: number;
|
|
10
|
+
contentHash: string;
|
|
11
|
+
unchanged: boolean;
|
|
12
|
+
metricCoverage: Record<string, number>;
|
|
13
|
+
}
|
|
14
|
+
export interface BenchmarkUpdateFailure {
|
|
15
|
+
ok: false;
|
|
16
|
+
code: BenchmarkUpdateErrorCode;
|
|
17
|
+
message: string;
|
|
18
|
+
status?: number;
|
|
19
|
+
}
|
|
20
|
+
export type BenchmarkUpdateResult = BenchmarkUpdateSuccess | BenchmarkUpdateFailure;
|
|
21
|
+
export interface BenchmarkUpdateDeps {
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
env?: Record<string, string | undefined>;
|
|
24
|
+
apiKey?: string;
|
|
25
|
+
now?: () => string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
maxBytes?: number;
|
|
28
|
+
randomId?: () => string;
|
|
29
|
+
currentPid?: number;
|
|
30
|
+
processAlive?: (pid: number) => boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function updateBenchmarkSnapshot(targetPath: string, deps?: BenchmarkUpdateDeps): Promise<BenchmarkUpdateResult>;
|
|
33
|
+
//# sourceMappingURL=benchmarkUpdate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmarkUpdate.d.ts","sourceRoot":"","sources":["../src/benchmarkUpdate.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,0BAA0B,QAAmB,CAAC;AAE3D,MAAM,MAAM,wBAAwB,GAChC,aAAa,GACb,cAAc,GACd,SAAS,GACT,WAAW,GACX,MAAM,GACN,WAAW,GACX,kBAAkB,GAClB,MAAM,GACN,cAAc,CAAC;AAEnB,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,wBAAwB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAC/B,sBAAsB,GAAG,sBAAsB,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACzC;AAwLD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAyIhC"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Transactional Artificial Analysis snapshot refresh: one bounded,
|
|
2
|
+
// authenticated fetch is validated, staged next to the target, and
|
|
3
|
+
// atomically renamed over it. Any failure before the rename leaves the
|
|
4
|
+
// previous file byte-for-byte intact. A per-target PID lock coordinates
|
|
5
|
+
// concurrent updaters; stale locks from dead processes are recovered,
|
|
6
|
+
// live locks fail promptly with `busy`.
|
|
7
|
+
import { mkdir, open, readdir, readFile, rename, rm, writeFile, } from "node:fs/promises";
|
|
8
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
9
|
+
import { AA_LLM_MODELS_ENDPOINT, normalizeAAResponse, } from "./artificialAnalysis.js";
|
|
10
|
+
import { RECOGNIZED_METRICS, } from "./benchmarkTypes.js";
|
|
11
|
+
export const AA_API_KEY_ENV = "ARTIFICIAL_ANALYSIS_API_KEY";
|
|
12
|
+
export const BENCHMARK_UPDATE_TIMEOUT_MS = 30_000;
|
|
13
|
+
export const BENCHMARK_UPDATE_MAX_BYTES = 16 * 1024 * 1024;
|
|
14
|
+
function defaultProcessAlive(pid) {
|
|
15
|
+
try {
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function defaultRandomId() {
|
|
24
|
+
return `${process.pid}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
|
|
25
|
+
}
|
|
26
|
+
function lockPathFor(targetPath) {
|
|
27
|
+
return `${targetPath}.lock`;
|
|
28
|
+
}
|
|
29
|
+
function stagePrefixFor(targetPath) {
|
|
30
|
+
return `.${basename(targetPath)}.`;
|
|
31
|
+
}
|
|
32
|
+
// Removes interrupted staging files for this target left by crashed runs.
|
|
33
|
+
// Other targets sharing the directory use a different prefix and are kept.
|
|
34
|
+
async function reapStaleStaging(targetPath) {
|
|
35
|
+
const dir = dirname(targetPath);
|
|
36
|
+
const prefix = stagePrefixFor(targetPath);
|
|
37
|
+
let names;
|
|
38
|
+
try {
|
|
39
|
+
names = await readdir(dir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for (const name of names) {
|
|
45
|
+
if (!name.startsWith(prefix) || !name.endsWith(".tmp"))
|
|
46
|
+
continue;
|
|
47
|
+
try {
|
|
48
|
+
await rm(join(dir, name), { force: true });
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function readLockPid(lockPath) {
|
|
54
|
+
try {
|
|
55
|
+
const raw = await readFile(lockPath, "utf8");
|
|
56
|
+
const parsed = JSON.parse(raw);
|
|
57
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
58
|
+
return null;
|
|
59
|
+
const pid = parsed.pid;
|
|
60
|
+
return typeof pid === "number" && Number.isInteger(pid) && pid > 0
|
|
61
|
+
? pid
|
|
62
|
+
: null;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Acquires the per-target lock. Returns true when held; a live holder means
|
|
69
|
+
// `busy`, while a dead or corrupt holder is removed and acquisition retried
|
|
70
|
+
// once (losing a takeover race still reports `busy`).
|
|
71
|
+
async function acquireLock(lockPath, currentPid, processAlive, fetchedAt) {
|
|
72
|
+
const payload = JSON.stringify({ pid: currentPid, createdAt: fetchedAt });
|
|
73
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
await writeFile(lockPath, payload, { flag: "wx" });
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error?.code !== "EEXIST")
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
const holder = await readLockPid(lockPath);
|
|
83
|
+
if (holder !== null && processAlive(holder))
|
|
84
|
+
return false;
|
|
85
|
+
try {
|
|
86
|
+
await rm(lockPath, { force: true });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
async function releaseLock(lockPath) {
|
|
95
|
+
try {
|
|
96
|
+
await rm(lockPath, { force: true });
|
|
97
|
+
}
|
|
98
|
+
catch { }
|
|
99
|
+
}
|
|
100
|
+
function classifyFetchError(error) {
|
|
101
|
+
const name = error && typeof error === "object" && "name" in error
|
|
102
|
+
? String(error.name)
|
|
103
|
+
: "";
|
|
104
|
+
return name === "TimeoutError" || name === "AbortError"
|
|
105
|
+
? "timeout"
|
|
106
|
+
: "transport";
|
|
107
|
+
}
|
|
108
|
+
async function readBoundedBody(response, maxBytes) {
|
|
109
|
+
const declared = response.headers.get("content-length");
|
|
110
|
+
if (declared && Number(declared) > maxBytes) {
|
|
111
|
+
try {
|
|
112
|
+
await response.body?.cancel();
|
|
113
|
+
}
|
|
114
|
+
catch { }
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
code: "too_large",
|
|
118
|
+
message: `Artificial Analysis response exceeds the ${maxBytes} byte limit.`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (!response.body)
|
|
122
|
+
return "";
|
|
123
|
+
const reader = response.body.getReader();
|
|
124
|
+
const chunks = [];
|
|
125
|
+
let total = 0;
|
|
126
|
+
try {
|
|
127
|
+
for (;;) {
|
|
128
|
+
const { done, value } = await reader.read();
|
|
129
|
+
if (done)
|
|
130
|
+
break;
|
|
131
|
+
total += value.byteLength;
|
|
132
|
+
if (total > maxBytes) {
|
|
133
|
+
try {
|
|
134
|
+
await reader.cancel();
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
code: "too_large",
|
|
140
|
+
message: `Artificial Analysis response exceeds the ${maxBytes} byte limit.`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
chunks.push(value);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
reader.releaseLock();
|
|
148
|
+
}
|
|
149
|
+
let size = 0;
|
|
150
|
+
for (const chunk of chunks)
|
|
151
|
+
size += chunk.byteLength;
|
|
152
|
+
const merged = new Uint8Array(size);
|
|
153
|
+
let offset = 0;
|
|
154
|
+
for (const chunk of chunks) {
|
|
155
|
+
merged.set(chunk, offset);
|
|
156
|
+
offset += chunk.byteLength;
|
|
157
|
+
}
|
|
158
|
+
return new TextDecoder().decode(merged);
|
|
159
|
+
}
|
|
160
|
+
function httpFailure(status) {
|
|
161
|
+
let hint = `Artificial Analysis request failed with HTTP ${status}.`;
|
|
162
|
+
if (status === 401 || status === 403) {
|
|
163
|
+
hint += " Check that ARTIFICIAL_ANALYSIS_API_KEY is valid.";
|
|
164
|
+
}
|
|
165
|
+
else if (status === 429) {
|
|
166
|
+
hint += " Rate limit exceeded; retry later.";
|
|
167
|
+
}
|
|
168
|
+
return { ok: false, code: "http", message: hint, status };
|
|
169
|
+
}
|
|
170
|
+
function metricCoverage(snapshot) {
|
|
171
|
+
const coverage = {};
|
|
172
|
+
for (const metric of RECOGNIZED_METRICS) {
|
|
173
|
+
coverage[metric.key] = snapshot.models.filter((model) => typeof model.evaluations[metric.key] === "number").length;
|
|
174
|
+
}
|
|
175
|
+
return coverage;
|
|
176
|
+
}
|
|
177
|
+
// Best-effort read of the previous snapshot hash for unchanged detection.
|
|
178
|
+
// Any failure means "treat as changed", never an update failure.
|
|
179
|
+
async function previousContentHash(targetPath) {
|
|
180
|
+
try {
|
|
181
|
+
const raw = await readFile(targetPath, "utf8");
|
|
182
|
+
const parsed = JSON.parse(raw);
|
|
183
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
184
|
+
return null;
|
|
185
|
+
const hash = parsed.contentHash;
|
|
186
|
+
return typeof hash === "string" ? hash : null;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export async function updateBenchmarkSnapshot(targetPath, deps = {}) {
|
|
193
|
+
if (typeof targetPath !== "string" || !isAbsolute(targetPath)) {
|
|
194
|
+
return {
|
|
195
|
+
ok: false,
|
|
196
|
+
code: "invalid_path",
|
|
197
|
+
message: "Benchmark snapshot path must be an absolute path.",
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const env = deps.env ?? process.env;
|
|
201
|
+
const apiKey = (deps.apiKey ?? env[AA_API_KEY_ENV] ?? "").trim();
|
|
202
|
+
if (!apiKey) {
|
|
203
|
+
return {
|
|
204
|
+
ok: false,
|
|
205
|
+
code: "missing_key",
|
|
206
|
+
message: `Set ${AA_API_KEY_ENV} to refresh Artificial Analysis benchmarks.`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const timeoutMs = deps.timeoutMs ?? BENCHMARK_UPDATE_TIMEOUT_MS;
|
|
210
|
+
const maxBytes = deps.maxBytes ?? BENCHMARK_UPDATE_MAX_BYTES;
|
|
211
|
+
const now = deps.now ?? (() => new Date().toISOString());
|
|
212
|
+
const randomId = deps.randomId ?? defaultRandomId;
|
|
213
|
+
const currentPid = deps.currentPid ?? process.pid;
|
|
214
|
+
const processAlive = deps.processAlive ?? defaultProcessAlive;
|
|
215
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
216
|
+
const dir = dirname(targetPath);
|
|
217
|
+
try {
|
|
218
|
+
await mkdir(dir, { recursive: true });
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return {
|
|
222
|
+
ok: false,
|
|
223
|
+
code: "write_failed",
|
|
224
|
+
message: `Cannot create the benchmark directory for ${targetPath}.`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const lockPath = lockPathFor(targetPath);
|
|
228
|
+
const fetchedAt = now();
|
|
229
|
+
let locked = false;
|
|
230
|
+
try {
|
|
231
|
+
locked = await acquireLock(lockPath, currentPid, processAlive, fetchedAt);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
code: "write_failed",
|
|
237
|
+
message: `Cannot coordinate the benchmark update for ${targetPath}.`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (!locked) {
|
|
241
|
+
return {
|
|
242
|
+
ok: false,
|
|
243
|
+
code: "busy",
|
|
244
|
+
message: `Another benchmarks update is already running for ${targetPath}. ` +
|
|
245
|
+
`If no updater is running, remove ${lockPath} and retry.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const stagePath = join(dir, `${stagePrefixFor(targetPath)}${currentPid}.${randomId()}.tmp`);
|
|
249
|
+
try {
|
|
250
|
+
await reapStaleStaging(targetPath);
|
|
251
|
+
let response;
|
|
252
|
+
try {
|
|
253
|
+
response = await fetchImpl(AA_LLM_MODELS_ENDPOINT, {
|
|
254
|
+
headers: { "x-api-key": apiKey },
|
|
255
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
256
|
+
redirect: "error",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
const code = classifyFetchError(error);
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
code,
|
|
264
|
+
message: code === "timeout"
|
|
265
|
+
? `Artificial Analysis request timed out after ${timeoutMs} ms.`
|
|
266
|
+
: "Artificial Analysis request failed before a response arrived.",
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (!response.ok)
|
|
270
|
+
return httpFailure(response.status);
|
|
271
|
+
const text = await readBoundedBody(response, maxBytes);
|
|
272
|
+
if (typeof text !== "string")
|
|
273
|
+
return text;
|
|
274
|
+
let body;
|
|
275
|
+
try {
|
|
276
|
+
body = JSON.parse(text);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
code: "invalid_response",
|
|
282
|
+
message: "Artificial Analysis response was not valid JSON.",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const snapshot = normalizeAAResponse(body, fetchedAt);
|
|
286
|
+
if ("error" in snapshot) {
|
|
287
|
+
return { ok: false, code: "invalid_response", message: snapshot.error };
|
|
288
|
+
}
|
|
289
|
+
const unchanged = (await previousContentHash(targetPath)) === snapshot.contentHash;
|
|
290
|
+
const serialized = JSON.stringify(snapshot, null, 2);
|
|
291
|
+
try {
|
|
292
|
+
const handle = await open(stagePath, "w");
|
|
293
|
+
try {
|
|
294
|
+
await handle.writeFile(serialized, "utf8");
|
|
295
|
+
await handle.sync();
|
|
296
|
+
}
|
|
297
|
+
finally {
|
|
298
|
+
await handle.close();
|
|
299
|
+
}
|
|
300
|
+
await rename(stagePath, targetPath);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return {
|
|
304
|
+
ok: false,
|
|
305
|
+
code: "write_failed",
|
|
306
|
+
message: `Cannot publish the benchmark snapshot to ${targetPath}.`,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
ok: true,
|
|
311
|
+
path: targetPath,
|
|
312
|
+
fetchedAt: snapshot.fetchedAt,
|
|
313
|
+
modelCount: snapshot.models.length,
|
|
314
|
+
contentHash: snapshot.contentHash,
|
|
315
|
+
unchanged,
|
|
316
|
+
metricCoverage: metricCoverage(snapshot),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
try {
|
|
321
|
+
await rm(stagePath, { force: true });
|
|
322
|
+
}
|
|
323
|
+
catch { }
|
|
324
|
+
await releaseLock(lockPath);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=benchmarkUpdate.js.map
|