@axlsdk/studio 0.23.2 → 0.24.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.
@@ -138,42 +138,177 @@ function sanitizeRichInputFailure(event) {
138
138
  }
139
139
  return event;
140
140
  }
141
+ var MEASURED_METADATA_KEYS = /* @__PURE__ */ new Set([
142
+ "models",
143
+ "modelCallCounts",
144
+ "workflows",
145
+ "workflowCallCounts",
146
+ "tokens",
147
+ "agentCalls"
148
+ ]);
149
+ function redactItemMetadata(metadata) {
150
+ if (!metadata) return metadata;
151
+ const out = {};
152
+ for (const [key, value] of Object.entries(metadata)) {
153
+ out[key] = MEASURED_METADATA_KEYS.has(key) ? value : REDACTED;
154
+ }
155
+ return out;
156
+ }
157
+ function projectItemFailure(failure) {
158
+ if (!failure || typeof failure !== "object") return void 0;
159
+ const f = failure;
160
+ if (typeof f.name !== "string") return void 0;
161
+ return {
162
+ name: f.name,
163
+ ...typeof f.provider === "string" ? { provider: f.provider } : {},
164
+ ...typeof f.status === "number" && Number.isFinite(f.status) ? { status: f.status } : {},
165
+ ...typeof f.retryable === "boolean" ? { retryable: f.retryable } : {},
166
+ ...typeof f.requestId === "string" ? { requestId: f.requestId } : {}
167
+ };
168
+ }
169
+ function isPlainObject(value) {
170
+ return typeof value === "object" && value !== null && !Array.isArray(value);
171
+ }
172
+ function redactScorerDetail(detail) {
173
+ if (!isPlainObject(detail)) return REDACTED;
174
+ const d = detail;
175
+ return {
176
+ score: d.score,
177
+ ...d.duration !== void 0 ? { duration: d.duration } : {},
178
+ ...d.cost !== void 0 ? { cost: d.cost } : {},
179
+ // `skipped` is a structural boolean (the `applies` predicate verdict),
180
+ // not user/LLM content — preserve it so the client's N/A chip renders.
181
+ ...d.skipped !== void 0 ? { skipped: d.skipped } : {},
182
+ // `outcome` and `accounting` are structural (a classification and a set
183
+ // of counts), so they survive redaction the way `skipped` does — without
184
+ // them a compliance-mode reader cannot tell a judge that was stopped on
185
+ // budget from one that scored 0.
186
+ ...d.outcome !== void 0 ? { outcome: d.outcome } : {},
187
+ ...d.accounting !== void 0 ? { accounting: d.accounting } : {},
188
+ // `diagnostics` survives for the same reason `accounting` does: it is a
189
+ // list of operation ids and statuses, not content.
190
+ ...d.diagnostics !== void 0 ? { diagnostics: d.diagnostics } : {}
191
+ // metadata deliberately omitted — may contain LLM scorer reasoning
192
+ };
193
+ }
141
194
  function redactEvalItem(item) {
195
+ if (!isPlainObject(item)) return REDACTED;
196
+ const { failure, ...rest } = item;
197
+ const typed = item;
198
+ const projectedFailure = failure !== void 0 ? projectItemFailure(failure) : void 0;
142
199
  const scrubbed = {
143
- ...item,
200
+ ...rest,
201
+ ...projectedFailure ? { failure: projectedFailure } : {},
144
202
  input: REDACTED,
145
203
  output: REDACTED,
146
- ...item.annotations !== void 0 ? { annotations: REDACTED } : {},
147
- ...item.error !== void 0 ? { error: REDACTED } : {},
148
- ...item.scorerErrors !== void 0 ? { scorerErrors: item.scorerErrors.map(() => REDACTED) } : {}
149
- };
150
- if (item.scoreDetails) {
151
- const detailsOut = {};
152
- for (const [name, detail] of Object.entries(item.scoreDetails)) {
153
- detailsOut[name] = {
154
- score: detail.score,
155
- ...detail.duration !== void 0 ? { duration: detail.duration } : {},
156
- ...detail.cost !== void 0 ? { cost: detail.cost } : {},
157
- // `skipped` is a structural boolean (the `applies` predicate verdict),
158
- // not user/LLM content — preserve it so the client's N/A chip renders.
159
- ...detail.skipped !== void 0 ? { skipped: detail.skipped } : {}
160
- // metadata deliberately omitted — may contain LLM scorer reasoning
161
- };
204
+ // `diagnostics` is spread through untouched on purpose: it holds operation
205
+ // IDs, kinds, turn/attempt indexes and a status — pointers into an artifact,
206
+ // never content. The records themselves are redacted by the core rule at
207
+ // both write time and delivery time (`redactRecordLine`).
208
+ ...typed.metadata !== void 0 ? {
209
+ metadata: isPlainObject(typed.metadata) ? redactItemMetadata(typed.metadata) : REDACTED
210
+ } : {},
211
+ ...typed.annotations !== void 0 ? { annotations: REDACTED } : {},
212
+ ...typed.error !== void 0 ? { error: REDACTED } : {},
213
+ ...typed.scorerErrors !== void 0 ? {
214
+ scorerErrors: Array.isArray(typed.scorerErrors) ? typed.scorerErrors.map(() => REDACTED) : REDACTED
215
+ } : {},
216
+ // `callerReport.metadata` is whatever the workflow callback returned — free-
217
+ // form user content, exactly like `output`, so it is dropped rather than
218
+ // masked (same treatment as scorer metadata below). Its sibling `cost` is a
219
+ // plain number and stays. (`outcome` and `accounting` are spread through
220
+ // untouched: both are structural counts and classifications, no content.)
221
+ ...typed.callerReport !== void 0 ? {
222
+ callerReport: isPlainObject(typed.callerReport) ? typeof typed.callerReport.cost === "number" ? { cost: typed.callerReport.cost } : {} : REDACTED
223
+ } : {}
224
+ };
225
+ if (typed.scoreDetails !== void 0) {
226
+ if (isPlainObject(typed.scoreDetails)) {
227
+ const detailsOut = {};
228
+ for (const [name, detail] of Object.entries(typed.scoreDetails)) {
229
+ detailsOut[name] = redactScorerDetail(detail);
230
+ }
231
+ scrubbed.scoreDetails = detailsOut;
232
+ } else {
233
+ scrubbed.scoreDetails = REDACTED;
162
234
  }
163
- scrubbed.scoreDetails = detailsOut;
164
235
  }
165
236
  return scrubbed;
166
237
  }
167
238
  function redactEvalResult(result, redact) {
168
239
  if (!redact) return result;
169
- const meta3 = result.metadata;
170
- const scrubbedMetadata = meta3 && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : result.metadata;
240
+ return redactResultShape(result);
241
+ }
242
+ function redactResultShape(result) {
243
+ const scrubbedMetadata = redactResultMetadata(result.metadata);
244
+ const hasMultiRun = "_multiRun" in result;
245
+ const multiRun = result._multiRun;
171
246
  return {
172
247
  ...result,
173
248
  metadata: scrubbedMetadata,
174
- items: result.items.map(redactEvalItem)
249
+ items: Array.isArray(result.items) ? result.items.map(redactEvalItem) : REDACTED,
250
+ ...hasMultiRun && multiRun !== void 0 ? {
251
+ _multiRun: isPlainObject(multiRun) ? redactMultiRun(multiRun) : REDACTED
252
+ } : {}
253
+ };
254
+ }
255
+ function redactResultMetadata(metadata) {
256
+ const meta3 = metadata;
257
+ return isPlainObject(meta3) && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : metadata;
258
+ }
259
+ function redactMultiRun(multiRun) {
260
+ const { allRuns } = multiRun;
261
+ return {
262
+ ...multiRun,
263
+ ...allRuns !== void 0 ? {
264
+ allRuns: Array.isArray(allRuns) ? allRuns.map(
265
+ (run) => isPlainObject(run) && Array.isArray(run.items) ? redactResultShape(run) : REDACTED
266
+ ) : REDACTED
267
+ } : {},
268
+ ...typeof multiRun.batchFailure === "string" ? { batchFailure: REDACTED } : {}
269
+ };
270
+ }
271
+ function redactEvalComparison(comparison, redact) {
272
+ if (!redact) return comparison;
273
+ const maskInput = (r) => ({ ...r, input: REDACTED });
274
+ const side = (s) => ({
275
+ ...s,
276
+ metadata: redactResultMetadata(s.metadata)
277
+ });
278
+ return {
279
+ ...comparison,
280
+ baseline: side(comparison.baseline),
281
+ candidate: side(comparison.candidate),
282
+ regressions: comparison.regressions.map(maskInput),
283
+ improvements: comparison.improvements.map(maskInput)
175
284
  };
176
285
  }
286
+ function redactRecordLine(line, redact) {
287
+ if (!redact) return line;
288
+ let parsed;
289
+ try {
290
+ parsed = JSON.parse(line);
291
+ } catch {
292
+ const stub = {
293
+ v: 1,
294
+ phase: "end",
295
+ operationId: "unknown",
296
+ kind: "chat",
297
+ transportAttempts: 1,
298
+ provider: "unknown",
299
+ model: "unknown",
300
+ termination: "the stored record could not be parsed",
301
+ captured: {
302
+ fidelity: "runtime_request",
303
+ redacted: true,
304
+ truncated: true,
305
+ omitted: ["record"]
306
+ }
307
+ };
308
+ return JSON.stringify(stub);
309
+ }
310
+ return JSON.stringify((0, import_axl.redactCapturedRequest)(parsed));
311
+ }
177
312
  function redactEvalHistoryEntry(entry, redact) {
178
313
  if (!redact) return entry;
179
314
  return {
@@ -950,7 +1085,50 @@ function reduceCost(acc, event) {
950
1085
  };
951
1086
  }
952
1087
  function emptyEvalTrendData() {
953
- return { byEval: {}, totalRuns: 0, totalCost: 0 };
1088
+ return { byEval: {}, totalRuns: 0, totalCost: 0, totalCostCompleteness: "complete" };
1089
+ }
1090
+ function worse(a, b) {
1091
+ if (a === "unverified" || b === "unverified") return "unverified";
1092
+ if (a === "incomplete" || b === "incomplete") return "incomplete";
1093
+ return "complete";
1094
+ }
1095
+ function extractAccounting(data) {
1096
+ const result = data ?? {};
1097
+ const accounting = result.accounting;
1098
+ if (!accounting || typeof accounting !== "object") {
1099
+ return { cost: usable(extractCost(data)), completeness: "unverified", budgetStopped: false };
1100
+ }
1101
+ const completeness = accounting.completeness === "complete" || accounting.completeness === "incomplete" ? accounting.completeness : "unverified";
1102
+ const knownCost = usable(accounting.knownCost);
1103
+ const summary = result.summary;
1104
+ return {
1105
+ cost: knownCost,
1106
+ completeness,
1107
+ budgetStopped: isBudgetStopped(accounting.budget?.status, summary?.coverage)
1108
+ };
1109
+ }
1110
+ function usable(value) {
1111
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
1112
+ }
1113
+ function refusedWork(coverage) {
1114
+ if (!coverage || typeof coverage !== "object") return 0;
1115
+ const { items, scorers } = coverage;
1116
+ let refused = count(items, "budget_skipped") + count(items, "budget_interrupted");
1117
+ if (scorers && typeof scorers === "object") {
1118
+ for (const s of Object.values(scorers)) {
1119
+ refused += count(s, "budget_skipped") + count(s, "budget_interrupted");
1120
+ }
1121
+ }
1122
+ return refused;
1123
+ }
1124
+ function count(block, key) {
1125
+ if (!block || typeof block !== "object") return 0;
1126
+ const value = block[key];
1127
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1128
+ }
1129
+ function isBudgetStopped(status, coverage) {
1130
+ if (status !== "closed") return false;
1131
+ return refusedWork(coverage) > 0;
954
1132
  }
955
1133
  function extractScores(data) {
956
1134
  if (!data || typeof data !== "object") return {};
@@ -1017,7 +1195,7 @@ function computeScoreStats(runs) {
1017
1195
  }
1018
1196
  function reduceEvalTrends(acc, entry) {
1019
1197
  const scores = extractScores(entry.data);
1020
- const cost = extractCost(entry.data);
1198
+ const { cost, completeness, budgetStopped } = extractAccounting(entry.data);
1021
1199
  const model = extractModel(entry.data);
1022
1200
  const duration3 = extractDuration(entry.data);
1023
1201
  const metadata = entry.data?.metadata;
@@ -1028,6 +1206,8 @@ function reduceEvalTrends(acc, entry) {
1028
1206
  id: entry.id,
1029
1207
  scores,
1030
1208
  cost,
1209
+ completeness,
1210
+ ...budgetStopped ? { budgetStopped: true } : {},
1031
1211
  ...model !== void 0 ? { model } : {},
1032
1212
  ...duration3 !== void 0 ? { duration: duration3 } : {},
1033
1213
  ...runGroupId !== void 0 ? { runGroupId } : {},
@@ -1046,12 +1226,15 @@ function reduceEvalTrends(acc, entry) {
1046
1226
  scoreMean: mean,
1047
1227
  scoreStd: std,
1048
1228
  costTotal: (prev?.costTotal ?? 0) + cost,
1229
+ costCompleteness: worse(prev?.costCompleteness ?? "complete", completeness),
1230
+ budgetStoppedRuns: (prev?.budgetStoppedRuns ?? 0) + (budgetStopped ? 1 : 0),
1049
1231
  runCount: (prev?.runCount ?? 0) + 1
1050
1232
  };
1051
1233
  return {
1052
1234
  byEval,
1053
1235
  totalRuns: acc.totalRuns + 1,
1054
- totalCost: acc.totalCost + cost
1236
+ totalCost: acc.totalCost + cost,
1237
+ totalCostCompleteness: worse(acc.totalCostCompleteness ?? "complete", completeness)
1055
1238
  };
1056
1239
  }
1057
1240
  var MAX_DURATIONS = 200;
@@ -1373,21 +1556,32 @@ var executions_default = createExecutionRoutes();
1373
1556
 
1374
1557
  // src/server/routes/sessions.ts
1375
1558
  var import_hono4 = require("hono");
1559
+ function mediaFields(source) {
1560
+ const bytes = source.type === "bytes" ? source.data.byteLength : source.type === "base64" ? Math.floor(source.data.length * 3 / 4) - (source.data.endsWith("==") ? 2 : source.data.endsWith("=") ? 1 : 0) : void 0;
1561
+ return {
1562
+ source: source.type,
1563
+ ...source.mediaType ? { mediaType: source.mediaType } : {},
1564
+ ...bytes !== void 0 ? { bytes } : {}
1565
+ };
1566
+ }
1376
1567
  function describeStudioInput(content) {
1377
1568
  if (typeof content === "string") return void 0;
1378
1569
  return {
1379
1570
  parts: content.map((part) => {
1380
- if (part.type === "text") return { type: "text", characters: part.text.length };
1381
- const { source } = part;
1382
- const bytes = source.type === "bytes" ? source.data.byteLength : source.type === "base64" ? Math.floor(source.data.length * 3 / 4) - (source.data.endsWith("==") ? 2 : source.data.endsWith("=") ? 1 : 0) : void 0;
1383
- return {
1384
- type: "image",
1385
- source: source.type,
1386
- ...source.mediaType ? { mediaType: source.mediaType } : {},
1387
- ...bytes !== void 0 ? { bytes } : {}
1388
- // URLs, provider-file references, and labels are intentionally not
1389
- // included: this is a Studio-safe descriptor, not a replay payload.
1390
- };
1571
+ switch (part.type) {
1572
+ case "text":
1573
+ return { type: "text", characters: part.text.length };
1574
+ case "image":
1575
+ return { type: "image", ...mediaFields(part.source) };
1576
+ case "audio":
1577
+ return { type: "audio", ...mediaFields(part.source) };
1578
+ default: {
1579
+ const unreachable = part;
1580
+ throw new Error(
1581
+ `unsupported model input part: ${String(unreachable.type)}`
1582
+ );
1583
+ }
1584
+ }
1391
1585
  })
1392
1586
  };
1393
1587
  }
@@ -1741,6 +1935,184 @@ function createCostRoutes(costAggregator) {
1741
1935
  // src/server/routes/evals.ts
1742
1936
  var import_node_crypto = require("crypto");
1743
1937
  var import_hono10 = require("hono");
1938
+
1939
+ // src/server/eval-import.ts
1940
+ var ITEM_OUTCOMES = [
1941
+ "completed",
1942
+ "failed",
1943
+ "cancelled",
1944
+ "budget_skipped",
1945
+ "budget_interrupted"
1946
+ ];
1947
+ var SCORER_OUTCOMES = [
1948
+ "scored",
1949
+ "failed",
1950
+ "skipped",
1951
+ "cancelled",
1952
+ "budget_skipped",
1953
+ "budget_interrupted"
1954
+ ];
1955
+ var EPSILON = 1e-9;
1956
+ function isFiniteNonNegative(value) {
1957
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
1958
+ }
1959
+ function isCount(value) {
1960
+ return isFiniteNonNegative(value) && Number.isInteger(value);
1961
+ }
1962
+ function isValidImportedBudget(value) {
1963
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1964
+ const b = value;
1965
+ if (!isFiniteNonNegative(b.limit)) return false;
1966
+ if (!isFiniteNonNegative(b.knownSpend)) return false;
1967
+ if (!isFiniteNonNegative(b.knownOvershoot)) return false;
1968
+ if (b.status !== "open" && b.status !== "closed") return false;
1969
+ if (b.closedBy !== void 0 && b.closedBy !== "case" && b.closedBy !== "scorer" && b.closedBy !== "operation") {
1970
+ return false;
1971
+ }
1972
+ if (b.knownSpend >= b.limit !== (b.status === "closed")) return false;
1973
+ const expected = Math.max(0, b.knownSpend - b.limit);
1974
+ return Math.abs(b.knownOvershoot - expected) <= EPSILON;
1975
+ }
1976
+ function isValidImportedCoverage(value) {
1977
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1978
+ const c = value;
1979
+ if (!c.items || typeof c.items !== "object" || Array.isArray(c.items)) return false;
1980
+ const items = c.items;
1981
+ for (const key of ITEM_OUTCOMES) {
1982
+ if (!isCount(items[key])) return false;
1983
+ }
1984
+ if (!c.scorers || typeof c.scorers !== "object" || Array.isArray(c.scorers)) return false;
1985
+ for (const bucket of Object.values(c.scorers)) {
1986
+ if (!bucket || typeof bucket !== "object" || Array.isArray(bucket)) return false;
1987
+ const counts = bucket;
1988
+ for (const key of SCORER_OUTCOMES) {
1989
+ if (!isCount(counts[key])) return false;
1990
+ }
1991
+ }
1992
+ return true;
1993
+ }
1994
+ function isValidImportedItemErrorRate(value) {
1995
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1996
+ const r = value;
1997
+ const { failed, attempted, rate, limit, exceeded } = r;
1998
+ if (!isCount(failed) || !isCount(attempted) || failed > attempted) return false;
1999
+ if (!isFiniteNonNegative(limit) || limit > 1) return false;
2000
+ if (typeof rate !== "number" || typeof exceeded !== "boolean") return false;
2001
+ const expectedRate = attempted > 0 ? failed / attempted : 0;
2002
+ if (Math.abs(rate - expectedRate) > EPSILON) return false;
2003
+ return exceeded === (attempted > 0 && rate > limit);
2004
+ }
2005
+ function isValidImportedMultiRun(value) {
2006
+ if (!isPlainRecord(value)) return false;
2007
+ const { allRuns } = value;
2008
+ if (allRuns === void 0) return true;
2009
+ if (!Array.isArray(allRuns)) return false;
2010
+ return allRuns.every(
2011
+ (run) => isPlainRecord(run) && Array.isArray(run.items) && run.items.every(isPlainRecord)
2012
+ );
2013
+ }
2014
+ function isPlainRecord(value) {
2015
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2016
+ }
2017
+ function sumOf(record2) {
2018
+ if (record2 === void 0) return 0;
2019
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return void 0;
2020
+ let total = 0;
2021
+ for (const value of Object.values(record2)) {
2022
+ if (!isFiniteNonNegative(value)) return void 0;
2023
+ total += value;
2024
+ }
2025
+ return total;
2026
+ }
2027
+ function allNumeric(record2) {
2028
+ if (record2 === void 0) return true;
2029
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return false;
2030
+ return Object.values(record2).every(
2031
+ (value) => typeof value === "number" && Number.isFinite(value)
2032
+ );
2033
+ }
2034
+ function isValidImportedAccounting(value) {
2035
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2036
+ const a = value;
2037
+ if (a.version !== 1) return false;
2038
+ if (a.currency !== "USD") return false;
2039
+ if (!isFiniteNonNegative(a.knownCost)) return false;
2040
+ if (a.completeness !== "complete" && a.completeness !== "incomplete" && a.completeness !== "unverified") {
2041
+ return false;
2042
+ }
2043
+ if (!allNumeric(a.reasons)) return false;
2044
+ if (!allNumeric(a.usage)) return false;
2045
+ const ops = a.operations;
2046
+ if (!ops || typeof ops !== "object") return false;
2047
+ for (const key of ["total", "settled", "unknown", "denied"]) {
2048
+ if (!isFiniteNonNegative(ops[key])) return false;
2049
+ }
2050
+ if (!allNumeric(ops.byKind)) return false;
2051
+ if (ops.total !== ops.settled + ops.unknown) return false;
2052
+ if (a.completeness === "unverified") return true;
2053
+ const provenanceTotal = sumOf(a.provenance);
2054
+ if (provenanceTotal === void 0) return false;
2055
+ if (Math.abs(provenanceTotal - a.knownCost) > EPSILON) return false;
2056
+ const breakdown = a.breakdown;
2057
+ if (!breakdown || typeof breakdown !== "object") return false;
2058
+ const breakdownTotal = sumOf({
2059
+ generation: breakdown.generation,
2060
+ judging: breakdown.judging,
2061
+ external: breakdown.external
2062
+ });
2063
+ if (breakdownTotal === void 0) return false;
2064
+ if (Math.abs(breakdownTotal - a.knownCost) > EPSILON) return false;
2065
+ return true;
2066
+ }
2067
+ function importedAccountingIsTrustworthy(result) {
2068
+ if (!result || typeof result !== "object") return false;
2069
+ const r = result;
2070
+ if (r.accounting !== void 0 && !isValidImportedAccounting(r.accounting)) return false;
2071
+ const budget = r.accounting?.budget;
2072
+ if (budget !== void 0 && !isValidImportedBudget(budget)) return false;
2073
+ const coverage = r.summary?.coverage;
2074
+ if (coverage !== void 0 && !isValidImportedCoverage(coverage)) return false;
2075
+ if (!Array.isArray(r.items)) return true;
2076
+ for (const item of r.items) {
2077
+ if (!item || typeof item !== "object") continue;
2078
+ const i = item;
2079
+ if (i.accounting !== void 0 && !isValidImportedAccounting(i.accounting)) return false;
2080
+ if (i.scoreDetails && typeof i.scoreDetails === "object") {
2081
+ for (const detail of Object.values(i.scoreDetails)) {
2082
+ const d = detail;
2083
+ if (d?.accounting !== void 0 && !isValidImportedAccounting(d.accounting)) return false;
2084
+ }
2085
+ }
2086
+ }
2087
+ return true;
2088
+ }
2089
+ function stripAccounting(result) {
2090
+ const copy = structuredClone(result);
2091
+ delete copy.accounting;
2092
+ const summary = copy.summary;
2093
+ if (summary && typeof summary === "object") {
2094
+ delete summary.coverage;
2095
+ }
2096
+ const items = copy.items;
2097
+ if (Array.isArray(items)) {
2098
+ for (const item of items) {
2099
+ if (!item || typeof item !== "object") continue;
2100
+ const i = item;
2101
+ delete i.accounting;
2102
+ const details = i.scoreDetails;
2103
+ if (details && typeof details === "object") {
2104
+ for (const detail of Object.values(details)) {
2105
+ if (detail && typeof detail === "object") {
2106
+ delete detail.accounting;
2107
+ }
2108
+ }
2109
+ }
2110
+ }
2111
+ }
2112
+ return copy;
2113
+ }
2114
+
2115
+ // src/server/routes/evals.ts
1744
2116
  function createEvalRoutes(connMgr, evalLoader) {
1745
2117
  const app5 = new import_hono10.Hono();
1746
2118
  const activeRuns = /* @__PURE__ */ new Map();
@@ -1788,6 +2160,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1788
2160
  let runs = 1;
1789
2161
  let stream = false;
1790
2162
  let captureTraces = false;
2163
+ let captureRequests = false;
1791
2164
  try {
1792
2165
  const body = await c.req.json().catch(() => ({}));
1793
2166
  if (typeof body.runs === "number" && Number.isFinite(body.runs) && body.runs > 1) {
@@ -1799,6 +2172,9 @@ function createEvalRoutes(connMgr, evalLoader) {
1799
2172
  if (body.captureTraces === true) {
1800
2173
  captureTraces = true;
1801
2174
  }
2175
+ if (body.captureRequests === true) {
2176
+ captureRequests = true;
2177
+ }
1802
2178
  } catch {
1803
2179
  }
1804
2180
  if (stream) {
@@ -1822,6 +2198,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1822
2198
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1823
2199
  signal: ac.signal,
1824
2200
  captureTraces,
2201
+ captureRequests,
1825
2202
  onProgress: (event) => {
1826
2203
  if (event.type === "run_done") return;
1827
2204
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, {
@@ -1894,6 +2271,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1894
2271
  const result = await runtime.runRegisteredEval(name, {
1895
2272
  signal: ac.signal,
1896
2273
  captureTraces,
2274
+ captureRequests,
1897
2275
  onProgress: (event) => {
1898
2276
  if (event.type === "run_done") return;
1899
2277
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, event);
@@ -1925,7 +2303,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1925
2303
  try {
1926
2304
  const result2 = await runtime.runRegisteredEval(name, {
1927
2305
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1928
- captureTraces
2306
+ captureTraces,
2307
+ captureRequests
1929
2308
  });
1930
2309
  results.push(result2);
1931
2310
  } catch (err) {
@@ -1941,11 +2320,14 @@ function createEvalRoutes(connMgr, evalLoader) {
1941
2320
  const partial2 = results.length < runs;
1942
2321
  const failureMsg = runFailure ? redactErrorMessage(runFailure, redactOn) || String(runFailure) || void 0 : void 0;
1943
2322
  const aggDegraded = unionDegradedScorers(results);
2323
+ const worstItemRate = worstItemErrorRate(results);
2324
+ const { modelTiming: _runOneModelTiming, ...firstSummary } = first.summary;
1944
2325
  const result = {
1945
2326
  ...first,
1946
2327
  summary: {
1947
- ...first.summary,
1948
- ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {}
2328
+ ...firstSummary,
2329
+ ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {},
2330
+ ...worstItemRate ? { itemErrorRate: worstItemRate } : {}
1949
2331
  },
1950
2332
  _multiRun: {
1951
2333
  aggregate,
@@ -1963,7 +2345,10 @@ function createEvalRoutes(connMgr, evalLoader) {
1963
2345
  data: redactEvalResult(result, redactOn)
1964
2346
  });
1965
2347
  } else {
1966
- const result = await runtime.runRegisteredEval(name, { captureTraces });
2348
+ const result = await runtime.runRegisteredEval(name, {
2349
+ captureTraces,
2350
+ captureRequests
2351
+ });
1967
2352
  return c.json({
1968
2353
  ok: true,
1969
2354
  data: redactEvalResult(result, redactOn)
@@ -1976,6 +2361,106 @@ function createEvalRoutes(connMgr, evalLoader) {
1976
2361
  );
1977
2362
  }
1978
2363
  });
2364
+ async function resolveDiagnostics(runtime, id) {
2365
+ const entry = await runtime.getEvalResult(id);
2366
+ const artifactId = entry?.data?.diagnostics?.artifactId;
2367
+ return typeof artifactId === "string" && artifactId !== "" ? { artifactId } : void 0;
2368
+ }
2369
+ app5.get("/evals/:id/diagnostics", async (c) => {
2370
+ const runtime = c.get("runtime");
2371
+ const id = c.req.param("id");
2372
+ const resolved = await resolveDiagnostics(runtime, id);
2373
+ if (!resolved) {
2374
+ return c.json(
2375
+ {
2376
+ ok: false,
2377
+ error: {
2378
+ code: "NOT_FOUND",
2379
+ message: `Eval result "${id}" has no captured requests`
2380
+ }
2381
+ },
2382
+ 404
2383
+ );
2384
+ }
2385
+ const opened = await runtime.openDiagnosticArtifact(resolved.artifactId);
2386
+ if (!opened) {
2387
+ return c.json(
2388
+ {
2389
+ ok: false,
2390
+ error: {
2391
+ code: "NOT_FOUND",
2392
+ message: `Captured requests for eval result "${id}" are no longer available`
2393
+ }
2394
+ },
2395
+ 404
2396
+ );
2397
+ }
2398
+ return c.json({
2399
+ ok: true,
2400
+ data: {
2401
+ artifactId: opened.manifest.artifactId,
2402
+ status: opened.manifest.status,
2403
+ ...opened.manifest.reason !== void 0 ? { reason: opened.manifest.reason } : {},
2404
+ records: opened.manifest.records,
2405
+ bytes: opened.manifest.bytes,
2406
+ fidelity: opened.manifest.fidelity,
2407
+ redaction: opened.manifest.redaction,
2408
+ ...opened.manifest.expiresAt !== void 0 ? { expiresAt: opened.manifest.expiresAt } : {},
2409
+ ...opened.manifest.copiedFrom !== void 0 ? { copiedFrom: opened.manifest.copiedFrom } : {}
2410
+ }
2411
+ });
2412
+ });
2413
+ app5.get("/evals/:id/diagnostics/records", async (c) => {
2414
+ const runtime = c.get("runtime");
2415
+ const redactOn = runtime.isRedactEnabled();
2416
+ const id = c.req.param("id");
2417
+ const resolved = await resolveDiagnostics(runtime, id);
2418
+ const opened = resolved ? await runtime.openDiagnosticArtifact(resolved.artifactId) : void 0;
2419
+ if (!opened) {
2420
+ return c.json(
2421
+ {
2422
+ ok: false,
2423
+ error: {
2424
+ code: "NOT_FOUND",
2425
+ message: `Captured requests for eval result "${id}" are not available`
2426
+ }
2427
+ },
2428
+ 404
2429
+ );
2430
+ }
2431
+ let cancelled = false;
2432
+ const lines = opened.lines[Symbol.asyncIterator]();
2433
+ return new Response(
2434
+ new ReadableStream({
2435
+ async start(controller) {
2436
+ const encoder = new TextEncoder();
2437
+ try {
2438
+ for (; ; ) {
2439
+ const next = await lines.next();
2440
+ if (next.done || cancelled) break;
2441
+ controller.enqueue(encoder.encode(`${redactRecordLine(next.value, redactOn)}
2442
+ `));
2443
+ }
2444
+ if (!cancelled) controller.close();
2445
+ } catch (error48) {
2446
+ if (!cancelled) controller.error(error48);
2447
+ } finally {
2448
+ await lines.return?.().catch(() => void 0);
2449
+ }
2450
+ },
2451
+ cancel() {
2452
+ cancelled = true;
2453
+ }
2454
+ }),
2455
+ {
2456
+ status: 200,
2457
+ headers: {
2458
+ "Content-Type": "application/x-ndjson; charset=utf-8",
2459
+ "Cache-Control": "no-store"
2460
+ }
2461
+ }
2462
+ );
2463
+ });
1979
2464
  app5.post("/evals/runs/:evalRunId/cancel", (c) => {
1980
2465
  const evalRunId = c.req.param("evalRunId");
1981
2466
  const ac = activeRuns.get(evalRunId);
@@ -1995,6 +2480,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1995
2480
  const redactOn = runtime.isRedactEnabled();
1996
2481
  const name = c.req.param("name");
1997
2482
  const body = await c.req.json();
2483
+ const captureRequests = body.captureRequests === true;
1998
2484
  if (!body.resultId || typeof body.resultId !== "string") {
1999
2485
  return c.json(
2000
2486
  { ok: false, error: { code: "BAD_REQUEST", message: "resultId is required" } },
@@ -2008,8 +2494,7 @@ function createEvalRoutes(connMgr, evalLoader) {
2008
2494
  404
2009
2495
  );
2010
2496
  }
2011
- const history = await runtime.getEvalHistory();
2012
- const historyEntry = history.find((h) => h.id === body.resultId);
2497
+ const historyEntry = await runtime.getEvalResult(body.resultId);
2013
2498
  if (!historyEntry) {
2014
2499
  return c.json(
2015
2500
  { ok: false, error: { code: "NOT_FOUND", message: `Result "${body.resultId}" not found` } },
@@ -2022,7 +2507,8 @@ function createEvalRoutes(connMgr, evalLoader) {
2022
2507
  const result = await rescore(
2023
2508
  historyEntry.data,
2024
2509
  config2.scorers,
2025
- runtime
2510
+ runtime,
2511
+ captureRequests ? { captureRequests: true } : void 0
2026
2512
  );
2027
2513
  await runtime.saveEvalResult({
2028
2514
  id: result.id,
@@ -2076,8 +2562,13 @@ function createEvalRoutes(connMgr, evalLoader) {
2076
2562
  400
2077
2563
  );
2078
2564
  }
2079
- const history = await runtime.getEvalHistory();
2080
- const byId = new Map(history.map((h) => [h.id, h.data]));
2565
+ const idsOf = (v) => Array.isArray(v) ? v : [v];
2566
+ const requested = /* @__PURE__ */ new Set([...idsOf(body.baselineId), ...idsOf(body.candidateId)]);
2567
+ const byId = /* @__PURE__ */ new Map();
2568
+ for (const id of requested) {
2569
+ const entry = await runtime.getEvalResult(id);
2570
+ if (entry) byId.set(id, entry.data);
2571
+ }
2081
2572
  const missing = [];
2082
2573
  const resolveOne = (id) => {
2083
2574
  const data = byId.get(id);
@@ -2113,7 +2604,10 @@ function createEvalRoutes(connMgr, evalLoader) {
2113
2604
  }
2114
2605
  try {
2115
2606
  const result = await runtime.evalCompare(baseline, candidate, body.options);
2116
- return c.json({ ok: true, data: result });
2607
+ return c.json({
2608
+ ok: true,
2609
+ data: redactEvalComparison(result, redactOn)
2610
+ });
2117
2611
  } catch (err) {
2118
2612
  return c.json(
2119
2613
  {
@@ -2181,6 +2675,26 @@ function createEvalRoutes(connMgr, evalLoader) {
2181
2675
  const workflowsFromMeta = Array.isArray(metadataObj.workflows) ? metadataObj.workflows : [];
2182
2676
  const primaryWorkflow = workflowsFromMeta.find((w) => typeof w === "string");
2183
2677
  const evalName = trim(body.eval) ?? trim(primaryWorkflow) ?? trim(firstResult.workflow) ?? "imported";
2678
+ let readAccounting;
2679
+ let validateRequestSidecar;
2680
+ try {
2681
+ ({ readAccounting, validateRequestSidecar } = await import("@axlsdk/eval"));
2682
+ } catch {
2683
+ readAccounting = void 0;
2684
+ validateRequestSidecar = void 0;
2685
+ }
2686
+ let sidecarLines;
2687
+ if (body.requests !== void 0 && body.requests !== null) {
2688
+ if (!validateRequestSidecar) {
2689
+ return bad("importing captured requests requires @axlsdk/eval to be installed");
2690
+ }
2691
+ const validation = validateRequestSidecar(body.requests);
2692
+ if (!validation.ok) return bad(validation.reason);
2693
+ sidecarLines = validation.lines;
2694
+ }
2695
+ if (sidecarLines && validatedResults.length > 1) {
2696
+ return bad("a requests sidecar can only accompany a single result");
2697
+ }
2184
2698
  const timestamp = Date.now();
2185
2699
  const imported = [];
2186
2700
  for (const r of validatedResults) {
@@ -2190,6 +2704,42 @@ function createEvalRoutes(connMgr, evalLoader) {
2190
2704
  id,
2191
2705
  metadata: typeof r.metadata === "object" && r.metadata != null ? r.metadata : {}
2192
2706
  };
2707
+ if (entry.accounting === void 0) {
2708
+ if (entry.summary?.coverage !== void 0 && !isValidImportedCoverage(entry.summary.coverage)) {
2709
+ delete entry.summary.coverage;
2710
+ entry.metadata.importedAccounting = "invalid";
2711
+ }
2712
+ if (readAccounting) {
2713
+ entry.accounting = readAccounting(entry);
2714
+ }
2715
+ } else if (importedAccountingIsTrustworthy(entry)) {
2716
+ entry.metadata.importedAccounting = "declared";
2717
+ } else {
2718
+ const stripped = stripAccounting(entry);
2719
+ entry.accounting = readAccounting ? readAccounting(stripped) : void 0;
2720
+ entry.items = stripped.items;
2721
+ entry.summary = stripped.summary;
2722
+ entry.metadata.importedAccounting = "invalid";
2723
+ }
2724
+ if (entry.summary?.itemErrorRate !== void 0 && !isValidImportedItemErrorRate(entry.summary.itemErrorRate)) {
2725
+ delete entry.summary.itemErrorRate;
2726
+ entry.metadata.importedItemErrorRate = "invalid";
2727
+ }
2728
+ const multiRun = entry._multiRun;
2729
+ if (multiRun !== void 0 && !isValidImportedMultiRun(multiRun)) {
2730
+ delete entry._multiRun;
2731
+ entry.metadata.importedMultiRun = "invalid";
2732
+ }
2733
+ if (sidecarLines) {
2734
+ entry.diagnostics = await restageImportedRequests(runtime, id, sidecarLines);
2735
+ } else if (entry.diagnostics) {
2736
+ entry.diagnostics = {
2737
+ ...entry.diagnostics,
2738
+ artifactId: "",
2739
+ status: "unavailable",
2740
+ reason: "imported without its captured-request sidecar"
2741
+ };
2742
+ }
2193
2743
  await runtime.saveEvalResult({ id, eval: evalName, timestamp, data: entry });
2194
2744
  imported.push({ id, eval: evalName, timestamp });
2195
2745
  }
@@ -2198,12 +2748,72 @@ function createEvalRoutes(connMgr, evalLoader) {
2198
2748
  }
2199
2749
  return c.json({ ok: true, data: { imported } });
2200
2750
  });
2751
+ async function restageImportedRequests(runtime, ownerId, lines) {
2752
+ let stagedId;
2753
+ try {
2754
+ const staged = await runtime.stageDiagnosticArtifact({ kind: "eval", id: ownerId });
2755
+ stagedId = staged.artifactId;
2756
+ let bytes = 0;
2757
+ let allRedacted = lines.length > 0;
2758
+ for (const line of lines) {
2759
+ await staged.sink.append(line);
2760
+ bytes += Buffer.byteLength(line, "utf-8");
2761
+ if (allRedacted) {
2762
+ try {
2763
+ allRedacted = JSON.parse(line).captured?.redacted === true;
2764
+ } catch {
2765
+ allRedacted = false;
2766
+ }
2767
+ }
2768
+ }
2769
+ const manifest = await runtime.finalizeDiagnosticArtifact(
2770
+ staged.artifactId,
2771
+ "complete",
2772
+ void 0,
2773
+ allRedacted ? "applied" : "none"
2774
+ );
2775
+ return {
2776
+ version: 1,
2777
+ artifactId: manifest.artifactId,
2778
+ fidelity: "runtime_request",
2779
+ status: manifest.status,
2780
+ records: lines.length,
2781
+ bytes,
2782
+ redaction: manifest.redaction
2783
+ };
2784
+ } catch (error48) {
2785
+ if (stagedId !== void 0) {
2786
+ await runtime.rollbackDiagnosticArtifact(stagedId).catch(() => void 0);
2787
+ }
2788
+ return {
2789
+ version: 1,
2790
+ artifactId: "",
2791
+ fidelity: "runtime_request",
2792
+ status: "unavailable",
2793
+ reason: `captured requests could not be stored: ${error48 instanceof Error ? error48.message : String(error48)}`,
2794
+ records: 0,
2795
+ bytes: 0,
2796
+ redaction: "none"
2797
+ };
2798
+ }
2799
+ }
2201
2800
  function closeActiveRuns() {
2202
2801
  for (const ac of activeRuns.values()) ac.abort();
2203
2802
  activeRuns.clear();
2204
2803
  }
2205
2804
  return { app: app5, closeActiveRuns };
2206
2805
  }
2806
+ function worstItemErrorRate(results) {
2807
+ let worst;
2808
+ let runsExceeded = 0;
2809
+ for (const run of results) {
2810
+ const r = run.summary?.itemErrorRate;
2811
+ if (!r) continue;
2812
+ if (r.exceeded) runsExceeded++;
2813
+ if (!worst || r.rate > worst.rate) worst = r;
2814
+ }
2815
+ return worst ? { ...worst, runsExceeded } : void 0;
2816
+ }
2207
2817
  function unionDegradedScorers(results) {
2208
2818
  const byScorer = /* @__PURE__ */ new Map();
2209
2819
  for (const run of results) {
@@ -2214,8 +2824,8 @@ function unionDegradedScorers(results) {
2214
2824
  if (!existing) {
2215
2825
  byScorer.set(d.scorer, { ...d, runsAffected: 1 });
2216
2826
  } else {
2217
- const worse = d.rate > existing.rate ? d : existing;
2218
- byScorer.set(d.scorer, { ...worse, runsAffected: existing.runsAffected + 1 });
2827
+ const worse2 = d.rate > existing.rate ? d : existing;
2828
+ byScorer.set(d.scorer, { ...worse2, runsAffected: existing.runsAffected + 1 });
2219
2829
  }
2220
2830
  }
2221
2831
  }
@@ -2855,7 +3465,7 @@ __export(util_exports, {
2855
3465
  getSizableOrigin: () => getSizableOrigin,
2856
3466
  hexToUint8Array: () => hexToUint8Array,
2857
3467
  isObject: () => isObject,
2858
- isPlainObject: () => isPlainObject,
3468
+ isPlainObject: () => isPlainObject2,
2859
3469
  issue: () => issue,
2860
3470
  joinValues: () => joinValues,
2861
3471
  jsonStringifyReplacer: () => jsonStringifyReplacer,
@@ -3039,7 +3649,7 @@ var allowsEval = cached(() => {
3039
3649
  return false;
3040
3650
  }
3041
3651
  });
3042
- function isPlainObject(o) {
3652
+ function isPlainObject2(o) {
3043
3653
  if (isObject(o) === false)
3044
3654
  return false;
3045
3655
  const ctor = o.constructor;
@@ -3056,7 +3666,7 @@ function isPlainObject(o) {
3056
3666
  return true;
3057
3667
  }
3058
3668
  function shallowClone(o) {
3059
- if (isPlainObject(o))
3669
+ if (isPlainObject2(o))
3060
3670
  return { ...o };
3061
3671
  if (Array.isArray(o))
3062
3672
  return [...o];
@@ -3249,7 +3859,7 @@ function omit(schema, mask) {
3249
3859
  return clone(schema, def);
3250
3860
  }
3251
3861
  function extend(schema, shape) {
3252
- if (!isPlainObject(shape)) {
3862
+ if (!isPlainObject2(shape)) {
3253
3863
  throw new Error("Invalid input to extend: expected a plain object");
3254
3864
  }
3255
3865
  const checks = schema._zod.def.checks;
@@ -3272,7 +3882,7 @@ function extend(schema, shape) {
3272
3882
  return clone(schema, def);
3273
3883
  }
3274
3884
  function safeExtend(schema, shape) {
3275
- if (!isPlainObject(shape)) {
3885
+ if (!isPlainObject2(shape)) {
3276
3886
  throw new Error("Invalid input to safeExtend: expected a plain object");
3277
3887
  }
3278
3888
  const def = mergeDefs(schema._zod.def, {
@@ -5547,7 +6157,7 @@ function mergeValues(a, b) {
5547
6157
  if (a instanceof Date && b instanceof Date && +a === +b) {
5548
6158
  return { valid: true, data: a };
5549
6159
  }
5550
- if (isPlainObject(a) && isPlainObject(b)) {
6160
+ if (isPlainObject2(a) && isPlainObject2(b)) {
5551
6161
  const bKeys = Object.keys(b);
5552
6162
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
5553
6163
  const newObj = { ...a, ...b };
@@ -5701,7 +6311,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
5701
6311
  $ZodType.init(inst, def);
5702
6312
  inst._zod.parse = (payload, ctx) => {
5703
6313
  const input = payload.value;
5704
- if (!isPlainObject(input)) {
6314
+ if (!isPlainObject2(input)) {
5705
6315
  payload.issues.push({
5706
6316
  expected: "record",
5707
6317
  code: "invalid_type",
@@ -6709,8 +7319,8 @@ function az_default() {
6709
7319
  }
6710
7320
 
6711
7321
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/be.js
6712
- function getBelarusianPlural(count, one, few, many) {
6713
- const absCount = Math.abs(count);
7322
+ function getBelarusianPlural(count2, one, few, many) {
7323
+ const absCount = Math.abs(count2);
6714
7324
  const lastDigit = absCount % 10;
6715
7325
  const lastTwoDigits = absCount % 100;
6716
7326
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -8534,8 +9144,8 @@ function hu_default() {
8534
9144
  }
8535
9145
 
8536
9146
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/hy.js
8537
- function getArmenianPlural(count, one, many) {
8538
- return Math.abs(count) === 1 ? one : many;
9147
+ function getArmenianPlural(count2, one, many) {
9148
+ return Math.abs(count2) === 1 ? one : many;
8539
9149
  }
8540
9150
  function withDefiniteArticle(word) {
8541
9151
  if (!word)
@@ -10543,8 +11153,8 @@ function pt_default() {
10543
11153
  }
10544
11154
 
10545
11155
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/ru.js
10546
- function getRussianPlural(count, one, few, many) {
10547
- const absCount = Math.abs(count);
11156
+ function getRussianPlural(count2, one, few, many) {
11157
+ const absCount = Math.abs(count2);
10548
11158
  const lastDigit = absCount % 10;
10549
11159
  const lastTwoDigits = absCount % 100;
10550
11160
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {