@axlsdk/studio 0.23.3 → 0.24.0

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