@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.
@@ -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
+ } : {}
139
217
  };
140
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)
248
+ };
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;
@@ -1716,6 +1899,184 @@ function createCostRoutes(costAggregator) {
1716
1899
  // src/server/routes/evals.ts
1717
1900
  import { randomUUID } from "crypto";
1718
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
1719
2080
  function createEvalRoutes(connMgr, evalLoader) {
1720
2081
  const app5 = new Hono10();
1721
2082
  const activeRuns = /* @__PURE__ */ new Map();
@@ -1763,6 +2124,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1763
2124
  let runs = 1;
1764
2125
  let stream = false;
1765
2126
  let captureTraces = false;
2127
+ let captureRequests = false;
1766
2128
  try {
1767
2129
  const body = await c.req.json().catch(() => ({}));
1768
2130
  if (typeof body.runs === "number" && Number.isFinite(body.runs) && body.runs > 1) {
@@ -1774,6 +2136,9 @@ function createEvalRoutes(connMgr, evalLoader) {
1774
2136
  if (body.captureTraces === true) {
1775
2137
  captureTraces = true;
1776
2138
  }
2139
+ if (body.captureRequests === true) {
2140
+ captureRequests = true;
2141
+ }
1777
2142
  } catch {
1778
2143
  }
1779
2144
  if (stream) {
@@ -1797,6 +2162,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1797
2162
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1798
2163
  signal: ac.signal,
1799
2164
  captureTraces,
2165
+ captureRequests,
1800
2166
  onProgress: (event) => {
1801
2167
  if (event.type === "run_done") return;
1802
2168
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, {
@@ -1869,6 +2235,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1869
2235
  const result = await runtime.runRegisteredEval(name, {
1870
2236
  signal: ac.signal,
1871
2237
  captureTraces,
2238
+ captureRequests,
1872
2239
  onProgress: (event) => {
1873
2240
  if (event.type === "run_done") return;
1874
2241
  connMgr.broadcastWithWildcard(`eval:${evalRunId}`, event);
@@ -1900,7 +2267,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1900
2267
  try {
1901
2268
  const result2 = await runtime.runRegisteredEval(name, {
1902
2269
  metadata: { runGroupId, runIndex: r, batchAttempted: runs },
1903
- captureTraces
2270
+ captureTraces,
2271
+ captureRequests
1904
2272
  });
1905
2273
  results.push(result2);
1906
2274
  } catch (err) {
@@ -1916,11 +2284,14 @@ function createEvalRoutes(connMgr, evalLoader) {
1916
2284
  const partial2 = results.length < runs;
1917
2285
  const failureMsg = runFailure ? redactErrorMessage(runFailure, redactOn) || String(runFailure) || void 0 : void 0;
1918
2286
  const aggDegraded = unionDegradedScorers(results);
2287
+ const worstItemRate = worstItemErrorRate(results);
2288
+ const { modelTiming: _runOneModelTiming, ...firstSummary } = first.summary;
1919
2289
  const result = {
1920
2290
  ...first,
1921
2291
  summary: {
1922
- ...first.summary,
1923
- ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {}
2292
+ ...firstSummary,
2293
+ ...aggDegraded.length > 0 ? { degraded: aggDegraded } : {},
2294
+ ...worstItemRate ? { itemErrorRate: worstItemRate } : {}
1924
2295
  },
1925
2296
  _multiRun: {
1926
2297
  aggregate,
@@ -1938,7 +2309,10 @@ function createEvalRoutes(connMgr, evalLoader) {
1938
2309
  data: redactEvalResult(result, redactOn)
1939
2310
  });
1940
2311
  } else {
1941
- const result = await runtime.runRegisteredEval(name, { captureTraces });
2312
+ const result = await runtime.runRegisteredEval(name, {
2313
+ captureTraces,
2314
+ captureRequests
2315
+ });
1942
2316
  return c.json({
1943
2317
  ok: true,
1944
2318
  data: redactEvalResult(result, redactOn)
@@ -1951,6 +2325,106 @@ function createEvalRoutes(connMgr, evalLoader) {
1951
2325
  );
1952
2326
  }
1953
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
+ });
1954
2428
  app5.post("/evals/runs/:evalRunId/cancel", (c) => {
1955
2429
  const evalRunId = c.req.param("evalRunId");
1956
2430
  const ac = activeRuns.get(evalRunId);
@@ -1970,6 +2444,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1970
2444
  const redactOn = runtime.isRedactEnabled();
1971
2445
  const name = c.req.param("name");
1972
2446
  const body = await c.req.json();
2447
+ const captureRequests = body.captureRequests === true;
1973
2448
  if (!body.resultId || typeof body.resultId !== "string") {
1974
2449
  return c.json(
1975
2450
  { ok: false, error: { code: "BAD_REQUEST", message: "resultId is required" } },
@@ -1983,8 +2458,7 @@ function createEvalRoutes(connMgr, evalLoader) {
1983
2458
  404
1984
2459
  );
1985
2460
  }
1986
- const history = await runtime.getEvalHistory();
1987
- const historyEntry = history.find((h) => h.id === body.resultId);
2461
+ const historyEntry = await runtime.getEvalResult(body.resultId);
1988
2462
  if (!historyEntry) {
1989
2463
  return c.json(
1990
2464
  { ok: false, error: { code: "NOT_FOUND", message: `Result "${body.resultId}" not found` } },
@@ -1997,7 +2471,8 @@ function createEvalRoutes(connMgr, evalLoader) {
1997
2471
  const result = await rescore(
1998
2472
  historyEntry.data,
1999
2473
  config2.scorers,
2000
- runtime
2474
+ runtime,
2475
+ captureRequests ? { captureRequests: true } : void 0
2001
2476
  );
2002
2477
  await runtime.saveEvalResult({
2003
2478
  id: result.id,
@@ -2051,8 +2526,13 @@ function createEvalRoutes(connMgr, evalLoader) {
2051
2526
  400
2052
2527
  );
2053
2528
  }
2054
- const history = await runtime.getEvalHistory();
2055
- 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
+ }
2056
2536
  const missing = [];
2057
2537
  const resolveOne = (id) => {
2058
2538
  const data = byId.get(id);
@@ -2088,7 +2568,10 @@ function createEvalRoutes(connMgr, evalLoader) {
2088
2568
  }
2089
2569
  try {
2090
2570
  const result = await runtime.evalCompare(baseline, candidate, body.options);
2091
- return c.json({ ok: true, data: result });
2571
+ return c.json({
2572
+ ok: true,
2573
+ data: redactEvalComparison(result, redactOn)
2574
+ });
2092
2575
  } catch (err) {
2093
2576
  return c.json(
2094
2577
  {
@@ -2156,6 +2639,26 @@ function createEvalRoutes(connMgr, evalLoader) {
2156
2639
  const workflowsFromMeta = Array.isArray(metadataObj.workflows) ? metadataObj.workflows : [];
2157
2640
  const primaryWorkflow = workflowsFromMeta.find((w) => typeof w === "string");
2158
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
+ }
2159
2662
  const timestamp = Date.now();
2160
2663
  const imported = [];
2161
2664
  for (const r of validatedResults) {
@@ -2165,6 +2668,42 @@ function createEvalRoutes(connMgr, evalLoader) {
2165
2668
  id,
2166
2669
  metadata: typeof r.metadata === "object" && r.metadata != null ? r.metadata : {}
2167
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
+ }
2168
2707
  await runtime.saveEvalResult({ id, eval: evalName, timestamp, data: entry });
2169
2708
  imported.push({ id, eval: evalName, timestamp });
2170
2709
  }
@@ -2173,12 +2712,72 @@ function createEvalRoutes(connMgr, evalLoader) {
2173
2712
  }
2174
2713
  return c.json({ ok: true, data: { imported } });
2175
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
+ }
2176
2764
  function closeActiveRuns() {
2177
2765
  for (const ac of activeRuns.values()) ac.abort();
2178
2766
  activeRuns.clear();
2179
2767
  }
2180
2768
  return { app: app5, closeActiveRuns };
2181
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
+ }
2182
2781
  function unionDegradedScorers(results) {
2183
2782
  const byScorer = /* @__PURE__ */ new Map();
2184
2783
  for (const run of results) {
@@ -2189,8 +2788,8 @@ function unionDegradedScorers(results) {
2189
2788
  if (!existing) {
2190
2789
  byScorer.set(d.scorer, { ...d, runsAffected: 1 });
2191
2790
  } else {
2192
- const worse = d.rate > existing.rate ? d : existing;
2193
- 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 });
2194
2793
  }
2195
2794
  }
2196
2795
  }
@@ -2830,7 +3429,7 @@ __export(util_exports, {
2830
3429
  getSizableOrigin: () => getSizableOrigin,
2831
3430
  hexToUint8Array: () => hexToUint8Array,
2832
3431
  isObject: () => isObject,
2833
- isPlainObject: () => isPlainObject,
3432
+ isPlainObject: () => isPlainObject2,
2834
3433
  issue: () => issue,
2835
3434
  joinValues: () => joinValues,
2836
3435
  jsonStringifyReplacer: () => jsonStringifyReplacer,
@@ -3014,7 +3613,7 @@ var allowsEval = cached(() => {
3014
3613
  return false;
3015
3614
  }
3016
3615
  });
3017
- function isPlainObject(o) {
3616
+ function isPlainObject2(o) {
3018
3617
  if (isObject(o) === false)
3019
3618
  return false;
3020
3619
  const ctor = o.constructor;
@@ -3031,7 +3630,7 @@ function isPlainObject(o) {
3031
3630
  return true;
3032
3631
  }
3033
3632
  function shallowClone(o) {
3034
- if (isPlainObject(o))
3633
+ if (isPlainObject2(o))
3035
3634
  return { ...o };
3036
3635
  if (Array.isArray(o))
3037
3636
  return [...o];
@@ -3224,7 +3823,7 @@ function omit(schema, mask) {
3224
3823
  return clone(schema, def);
3225
3824
  }
3226
3825
  function extend(schema, shape) {
3227
- if (!isPlainObject(shape)) {
3826
+ if (!isPlainObject2(shape)) {
3228
3827
  throw new Error("Invalid input to extend: expected a plain object");
3229
3828
  }
3230
3829
  const checks = schema._zod.def.checks;
@@ -3247,7 +3846,7 @@ function extend(schema, shape) {
3247
3846
  return clone(schema, def);
3248
3847
  }
3249
3848
  function safeExtend(schema, shape) {
3250
- if (!isPlainObject(shape)) {
3849
+ if (!isPlainObject2(shape)) {
3251
3850
  throw new Error("Invalid input to safeExtend: expected a plain object");
3252
3851
  }
3253
3852
  const def = mergeDefs(schema._zod.def, {
@@ -5522,7 +6121,7 @@ function mergeValues(a, b) {
5522
6121
  if (a instanceof Date && b instanceof Date && +a === +b) {
5523
6122
  return { valid: true, data: a };
5524
6123
  }
5525
- if (isPlainObject(a) && isPlainObject(b)) {
6124
+ if (isPlainObject2(a) && isPlainObject2(b)) {
5526
6125
  const bKeys = Object.keys(b);
5527
6126
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
5528
6127
  const newObj = { ...a, ...b };
@@ -5676,7 +6275,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
5676
6275
  $ZodType.init(inst, def);
5677
6276
  inst._zod.parse = (payload, ctx) => {
5678
6277
  const input = payload.value;
5679
- if (!isPlainObject(input)) {
6278
+ if (!isPlainObject2(input)) {
5680
6279
  payload.issues.push({
5681
6280
  expected: "record",
5682
6281
  code: "invalid_type",
@@ -6684,8 +7283,8 @@ function az_default() {
6684
7283
  }
6685
7284
 
6686
7285
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/be.js
6687
- function getBelarusianPlural(count, one, few, many) {
6688
- const absCount = Math.abs(count);
7286
+ function getBelarusianPlural(count2, one, few, many) {
7287
+ const absCount = Math.abs(count2);
6689
7288
  const lastDigit = absCount % 10;
6690
7289
  const lastTwoDigits = absCount % 100;
6691
7290
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -8509,8 +9108,8 @@ function hu_default() {
8509
9108
  }
8510
9109
 
8511
9110
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/hy.js
8512
- function getArmenianPlural(count, one, many) {
8513
- return Math.abs(count) === 1 ? one : many;
9111
+ function getArmenianPlural(count2, one, many) {
9112
+ return Math.abs(count2) === 1 ? one : many;
8514
9113
  }
8515
9114
  function withDefiniteArticle(word) {
8516
9115
  if (!word)
@@ -10518,8 +11117,8 @@ function pt_default() {
10518
11117
  }
10519
11118
 
10520
11119
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/ru.js
10521
- function getRussianPlural(count, one, few, many) {
10522
- const absCount = Math.abs(count);
11120
+ function getRussianPlural(count2, one, few, many) {
11121
+ const absCount = Math.abs(count2);
10523
11122
  const lastDigit = absCount % 10;
10524
11123
  const lastTwoDigits = absCount % 100;
10525
11124
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -16472,4 +17071,4 @@ export {
16472
17071
  EvalAggregator,
16473
17072
  createServer
16474
17073
  };
16475
- //# sourceMappingURL=chunk-XK2DRXKB.js.map
17074
+ //# sourceMappingURL=chunk-PFPLZQ5X.js.map