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