@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.
@@ -12,7 +12,7 @@ import { cors } from "hono/cors";
12
12
  import { serveStatic } from "@hono/node-server/serve-static";
13
13
 
14
14
  // src/server/redact.ts
15
- import { redactHistoricalEvent } from "@axlsdk/axl";
15
+ import { redactCapturedRequest, redactHistoricalEvent } from "@axlsdk/axl";
16
16
  var REDACTED = "[redacted]";
17
17
  var SAFE_ERROR_NAMES = /* @__PURE__ */ new Set([
18
18
  "QuorumNotMet",
@@ -102,42 +102,177 @@ function sanitizeRichInputFailure(event) {
102
102
  }
103
103
  return event;
104
104
  }
105
+ var MEASURED_METADATA_KEYS = /* @__PURE__ */ new Set([
106
+ "models",
107
+ "modelCallCounts",
108
+ "workflows",
109
+ "workflowCallCounts",
110
+ "tokens",
111
+ "agentCalls"
112
+ ]);
113
+ function redactItemMetadata(metadata) {
114
+ if (!metadata) return metadata;
115
+ const out = {};
116
+ for (const [key, value] of Object.entries(metadata)) {
117
+ out[key] = MEASURED_METADATA_KEYS.has(key) ? value : REDACTED;
118
+ }
119
+ return out;
120
+ }
121
+ function projectItemFailure(failure) {
122
+ if (!failure || typeof failure !== "object") return void 0;
123
+ const f = failure;
124
+ if (typeof f.name !== "string") return void 0;
125
+ return {
126
+ name: f.name,
127
+ ...typeof f.provider === "string" ? { provider: f.provider } : {},
128
+ ...typeof f.status === "number" && Number.isFinite(f.status) ? { status: f.status } : {},
129
+ ...typeof f.retryable === "boolean" ? { retryable: f.retryable } : {},
130
+ ...typeof f.requestId === "string" ? { requestId: f.requestId } : {}
131
+ };
132
+ }
133
+ function isPlainObject(value) {
134
+ return typeof value === "object" && value !== null && !Array.isArray(value);
135
+ }
136
+ function redactScorerDetail(detail) {
137
+ if (!isPlainObject(detail)) return REDACTED;
138
+ const d = detail;
139
+ return {
140
+ score: d.score,
141
+ ...d.duration !== void 0 ? { duration: d.duration } : {},
142
+ ...d.cost !== void 0 ? { cost: d.cost } : {},
143
+ // `skipped` is a structural boolean (the `applies` predicate verdict),
144
+ // not user/LLM content — preserve it so the client's N/A chip renders.
145
+ ...d.skipped !== void 0 ? { skipped: d.skipped } : {},
146
+ // `outcome` and `accounting` are structural (a classification and a set
147
+ // of counts), so they survive redaction the way `skipped` does — without
148
+ // them a compliance-mode reader cannot tell a judge that was stopped on
149
+ // budget from one that scored 0.
150
+ ...d.outcome !== void 0 ? { outcome: d.outcome } : {},
151
+ ...d.accounting !== void 0 ? { accounting: d.accounting } : {},
152
+ // `diagnostics` survives for the same reason `accounting` does: it is a
153
+ // list of operation ids and statuses, not content.
154
+ ...d.diagnostics !== void 0 ? { diagnostics: d.diagnostics } : {}
155
+ // metadata deliberately omitted — may contain LLM scorer reasoning
156
+ };
157
+ }
105
158
  function redactEvalItem(item) {
159
+ if (!isPlainObject(item)) return REDACTED;
160
+ const { failure, ...rest } = item;
161
+ const typed = item;
162
+ const projectedFailure = failure !== void 0 ? projectItemFailure(failure) : void 0;
106
163
  const scrubbed = {
107
- ...item,
164
+ ...rest,
165
+ ...projectedFailure ? { failure: projectedFailure } : {},
108
166
  input: REDACTED,
109
167
  output: REDACTED,
110
- ...item.annotations !== void 0 ? { annotations: REDACTED } : {},
111
- ...item.error !== void 0 ? { error: REDACTED } : {},
112
- ...item.scorerErrors !== void 0 ? { scorerErrors: item.scorerErrors.map(() => REDACTED) } : {}
113
- };
114
- if (item.scoreDetails) {
115
- const detailsOut = {};
116
- for (const [name, detail] of Object.entries(item.scoreDetails)) {
117
- detailsOut[name] = {
118
- score: detail.score,
119
- ...detail.duration !== void 0 ? { duration: detail.duration } : {},
120
- ...detail.cost !== void 0 ? { cost: detail.cost } : {},
121
- // `skipped` is a structural boolean (the `applies` predicate verdict),
122
- // not user/LLM content — preserve it so the client's N/A chip renders.
123
- ...detail.skipped !== void 0 ? { skipped: detail.skipped } : {}
124
- // metadata deliberately omitted — may contain LLM scorer reasoning
125
- };
168
+ // `diagnostics` is spread through untouched on purpose: it holds operation
169
+ // IDs, kinds, turn/attempt indexes and a status — pointers into an artifact,
170
+ // never content. The records themselves are redacted by the core rule at
171
+ // both write time and delivery time (`redactRecordLine`).
172
+ ...typed.metadata !== void 0 ? {
173
+ metadata: isPlainObject(typed.metadata) ? redactItemMetadata(typed.metadata) : REDACTED
174
+ } : {},
175
+ ...typed.annotations !== void 0 ? { annotations: REDACTED } : {},
176
+ ...typed.error !== void 0 ? { error: REDACTED } : {},
177
+ ...typed.scorerErrors !== void 0 ? {
178
+ scorerErrors: Array.isArray(typed.scorerErrors) ? typed.scorerErrors.map(() => REDACTED) : REDACTED
179
+ } : {},
180
+ // `callerReport.metadata` is whatever the workflow callback returned — free-
181
+ // form user content, exactly like `output`, so it is dropped rather than
182
+ // masked (same treatment as scorer metadata below). Its sibling `cost` is a
183
+ // plain number and stays. (`outcome` and `accounting` are spread through
184
+ // untouched: both are structural counts and classifications, no content.)
185
+ ...typed.callerReport !== void 0 ? {
186
+ callerReport: isPlainObject(typed.callerReport) ? typeof typed.callerReport.cost === "number" ? { cost: typed.callerReport.cost } : {} : REDACTED
187
+ } : {}
188
+ };
189
+ if (typed.scoreDetails !== void 0) {
190
+ if (isPlainObject(typed.scoreDetails)) {
191
+ const detailsOut = {};
192
+ for (const [name, detail] of Object.entries(typed.scoreDetails)) {
193
+ detailsOut[name] = redactScorerDetail(detail);
194
+ }
195
+ scrubbed.scoreDetails = detailsOut;
196
+ } else {
197
+ scrubbed.scoreDetails = REDACTED;
126
198
  }
127
- scrubbed.scoreDetails = detailsOut;
128
199
  }
129
200
  return scrubbed;
130
201
  }
131
202
  function redactEvalResult(result, redact) {
132
203
  if (!redact) return result;
133
- const meta3 = result.metadata;
134
- const scrubbedMetadata = meta3 && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : result.metadata;
204
+ return redactResultShape(result);
205
+ }
206
+ function redactResultShape(result) {
207
+ const scrubbedMetadata = redactResultMetadata(result.metadata);
208
+ const hasMultiRun = "_multiRun" in result;
209
+ const multiRun = result._multiRun;
135
210
  return {
136
211
  ...result,
137
212
  metadata: scrubbedMetadata,
138
- items: result.items.map(redactEvalItem)
213
+ items: Array.isArray(result.items) ? result.items.map(redactEvalItem) : REDACTED,
214
+ ...hasMultiRun && multiRun !== void 0 ? {
215
+ _multiRun: isPlainObject(multiRun) ? redactMultiRun(multiRun) : REDACTED
216
+ } : {}
217
+ };
218
+ }
219
+ function redactResultMetadata(metadata) {
220
+ const meta3 = metadata;
221
+ return isPlainObject(meta3) && typeof meta3.batchFailure === "string" ? { ...meta3, batchFailure: REDACTED } : metadata;
222
+ }
223
+ function redactMultiRun(multiRun) {
224
+ const { allRuns } = multiRun;
225
+ return {
226
+ ...multiRun,
227
+ ...allRuns !== void 0 ? {
228
+ allRuns: Array.isArray(allRuns) ? allRuns.map(
229
+ (run) => isPlainObject(run) && Array.isArray(run.items) ? redactResultShape(run) : REDACTED
230
+ ) : REDACTED
231
+ } : {},
232
+ ...typeof multiRun.batchFailure === "string" ? { batchFailure: REDACTED } : {}
233
+ };
234
+ }
235
+ function redactEvalComparison(comparison, redact) {
236
+ if (!redact) return comparison;
237
+ const maskInput = (r) => ({ ...r, input: REDACTED });
238
+ const side = (s) => ({
239
+ ...s,
240
+ metadata: redactResultMetadata(s.metadata)
241
+ });
242
+ return {
243
+ ...comparison,
244
+ baseline: side(comparison.baseline),
245
+ candidate: side(comparison.candidate),
246
+ regressions: comparison.regressions.map(maskInput),
247
+ improvements: comparison.improvements.map(maskInput)
139
248
  };
140
249
  }
250
+ function redactRecordLine(line, redact) {
251
+ if (!redact) return line;
252
+ let parsed;
253
+ try {
254
+ parsed = JSON.parse(line);
255
+ } catch {
256
+ const stub = {
257
+ v: 1,
258
+ phase: "end",
259
+ operationId: "unknown",
260
+ kind: "chat",
261
+ transportAttempts: 1,
262
+ provider: "unknown",
263
+ model: "unknown",
264
+ termination: "the stored record could not be parsed",
265
+ captured: {
266
+ fidelity: "runtime_request",
267
+ redacted: true,
268
+ truncated: true,
269
+ omitted: ["record"]
270
+ }
271
+ };
272
+ return JSON.stringify(stub);
273
+ }
274
+ return JSON.stringify(redactCapturedRequest(parsed));
275
+ }
141
276
  function redactEvalHistoryEntry(entry, redact) {
142
277
  if (!redact) return entry;
143
278
  return {
@@ -914,7 +1049,50 @@ function reduceCost(acc, event) {
914
1049
  };
915
1050
  }
916
1051
  function emptyEvalTrendData() {
917
- return { byEval: {}, totalRuns: 0, totalCost: 0 };
1052
+ return { byEval: {}, totalRuns: 0, totalCost: 0, totalCostCompleteness: "complete" };
1053
+ }
1054
+ function worse(a, b) {
1055
+ if (a === "unverified" || b === "unverified") return "unverified";
1056
+ if (a === "incomplete" || b === "incomplete") return "incomplete";
1057
+ return "complete";
1058
+ }
1059
+ function extractAccounting(data) {
1060
+ const result = data ?? {};
1061
+ const accounting = result.accounting;
1062
+ if (!accounting || typeof accounting !== "object") {
1063
+ return { cost: usable(extractCost(data)), completeness: "unverified", budgetStopped: false };
1064
+ }
1065
+ const completeness = accounting.completeness === "complete" || accounting.completeness === "incomplete" ? accounting.completeness : "unverified";
1066
+ const knownCost = usable(accounting.knownCost);
1067
+ const summary = result.summary;
1068
+ return {
1069
+ cost: knownCost,
1070
+ completeness,
1071
+ budgetStopped: isBudgetStopped(accounting.budget?.status, summary?.coverage)
1072
+ };
1073
+ }
1074
+ function usable(value) {
1075
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
1076
+ }
1077
+ function refusedWork(coverage) {
1078
+ if (!coverage || typeof coverage !== "object") return 0;
1079
+ const { items, scorers } = coverage;
1080
+ let refused = count(items, "budget_skipped") + count(items, "budget_interrupted");
1081
+ if (scorers && typeof scorers === "object") {
1082
+ for (const s of Object.values(scorers)) {
1083
+ refused += count(s, "budget_skipped") + count(s, "budget_interrupted");
1084
+ }
1085
+ }
1086
+ return refused;
1087
+ }
1088
+ function count(block, key) {
1089
+ if (!block || typeof block !== "object") return 0;
1090
+ const value = block[key];
1091
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1092
+ }
1093
+ function isBudgetStopped(status, coverage) {
1094
+ if (status !== "closed") return false;
1095
+ return refusedWork(coverage) > 0;
918
1096
  }
919
1097
  function extractScores(data) {
920
1098
  if (!data || typeof data !== "object") return {};
@@ -981,7 +1159,7 @@ function computeScoreStats(runs) {
981
1159
  }
982
1160
  function reduceEvalTrends(acc, entry) {
983
1161
  const scores = extractScores(entry.data);
984
- const cost = extractCost(entry.data);
1162
+ const { cost, completeness, budgetStopped } = extractAccounting(entry.data);
985
1163
  const model = extractModel(entry.data);
986
1164
  const duration3 = extractDuration(entry.data);
987
1165
  const metadata = entry.data?.metadata;
@@ -992,6 +1170,8 @@ function reduceEvalTrends(acc, entry) {
992
1170
  id: entry.id,
993
1171
  scores,
994
1172
  cost,
1173
+ completeness,
1174
+ ...budgetStopped ? { budgetStopped: true } : {},
995
1175
  ...model !== void 0 ? { model } : {},
996
1176
  ...duration3 !== void 0 ? { duration: duration3 } : {},
997
1177
  ...runGroupId !== void 0 ? { runGroupId } : {},
@@ -1010,12 +1190,15 @@ function reduceEvalTrends(acc, entry) {
1010
1190
  scoreMean: mean,
1011
1191
  scoreStd: std,
1012
1192
  costTotal: (prev?.costTotal ?? 0) + cost,
1193
+ costCompleteness: worse(prev?.costCompleteness ?? "complete", completeness),
1194
+ budgetStoppedRuns: (prev?.budgetStoppedRuns ?? 0) + (budgetStopped ? 1 : 0),
1013
1195
  runCount: (prev?.runCount ?? 0) + 1
1014
1196
  };
1015
1197
  return {
1016
1198
  byEval,
1017
1199
  totalRuns: acc.totalRuns + 1,
1018
- totalCost: acc.totalCost + cost
1200
+ totalCost: acc.totalCost + cost,
1201
+ totalCostCompleteness: worse(acc.totalCostCompleteness ?? "complete", completeness)
1019
1202
  };
1020
1203
  }
1021
1204
  var MAX_DURATIONS = 200;
@@ -1337,21 +1520,32 @@ var executions_default = createExecutionRoutes();
1337
1520
 
1338
1521
  // src/server/routes/sessions.ts
1339
1522
  import { Hono as Hono4 } from "hono";
1523
+ function mediaFields(source) {
1524
+ 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;
1525
+ return {
1526
+ source: source.type,
1527
+ ...source.mediaType ? { mediaType: source.mediaType } : {},
1528
+ ...bytes !== void 0 ? { bytes } : {}
1529
+ };
1530
+ }
1340
1531
  function describeStudioInput(content) {
1341
1532
  if (typeof content === "string") return void 0;
1342
1533
  return {
1343
1534
  parts: content.map((part) => {
1344
- if (part.type === "text") return { type: "text", characters: part.text.length };
1345
- const { source } = part;
1346
- 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;
1347
- return {
1348
- type: "image",
1349
- source: source.type,
1350
- ...source.mediaType ? { mediaType: source.mediaType } : {},
1351
- ...bytes !== void 0 ? { bytes } : {}
1352
- // URLs, provider-file references, and labels are intentionally not
1353
- // included: this is a Studio-safe descriptor, not a replay payload.
1354
- };
1535
+ switch (part.type) {
1536
+ case "text":
1537
+ return { type: "text", characters: part.text.length };
1538
+ case "image":
1539
+ return { type: "image", ...mediaFields(part.source) };
1540
+ case "audio":
1541
+ return { type: "audio", ...mediaFields(part.source) };
1542
+ default: {
1543
+ const unreachable = part;
1544
+ throw new Error(
1545
+ `unsupported model input part: ${String(unreachable.type)}`
1546
+ );
1547
+ }
1548
+ }
1355
1549
  })
1356
1550
  };
1357
1551
  }
@@ -1705,6 +1899,184 @@ function createCostRoutes(costAggregator) {
1705
1899
  // src/server/routes/evals.ts
1706
1900
  import { randomUUID } from "crypto";
1707
1901
  import { Hono as Hono10 } from "hono";
1902
+
1903
+ // src/server/eval-import.ts
1904
+ var ITEM_OUTCOMES = [
1905
+ "completed",
1906
+ "failed",
1907
+ "cancelled",
1908
+ "budget_skipped",
1909
+ "budget_interrupted"
1910
+ ];
1911
+ var SCORER_OUTCOMES = [
1912
+ "scored",
1913
+ "failed",
1914
+ "skipped",
1915
+ "cancelled",
1916
+ "budget_skipped",
1917
+ "budget_interrupted"
1918
+ ];
1919
+ var EPSILON = 1e-9;
1920
+ function isFiniteNonNegative(value) {
1921
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
1922
+ }
1923
+ function isCount(value) {
1924
+ return isFiniteNonNegative(value) && Number.isInteger(value);
1925
+ }
1926
+ function isValidImportedBudget(value) {
1927
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1928
+ const b = value;
1929
+ if (!isFiniteNonNegative(b.limit)) return false;
1930
+ if (!isFiniteNonNegative(b.knownSpend)) return false;
1931
+ if (!isFiniteNonNegative(b.knownOvershoot)) return false;
1932
+ if (b.status !== "open" && b.status !== "closed") return false;
1933
+ if (b.closedBy !== void 0 && b.closedBy !== "case" && b.closedBy !== "scorer" && b.closedBy !== "operation") {
1934
+ return false;
1935
+ }
1936
+ if (b.knownSpend >= b.limit !== (b.status === "closed")) return false;
1937
+ const expected = Math.max(0, b.knownSpend - b.limit);
1938
+ return Math.abs(b.knownOvershoot - expected) <= EPSILON;
1939
+ }
1940
+ function isValidImportedCoverage(value) {
1941
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1942
+ const c = value;
1943
+ if (!c.items || typeof c.items !== "object" || Array.isArray(c.items)) return false;
1944
+ const items = c.items;
1945
+ for (const key of ITEM_OUTCOMES) {
1946
+ if (!isCount(items[key])) return false;
1947
+ }
1948
+ if (!c.scorers || typeof c.scorers !== "object" || Array.isArray(c.scorers)) return false;
1949
+ for (const bucket of Object.values(c.scorers)) {
1950
+ if (!bucket || typeof bucket !== "object" || Array.isArray(bucket)) return false;
1951
+ const counts = bucket;
1952
+ for (const key of SCORER_OUTCOMES) {
1953
+ if (!isCount(counts[key])) return false;
1954
+ }
1955
+ }
1956
+ return true;
1957
+ }
1958
+ function isValidImportedItemErrorRate(value) {
1959
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1960
+ const r = value;
1961
+ const { failed, attempted, rate, limit, exceeded } = r;
1962
+ if (!isCount(failed) || !isCount(attempted) || failed > attempted) return false;
1963
+ if (!isFiniteNonNegative(limit) || limit > 1) return false;
1964
+ if (typeof rate !== "number" || typeof exceeded !== "boolean") return false;
1965
+ const expectedRate = attempted > 0 ? failed / attempted : 0;
1966
+ if (Math.abs(rate - expectedRate) > EPSILON) return false;
1967
+ return exceeded === (attempted > 0 && rate > limit);
1968
+ }
1969
+ function isValidImportedMultiRun(value) {
1970
+ if (!isPlainRecord(value)) return false;
1971
+ const { allRuns } = value;
1972
+ if (allRuns === void 0) return true;
1973
+ if (!Array.isArray(allRuns)) return false;
1974
+ return allRuns.every(
1975
+ (run) => isPlainRecord(run) && Array.isArray(run.items) && run.items.every(isPlainRecord)
1976
+ );
1977
+ }
1978
+ function isPlainRecord(value) {
1979
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1980
+ }
1981
+ function sumOf(record2) {
1982
+ if (record2 === void 0) return 0;
1983
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return void 0;
1984
+ let total = 0;
1985
+ for (const value of Object.values(record2)) {
1986
+ if (!isFiniteNonNegative(value)) return void 0;
1987
+ total += value;
1988
+ }
1989
+ return total;
1990
+ }
1991
+ function allNumeric(record2) {
1992
+ if (record2 === void 0) return true;
1993
+ if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) return false;
1994
+ return Object.values(record2).every(
1995
+ (value) => typeof value === "number" && Number.isFinite(value)
1996
+ );
1997
+ }
1998
+ function isValidImportedAccounting(value) {
1999
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2000
+ const a = value;
2001
+ if (a.version !== 1) return false;
2002
+ if (a.currency !== "USD") return false;
2003
+ if (!isFiniteNonNegative(a.knownCost)) return false;
2004
+ if (a.completeness !== "complete" && a.completeness !== "incomplete" && a.completeness !== "unverified") {
2005
+ return false;
2006
+ }
2007
+ if (!allNumeric(a.reasons)) return false;
2008
+ if (!allNumeric(a.usage)) return false;
2009
+ const ops = a.operations;
2010
+ if (!ops || typeof ops !== "object") return false;
2011
+ for (const key of ["total", "settled", "unknown", "denied"]) {
2012
+ if (!isFiniteNonNegative(ops[key])) return false;
2013
+ }
2014
+ if (!allNumeric(ops.byKind)) return false;
2015
+ if (ops.total !== ops.settled + ops.unknown) return false;
2016
+ if (a.completeness === "unverified") return true;
2017
+ const provenanceTotal = sumOf(a.provenance);
2018
+ if (provenanceTotal === void 0) return false;
2019
+ if (Math.abs(provenanceTotal - a.knownCost) > EPSILON) return false;
2020
+ const breakdown = a.breakdown;
2021
+ if (!breakdown || typeof breakdown !== "object") return false;
2022
+ const breakdownTotal = sumOf({
2023
+ generation: breakdown.generation,
2024
+ judging: breakdown.judging,
2025
+ external: breakdown.external
2026
+ });
2027
+ if (breakdownTotal === void 0) return false;
2028
+ if (Math.abs(breakdownTotal - a.knownCost) > EPSILON) return false;
2029
+ return true;
2030
+ }
2031
+ function importedAccountingIsTrustworthy(result) {
2032
+ if (!result || typeof result !== "object") return false;
2033
+ const r = result;
2034
+ if (r.accounting !== void 0 && !isValidImportedAccounting(r.accounting)) return false;
2035
+ const budget = r.accounting?.budget;
2036
+ if (budget !== void 0 && !isValidImportedBudget(budget)) return false;
2037
+ const coverage = r.summary?.coverage;
2038
+ if (coverage !== void 0 && !isValidImportedCoverage(coverage)) return false;
2039
+ if (!Array.isArray(r.items)) return true;
2040
+ for (const item of r.items) {
2041
+ if (!item || typeof item !== "object") continue;
2042
+ const i = item;
2043
+ if (i.accounting !== void 0 && !isValidImportedAccounting(i.accounting)) return false;
2044
+ if (i.scoreDetails && typeof i.scoreDetails === "object") {
2045
+ for (const detail of Object.values(i.scoreDetails)) {
2046
+ const d = detail;
2047
+ if (d?.accounting !== void 0 && !isValidImportedAccounting(d.accounting)) return false;
2048
+ }
2049
+ }
2050
+ }
2051
+ return true;
2052
+ }
2053
+ function stripAccounting(result) {
2054
+ const copy = structuredClone(result);
2055
+ delete copy.accounting;
2056
+ const summary = copy.summary;
2057
+ if (summary && typeof summary === "object") {
2058
+ delete summary.coverage;
2059
+ }
2060
+ const items = copy.items;
2061
+ if (Array.isArray(items)) {
2062
+ for (const item of items) {
2063
+ if (!item || typeof item !== "object") continue;
2064
+ const i = item;
2065
+ delete i.accounting;
2066
+ const details = i.scoreDetails;
2067
+ if (details && typeof details === "object") {
2068
+ for (const detail of Object.values(details)) {
2069
+ if (detail && typeof detail === "object") {
2070
+ delete detail.accounting;
2071
+ }
2072
+ }
2073
+ }
2074
+ }
2075
+ }
2076
+ return copy;
2077
+ }
2078
+
2079
+ // src/server/routes/evals.ts
1708
2080
  function createEvalRoutes(connMgr, evalLoader) {
1709
2081
  const app5 = new Hono10();
1710
2082
  const activeRuns = /* @__PURE__ */ new Map();
@@ -1752,6 +2124,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1752
2124
  let runs = 1;
1753
2125
  let stream = false;
1754
2126
  let captureTraces = false;
2127
+ let captureRequests = false;
1755
2128
  try {
1756
2129
  const body = await c.req.json().catch(() => ({}));
1757
2130
  if (typeof body.runs === "number" && Number.isFinite(body.runs) && body.runs > 1) {
@@ -1763,6 +2136,9 @@ function createEvalRoutes(connMgr, evalLoader) {
1763
2136
  if (body.captureTraces === true) {
1764
2137
  captureTraces = true;
1765
2138
  }
2139
+ if (body.captureRequests === true) {
2140
+ captureRequests = true;
2141
+ }
1766
2142
  } catch {
1767
2143
  }
1768
2144
  if (stream) {
@@ -1786,6 +2162,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1786
2162
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1787
2163
  signal: ac.signal,
1788
2164
  captureTraces,
2165
+ captureRequests,
1789
2166
  onProgress: (event) => {
1790
2167
  if (event.type === "run_done") return;
1791
2168
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, {
@@ -1858,6 +2235,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1858
2235
  const result = await runtime.runRegisteredEval(name, {
1859
2236
  signal: ac.signal,
1860
2237
  captureTraces,
2238
+ captureRequests,
1861
2239
  onProgress: (event) => {
1862
2240
  if (event.type === "run_done") return;
1863
2241
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, event);
@@ -1889,7 +2267,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1889
2267
  try {
1890
2268
  const result2 = await runtime.runRegisteredEval(name, {
1891
2269
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1892
- captureTraces
2270
+ captureTraces,
2271
+ captureRequests
1893
2272
  });
1894
2273
  results.push(result2);
1895
2274
  } catch (err) {
@@ -1905,11 +2284,14 @@ function createEvalRoutes(connMgr, evalLoader) {
1905
2284
  const partial2 = results.length < runs;
1906
2285
  const failureMsg = runFailure ? redactErrorMessage(runFailure, redactOn) || String(runFailure) || void 0 : void 0;
1907
2286
  const aggDegraded = unionDegradedScorers(results);
2287
+ const worstItemRate = worstItemErrorRate(results);
2288
+ const { modelTiming: _runOneModelTiming, ...firstSummary } = first.summary;
1908
2289
  const result = {
1909
2290
  ...first,
1910
2291
  summary: {
1911
- ...first.summary,
1912
- ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {}
2292
+ ...firstSummary,
2293
+ ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {},
2294
+ ...worstItemRate ? { itemErrorRate: worstItemRate } : {}
1913
2295
  },
1914
2296
  _multiRun: {
1915
2297
  aggregate,
@@ -1927,7 +2309,10 @@ function createEvalRoutes(connMgr, evalLoader) {
1927
2309
  data: redactEvalResult(result, redactOn)
1928
2310
  });
1929
2311
  } else {
1930
- const result = await runtime.runRegisteredEval(name, { captureTraces });
2312
+ const result = await runtime.runRegisteredEval(name, {
2313
+ captureTraces,
2314
+ captureRequests
2315
+ });
1931
2316
  return c.json({
1932
2317
  ok: true,
1933
2318
  data: redactEvalResult(result, redactOn)
@@ -1940,6 +2325,106 @@ function createEvalRoutes(connMgr, evalLoader) {
1940
2325
  );
1941
2326
  }
1942
2327
  });
2328
+ async function resolveDiagnostics(runtime, id) {
2329
+ const entry = await runtime.getEvalResult(id);
2330
+ const artifactId = entry?.data?.diagnostics?.artifactId;
2331
+ return typeof artifactId === "string" && artifactId !== "" ? { artifactId } : void 0;
2332
+ }
2333
+ app5.get("/evals/:id/diagnostics", async (c) => {
2334
+ const runtime = c.get("runtime");
2335
+ const id = c.req.param("id");
2336
+ const resolved = await resolveDiagnostics(runtime, id);
2337
+ if (!resolved) {
2338
+ return c.json(
2339
+ {
2340
+ ok: false,
2341
+ error: {
2342
+ code: "NOT_FOUND",
2343
+ message: `Eval result "${id}" has no captured requests`
2344
+ }
2345
+ },
2346
+ 404
2347
+ );
2348
+ }
2349
+ const opened = await runtime.openDiagnosticArtifact(resolved.artifactId);
2350
+ if (!opened) {
2351
+ return c.json(
2352
+ {
2353
+ ok: false,
2354
+ error: {
2355
+ code: "NOT_FOUND",
2356
+ message: `Captured requests for eval result "${id}" are no longer available`
2357
+ }
2358
+ },
2359
+ 404
2360
+ );
2361
+ }
2362
+ return c.json({
2363
+ ok: true,
2364
+ data: {
2365
+ artifactId: opened.manifest.artifactId,
2366
+ status: opened.manifest.status,
2367
+ ...opened.manifest.reason !== void 0 ? { reason: opened.manifest.reason } : {},
2368
+ records: opened.manifest.records,
2369
+ bytes: opened.manifest.bytes,
2370
+ fidelity: opened.manifest.fidelity,
2371
+ redaction: opened.manifest.redaction,
2372
+ ...opened.manifest.expiresAt !== void 0 ? { expiresAt: opened.manifest.expiresAt } : {},
2373
+ ...opened.manifest.copiedFrom !== void 0 ? { copiedFrom: opened.manifest.copiedFrom } : {}
2374
+ }
2375
+ });
2376
+ });
2377
+ app5.get("/evals/:id/diagnostics/records", async (c) => {
2378
+ const runtime = c.get("runtime");
2379
+ const redactOn = runtime.isRedactEnabled();
2380
+ const id = c.req.param("id");
2381
+ const resolved = await resolveDiagnostics(runtime, id);
2382
+ const opened = resolved ? await runtime.openDiagnosticArtifact(resolved.artifactId) : void 0;
2383
+ if (!opened) {
2384
+ return c.json(
2385
+ {
2386
+ ok: false,
2387
+ error: {
2388
+ code: "NOT_FOUND",
2389
+ message: `Captured requests for eval result "${id}" are not available`
2390
+ }
2391
+ },
2392
+ 404
2393
+ );
2394
+ }
2395
+ let cancelled = false;
2396
+ const lines = opened.lines[Symbol.asyncIterator]();
2397
+ return new Response(
2398
+ new ReadableStream({
2399
+ async start(controller) {
2400
+ const encoder = new TextEncoder();
2401
+ try {
2402
+ for (; ; ) {
2403
+ const next = await lines.next();
2404
+ if (next.done || cancelled) break;
2405
+ controller.enqueue(encoder.encode(`${redactRecordLine(next.value, redactOn)}
2406
+ `));
2407
+ }
2408
+ if (!cancelled) controller.close();
2409
+ } catch (error48) {
2410
+ if (!cancelled) controller.error(error48);
2411
+ } finally {
2412
+ await lines.return?.().catch(() => void 0);
2413
+ }
2414
+ },
2415
+ cancel() {
2416
+ cancelled = true;
2417
+ }
2418
+ }),
2419
+ {
2420
+ status: 200,
2421
+ headers: {
2422
+ "Content-Type": "application/x-ndjson; charset=utf-8",
2423
+ "Cache-Control": "no-store"
2424
+ }
2425
+ }
2426
+ );
2427
+ });
1943
2428
  app5.post("/evals/runs/:evalRunId/cancel", (c) => {
1944
2429
  const evalRunId = c.req.param("evalRunId");
1945
2430
  const ac = activeRuns.get(evalRunId);
@@ -1959,6 +2444,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1959
2444
  const redactOn = runtime.isRedactEnabled();
1960
2445
  const name = c.req.param("name");
1961
2446
  const body = await c.req.json();
2447
+ const captureRequests = body.captureRequests === true;
1962
2448
  if (!body.resultId || typeof body.resultId !== "string") {
1963
2449
  return c.json(
1964
2450
  { ok: false, error: { code: "BAD_REQUEST", message: "resultId is required" } },
@@ -1972,8 +2458,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1972
2458
  404
1973
2459
  );
1974
2460
  }
1975
- const history = await runtime.getEvalHistory();
1976
- const historyEntry = history.find((h) => h.id === body.resultId);
2461
+ const historyEntry = await runtime.getEvalResult(body.resultId);
1977
2462
  if (!historyEntry) {
1978
2463
  return c.json(
1979
2464
  { ok: false, error: { code: "NOT_FOUND", message: `Result "${body.resultId}" not found` } },
@@ -1986,7 +2471,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1986
2471
  const result = await rescore(
1987
2472
  historyEntry.data,
1988
2473
  config2.scorers,
1989
- runtime
2474
+ runtime,
2475
+ captureRequests ? { captureRequests: true } : void 0
1990
2476
  );
1991
2477
  await runtime.saveEvalResult({
1992
2478
  id: result.id,
@@ -2040,8 +2526,13 @@ function createEvalRoutes(connMgr, evalLoader) {
2040
2526
  400
2041
2527
  );
2042
2528
  }
2043
- const history = await runtime.getEvalHistory();
2044
- const byId = new Map(history.map((h) => [h.id, h.data]));
2529
+ const idsOf = (v) => Array.isArray(v) ? v : [v];
2530
+ const requested = /* @__PURE__ */ new Set([...idsOf(body.baselineId), ...idsOf(body.candidateId)]);
2531
+ const byId = /* @__PURE__ */ new Map();
2532
+ for (const id of requested) {
2533
+ const entry = await runtime.getEvalResult(id);
2534
+ if (entry) byId.set(id, entry.data);
2535
+ }
2045
2536
  const missing = [];
2046
2537
  const resolveOne = (id) => {
2047
2538
  const data = byId.get(id);
@@ -2077,7 +2568,10 @@ function createEvalRoutes(connMgr, evalLoader) {
2077
2568
  }
2078
2569
  try {
2079
2570
  const result = await runtime.evalCompare(baseline, candidate, body.options);
2080
- return c.json({ ok: true, data: result });
2571
+ return c.json({
2572
+ ok: true,
2573
+ data: redactEvalComparison(result, redactOn)
2574
+ });
2081
2575
  } catch (err) {
2082
2576
  return c.json(
2083
2577
  {
@@ -2145,6 +2639,26 @@ function createEvalRoutes(connMgr, evalLoader) {
2145
2639
  const workflowsFromMeta = Array.isArray(metadataObj.workflows) ? metadataObj.workflows : [];
2146
2640
  const primaryWorkflow = workflowsFromMeta.find((w) => typeof w === "string");
2147
2641
  const evalName = trim(body.eval) ?? trim(primaryWorkflow) ?? trim(firstResult.workflow) ?? "imported";
2642
+ let readAccounting;
2643
+ let validateRequestSidecar;
2644
+ try {
2645
+ ({ readAccounting, validateRequestSidecar } = await import("@axlsdk/eval"));
2646
+ } catch {
2647
+ readAccounting = void 0;
2648
+ validateRequestSidecar = void 0;
2649
+ }
2650
+ let sidecarLines;
2651
+ if (body.requests !== void 0 && body.requests !== null) {
2652
+ if (!validateRequestSidecar) {
2653
+ return bad("importing captured requests requires @axlsdk/eval to be installed");
2654
+ }
2655
+ const validation = validateRequestSidecar(body.requests);
2656
+ if (!validation.ok) return bad(validation.reason);
2657
+ sidecarLines = validation.lines;
2658
+ }
2659
+ if (sidecarLines && validatedResults.length > 1) {
2660
+ return bad("a requests sidecar can only accompany a single result");
2661
+ }
2148
2662
  const timestamp = Date.now();
2149
2663
  const imported = [];
2150
2664
  for (const r of validatedResults) {
@@ -2154,6 +2668,42 @@ function createEvalRoutes(connMgr, evalLoader) {
2154
2668
  id,
2155
2669
  metadata: typeof r.metadata === "object" && r.metadata != null ? r.metadata : {}
2156
2670
  };
2671
+ if (entry.accounting === void 0) {
2672
+ if (entry.summary?.coverage !== void 0 && !isValidImportedCoverage(entry.summary.coverage)) {
2673
+ delete entry.summary.coverage;
2674
+ entry.metadata.importedAccounting = "invalid";
2675
+ }
2676
+ if (readAccounting) {
2677
+ entry.accounting = readAccounting(entry);
2678
+ }
2679
+ } else if (importedAccountingIsTrustworthy(entry)) {
2680
+ entry.metadata.importedAccounting = "declared";
2681
+ } else {
2682
+ const stripped = stripAccounting(entry);
2683
+ entry.accounting = readAccounting ? readAccounting(stripped) : void 0;
2684
+ entry.items = stripped.items;
2685
+ entry.summary = stripped.summary;
2686
+ entry.metadata.importedAccounting = "invalid";
2687
+ }
2688
+ if (entry.summary?.itemErrorRate !== void 0 && !isValidImportedItemErrorRate(entry.summary.itemErrorRate)) {
2689
+ delete entry.summary.itemErrorRate;
2690
+ entry.metadata.importedItemErrorRate = "invalid";
2691
+ }
2692
+ const multiRun = entry._multiRun;
2693
+ if (multiRun !== void 0 && !isValidImportedMultiRun(multiRun)) {
2694
+ delete entry._multiRun;
2695
+ entry.metadata.importedMultiRun = "invalid";
2696
+ }
2697
+ if (sidecarLines) {
2698
+ entry.diagnostics = await restageImportedRequests(runtime, id, sidecarLines);
2699
+ } else if (entry.diagnostics) {
2700
+ entry.diagnostics = {
2701
+ ...entry.diagnostics,
2702
+ artifactId: "",
2703
+ status: "unavailable",
2704
+ reason: "imported without its captured-request sidecar"
2705
+ };
2706
+ }
2157
2707
  await runtime.saveEvalResult({ id, eval: evalName, timestamp, data: entry });
2158
2708
  imported.push({ id, eval: evalName, timestamp });
2159
2709
  }
@@ -2162,12 +2712,72 @@ function createEvalRoutes(connMgr, evalLoader) {
2162
2712
  }
2163
2713
  return c.json({ ok: true, data: { imported } });
2164
2714
  });
2715
+ async function restageImportedRequests(runtime, ownerId, lines) {
2716
+ let stagedId;
2717
+ try {
2718
+ const staged = await runtime.stageDiagnosticArtifact({ kind: "eval", id: ownerId });
2719
+ stagedId = staged.artifactId;
2720
+ let bytes = 0;
2721
+ let allRedacted = lines.length > 0;
2722
+ for (const line of lines) {
2723
+ await staged.sink.append(line);
2724
+ bytes += Buffer.byteLength(line, "utf-8");
2725
+ if (allRedacted) {
2726
+ try {
2727
+ allRedacted = JSON.parse(line).captured?.redacted === true;
2728
+ } catch {
2729
+ allRedacted = false;
2730
+ }
2731
+ }
2732
+ }
2733
+ const manifest = await runtime.finalizeDiagnosticArtifact(
2734
+ staged.artifactId,
2735
+ "complete",
2736
+ void 0,
2737
+ allRedacted ? "applied" : "none"
2738
+ );
2739
+ return {
2740
+ version: 1,
2741
+ artifactId: manifest.artifactId,
2742
+ fidelity: "runtime_request",
2743
+ status: manifest.status,
2744
+ records: lines.length,
2745
+ bytes,
2746
+ redaction: manifest.redaction
2747
+ };
2748
+ } catch (error48) {
2749
+ if (stagedId !== void 0) {
2750
+ await runtime.rollbackDiagnosticArtifact(stagedId).catch(() => void 0);
2751
+ }
2752
+ return {
2753
+ version: 1,
2754
+ artifactId: "",
2755
+ fidelity: "runtime_request",
2756
+ status: "unavailable",
2757
+ reason: `captured requests could not be stored: ${error48 instanceof Error ? error48.message : String(error48)}`,
2758
+ records: 0,
2759
+ bytes: 0,
2760
+ redaction: "none"
2761
+ };
2762
+ }
2763
+ }
2165
2764
  function closeActiveRuns() {
2166
2765
  for (const ac of activeRuns.values()) ac.abort();
2167
2766
  activeRuns.clear();
2168
2767
  }
2169
2768
  return { app: app5, closeActiveRuns };
2170
2769
  }
2770
+ function worstItemErrorRate(results) {
2771
+ let worst;
2772
+ let runsExceeded = 0;
2773
+ for (const run of results) {
2774
+ const r = run.summary?.itemErrorRate;
2775
+ if (!r) continue;
2776
+ if (r.exceeded) runsExceeded++;
2777
+ if (!worst || r.rate > worst.rate) worst = r;
2778
+ }
2779
+ return worst ? { ...worst, runsExceeded } : void 0;
2780
+ }
2171
2781
  function unionDegradedScorers(results) {
2172
2782
  const byScorer = /* @__PURE__ */ new Map();
2173
2783
  for (const run of results) {
@@ -2178,8 +2788,8 @@ function unionDegradedScorers(results) {
2178
2788
  if (!existing) {
2179
2789
  byScorer.set(d.scorer, { ...d, runsAffected: 1 });
2180
2790
  } else {
2181
- const worse = d.rate > existing.rate ? d : existing;
2182
- byScorer.set(d.scorer, { ...worse, runsAffected: existing.runsAffected + 1 });
2791
+ const worse2 = d.rate > existing.rate ? d : existing;
2792
+ byScorer.set(d.scorer, { ...worse2, runsAffected: existing.runsAffected + 1 });
2183
2793
  }
2184
2794
  }
2185
2795
  }
@@ -2819,7 +3429,7 @@ __export(util_exports, {
2819
3429
  getSizableOrigin: () => getSizableOrigin,
2820
3430
  hexToUint8Array: () => hexToUint8Array,
2821
3431
  isObject: () => isObject,
2822
- isPlainObject: () => isPlainObject,
3432
+ isPlainObject: () => isPlainObject2,
2823
3433
  issue: () => issue,
2824
3434
  joinValues: () => joinValues,
2825
3435
  jsonStringifyReplacer: () => jsonStringifyReplacer,
@@ -3003,7 +3613,7 @@ var allowsEval = cached(() => {
3003
3613
  return false;
3004
3614
  }
3005
3615
  });
3006
- function isPlainObject(o) {
3616
+ function isPlainObject2(o) {
3007
3617
  if (isObject(o) === false)
3008
3618
  return false;
3009
3619
  const ctor = o.constructor;
@@ -3020,7 +3630,7 @@ function isPlainObject(o) {
3020
3630
  return true;
3021
3631
  }
3022
3632
  function shallowClone(o) {
3023
- if (isPlainObject(o))
3633
+ if (isPlainObject2(o))
3024
3634
  return { ...o };
3025
3635
  if (Array.isArray(o))
3026
3636
  return [...o];
@@ -3213,7 +3823,7 @@ function omit(schema, mask) {
3213
3823
  return clone(schema, def);
3214
3824
  }
3215
3825
  function extend(schema, shape) {
3216
- if (!isPlainObject(shape)) {
3826
+ if (!isPlainObject2(shape)) {
3217
3827
  throw new Error("Invalid input to extend: expected a plain object");
3218
3828
  }
3219
3829
  const checks = schema._zod.def.checks;
@@ -3236,7 +3846,7 @@ function extend(schema, shape) {
3236
3846
  return clone(schema, def);
3237
3847
  }
3238
3848
  function safeExtend(schema, shape) {
3239
- if (!isPlainObject(shape)) {
3849
+ if (!isPlainObject2(shape)) {
3240
3850
  throw new Error("Invalid input to safeExtend: expected a plain object");
3241
3851
  }
3242
3852
  const def = mergeDefs(schema._zod.def, {
@@ -5511,7 +6121,7 @@ function mergeValues(a, b) {
5511
6121
  if (a instanceof Date && b instanceof Date && +a === +b) {
5512
6122
  return { valid: true, data: a };
5513
6123
  }
5514
- if (isPlainObject(a) && isPlainObject(b)) {
6124
+ if (isPlainObject2(a) && isPlainObject2(b)) {
5515
6125
  const bKeys = Object.keys(b);
5516
6126
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
5517
6127
  const newObj = { ...a, ...b };
@@ -5665,7 +6275,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
5665
6275
  $ZodType.init(inst, def);
5666
6276
  inst._zod.parse = (payload, ctx) => {
5667
6277
  const input = payload.value;
5668
- if (!isPlainObject(input)) {
6278
+ if (!isPlainObject2(input)) {
5669
6279
  payload.issues.push({
5670
6280
  expected: "record",
5671
6281
  code: "invalid_type",
@@ -6673,8 +7283,8 @@ function az_default() {
6673
7283
  }
6674
7284
 
6675
7285
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/be.js
6676
- function getBelarusianPlural(count, one, few, many) {
6677
- const absCount = Math.abs(count);
7286
+ function getBelarusianPlural(count2, one, few, many) {
7287
+ const absCount = Math.abs(count2);
6678
7288
  const lastDigit = absCount % 10;
6679
7289
  const lastTwoDigits = absCount % 100;
6680
7290
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -8498,8 +9108,8 @@ function hu_default() {
8498
9108
  }
8499
9109
 
8500
9110
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/hy.js
8501
- function getArmenianPlural(count, one, many) {
8502
- return Math.abs(count) === 1 ? one : many;
9111
+ function getArmenianPlural(count2, one, many) {
9112
+ return Math.abs(count2) === 1 ? one : many;
8503
9113
  }
8504
9114
  function withDefiniteArticle(word) {
8505
9115
  if (!word)
@@ -10507,8 +11117,8 @@ function pt_default() {
10507
11117
  }
10508
11118
 
10509
11119
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/ru.js
10510
- function getRussianPlural(count, one, few, many) {
10511
- const absCount = Math.abs(count);
11120
+ function getRussianPlural(count2, one, few, many) {
11121
+ const absCount = Math.abs(count2);
10512
11122
  const lastDigit = absCount % 10;
10513
11123
  const lastTwoDigits = absCount % 100;
10514
11124
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -16461,4 +17071,4 @@ export {
16461
17071
  EvalAggregator,
16462
17072
  createServer
16463
17073
  };
16464
- //# sourceMappingURL=chunk-Z374DI7D.js.map
17074
+ //# sourceMappingURL=chunk-PFPLZQ5X.js.map