@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.
@@ -134,42 +134,177 @@ function sanitizeRichInputFailure(event) {
134
134
  }
135
135
  return event;
136
136
  }
137
+ var MEASURED_METADATA_KEYS = /* @__PURE__ */ new Set([
138
+ "models",
139
+ "modelCallCounts",
140
+ "workflows",
141
+ "workflowCallCounts",
142
+ "tokens",
143
+ "agentCalls"
144
+ ]);
145
+ function redactItemMetadata(metadata) {
146
+ if (!metadata) return metadata;
147
+ const out = {};
148
+ for (const [key, value] of Object.entries(metadata)) {
149
+ out[key] = MEASURED_METADATA_KEYS.has(key) ? value : REDACTED;
150
+ }
151
+ return out;
152
+ }
153
+ function projectItemFailure(failure) {
154
+ if (!failure || typeof failure !== "object") return void 0;
155
+ const f = failure;
156
+ if (typeof f.name !== "string") return void 0;
157
+ return {
158
+ name: f.name,
159
+ ...typeof f.provider === "string" ? { provider: f.provider } : {},
160
+ ...typeof f.status === "number" && Number.isFinite(f.status) ? { status: f.status } : {},
161
+ ...typeof f.retryable === "boolean" ? { retryable: f.retryable } : {},
162
+ ...typeof f.requestId === "string" ? { requestId: f.requestId } : {}
163
+ };
164
+ }
165
+ function isPlainObject(value) {
166
+ return typeof value === "object" && value !== null && !Array.isArray(value);
167
+ }
168
+ function redactScorerDetail(detail) {
169
+ if (!isPlainObject(detail)) return REDACTED;
170
+ const d = detail;
171
+ return {
172
+ score: d.score,
173
+ ...d.duration !== void 0 ? { duration: d.duration } : {},
174
+ ...d.cost !== void 0 ? { cost: d.cost } : {},
175
+ // `skipped` is a structural boolean (the `applies` predicate verdict),
176
+ // not user/LLM content — preserve it so the client's N/A chip renders.
177
+ ...d.skipped !== void 0 ? { skipped: d.skipped } : {},
178
+ // `outcome` and `accounting` are structural (a classification and a set
179
+ // of counts), so they survive redaction the way `skipped` does — without
180
+ // them a compliance-mode reader cannot tell a judge that was stopped on
181
+ // budget from one that scored 0.
182
+ ...d.outcome !== void 0 ? { outcome: d.outcome } : {},
183
+ ...d.accounting !== void 0 ? { accounting: d.accounting } : {},
184
+ // `diagnostics` survives for the same reason `accounting` does: it is a
185
+ // list of operation ids and statuses, not content.
186
+ ...d.diagnostics !== void 0 ? { diagnostics: d.diagnostics } : {}
187
+ // metadata deliberately omitted — may contain LLM scorer reasoning
188
+ };
189
+ }
137
190
  function redactEvalItem(item) {
191
+ if (!isPlainObject(item)) return REDACTED;
192
+ const { failure, ...rest } = item;
193
+ const typed = item;
194
+ const projectedFailure = failure !== void 0 ? projectItemFailure(failure) : void 0;
138
195
  const scrubbed = {
139
- ...item,
196
+ ...rest,
197
+ ...projectedFailure ? { failure: projectedFailure } : {},
140
198
  input: REDACTED,
141
199
  output: REDACTED,
142
- ...item.annotations !== void 0 ? { annotations: REDACTED } : {},
143
- ...item.error !== void 0 ? { error: REDACTED } : {},
144
- ...item.scorerErrors !== void 0 ? { scorerErrors: item.scorerErrors.map(() => REDACTED) } : {}
145
- };
146
- if (item.scoreDetails) {
147
- const detailsOut = {};
148
- for (const [name, detail] of Object.entries(item.scoreDetails)) {
149
- detailsOut[name] = {
150
- score: detail.score,
151
- ...detail.duration !== void 0 ? { duration: detail.duration } : {},
152
- ...detail.cost !== void 0 ? { cost: detail.cost } : {},
153
- // `skipped` is a structural boolean (the `applies` predicate verdict),
154
- // not user/LLM content — preserve it so the client's N/A chip renders.
155
- ...detail.skipped !== void 0 ? { skipped: detail.skipped } : {}
156
- // metadata deliberately omitted — may contain LLM scorer reasoning
157
- };
200
+ // `diagnostics` is spread through untouched on purpose: it holds operation
201
+ // IDs, kinds, turn/attempt indexes and a status — pointers into an artifact,
202
+ // never content. The records themselves are redacted by the core rule at
203
+ // both write time and delivery time (`redactRecordLine`).
204
+ ...typed.metadata !== void 0 ? {
205
+ metadata: isPlainObject(typed.metadata) ? redactItemMetadata(typed.metadata) : REDACTED
206
+ } : {},
207
+ ...typed.annotations !== void 0 ? { annotations: REDACTED } : {},
208
+ ...typed.error !== void 0 ? { error: REDACTED } : {},
209
+ ...typed.scorerErrors !== void 0 ? {
210
+ scorerErrors: Array.isArray(typed.scorerErrors) ? typed.scorerErrors.map(() => REDACTED) : REDACTED
211
+ } : {},
212
+ // `callerReport.metadata` is whatever the workflow callback returned — free-
213
+ // form user content, exactly like `output`, so it is dropped rather than
214
+ // masked (same treatment as scorer metadata below). Its sibling `cost` is a
215
+ // plain number and stays. (`outcome` and `accounting` are spread through
216
+ // untouched: both are structural counts and classifications, no content.)
217
+ ...typed.callerReport !== void 0 ? {
218
+ callerReport: isPlainObject(typed.callerReport) ? typeof typed.callerReport.cost === "number" ? { cost: typed.callerReport.cost } : {} : REDACTED
219
+ } : {}
220
+ };
221
+ if (typed.scoreDetails !== void 0) {
222
+ if (isPlainObject(typed.scoreDetails)) {
223
+ const detailsOut = {};
224
+ for (const [name, detail] of Object.entries(typed.scoreDetails)) {
225
+ detailsOut[name] = redactScorerDetail(detail);
226
+ }
227
+ scrubbed.scoreDetails = detailsOut;
228
+ } else {
229
+ scrubbed.scoreDetails = REDACTED;
158
230
  }
159
- scrubbed.scoreDetails = detailsOut;
160
231
  }
161
232
  return scrubbed;
162
233
  }
163
234
  function redactEvalResult(result, redact) {
164
235
  if (!redact) return result;
165
- const meta3 = result.metadata;
166
- const scrubbedMetadata = meta3 && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : result.metadata;
236
+ return redactResultShape(result);
237
+ }
238
+ function redactResultShape(result) {
239
+ const scrubbedMetadata = redactResultMetadata(result.metadata);
240
+ const hasMultiRun = "_multiRun" in result;
241
+ const multiRun = result._multiRun;
167
242
  return {
168
243
  ...result,
169
244
  metadata: scrubbedMetadata,
170
- items: result.items.map(redactEvalItem)
245
+ items: Array.isArray(result.items) ? result.items.map(redactEvalItem) : REDACTED,
246
+ ...hasMultiRun && multiRun !== void 0 ? {
247
+ _multiRun: isPlainObject(multiRun) ? redactMultiRun(multiRun) : REDACTED
248
+ } : {}
249
+ };
250
+ }
251
+ function redactResultMetadata(metadata) {
252
+ const meta3 = metadata;
253
+ return isPlainObject(meta3) && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : metadata;
254
+ }
255
+ function redactMultiRun(multiRun) {
256
+ const { allRuns } = multiRun;
257
+ return {
258
+ ...multiRun,
259
+ ...allRuns !== void 0 ? {
260
+ allRuns: Array.isArray(allRuns) ? allRuns.map(
261
+ (run) => isPlainObject(run) && Array.isArray(run.items) ? redactResultShape(run) : REDACTED
262
+ ) : REDACTED
263
+ } : {},
264
+ ...typeof multiRun.batchFailure === "string" ? { batchFailure: REDACTED } : {}
265
+ };
266
+ }
267
+ function redactEvalComparison(comparison, redact) {
268
+ if (!redact) return comparison;
269
+ const maskInput = (r) => ({ ...r, input: REDACTED });
270
+ const side = (s) => ({
271
+ ...s,
272
+ metadata: redactResultMetadata(s.metadata)
273
+ });
274
+ return {
275
+ ...comparison,
276
+ baseline: side(comparison.baseline),
277
+ candidate: side(comparison.candidate),
278
+ regressions: comparison.regressions.map(maskInput),
279
+ improvements: comparison.improvements.map(maskInput)
171
280
  };
172
281
  }
282
+ function redactRecordLine(line, redact) {
283
+ if (!redact) return line;
284
+ let parsed;
285
+ try {
286
+ parsed = JSON.parse(line);
287
+ } catch {
288
+ const stub = {
289
+ v: 1,
290
+ phase: "end",
291
+ operationId: "unknown",
292
+ kind: "chat",
293
+ transportAttempts: 1,
294
+ provider: "unknown",
295
+ model: "unknown",
296
+ termination: "the stored record could not be parsed",
297
+ captured: {
298
+ fidelity: "runtime_request",
299
+ redacted: true,
300
+ truncated: true,
301
+ omitted: ["record"]
302
+ }
303
+ };
304
+ return JSON.stringify(stub);
305
+ }
306
+ return JSON.stringify((0, import_axl.redactCapturedRequest)(parsed));
307
+ }
173
308
  function redactEvalHistoryEntry(entry, redact) {
174
309
  if (!redact) return entry;
175
310
  return {
@@ -946,7 +1081,50 @@ function reduceCost(acc, event) {
946
1081
  };
947
1082
  }
948
1083
  function emptyEvalTrendData() {
949
- return { byEval: {}, totalRuns: 0, totalCost: 0 };
1084
+ return { byEval: {}, totalRuns: 0, totalCost: 0, totalCostCompleteness: "complete" };
1085
+ }
1086
+ function worse(a, b) {
1087
+ if (a === "unverified" || b === "unverified") return "unverified";
1088
+ if (a === "incomplete" || b === "incomplete") return "incomplete";
1089
+ return "complete";
1090
+ }
1091
+ function extractAccounting(data) {
1092
+ const result = data ?? {};
1093
+ const accounting = result.accounting;
1094
+ if (!accounting || typeof accounting !== "object") {
1095
+ return { cost: usable(extractCost(data)), completeness: "unverified", budgetStopped: false };
1096
+ }
1097
+ const completeness = accounting.completeness === "complete" || accounting.completeness === "incomplete" ? accounting.completeness : "unverified";
1098
+ const knownCost = usable(accounting.knownCost);
1099
+ const summary = result.summary;
1100
+ return {
1101
+ cost: knownCost,
1102
+ completeness,
1103
+ budgetStopped: isBudgetStopped(accounting.budget?.status, summary?.coverage)
1104
+ };
1105
+ }
1106
+ function usable(value) {
1107
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
1108
+ }
1109
+ function refusedWork(coverage) {
1110
+ if (!coverage || typeof coverage !== "object") return 0;
1111
+ const { items, scorers } = coverage;
1112
+ let refused = count(items, "budget_skipped") + count(items, "budget_interrupted");
1113
+ if (scorers && typeof scorers === "object") {
1114
+ for (const s of Object.values(scorers)) {
1115
+ refused += count(s, "budget_skipped") + count(s, "budget_interrupted");
1116
+ }
1117
+ }
1118
+ return refused;
1119
+ }
1120
+ function count(block, key) {
1121
+ if (!block || typeof block !== "object") return 0;
1122
+ const value = block[key];
1123
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1124
+ }
1125
+ function isBudgetStopped(status, coverage) {
1126
+ if (status !== "closed") return false;
1127
+ return refusedWork(coverage) > 0;
950
1128
  }
951
1129
  function extractScores(data) {
952
1130
  if (!data || typeof data !== "object") return {};
@@ -1013,7 +1191,7 @@ function computeScoreStats(runs) {
1013
1191
  }
1014
1192
  function reduceEvalTrends(acc, entry) {
1015
1193
  const scores = extractScores(entry.data);
1016
- const cost = extractCost(entry.data);
1194
+ const { cost, completeness, budgetStopped } = extractAccounting(entry.data);
1017
1195
  const model = extractModel(entry.data);
1018
1196
  const duration3 = extractDuration(entry.data);
1019
1197
  const metadata = entry.data?.metadata;
@@ -1024,6 +1202,8 @@ function reduceEvalTrends(acc, entry) {
1024
1202
  id: entry.id,
1025
1203
  scores,
1026
1204
  cost,
1205
+ completeness,
1206
+ ...budgetStopped ? { budgetStopped: true } : {},
1027
1207
  ...model !== void 0 ? { model } : {},
1028
1208
  ...duration3 !== void 0 ? { duration: duration3 } : {},
1029
1209
  ...runGroupId !== void 0 ? { runGroupId } : {},
@@ -1042,12 +1222,15 @@ function reduceEvalTrends(acc, entry) {
1042
1222
  scoreMean: mean,
1043
1223
  scoreStd: std,
1044
1224
  costTotal: (prev?.costTotal ?? 0) + cost,
1225
+ costCompleteness: worse(prev?.costCompleteness ?? "complete", completeness),
1226
+ budgetStoppedRuns: (prev?.budgetStoppedRuns ?? 0) + (budgetStopped ? 1 : 0),
1045
1227
  runCount: (prev?.runCount ?? 0) + 1
1046
1228
  };
1047
1229
  return {
1048
1230
  byEval,
1049
1231
  totalRuns: acc.totalRuns + 1,
1050
- totalCost: acc.totalCost + cost
1232
+ totalCost: acc.totalCost + cost,
1233
+ totalCostCompleteness: worse(acc.totalCostCompleteness ?? "complete", completeness)
1051
1234
  };
1052
1235
  }
1053
1236
  var MAX_DURATIONS = 200;
@@ -1369,21 +1552,32 @@ var executions_default = createExecutionRoutes();
1369
1552
 
1370
1553
  // src/server/routes/sessions.ts
1371
1554
  var import_hono4 = require("hono");
1555
+ function mediaFields(source) {
1556
+ 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;
1557
+ return {
1558
+ source: source.type,
1559
+ ...source.mediaType ? { mediaType: source.mediaType } : {},
1560
+ ...bytes !== void 0 ? { bytes } : {}
1561
+ };
1562
+ }
1372
1563
  function describeStudioInput(content) {
1373
1564
  if (typeof content === "string") return void 0;
1374
1565
  return {
1375
1566
  parts: content.map((part) => {
1376
- if (part.type === "text") return { type: "text", characters: part.text.length };
1377
- const { source } = part;
1378
- 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;
1379
- return {
1380
- type: "image",
1381
- source: source.type,
1382
- ...source.mediaType ? { mediaType: source.mediaType } : {},
1383
- ...bytes !== void 0 ? { bytes } : {}
1384
- // URLs, provider-file references, and labels are intentionally not
1385
- // included: this is a Studio-safe descriptor, not a replay payload.
1386
- };
1567
+ switch (part.type) {
1568
+ case "text":
1569
+ return { type: "text", characters: part.text.length };
1570
+ case "image":
1571
+ return { type: "image", ...mediaFields(part.source) };
1572
+ case "audio":
1573
+ return { type: "audio", ...mediaFields(part.source) };
1574
+ default: {
1575
+ const unreachable = part;
1576
+ throw new Error(
1577
+ `unsupported model input part: ${String(unreachable.type)}`
1578
+ );
1579
+ }
1580
+ }
1387
1581
  })
1388
1582
  };
1389
1583
  }
@@ -1737,6 +1931,184 @@ function createCostRoutes(costAggregator) {
1737
1931
  // src/server/routes/evals.ts
1738
1932
  var import_node_crypto = require("crypto");
1739
1933
  var import_hono10 = require("hono");
1934
+
1935
+ // src/server/eval-import.ts
1936
+ var ITEM_OUTCOMES = [
1937
+ "completed",
1938
+ "failed",
1939
+ "cancelled",
1940
+ "budget_skipped",
1941
+ "budget_interrupted"
1942
+ ];
1943
+ var SCORER_OUTCOMES = [
1944
+ "scored",
1945
+ "failed",
1946
+ "skipped",
1947
+ "cancelled",
1948
+ "budget_skipped",
1949
+ "budget_interrupted"
1950
+ ];
1951
+ var EPSILON = 1e-9;
1952
+ function isFiniteNonNegative(value) {
1953
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
1954
+ }
1955
+ function isCount(value) {
1956
+ return isFiniteNonNegative(value) && Number.isInteger(value);
1957
+ }
1958
+ function isValidImportedBudget(value) {
1959
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1960
+ const b = value;
1961
+ if (!isFiniteNonNegative(b.limit)) return false;
1962
+ if (!isFiniteNonNegative(b.knownSpend)) return false;
1963
+ if (!isFiniteNonNegative(b.knownOvershoot)) return false;
1964
+ if (b.status !== "open" && b.status !== "closed") return false;
1965
+ if (b.closedBy !== void 0 && b.closedBy !== "case" && b.closedBy !== "scorer" && b.closedBy !== "operation") {
1966
+ return false;
1967
+ }
1968
+ if (b.knownSpend >= b.limit !== (b.status === "closed")) return false;
1969
+ const expected = Math.max(0, b.knownSpend - b.limit);
1970
+ return Math.abs(b.knownOvershoot - expected) <= EPSILON;
1971
+ }
1972
+ function isValidImportedCoverage(value) {
1973
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1974
+ const c = value;
1975
+ if (!c.items || typeof c.items !== "object" || Array.isArray(c.items)) return false;
1976
+ const items = c.items;
1977
+ for (const key of ITEM_OUTCOMES) {
1978
+ if (!isCount(items[key])) return false;
1979
+ }
1980
+ if (!c.scorers || typeof c.scorers !== "object" || Array.isArray(c.scorers)) return false;
1981
+ for (const bucket of Object.values(c.scorers)) {
1982
+ if (!bucket || typeof bucket !== "object" || Array.isArray(bucket)) return false;
1983
+ const counts = bucket;
1984
+ for (const key of SCORER_OUTCOMES) {
1985
+ if (!isCount(counts[key])) return false;
1986
+ }
1987
+ }
1988
+ return true;
1989
+ }
1990
+ function isValidImportedItemErrorRate(value) {
1991
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1992
+ const r = value;
1993
+ const { failed, attempted, rate, limit, exceeded } = r;
1994
+ if (!isCount(failed) || !isCount(attempted) || failed > attempted) return false;
1995
+ if (!isFiniteNonNegative(limit) || limit > 1) return false;
1996
+ if (typeof rate !== "number" || typeof exceeded !== "boolean") return false;
1997
+ const expectedRate = attempted > 0 ? failed / attempted : 0;
1998
+ if (Math.abs(rate - expectedRate) > EPSILON) return false;
1999
+ return exceeded === (attempted > 0 && rate > limit);
2000
+ }
2001
+ function isValidImportedMultiRun(value) {
2002
+ if (!isPlainRecord(value)) return false;
2003
+ const { allRuns } = value;
2004
+ if (allRuns === void 0) return true;
2005
+ if (!Array.isArray(allRuns)) return false;
2006
+ return allRuns.every(
2007
+ (run) => isPlainRecord(run) && Array.isArray(run.items) && run.items.every(isPlainRecord)
2008
+ );
2009
+ }
2010
+ function isPlainRecord(value) {
2011
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2012
+ }
2013
+ function sumOf(record2) {
2014
+ if (record2 === void 0) return 0;
2015
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return void 0;
2016
+ let total = 0;
2017
+ for (const value of Object.values(record2)) {
2018
+ if (!isFiniteNonNegative(value)) return void 0;
2019
+ total += value;
2020
+ }
2021
+ return total;
2022
+ }
2023
+ function allNumeric(record2) {
2024
+ if (record2 === void 0) return true;
2025
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return false;
2026
+ return Object.values(record2).every(
2027
+ (value) => typeof value === "number" && Number.isFinite(value)
2028
+ );
2029
+ }
2030
+ function isValidImportedAccounting(value) {
2031
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2032
+ const a = value;
2033
+ if (a.version !== 1) return false;
2034
+ if (a.currency !== "USD") return false;
2035
+ if (!isFiniteNonNegative(a.knownCost)) return false;
2036
+ if (a.completeness !== "complete" && a.completeness !== "incomplete" && a.completeness !== "unverified") {
2037
+ return false;
2038
+ }
2039
+ if (!allNumeric(a.reasons)) return false;
2040
+ if (!allNumeric(a.usage)) return false;
2041
+ const ops = a.operations;
2042
+ if (!ops || typeof ops !== "object") return false;
2043
+ for (const key of ["total", "settled", "unknown", "denied"]) {
2044
+ if (!isFiniteNonNegative(ops[key])) return false;
2045
+ }
2046
+ if (!allNumeric(ops.byKind)) return false;
2047
+ if (ops.total !== ops.settled + ops.unknown) return false;
2048
+ if (a.completeness === "unverified") return true;
2049
+ const provenanceTotal = sumOf(a.provenance);
2050
+ if (provenanceTotal === void 0) return false;
2051
+ if (Math.abs(provenanceTotal - a.knownCost) > EPSILON) return false;
2052
+ const breakdown = a.breakdown;
2053
+ if (!breakdown || typeof breakdown !== "object") return false;
2054
+ const breakdownTotal = sumOf({
2055
+ generation: breakdown.generation,
2056
+ judging: breakdown.judging,
2057
+ external: breakdown.external
2058
+ });
2059
+ if (breakdownTotal === void 0) return false;
2060
+ if (Math.abs(breakdownTotal - a.knownCost) > EPSILON) return false;
2061
+ return true;
2062
+ }
2063
+ function importedAccountingIsTrustworthy(result) {
2064
+ if (!result || typeof result !== "object") return false;
2065
+ const r = result;
2066
+ if (r.accounting !== void 0 && !isValidImportedAccounting(r.accounting)) return false;
2067
+ const budget = r.accounting?.budget;
2068
+ if (budget !== void 0 && !isValidImportedBudget(budget)) return false;
2069
+ const coverage = r.summary?.coverage;
2070
+ if (coverage !== void 0 && !isValidImportedCoverage(coverage)) return false;
2071
+ if (!Array.isArray(r.items)) return true;
2072
+ for (const item of r.items) {
2073
+ if (!item || typeof item !== "object") continue;
2074
+ const i = item;
2075
+ if (i.accounting !== void 0 && !isValidImportedAccounting(i.accounting)) return false;
2076
+ if (i.scoreDetails && typeof i.scoreDetails === "object") {
2077
+ for (const detail of Object.values(i.scoreDetails)) {
2078
+ const d = detail;
2079
+ if (d?.accounting !== void 0 && !isValidImportedAccounting(d.accounting)) return false;
2080
+ }
2081
+ }
2082
+ }
2083
+ return true;
2084
+ }
2085
+ function stripAccounting(result) {
2086
+ const copy = structuredClone(result);
2087
+ delete copy.accounting;
2088
+ const summary = copy.summary;
2089
+ if (summary && typeof summary === "object") {
2090
+ delete summary.coverage;
2091
+ }
2092
+ const items = copy.items;
2093
+ if (Array.isArray(items)) {
2094
+ for (const item of items) {
2095
+ if (!item || typeof item !== "object") continue;
2096
+ const i = item;
2097
+ delete i.accounting;
2098
+ const details = i.scoreDetails;
2099
+ if (details && typeof details === "object") {
2100
+ for (const detail of Object.values(details)) {
2101
+ if (detail && typeof detail === "object") {
2102
+ delete detail.accounting;
2103
+ }
2104
+ }
2105
+ }
2106
+ }
2107
+ }
2108
+ return copy;
2109
+ }
2110
+
2111
+ // src/server/routes/evals.ts
1740
2112
  function createEvalRoutes(connMgr, evalLoader) {
1741
2113
  const app5 = new import_hono10.Hono();
1742
2114
  const activeRuns = /* @__PURE__ */ new Map();
@@ -1784,6 +2156,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1784
2156
  let runs = 1;
1785
2157
  let stream = false;
1786
2158
  let captureTraces = false;
2159
+ let captureRequests = false;
1787
2160
  try {
1788
2161
  const body = await c.req.json().catch(() => ({}));
1789
2162
  if (typeof body.runs === "number" && Number.isFinite(body.runs) && body.runs > 1) {
@@ -1795,6 +2168,9 @@ function createEvalRoutes(connMgr, evalLoader) {
1795
2168
  if (body.captureTraces === true) {
1796
2169
  captureTraces = true;
1797
2170
  }
2171
+ if (body.captureRequests === true) {
2172
+ captureRequests = true;
2173
+ }
1798
2174
  } catch {
1799
2175
  }
1800
2176
  if (stream) {
@@ -1818,6 +2194,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1818
2194
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1819
2195
  signal: ac.signal,
1820
2196
  captureTraces,
2197
+ captureRequests,
1821
2198
  onProgress: (event) => {
1822
2199
  if (event.type === "run_done") return;
1823
2200
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, {
@@ -1890,6 +2267,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1890
2267
  const result = await runtime.runRegisteredEval(name, {
1891
2268
  signal: ac.signal,
1892
2269
  captureTraces,
2270
+ captureRequests,
1893
2271
  onProgress: (event) => {
1894
2272
  if (event.type === "run_done") return;
1895
2273
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, event);
@@ -1921,7 +2299,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1921
2299
  try {
1922
2300
  const result2 = await runtime.runRegisteredEval(name, {
1923
2301
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1924
- captureTraces
2302
+ captureTraces,
2303
+ captureRequests
1925
2304
  });
1926
2305
  results.push(result2);
1927
2306
  } catch (err) {
@@ -1937,11 +2316,14 @@ function createEvalRoutes(connMgr, evalLoader) {
1937
2316
  const partial2 = results.length < runs;
1938
2317
  const failureMsg = runFailure ? redactErrorMessage(runFailure, redactOn) || String(runFailure) || void 0 : void 0;
1939
2318
  const aggDegraded = unionDegradedScorers(results);
2319
+ const worstItemRate = worstItemErrorRate(results);
2320
+ const { modelTiming: _runOneModelTiming, ...firstSummary } = first.summary;
1940
2321
  const result = {
1941
2322
  ...first,
1942
2323
  summary: {
1943
- ...first.summary,
1944
- ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {}
2324
+ ...firstSummary,
2325
+ ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {},
2326
+ ...worstItemRate ? { itemErrorRate: worstItemRate } : {}
1945
2327
  },
1946
2328
  _multiRun: {
1947
2329
  aggregate,
@@ -1959,7 +2341,10 @@ function createEvalRoutes(connMgr, evalLoader) {
1959
2341
  data: redactEvalResult(result, redactOn)
1960
2342
  });
1961
2343
  } else {
1962
- const result = await runtime.runRegisteredEval(name, { captureTraces });
2344
+ const result = await runtime.runRegisteredEval(name, {
2345
+ captureTraces,
2346
+ captureRequests
2347
+ });
1963
2348
  return c.json({
1964
2349
  ok: true,
1965
2350
  data: redactEvalResult(result, redactOn)
@@ -1972,6 +2357,106 @@ function createEvalRoutes(connMgr, evalLoader) {
1972
2357
  );
1973
2358
  }
1974
2359
  });
2360
+ async function resolveDiagnostics(runtime, id) {
2361
+ const entry = await runtime.getEvalResult(id);
2362
+ const artifactId = entry?.data?.diagnostics?.artifactId;
2363
+ return typeof artifactId === "string" && artifactId !== "" ? { artifactId } : void 0;
2364
+ }
2365
+ app5.get("/evals/:id/diagnostics", async (c) => {
2366
+ const runtime = c.get("runtime");
2367
+ const id = c.req.param("id");
2368
+ const resolved = await resolveDiagnostics(runtime, id);
2369
+ if (!resolved) {
2370
+ return c.json(
2371
+ {
2372
+ ok: false,
2373
+ error: {
2374
+ code: "NOT_FOUND",
2375
+ message: `Eval result "${id}" has no captured requests`
2376
+ }
2377
+ },
2378
+ 404
2379
+ );
2380
+ }
2381
+ const opened = await runtime.openDiagnosticArtifact(resolved.artifactId);
2382
+ if (!opened) {
2383
+ return c.json(
2384
+ {
2385
+ ok: false,
2386
+ error: {
2387
+ code: "NOT_FOUND",
2388
+ message: `Captured requests for eval result "${id}" are no longer available`
2389
+ }
2390
+ },
2391
+ 404
2392
+ );
2393
+ }
2394
+ return c.json({
2395
+ ok: true,
2396
+ data: {
2397
+ artifactId: opened.manifest.artifactId,
2398
+ status: opened.manifest.status,
2399
+ ...opened.manifest.reason !== void 0 ? { reason: opened.manifest.reason } : {},
2400
+ records: opened.manifest.records,
2401
+ bytes: opened.manifest.bytes,
2402
+ fidelity: opened.manifest.fidelity,
2403
+ redaction: opened.manifest.redaction,
2404
+ ...opened.manifest.expiresAt !== void 0 ? { expiresAt: opened.manifest.expiresAt } : {},
2405
+ ...opened.manifest.copiedFrom !== void 0 ? { copiedFrom: opened.manifest.copiedFrom } : {}
2406
+ }
2407
+ });
2408
+ });
2409
+ app5.get("/evals/:id/diagnostics/records", async (c) => {
2410
+ const runtime = c.get("runtime");
2411
+ const redactOn = runtime.isRedactEnabled();
2412
+ const id = c.req.param("id");
2413
+ const resolved = await resolveDiagnostics(runtime, id);
2414
+ const opened = resolved ? await runtime.openDiagnosticArtifact(resolved.artifactId) : void 0;
2415
+ if (!opened) {
2416
+ return c.json(
2417
+ {
2418
+ ok: false,
2419
+ error: {
2420
+ code: "NOT_FOUND",
2421
+ message: `Captured requests for eval result "${id}" are not available`
2422
+ }
2423
+ },
2424
+ 404
2425
+ );
2426
+ }
2427
+ let cancelled = false;
2428
+ const lines = opened.lines[Symbol.asyncIterator]();
2429
+ return new Response(
2430
+ new ReadableStream({
2431
+ async start(controller) {
2432
+ const encoder = new TextEncoder();
2433
+ try {
2434
+ for (; ; ) {
2435
+ const next = await lines.next();
2436
+ if (next.done || cancelled) break;
2437
+ controller.enqueue(encoder.encode(`${redactRecordLine(next.value, redactOn)}
2438
+ `));
2439
+ }
2440
+ if (!cancelled) controller.close();
2441
+ } catch (error48) {
2442
+ if (!cancelled) controller.error(error48);
2443
+ } finally {
2444
+ await lines.return?.().catch(() => void 0);
2445
+ }
2446
+ },
2447
+ cancel() {
2448
+ cancelled = true;
2449
+ }
2450
+ }),
2451
+ {
2452
+ status: 200,
2453
+ headers: {
2454
+ "Content-Type": "application/x-ndjson; charset=utf-8",
2455
+ "Cache-Control": "no-store"
2456
+ }
2457
+ }
2458
+ );
2459
+ });
1975
2460
  app5.post("/evals/runs/:evalRunId/cancel", (c) => {
1976
2461
  const evalRunId = c.req.param("evalRunId");
1977
2462
  const ac = activeRuns.get(evalRunId);
@@ -1991,6 +2476,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1991
2476
  const redactOn = runtime.isRedactEnabled();
1992
2477
  const name = c.req.param("name");
1993
2478
  const body = await c.req.json();
2479
+ const captureRequests = body.captureRequests === true;
1994
2480
  if (!body.resultId || typeof body.resultId !== "string") {
1995
2481
  return c.json(
1996
2482
  { ok: false, error: { code: "BAD_REQUEST", message: "resultId is required" } },
@@ -2004,8 +2490,7 @@ function createEvalRoutes(connMgr, evalLoader) {
2004
2490
  404
2005
2491
  );
2006
2492
  }
2007
- const history = await runtime.getEvalHistory();
2008
- const historyEntry = history.find((h) => h.id === body.resultId);
2493
+ const historyEntry = await runtime.getEvalResult(body.resultId);
2009
2494
  if (!historyEntry) {
2010
2495
  return c.json(
2011
2496
  { ok: false, error: { code: "NOT_FOUND", message: `Result "${body.resultId}" not found` } },
@@ -2018,7 +2503,8 @@ function createEvalRoutes(connMgr, evalLoader) {
2018
2503
  const result = await rescore(
2019
2504
  historyEntry.data,
2020
2505
  config2.scorers,
2021
- runtime
2506
+ runtime,
2507
+ captureRequests ? { captureRequests: true } : void 0
2022
2508
  );
2023
2509
  await runtime.saveEvalResult({
2024
2510
  id: result.id,
@@ -2072,8 +2558,13 @@ function createEvalRoutes(connMgr, evalLoader) {
2072
2558
  400
2073
2559
  );
2074
2560
  }
2075
- const history = await runtime.getEvalHistory();
2076
- const byId = new Map(history.map((h) => [h.id, h.data]));
2561
+ const idsOf = (v) => Array.isArray(v) ? v : [v];
2562
+ const requested = /* @__PURE__ */ new Set([...idsOf(body.baselineId), ...idsOf(body.candidateId)]);
2563
+ const byId = /* @__PURE__ */ new Map();
2564
+ for (const id of requested) {
2565
+ const entry = await runtime.getEvalResult(id);
2566
+ if (entry) byId.set(id, entry.data);
2567
+ }
2077
2568
  const missing = [];
2078
2569
  const resolveOne = (id) => {
2079
2570
  const data = byId.get(id);
@@ -2109,7 +2600,10 @@ function createEvalRoutes(connMgr, evalLoader) {
2109
2600
  }
2110
2601
  try {
2111
2602
  const result = await runtime.evalCompare(baseline, candidate, body.options);
2112
- return c.json({ ok: true, data: result });
2603
+ return c.json({
2604
+ ok: true,
2605
+ data: redactEvalComparison(result, redactOn)
2606
+ });
2113
2607
  } catch (err) {
2114
2608
  return c.json(
2115
2609
  {
@@ -2177,6 +2671,26 @@ function createEvalRoutes(connMgr, evalLoader) {
2177
2671
  const workflowsFromMeta = Array.isArray(metadataObj.workflows) ? metadataObj.workflows : [];
2178
2672
  const primaryWorkflow = workflowsFromMeta.find((w) => typeof w === "string");
2179
2673
  const evalName = trim(body.eval) ?? trim(primaryWorkflow) ?? trim(firstResult.workflow) ?? "imported";
2674
+ let readAccounting;
2675
+ let validateRequestSidecar;
2676
+ try {
2677
+ ({ readAccounting, validateRequestSidecar } = await import("@axlsdk/eval"));
2678
+ } catch {
2679
+ readAccounting = void 0;
2680
+ validateRequestSidecar = void 0;
2681
+ }
2682
+ let sidecarLines;
2683
+ if (body.requests !== void 0 && body.requests !== null) {
2684
+ if (!validateRequestSidecar) {
2685
+ return bad("importing captured requests requires @axlsdk/eval to be installed");
2686
+ }
2687
+ const validation = validateRequestSidecar(body.requests);
2688
+ if (!validation.ok) return bad(validation.reason);
2689
+ sidecarLines = validation.lines;
2690
+ }
2691
+ if (sidecarLines && validatedResults.length > 1) {
2692
+ return bad("a requests sidecar can only accompany a single result");
2693
+ }
2180
2694
  const timestamp = Date.now();
2181
2695
  const imported = [];
2182
2696
  for (const r of validatedResults) {
@@ -2186,6 +2700,42 @@ function createEvalRoutes(connMgr, evalLoader) {
2186
2700
  id,
2187
2701
  metadata: typeof r.metadata === "object" && r.metadata != null ? r.metadata : {}
2188
2702
  };
2703
+ if (entry.accounting === void 0) {
2704
+ if (entry.summary?.coverage !== void 0 && !isValidImportedCoverage(entry.summary.coverage)) {
2705
+ delete entry.summary.coverage;
2706
+ entry.metadata.importedAccounting = "invalid";
2707
+ }
2708
+ if (readAccounting) {
2709
+ entry.accounting = readAccounting(entry);
2710
+ }
2711
+ } else if (importedAccountingIsTrustworthy(entry)) {
2712
+ entry.metadata.importedAccounting = "declared";
2713
+ } else {
2714
+ const stripped = stripAccounting(entry);
2715
+ entry.accounting = readAccounting ? readAccounting(stripped) : void 0;
2716
+ entry.items = stripped.items;
2717
+ entry.summary = stripped.summary;
2718
+ entry.metadata.importedAccounting = "invalid";
2719
+ }
2720
+ if (entry.summary?.itemErrorRate !== void 0 && !isValidImportedItemErrorRate(entry.summary.itemErrorRate)) {
2721
+ delete entry.summary.itemErrorRate;
2722
+ entry.metadata.importedItemErrorRate = "invalid";
2723
+ }
2724
+ const multiRun = entry._multiRun;
2725
+ if (multiRun !== void 0 && !isValidImportedMultiRun(multiRun)) {
2726
+ delete entry._multiRun;
2727
+ entry.metadata.importedMultiRun = "invalid";
2728
+ }
2729
+ if (sidecarLines) {
2730
+ entry.diagnostics = await restageImportedRequests(runtime, id, sidecarLines);
2731
+ } else if (entry.diagnostics) {
2732
+ entry.diagnostics = {
2733
+ ...entry.diagnostics,
2734
+ artifactId: "",
2735
+ status: "unavailable",
2736
+ reason: "imported without its captured-request sidecar"
2737
+ };
2738
+ }
2189
2739
  await runtime.saveEvalResult({ id, eval: evalName, timestamp, data: entry });
2190
2740
  imported.push({ id, eval: evalName, timestamp });
2191
2741
  }
@@ -2194,12 +2744,72 @@ function createEvalRoutes(connMgr, evalLoader) {
2194
2744
  }
2195
2745
  return c.json({ ok: true, data: { imported } });
2196
2746
  });
2747
+ async function restageImportedRequests(runtime, ownerId, lines) {
2748
+ let stagedId;
2749
+ try {
2750
+ const staged = await runtime.stageDiagnosticArtifact({ kind: "eval", id: ownerId });
2751
+ stagedId = staged.artifactId;
2752
+ let bytes = 0;
2753
+ let allRedacted = lines.length > 0;
2754
+ for (const line of lines) {
2755
+ await staged.sink.append(line);
2756
+ bytes += Buffer.byteLength(line, "utf-8");
2757
+ if (allRedacted) {
2758
+ try {
2759
+ allRedacted = JSON.parse(line).captured?.redacted === true;
2760
+ } catch {
2761
+ allRedacted = false;
2762
+ }
2763
+ }
2764
+ }
2765
+ const manifest = await runtime.finalizeDiagnosticArtifact(
2766
+ staged.artifactId,
2767
+ "complete",
2768
+ void 0,
2769
+ allRedacted ? "applied" : "none"
2770
+ );
2771
+ return {
2772
+ version: 1,
2773
+ artifactId: manifest.artifactId,
2774
+ fidelity: "runtime_request",
2775
+ status: manifest.status,
2776
+ records: lines.length,
2777
+ bytes,
2778
+ redaction: manifest.redaction
2779
+ };
2780
+ } catch (error48) {
2781
+ if (stagedId !== void 0) {
2782
+ await runtime.rollbackDiagnosticArtifact(stagedId).catch(() => void 0);
2783
+ }
2784
+ return {
2785
+ version: 1,
2786
+ artifactId: "",
2787
+ fidelity: "runtime_request",
2788
+ status: "unavailable",
2789
+ reason: `captured requests could not be stored: ${error48 instanceof Error ? error48.message : String(error48)}`,
2790
+ records: 0,
2791
+ bytes: 0,
2792
+ redaction: "none"
2793
+ };
2794
+ }
2795
+ }
2197
2796
  function closeActiveRuns() {
2198
2797
  for (const ac of activeRuns.values()) ac.abort();
2199
2798
  activeRuns.clear();
2200
2799
  }
2201
2800
  return { app: app5, closeActiveRuns };
2202
2801
  }
2802
+ function worstItemErrorRate(results) {
2803
+ let worst;
2804
+ let runsExceeded = 0;
2805
+ for (const run of results) {
2806
+ const r = run.summary?.itemErrorRate;
2807
+ if (!r) continue;
2808
+ if (r.exceeded) runsExceeded++;
2809
+ if (!worst || r.rate > worst.rate) worst = r;
2810
+ }
2811
+ return worst ? { ...worst, runsExceeded } : void 0;
2812
+ }
2203
2813
  function unionDegradedScorers(results) {
2204
2814
  const byScorer = /* @__PURE__ */ new Map();
2205
2815
  for (const run of results) {
@@ -2210,8 +2820,8 @@ function unionDegradedScorers(results) {
2210
2820
  if (!existing) {
2211
2821
  byScorer.set(d.scorer, { ...d, runsAffected: 1 });
2212
2822
  } else {
2213
- const worse = d.rate > existing.rate ? d : existing;
2214
- byScorer.set(d.scorer, { ...worse, runsAffected: existing.runsAffected + 1 });
2823
+ const worse2 = d.rate > existing.rate ? d : existing;
2824
+ byScorer.set(d.scorer, { ...worse2, runsAffected: existing.runsAffected + 1 });
2215
2825
  }
2216
2826
  }
2217
2827
  }
@@ -2851,7 +3461,7 @@ __export(util_exports, {
2851
3461
  getSizableOrigin: () => getSizableOrigin,
2852
3462
  hexToUint8Array: () => hexToUint8Array,
2853
3463
  isObject: () => isObject,
2854
- isPlainObject: () => isPlainObject,
3464
+ isPlainObject: () => isPlainObject2,
2855
3465
  issue: () => issue,
2856
3466
  joinValues: () => joinValues,
2857
3467
  jsonStringifyReplacer: () => jsonStringifyReplacer,
@@ -3035,7 +3645,7 @@ var allowsEval = cached(() => {
3035
3645
  return false;
3036
3646
  }
3037
3647
  });
3038
- function isPlainObject(o) {
3648
+ function isPlainObject2(o) {
3039
3649
  if (isObject(o) === false)
3040
3650
  return false;
3041
3651
  const ctor = o.constructor;
@@ -3052,7 +3662,7 @@ function isPlainObject(o) {
3052
3662
  return true;
3053
3663
  }
3054
3664
  function shallowClone(o) {
3055
- if (isPlainObject(o))
3665
+ if (isPlainObject2(o))
3056
3666
  return { ...o };
3057
3667
  if (Array.isArray(o))
3058
3668
  return [...o];
@@ -3245,7 +3855,7 @@ function omit(schema, mask) {
3245
3855
  return clone(schema, def);
3246
3856
  }
3247
3857
  function extend(schema, shape) {
3248
- if (!isPlainObject(shape)) {
3858
+ if (!isPlainObject2(shape)) {
3249
3859
  throw new Error("Invalid input to extend: expected a plain object");
3250
3860
  }
3251
3861
  const checks = schema._zod.def.checks;
@@ -3268,7 +3878,7 @@ function extend(schema, shape) {
3268
3878
  return clone(schema, def);
3269
3879
  }
3270
3880
  function safeExtend(schema, shape) {
3271
- if (!isPlainObject(shape)) {
3881
+ if (!isPlainObject2(shape)) {
3272
3882
  throw new Error("Invalid input to safeExtend: expected a plain object");
3273
3883
  }
3274
3884
  const def = mergeDefs(schema._zod.def, {
@@ -5543,7 +6153,7 @@ function mergeValues(a, b) {
5543
6153
  if (a instanceof Date && b instanceof Date && +a === +b) {
5544
6154
  return { valid: true, data: a };
5545
6155
  }
5546
- if (isPlainObject(a) && isPlainObject(b)) {
6156
+ if (isPlainObject2(a) && isPlainObject2(b)) {
5547
6157
  const bKeys = Object.keys(b);
5548
6158
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
5549
6159
  const newObj = { ...a, ...b };
@@ -5697,7 +6307,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
5697
6307
  $ZodType.init(inst, def);
5698
6308
  inst._zod.parse = (payload, ctx) => {
5699
6309
  const input = payload.value;
5700
- if (!isPlainObject(input)) {
6310
+ if (!isPlainObject2(input)) {
5701
6311
  payload.issues.push({
5702
6312
  expected: "record",
5703
6313
  code: "invalid_type",
@@ -6705,8 +7315,8 @@ function az_default() {
6705
7315
  }
6706
7316
 
6707
7317
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/be.js
6708
- function getBelarusianPlural(count, one, few, many) {
6709
- const absCount = Math.abs(count);
7318
+ function getBelarusianPlural(count2, one, few, many) {
7319
+ const absCount = Math.abs(count2);
6710
7320
  const lastDigit = absCount % 10;
6711
7321
  const lastTwoDigits = absCount % 100;
6712
7322
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -8530,8 +9140,8 @@ function hu_default() {
8530
9140
  }
8531
9141
 
8532
9142
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/hy.js
8533
- function getArmenianPlural(count, one, many) {
8534
- return Math.abs(count) === 1 ? one : many;
9143
+ function getArmenianPlural(count2, one, many) {
9144
+ return Math.abs(count2) === 1 ? one : many;
8535
9145
  }
8536
9146
  function withDefiniteArticle(word) {
8537
9147
  if (!word)
@@ -10539,8 +11149,8 @@ function pt_default() {
10539
11149
  }
10540
11150
 
10541
11151
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/ru.js
10542
- function getRussianPlural(count, one, few, many) {
10543
- const absCount = Math.abs(count);
11152
+ function getRussianPlural(count2, one, few, many) {
11153
+ const absCount = Math.abs(count2);
10544
11154
  const lastDigit = absCount % 10;
10545
11155
  const lastTwoDigits = absCount % 100;
10546
11156
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {