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