@axlsdk/eval 0.25.3 → 0.27.0

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/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  runEval,
15
15
  scorerCounts,
16
16
  serializeRequestRecords
17
- } from "./chunk-F4MJ26YK.js";
17
+ } from "./chunk-3XLPDV3O.js";
18
18
 
19
19
  // src/cli.ts
20
20
  import { readdirSync, statSync } from "fs";
package/dist/index.cjs CHANGED
@@ -42,11 +42,15 @@ __export(index_exports, {
42
42
  evaluateScorerTolerance: () => evaluateScorerTolerance,
43
43
  isBudgetStopped: () => isBudgetStopped,
44
44
  llmScorer: () => llmScorer,
45
+ loadModelCalls: () => loadModelCalls,
46
+ modelCallDataset: () => modelCallDataset,
45
47
  normalizeScorerResult: () => normalizeScorerResult,
46
48
  pairedBootstrapCI: () => pairedBootstrapCI,
47
49
  parseRequestRecords: () => parseRequestRecords,
48
50
  readAccounting: () => readAccounting,
51
+ recordedModelCall: () => recordedModelCall,
49
52
  refusedWork: () => refusedWork,
53
+ replayModelCall: () => replayModelCall,
50
54
  rescore: () => rescore,
51
55
  resolveCaptureLimits: () => resolveCaptureLimits,
52
56
  runEval: () => runEval,
@@ -150,6 +154,401 @@ function dataset(config) {
150
154
  };
151
155
  }
152
156
 
157
+ // src/model-calls.ts
158
+ var import_promises2 = require("fs/promises");
159
+ var import_zod = require("zod");
160
+ var import_axl = require("@axlsdk/axl");
161
+ function invalid(location, message) {
162
+ throw new import_axl.AxlError("INVALID_MODEL_CALL_SOURCE", `${location}: ${message}`);
163
+ }
164
+ function versionOf(value) {
165
+ return value !== null && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "v")?.value : void 0;
166
+ }
167
+ function parseJson(value, location) {
168
+ try {
169
+ return JSON.parse(value);
170
+ } catch {
171
+ return invalid(location, "invalid JSON");
172
+ }
173
+ }
174
+ function arrayLength(source, location) {
175
+ let array;
176
+ let descriptor;
177
+ try {
178
+ array = Array.isArray(source);
179
+ if (array) descriptor = Object.getOwnPropertyDescriptor(source, "length");
180
+ } catch {
181
+ return invalid(location, "array could not be inspected");
182
+ }
183
+ if (!array || !descriptor || !("value" in descriptor)) invalid(location, "expected an array");
184
+ const length = descriptor.value;
185
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
186
+ invalid(location, "invalid array length");
187
+ return length;
188
+ }
189
+ function arraySlot(source, index, location) {
190
+ let descriptor;
191
+ try {
192
+ descriptor = Object.getOwnPropertyDescriptor(source, String(index));
193
+ } catch {
194
+ return invalid(location, "array entry could not be inspected");
195
+ }
196
+ if (!descriptor || !("value" in descriptor))
197
+ invalid(location, "array entry is missing or is an accessor");
198
+ return descriptor.value;
199
+ }
200
+ function* locateArray(source, at) {
201
+ const length = arrayLength(source, "source");
202
+ for (let index = 0; index < length; index++) {
203
+ const location = at(index);
204
+ yield { value: arraySlot(source, index, location), position: index, location };
205
+ }
206
+ }
207
+ async function loadModelCalls(source) {
208
+ let located;
209
+ if (typeof source === "string") {
210
+ const content = (await (0, import_promises2.readFile)(source, "utf8")).replace(/^\uFEFF/, "");
211
+ if (content.trimStart().startsWith("[")) {
212
+ const parsed = parseJson(content, `file ${source}`);
213
+ if (!Array.isArray(parsed)) invalid(`file ${source}`, "expected a JSON array");
214
+ located = locateArray(parsed, (index) => `entry ${index + 1} of ${source}`);
215
+ } else {
216
+ const rows = content.split(/\r?\n/);
217
+ if (content.endsWith("\n")) rows.pop();
218
+ located = rows.flatMap((row, index) => {
219
+ const location = `line ${index + 1} of ${source}`;
220
+ if (row.trim() === "") return [];
221
+ return [{ value: parseJson(row, location), position: index, location }];
222
+ });
223
+ }
224
+ } else {
225
+ located = locateArray(source, (index) => `item ${index + 1}`);
226
+ }
227
+ const journal = [];
228
+ const folded = [];
229
+ const firstJournal = /* @__PURE__ */ new Map();
230
+ let format;
231
+ for (const entry of located) {
232
+ let version;
233
+ try {
234
+ version = versionOf(entry.value);
235
+ } catch {
236
+ invalid(entry.location, "record version could not be inspected");
237
+ }
238
+ if (version === 1 || version === 2) {
239
+ if (format !== void 0 && format !== version)
240
+ invalid(entry.location, "one source cannot mix v1 journal lines with v2 records");
241
+ format = version;
242
+ }
243
+ if (version === 1) {
244
+ const parsed = import_axl.capturedJournalLineSchema.safeParse(entry.value);
245
+ if (!parsed.success) invalid(entry.location, parsed.error.message);
246
+ journal.push({ ...entry, value: parsed.data, operationId: parsed.data.operationId });
247
+ if (!firstJournal.has(parsed.data.operationId))
248
+ firstJournal.set(parsed.data.operationId, entry);
249
+ } else if (version === 2) {
250
+ const parsed = import_axl.foldedOperationRecordSchema.safeParse(entry.value);
251
+ if (!parsed.success) invalid(entry.location, parsed.error.message);
252
+ folded.push({ record: parsed.data, location: entry.location, position: entry.position });
253
+ } else {
254
+ invalid(entry.location, "unsupported capture version");
255
+ }
256
+ }
257
+ if (journal.length) {
258
+ let records;
259
+ try {
260
+ records = (0, import_axl.foldJournal)(journal.map((entry) => entry.value));
261
+ } catch (error) {
262
+ const message = error instanceof Error ? error.message : String(error);
263
+ const line = /\bat line (\d+)\b/.exec(message) ?? /^Invalid capture journal line (\d+):/.exec(message);
264
+ const indexed = line ? journal[Number(line[1]) - 1] : void 0;
265
+ const matched = indexed ?? [...journal].reverse().find((entry) => {
266
+ const marker = `capture operation ${entry.operationId}`;
267
+ const at = message.indexOf(marker);
268
+ if (at < 0) return false;
269
+ const suffix = message.slice(at + marker.length);
270
+ return suffix === "" || suffix.startsWith(":") || suffix.startsWith(" at line");
271
+ });
272
+ invalid(matched?.location ?? journal[0].location, message);
273
+ }
274
+ for (const record of records) {
275
+ const first = firstJournal.get(record.id);
276
+ folded.push({ record, location: first.location, position: first.position });
277
+ }
278
+ }
279
+ folded.sort((a, b) => a.position - b.position);
280
+ const seen = /* @__PURE__ */ new Map();
281
+ for (const item of folded) {
282
+ const previous = seen.get(item.record.id);
283
+ if (previous)
284
+ invalid(item.location, `duplicate operation id ${item.record.id} (first at ${previous})`);
285
+ seen.set(item.record.id, item.location);
286
+ }
287
+ return folded.map((item) => item.record);
288
+ }
289
+ var modelCallInputSchema = import_zod.z.object({ recorded: import_axl.modelCallRecordSchema }).transform(
290
+ ({ recorded }) => ({
291
+ ...recorded.request ? { request: recorded.request } : {},
292
+ ids: recorded.ids,
293
+ attributes: recorded.attributes,
294
+ recorded
295
+ })
296
+ );
297
+ function modelCallDataset(records, options) {
298
+ const items = [];
299
+ const length = arrayLength(records, "records");
300
+ for (let index = 0; index < length; index++) {
301
+ const parsed = import_axl.foldedOperationRecordSchema.safeParse(
302
+ arraySlot(records, index, `record ${index + 1}`)
303
+ );
304
+ if (!parsed.success) invalid(`record ${index + 1}`, parsed.error.message);
305
+ const record = parsed.data;
306
+ if (options?.filter && !options.filter(record)) continue;
307
+ if (record.kind === "external")
308
+ throw new import_axl.AxlError(
309
+ "MODEL_CALL_EXPECTED",
310
+ `record ${index + 1} (${record.id}) is external; supply a filter to exclude it`
311
+ );
312
+ const input = {
313
+ ...record.request ? { request: record.request } : {},
314
+ ids: record.ids,
315
+ attributes: record.attributes,
316
+ recorded: record
317
+ };
318
+ items.push({
319
+ input,
320
+ ...options?.annotate ? { annotations: options.annotate(record, index) } : {}
321
+ });
322
+ }
323
+ return dataset({
324
+ name: "model-calls",
325
+ schema: modelCallInputSchema,
326
+ annotations: import_zod.z.custom(),
327
+ items
328
+ });
329
+ }
330
+ function readInput(input) {
331
+ const candidate = input !== null && typeof input === "object" ? Object.getOwnPropertyDescriptor(input, "recorded")?.value : void 0;
332
+ if (candidate !== null && typeof candidate === "object" && Object.getOwnPropertyDescriptor(candidate, "kind")?.value === "external")
333
+ throw new import_axl.AxlError(
334
+ "MODEL_CALL_EXPECTED",
335
+ "external operations cannot be scored or replayed as model calls"
336
+ );
337
+ const parsed = import_axl.modelCallRecordSchema.safeParse(candidate);
338
+ if (!parsed.success)
339
+ throw new import_axl.AxlError(
340
+ "INVALID_MODEL_CALL_INPUT",
341
+ `expected a validated model-call input: ${parsed.error.message}`
342
+ );
343
+ return parsed.data;
344
+ }
345
+ function modelCallOutput(response, record) {
346
+ const output = {
347
+ content: response.content,
348
+ ...response.thinking ? { thinking: response.thinking } : {},
349
+ ...response.tool_calls?.length ? { tool_calls: response.tool_calls } : {},
350
+ ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
351
+ ...response.rawFinishReason !== void 0 ? { rawFinishReason: response.rawFinishReason } : {},
352
+ ...response.responseId !== void 0 ? { responseId: response.responseId } : {},
353
+ ...response.responseModel !== void 0 ? { responseModel: response.responseModel } : {}
354
+ };
355
+ const format = record.request?.responseFormat?.type;
356
+ if (format !== "json_object" && format !== "json_schema") return output;
357
+ if (output.content === "" && output.tool_calls) return output;
358
+ try {
359
+ return { ...output, json: JSON.parse(output.content) };
360
+ } catch {
361
+ throw new import_axl.AxlError(
362
+ "INVALID_MODEL_CALL_JSON",
363
+ `model call ${record.id} did not return valid JSON`
364
+ );
365
+ }
366
+ }
367
+ function providerOutput(response, record) {
368
+ return modelCallOutput(
369
+ {
370
+ content: response.content,
371
+ ...response.thinking_content !== void 0 ? { thinking: response.thinking_content } : {},
372
+ ...response.tool_calls !== void 0 ? { tool_calls: response.tool_calls } : {},
373
+ ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
374
+ ...response.rawFinishReason !== void 0 ? { rawFinishReason: response.rawFinishReason } : {},
375
+ ...response.responseId !== void 0 ? { responseId: response.responseId } : {},
376
+ ...response.responseModel !== void 0 ? { responseModel: response.responseModel } : {}
377
+ },
378
+ record
379
+ );
380
+ }
381
+ function recordedModelCall() {
382
+ return async (input) => {
383
+ const record = readInput(input);
384
+ if (record.captured.redacted)
385
+ throw new import_axl.AxlError("RECORDED_OUTPUT_UNAVAILABLE", `model call ${record.id} was redacted`);
386
+ if (record.outcome.status !== "ok" || !("response" in record.outcome))
387
+ throw new import_axl.AxlError(
388
+ "RECORDED_OUTPUT_UNAVAILABLE",
389
+ `model call ${record.id} has no complete successful output (${record.outcome.status})`
390
+ );
391
+ return { output: modelCallOutput(record.outcome.response, record) };
392
+ };
393
+ }
394
+ function replayRequest(record) {
395
+ const request = record.request;
396
+ if (!request || !record.captured.replayable || record.captured.continuityCaptured !== true || record.captured.redacted || record.captured.truncated || record.captured.omitted.length > 0 || (request.providerOptionKeys?.length ?? 0) > 0)
397
+ throw new import_axl.AxlError(
398
+ "MODEL_CALL_NOT_REPLAYABLE",
399
+ `model call ${record.id} does not contain an exact replayable request`
400
+ );
401
+ return request;
402
+ }
403
+ function chatOptions(request, model) {
404
+ if (!request.options.model)
405
+ throw new import_axl.AxlError("INVALID_MODEL_CALL_INPUT", "captured request model is empty");
406
+ const effort = request.options.effort;
407
+ if (effort !== void 0 && !["none", "low", "medium", "high", "xhigh", "max"].includes(effort))
408
+ throw new import_axl.AxlError("INVALID_MODEL_CALL_INPUT", `unsupported captured effort ${effort}`);
409
+ const toolChoice = request.options.toolChoice;
410
+ if (toolChoice !== void 0 && toolChoice !== "auto" && toolChoice !== "none" && toolChoice !== "required" && !(toolChoice !== null && typeof toolChoice === "object" && !Array.isArray(toolChoice) && toolChoice.type === "function" && Object.keys(toolChoice).every((key) => key === "type" || key === "function") && toolChoice.function !== null && typeof toolChoice.function === "object" && !Array.isArray(toolChoice.function) && typeof toolChoice.function.name === "string" && Object.keys(toolChoice.function).every((key) => key === "name")))
411
+ throw new import_axl.AxlError("INVALID_MODEL_CALL_INPUT", "unsupported captured tool choice");
412
+ return {
413
+ model,
414
+ ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {},
415
+ ...request.options.maxTokens !== void 0 ? { maxTokens: request.options.maxTokens } : {},
416
+ ...request.options.thinkingBudget !== void 0 ? { thinkingBudget: request.options.thinkingBudget } : {},
417
+ ...request.options.includeThoughts !== void 0 ? { includeThoughts: request.options.includeThoughts } : {},
418
+ ...request.options.stop !== void 0 ? { stop: [...request.options.stop] } : {},
419
+ ...request.options.promptCache !== void 0 ? { promptCache: request.options.promptCache } : {},
420
+ ...effort !== void 0 ? { effort } : {},
421
+ ...toolChoice !== void 0 ? { toolChoice } : {},
422
+ ...request.tools ? {
423
+ tools: request.tools.map((tool) => ({
424
+ type: "function",
425
+ function: {
426
+ name: tool.name,
427
+ description: tool.description ?? "",
428
+ parameters: tool.parameters,
429
+ ...tool.strict !== void 0 ? { strict: tool.strict } : {}
430
+ }
431
+ }))
432
+ } : {},
433
+ ...request.responseFormat ? { responseFormat: request.responseFormat } : {}
434
+ };
435
+ }
436
+ function chatMessages(request) {
437
+ return request.messages.map((message) => {
438
+ if (!["system", "user", "assistant", "tool"].includes(message.role))
439
+ throw new import_axl.AxlError("MODEL_CALL_NOT_REPLAYABLE", "captured message cannot be reconstructed");
440
+ let content;
441
+ if (message.input) {
442
+ const text = message.content ?? "";
443
+ let offset = 0;
444
+ const parts = [];
445
+ for (const part of message.input.parts) {
446
+ if (part.type !== "text" || offset + part.characters > text.length)
447
+ throw new import_axl.AxlError(
448
+ "MODEL_CALL_NOT_REPLAYABLE",
449
+ "captured content parts cannot be reconstructed"
450
+ );
451
+ parts.push({ type: "text", text: text.slice(offset, offset + part.characters) });
452
+ offset += part.characters;
453
+ }
454
+ if (offset !== text.length)
455
+ throw new import_axl.AxlError(
456
+ "MODEL_CALL_NOT_REPLAYABLE",
457
+ "captured text lengths do not match content"
458
+ );
459
+ content = parts;
460
+ } else if (message.content !== null) {
461
+ content = message.content;
462
+ } else {
463
+ throw new import_axl.AxlError("MODEL_CALL_NOT_REPLAYABLE", "captured message content is absent");
464
+ }
465
+ return {
466
+ role: message.role,
467
+ content,
468
+ ...message.name !== void 0 ? { name: message.name } : {},
469
+ ...message.agent !== void 0 ? { agent: message.agent } : {},
470
+ ...message.origin !== void 0 ? { origin: message.origin } : {},
471
+ ...message.tool_calls !== void 0 ? { tool_calls: message.tool_calls } : {},
472
+ ...message.tool_call_id !== void 0 ? { tool_call_id: message.tool_call_id } : {},
473
+ ...message.providerMetadata !== void 0 ? { providerMetadata: message.providerMetadata } : {}
474
+ };
475
+ });
476
+ }
477
+ function replayModelCall(options = {}) {
478
+ return async (input, runtime) => {
479
+ const record = readInput(input);
480
+ const request = replayRequest(record);
481
+ const messages = chatMessages(request);
482
+ const capturedOptions = chatOptions(request, request.options.model);
483
+ if (!runtime)
484
+ throw new import_axl.AxlError("MODEL_CALL_RUNTIME_REQUIRED", "replay requires an AxlRuntime");
485
+ const uri = options.model ?? `${record.provider}:${request.options.model}`;
486
+ if (!uri.includes(":"))
487
+ throw new import_axl.AxlError("MODEL_CALL_MODEL_UNRESOLVED", "model override must use provider:model");
488
+ let resolved;
489
+ try {
490
+ resolved = runtime.resolveProvider(uri);
491
+ } catch {
492
+ throw new import_axl.AxlError(
493
+ "MODEL_CALL_MODEL_UNRESOLVED",
494
+ `cannot resolve ${uri}; supply an explicit provider:model override`
495
+ );
496
+ }
497
+ if (!options.model && resolved.provider.name !== record.provider)
498
+ throw new import_axl.AxlError(
499
+ "MODEL_CALL_MODEL_UNRESOLVED",
500
+ `resolved ${uri} to a different provider; supply an explicit override`
501
+ );
502
+ const callOptions = {
503
+ ...capturedOptions,
504
+ model: options.model ? resolved.model : request.options.model
505
+ };
506
+ if (record.kind === "chat") {
507
+ const response = await resolved.provider.chat(messages, callOptions);
508
+ return { output: providerOutput(response, record) };
509
+ }
510
+ let content = "";
511
+ let thinking = "";
512
+ const calls = /* @__PURE__ */ new Map();
513
+ let done;
514
+ for await (const chunk of resolved.provider.stream(messages, callOptions)) {
515
+ if (chunk.type === "text_delta") content += chunk.content;
516
+ else if (chunk.type === "thinking_delta") thinking += chunk.content;
517
+ else if (chunk.type === "tool_call_delta") {
518
+ const call = calls.get(chunk.id) ?? { id: chunk.id, name: "", arguments: "" };
519
+ if (chunk.name) call.name = chunk.name;
520
+ if (chunk.arguments) call.arguments += chunk.arguments;
521
+ calls.set(chunk.id, call);
522
+ } else if (chunk.type === "done") {
523
+ done = chunk;
524
+ break;
525
+ }
526
+ }
527
+ if (!done)
528
+ throw new import_axl.AxlError("MODEL_CALL_STREAM_INCOMPLETE", "replayed stream ended without done");
529
+ return {
530
+ output: providerOutput(
531
+ {
532
+ content,
533
+ ...thinking ? { thinking_content: thinking } : {},
534
+ ...calls.size ? {
535
+ tool_calls: [...calls.values()].map((call) => ({
536
+ id: call.id,
537
+ type: "function",
538
+ function: { name: call.name, arguments: call.arguments }
539
+ }))
540
+ } : {},
541
+ ...done.finishReason !== void 0 ? { finishReason: done.finishReason } : {},
542
+ ...done.rawFinishReason !== void 0 ? { rawFinishReason: done.rawFinishReason } : {},
543
+ ...done.responseId !== void 0 ? { responseId: done.responseId } : {},
544
+ ...done.responseModel !== void 0 ? { responseModel: done.responseModel } : {}
545
+ },
546
+ record
547
+ )
548
+ };
549
+ };
550
+ }
551
+
153
552
  // src/scorer.ts
154
553
  function normalizeScorerResult(result) {
155
554
  return typeof result === "number" ? { score: result } : result;
@@ -174,16 +573,16 @@ function scorer(config) {
174
573
  }
175
574
 
176
575
  // src/llm-scorer.ts
177
- var import_zod = require("zod");
178
- var import_axl = require("@axlsdk/axl");
576
+ var import_zod2 = require("zod");
577
+ var import_axl2 = require("@axlsdk/axl");
179
578
  function isZodIssueLike(value) {
180
579
  if (!value || typeof value !== "object") return false;
181
580
  const issue = value;
182
581
  return Array.isArray(issue.path) && issue.path.every((segment) => typeof segment === "string" || typeof segment === "number") && typeof issue.message === "string";
183
582
  }
184
583
  function llmScorer(config) {
185
- const schema = config.schema ?? import_zod.z.object({ score: import_zod.z.number().min(0).max(1), reasoning: import_zod.z.string() });
186
- const schemaJson = JSON.stringify((0, import_axl.zodToJsonSchema)(schema), null, 2);
584
+ const schema = config.schema ?? import_zod2.z.object({ score: import_zod2.z.number().min(0).max(1), reasoning: import_zod2.z.string() });
585
+ const schemaJson = JSON.stringify((0, import_axl2.zodToJsonSchema)(schema), null, 2);
187
586
  return {
188
587
  name: config.name,
189
588
  description: config.description,
@@ -232,7 +631,7 @@ ${JSON.stringify(annotations, null, 2)}` : "",
232
631
  const responseCost = response.cost;
233
632
  let validated;
234
633
  try {
235
- const parsed = JSON.parse((0, import_axl.extractJson)(response.content));
634
+ const parsed = JSON.parse((0, import_axl2.extractJson)(response.content));
236
635
  validated = schema.parse(parsed);
237
636
  } catch (err) {
238
637
  attachScorerErrorCost(err, responseCost);
@@ -263,7 +662,7 @@ function defineEval(config) {
263
662
  }
264
663
 
265
664
  // src/runner.ts
266
- var import_axl3 = require("@axlsdk/axl");
665
+ var import_axl4 = require("@axlsdk/axl");
267
666
 
268
667
  // src/utils.ts
269
668
  function computeStats(scores) {
@@ -333,7 +732,7 @@ async function mapWithConcurrency(items, concurrency, task) {
333
732
  }
334
733
 
335
734
  // src/accounting.ts
336
- var import_axl2 = require("@axlsdk/axl");
735
+ var import_axl3 = require("@axlsdk/axl");
337
736
  var USAGE_KEYS = [
338
737
  "inputTokens",
339
738
  "outputTokens",
@@ -395,14 +794,14 @@ async function trackScope(runtime, fn, options) {
395
794
  function parseBudget(budget) {
396
795
  const match = typeof budget === "string" ? budget.match(/^\$?(\d+(?:\.\d+)?)$/) : null;
397
796
  if (!match) {
398
- throw new import_axl2.AxlError(
797
+ throw new import_axl3.AxlError(
399
798
  "INVALID_BUDGET",
400
799
  `Invalid budget "${String(budget)}": expected a non-negative USD amount like "$1" or "0.50".`
401
800
  );
402
801
  }
403
802
  const limit = Number(match[1]);
404
803
  if (!Number.isFinite(limit)) {
405
- throw new import_axl2.AxlError("INVALID_BUDGET", `Invalid budget "${budget}": not a finite amount.`);
804
+ throw new import_axl3.AxlError("INVALID_BUDGET", `Invalid budget "${budget}": not a finite amount.`);
406
805
  }
407
806
  return limit;
408
807
  }
@@ -410,7 +809,7 @@ function usableCost(value) {
410
809
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
411
810
  }
412
811
  function isAdmissionDenied(err) {
413
- return (0, import_axl2.isAdmissionDeniedError)(err);
812
+ return (0, import_axl3.isAdmissionDeniedError)(err);
414
813
  }
415
814
  function readAccounting(result) {
416
815
  if (result?.accounting) return result.accounting;
@@ -750,7 +1149,7 @@ async function stageCapture(runtime, ownerId, option) {
750
1149
  const staged = await runtime.stageDiagnosticArtifact({ kind: "eval", id: ownerId });
751
1150
  return {
752
1151
  artifactId: staged.artifactId,
753
- channel: new import_axl3.RequestCaptureChannel({
1152
+ channel: new import_axl4.RequestCaptureChannel({
754
1153
  sink: staged.sink,
755
1154
  ...limits,
756
1155
  // Redaction is the RUNTIME's policy, not the caller's: a run cannot opt
@@ -818,7 +1217,7 @@ function attachOperationRefs(items, channel) {
818
1217
  }
819
1218
  var MAX_CAUSE_DEPTH = 8;
820
1219
  function isProviderErrorLike(value) {
821
- if (value instanceof import_axl3.ProviderError) return true;
1220
+ if (value instanceof import_axl4.ProviderError) return true;
822
1221
  if (typeof value !== "object" || value === null) return false;
823
1222
  const candidate = value;
824
1223
  return candidate.code === "PROVIDER_ERROR" && candidate.name === "ProviderError";
@@ -826,7 +1225,7 @@ function isProviderErrorLike(value) {
826
1225
  function isTimeoutErrorLike(value) {
827
1226
  if (typeof value !== "object" || value === null) return false;
828
1227
  const candidate = value;
829
- return candidate.name === "TimeoutError" && (value instanceof import_axl3.TimeoutError || candidate.code === "TIMEOUT");
1228
+ return candidate.name === "TimeoutError" && (value instanceof import_axl4.TimeoutError || candidate.code === "TIMEOUT");
830
1229
  }
831
1230
  function describeTimeoutFailure(error) {
832
1231
  const breakdown = error.breakdown;
@@ -872,12 +1271,12 @@ async function runEval(config, executeWorkflow, runtime, options) {
872
1271
  const id = (0, import_node_crypto.randomUUID)();
873
1272
  const itemErrorRateLimit = config.failOnItemErrorRate ?? DEFAULT_ITEM_ERROR_RATE_LIMIT;
874
1273
  if (!isErrorRateLimit(itemErrorRateLimit)) {
875
- throw new import_axl3.AxlError(
1274
+ throw new import_axl4.AxlError(
876
1275
  "INVALID_ITEM_ERROR_RATE",
877
1276
  `Invalid failOnItemErrorRate (${String(itemErrorRateLimit)}): expected a number in [0, 1]; 1 disables the gate.`
878
1277
  );
879
1278
  }
880
- const admission = config.budget != null ? new import_axl3.AdmissionController({ limit: parseBudget(config.budget) }) : void 0;
1279
+ const admission = config.budget != null ? new import_axl4.AdmissionController({ limit: parseBudget(config.budget) }) : void 0;
881
1280
  const capture = await stageCapture(runtime, id, options?.captureRequests);
882
1281
  let items;
883
1282
  try {
@@ -1672,7 +2071,7 @@ function evaluateItemErrorRateGate(baseline, candidate, maxItemErrorRate = DEFAU
1672
2071
  }
1673
2072
 
1674
2073
  // src/rescore.ts
1675
- var import_axl4 = require("@axlsdk/axl");
2074
+ var import_axl5 = require("@axlsdk/axl");
1676
2075
  var import_node_crypto2 = require("crypto");
1677
2076
  var COPY_SHARE_OF_RUN_BOUND = 0.75;
1678
2077
  function describeFailure(error) {
@@ -1686,7 +2085,7 @@ async function beginCapture(source, ownerId, runtime, options) {
1686
2085
  if (!limits) return {};
1687
2086
  const open = (artifactId, sink, carriedBytes) => ({
1688
2087
  artifactId,
1689
- channel: new import_axl4.RequestCaptureChannel({
2088
+ channel: new import_axl5.RequestCaptureChannel({
1690
2089
  sink,
1691
2090
  ...limits,
1692
2091
  carriedBytes,
@@ -1769,7 +2168,7 @@ async function rescore(result, scorers, runtime, options) {
1769
2168
  const rescoredId = (0, import_node_crypto2.randomUUID)();
1770
2169
  const concurrency = options?.concurrency ?? 5;
1771
2170
  const scorerConcurrency = options?.scorerConcurrency ?? 5;
1772
- const admission = options?.budget != null ? new import_axl4.AdmissionController({ limit: parseBudget(options.budget) }) : void 0;
2171
+ const admission = options?.budget != null ? new import_axl5.AdmissionController({ limit: parseBudget(options.budget) }) : void 0;
1773
2172
  let closedBy;
1774
2173
  const noteClosure = (source) => {
1775
2174
  if (closedBy === void 0 && admission?.closed) closedBy = source;
@@ -2045,11 +2444,15 @@ function aggregateRuns(runs) {
2045
2444
  evaluateScorerTolerance,
2046
2445
  isBudgetStopped,
2047
2446
  llmScorer,
2447
+ loadModelCalls,
2448
+ modelCallDataset,
2048
2449
  normalizeScorerResult,
2049
2450
  pairedBootstrapCI,
2050
2451
  parseRequestRecords,
2051
2452
  readAccounting,
2453
+ recordedModelCall,
2052
2454
  refusedWork,
2455
+ replayModelCall,
2053
2456
  rescore,
2054
2457
  resolveCaptureLimits,
2055
2458
  runEval,