@opensearch-project/agent-health 0.5.2 → 0.6.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.
Files changed (126) hide show
  1. package/README.md +1 -0
  2. package/cli/dist/index.js +1592 -620
  3. package/dist/assets/index-BfxtxmKc.css +1 -0
  4. package/dist/assets/index-CrjAfDHu.js +243 -0
  5. package/dist/index.html +2 -2
  6. package/docs/CLI.md +45 -2
  7. package/docs/CONFIGURATION.md +1 -1
  8. package/docs/INSTRUMENT_WITH_OTEL.md +7 -0
  9. package/docs/SDK.md +52 -3
  10. package/docs/STORAGE_INDEX_FIELD_LIMITS.md +216 -0
  11. package/docs/skills/AGENT_HEALTH.md +55 -1
  12. package/examples/eval-files/demo.eval.js +1 -1
  13. package/examples/eval-files/ops-rca-classification.eval.js +71 -0
  14. package/examples/eval-files/ops-rca-evaluator.json +15 -0
  15. package/examples/eval-files/sdk-demo.eval.js +72 -0
  16. package/examples/eval-files/sdk-describe-demo.eval.js +50 -0
  17. package/examples/eval-files/sdk-hooks-demo.eval.js +1 -1
  18. package/lib/dist/lib/bedrockCompat.d.ts +27 -0
  19. package/lib/dist/lib/bedrockCompat.d.ts.map +1 -0
  20. package/lib/dist/lib/bedrockCompat.js +83 -0
  21. package/lib/dist/lib/bedrockCompat.js.map +1 -0
  22. package/lib/dist/lib/benchmarkImage.d.ts +52 -0
  23. package/lib/dist/lib/benchmarkImage.d.ts.map +1 -0
  24. package/lib/dist/lib/benchmarkImage.js +113 -0
  25. package/lib/dist/lib/benchmarkImage.js.map +1 -0
  26. package/lib/dist/lib/benchmarkVersionUtils.d.ts +13 -0
  27. package/lib/dist/lib/benchmarkVersionUtils.d.ts.map +1 -1
  28. package/lib/dist/lib/benchmarkVersionUtils.js +21 -0
  29. package/lib/dist/lib/benchmarkVersionUtils.js.map +1 -1
  30. package/lib/dist/lib/chunkedFetch.d.ts +18 -0
  31. package/lib/dist/lib/chunkedFetch.d.ts.map +1 -0
  32. package/lib/dist/lib/chunkedFetch.js +40 -0
  33. package/lib/dist/lib/chunkedFetch.js.map +1 -0
  34. package/lib/dist/lib/comparisonInsights.d.ts +104 -0
  35. package/lib/dist/lib/comparisonInsights.d.ts.map +1 -0
  36. package/lib/dist/lib/comparisonInsights.js +212 -0
  37. package/lib/dist/lib/comparisonInsights.js.map +1 -0
  38. package/lib/dist/lib/config/loader.d.ts.map +1 -1
  39. package/lib/dist/lib/config/loader.js +5 -0
  40. package/lib/dist/lib/config/loader.js.map +1 -1
  41. package/lib/dist/lib/config/types.d.ts +14 -0
  42. package/lib/dist/lib/config/types.d.ts.map +1 -1
  43. package/lib/dist/lib/constants.d.ts +11 -0
  44. package/lib/dist/lib/constants.d.ts.map +1 -1
  45. package/lib/dist/lib/constants.js +10 -1
  46. package/lib/dist/lib/constants.js.map +1 -1
  47. package/lib/dist/lib/contextFormat.d.ts +26 -0
  48. package/lib/dist/lib/contextFormat.d.ts.map +1 -0
  49. package/lib/dist/lib/contextFormat.js +28 -0
  50. package/lib/dist/lib/contextFormat.js.map +1 -0
  51. package/lib/dist/lib/envCompat.d.ts.map +1 -1
  52. package/lib/dist/lib/envCompat.js +14 -5
  53. package/lib/dist/lib/envCompat.js.map +1 -1
  54. package/lib/dist/lib/evaluationRerun.d.ts +63 -0
  55. package/lib/dist/lib/evaluationRerun.d.ts.map +1 -0
  56. package/lib/dist/lib/evaluationRerun.js +85 -0
  57. package/lib/dist/lib/evaluationRerun.js.map +1 -0
  58. package/lib/dist/lib/matchers/traces.d.ts +17 -2
  59. package/lib/dist/lib/matchers/traces.d.ts.map +1 -1
  60. package/lib/dist/lib/matchers/traces.js +136 -16
  61. package/lib/dist/lib/matchers/traces.js.map +1 -1
  62. package/lib/dist/lib/matchers/tracesPricing.d.ts +38 -0
  63. package/lib/dist/lib/matchers/tracesPricing.d.ts.map +1 -0
  64. package/lib/dist/lib/matchers/tracesPricing.js +64 -0
  65. package/lib/dist/lib/matchers/tracesPricing.js.map +1 -0
  66. package/lib/dist/lib/runStats.d.ts +24 -0
  67. package/lib/dist/lib/runStats.d.ts.map +1 -1
  68. package/lib/dist/lib/runStats.js +32 -0
  69. package/lib/dist/lib/runStats.js.map +1 -1
  70. package/lib/dist/lib/testCases/loader.d.ts +11 -1
  71. package/lib/dist/lib/testCases/loader.d.ts.map +1 -1
  72. package/lib/dist/lib/testCases/loader.js +35 -5
  73. package/lib/dist/lib/testCases/loader.js.map +1 -1
  74. package/lib/dist/lib/utils.d.ts +15 -0
  75. package/lib/dist/lib/utils.d.ts.map +1 -1
  76. package/lib/dist/lib/utils.js +22 -0
  77. package/lib/dist/lib/utils.js.map +1 -1
  78. package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts +10 -0
  79. package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts.map +1 -1
  80. package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js +17 -3
  81. package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js.map +1 -1
  82. package/lib/dist/services/connectors/subprocess/SubprocessConnector.d.ts.map +1 -1
  83. package/lib/dist/services/connectors/subprocess/SubprocessConnector.js +8 -0
  84. package/lib/dist/services/connectors/subprocess/SubprocessConnector.js.map +1 -1
  85. package/lib/dist/services/evaluation/bedrockJudge.d.ts +7 -0
  86. package/lib/dist/services/evaluation/bedrockJudge.d.ts.map +1 -1
  87. package/lib/dist/services/evaluation/bedrockJudge.js +2 -0
  88. package/lib/dist/services/evaluation/bedrockJudge.js.map +1 -1
  89. package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts +1 -1
  90. package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts.map +1 -1
  91. package/lib/dist/services/storage/asyncBenchmarkStorage.js +14 -3
  92. package/lib/dist/services/storage/asyncBenchmarkStorage.js.map +1 -1
  93. package/lib/dist/services/storage/asyncRunStorage.d.ts +9 -1
  94. package/lib/dist/services/storage/asyncRunStorage.d.ts.map +1 -1
  95. package/lib/dist/services/storage/asyncRunStorage.js +77 -1
  96. package/lib/dist/services/storage/asyncRunStorage.js.map +1 -1
  97. package/lib/dist/services/storage/asyncTestCaseStorage.d.ts +18 -4
  98. package/lib/dist/services/storage/asyncTestCaseStorage.d.ts.map +1 -1
  99. package/lib/dist/services/storage/asyncTestCaseStorage.js +20 -4
  100. package/lib/dist/services/storage/asyncTestCaseStorage.js.map +1 -1
  101. package/lib/dist/services/storage/opensearchClient.d.ts +35 -12
  102. package/lib/dist/services/storage/opensearchClient.d.ts.map +1 -1
  103. package/lib/dist/services/storage/opensearchClient.js +12 -5
  104. package/lib/dist/services/storage/opensearchClient.js.map +1 -1
  105. package/lib/dist/services/traces/browserRecovery.d.ts +12 -1
  106. package/lib/dist/services/traces/browserRecovery.d.ts.map +1 -1
  107. package/lib/dist/services/traces/browserRecovery.js +32 -5
  108. package/lib/dist/services/traces/browserRecovery.js.map +1 -1
  109. package/lib/dist/services/traces/messageExtraction.d.ts.map +1 -1
  110. package/lib/dist/services/traces/messageExtraction.js +95 -33
  111. package/lib/dist/services/traces/messageExtraction.js.map +1 -1
  112. package/lib/dist/services/traces/spansToTrajectory.d.ts.map +1 -1
  113. package/lib/dist/services/traces/spansToTrajectory.js +47 -9
  114. package/lib/dist/services/traces/spansToTrajectory.js.map +1 -1
  115. package/lib/dist/services/traces/tracePoller.d.ts +27 -3
  116. package/lib/dist/services/traces/tracePoller.d.ts.map +1 -1
  117. package/lib/dist/services/traces/tracePoller.js +199 -33
  118. package/lib/dist/services/traces/tracePoller.js.map +1 -1
  119. package/lib/dist/types/index.d.ts +55 -1
  120. package/lib/dist/types/index.d.ts.map +1 -1
  121. package/lib/dist/types/index.js.map +1 -1
  122. package/package.json +3 -1
  123. package/server/dist/app.js +2238 -777
  124. package/server/dist/index.js +2238 -777
  125. package/dist/assets/index-CCQRDlO0.js +0 -243
  126. package/dist/assets/index-CNHQVbcj.css +0 -1
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright OpenSearch Contributors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ const DEFAULT_RUN_NAME = 'Evaluation Run';
6
+ /**
7
+ * Matches a trailing "(re-run)" or "(re-run N)" suffix, case-insensitively,
8
+ * capturing the base name and the optional numeric suffix.
9
+ */
10
+ const RERUN_SUFFIX_RE = /^(.*?)\s*\(re-run(?:\s+(\d+))?\)$/i;
11
+ /**
12
+ * Compute the name for a new run being created as a re-run of `sourceName`.
13
+ *
14
+ * - "My Run" -> "My Run (re-run)"
15
+ * - "My Run (re-run)" -> "My Run (re-run 2)"
16
+ * - "My Run (re-run 2)" -> "My Run (re-run 3)"
17
+ * - undefined/empty -> "Evaluation Run (re-run)"
18
+ */
19
+ export function computeRerunName(sourceName) {
20
+ const trimmed = (sourceName || '').trim();
21
+ const base = trimmed || DEFAULT_RUN_NAME;
22
+ const match = base.match(RERUN_SUFFIX_RE);
23
+ if (!match) {
24
+ return `${base} (re-run)`;
25
+ }
26
+ const [, rawBaseName, suffixNumber] = match;
27
+ // A source named literally "(re-run)" / "(re-run 2)" (no prefix) captures
28
+ // an empty baseName — fall back to the default rather than emitting a
29
+ // leading-space name like " (re-run 2)".
30
+ const baseName = rawBaseName.trim() || DEFAULT_RUN_NAME;
31
+ const nextNumber = suffixNumber ? parseInt(suffixNumber, 10) + 1 : 2;
32
+ return `${baseName} (re-run ${nextNumber})`;
33
+ }
34
+ /**
35
+ * Duplicate a source run's execution config for a re-run, applying explicit,
36
+ * reported defaults for fields missing on legacy run documents. Pure
37
+ * function — no storage access (benchmark-existence checks live in
38
+ * services/evaluationRerun.ts, which needs the storage module).
39
+ *
40
+ * Returns `{ error }` only when the source run is missing a field with no
41
+ * safe default (nothing to run, or no agent to run it against).
42
+ */
43
+ export function buildRerunConfig(sourceRun) {
44
+ const defaultsApplied = [];
45
+ if (!sourceRun.agentKey) {
46
+ return { error: 'Source run is missing agentKey; cannot determine which agent to re-run.' };
47
+ }
48
+ let sources = sourceRun.sources;
49
+ if (!sources || sources.length === 0) {
50
+ const snapshotIds = (sourceRun.testCaseSnapshots || []).map(s => s.id);
51
+ if (snapshotIds.length === 0) {
52
+ return {
53
+ error: 'Source run has no test cases to re-run (missing both sources and testCaseSnapshots).',
54
+ };
55
+ }
56
+ sources = [{ type: 'test-case-ids', ids: snapshotIds }];
57
+ defaultsApplied.push(`sources -> derived from testCaseSnapshots (${snapshotIds.length} test case id(s); legacy run had no sources recorded)`);
58
+ }
59
+ let concurrency = sourceRun.concurrency;
60
+ if (concurrency == null) {
61
+ concurrency = 1;
62
+ defaultsApplied.push('concurrency -> 1 (default; not set on source run)');
63
+ }
64
+ const config = {
65
+ sources,
66
+ agentKey: sourceRun.agentKey,
67
+ modelId: sourceRun.modelId || '',
68
+ judgeModelId: sourceRun.judgeModelId,
69
+ evaluatorId: sourceRun.evaluatorId,
70
+ headers: sourceRun.headers,
71
+ concurrency,
72
+ agentEndpoint: sourceRun.agentEndpoint,
73
+ description: sourceRun.description,
74
+ benchmarkId: sourceRun.benchmarkId,
75
+ benchmarkVersion: sourceRun.benchmarkVersion,
76
+ };
77
+ // NOTE: evaluatorId/judgeModelId/agentEndpoint/headers/description are
78
+ // legitimately optional on ANY run (not just legacy ones), so leaving them
79
+ // `undefined` is normal behavior, not a legacy-config gap — they are NOT
80
+ // reported in `defaultsApplied` (that list is reserved for cases where we
81
+ // had to fabricate a value because the source run's config was
82
+ // incomplete).
83
+ return { config, defaultsApplied };
84
+ }
85
+ //# sourceMappingURL=evaluationRerun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluationRerun.js","sourceRoot":"","sources":["../../evaluationRerun.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAE1C;;;GAGG;AACH,MAAM,eAAe,GAAG,oCAAoC,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAqC;IACpE,MAAM,OAAO,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,OAAO,IAAI,gBAAgB,CAAC;IAEzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,GAAG,IAAI,WAAW,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC;IAC5C,0EAA0E;IAC1E,sEAAsE;IACtE,yCAAyC;IACzC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC;IACxD,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,GAAG,QAAQ,YAAY,UAAU,GAAG,CAAC;AAC9C,CAAC;AAsCD;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAwB;IAExB,MAAM,eAAe,GAAa,EAAE,CAAC;IAErC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,yEAAyE,EAAE,CAAC;IAC9F,CAAC;IAED,IAAI,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAChC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,CAAC,SAAS,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,KAAK,EAAE,sFAAsF;aAC9F,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QACxD,eAAe,CAAC,IAAI,CAClB,8CAA8C,WAAW,CAAC,MAAM,uDAAuD,CACxH,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IACxC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;QACxB,WAAW,GAAG,CAAC,CAAC;QAChB,eAAe,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,MAAM,GAAgB;QAC1B,OAAO;QACP,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,EAAE;QAChC,YAAY,EAAE,SAAS,CAAC,YAAY;QACpC,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,WAAW;QACX,aAAa,EAAE,SAAS,CAAC,aAAa;QACtC,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,gBAAgB,EAAE,SAAS,CAAC,gBAAgB;KAC7C,CAAC;IAEF,uEAAuE;IACvE,2EAA2E;IAC3E,yEAAyE;IACzE,0EAA0E;IAC1E,+DAA+D;IAC/D,eAAe;IAEf,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;AACrC,CAAC"}
@@ -18,11 +18,26 @@
18
18
  * accessor.
19
19
  */
20
20
  import type { Span } from '../../types/index.js';
21
+ /**
22
+ * Where `totalCost` came from:
23
+ * - `'reported'` — every dollar came from a real cost attribute on a span.
24
+ * - `'computed'` — no span carried a cost attribute; the whole total was
25
+ * derived from token counts via the fallback pricing table.
26
+ * - `'mixed'` — some spans reported cost, others were computed.
27
+ * - `'none'` — no LLM spans, or no cost could be determined at all.
28
+ */
29
+ export type CostSource = 'reported' | 'computed' | 'mixed' | 'none';
21
30
  export interface TracesAccessor {
22
- /** Sum of all `gen_ai.usage.prompt_tokens + completion_tokens` across LLM spans. */
31
+ /** Sum of all prompt/completion/cache token-usage attrs across LLM spans. */
23
32
  totalTokens: number;
24
- /** Sum of all `gen_ai.usage.cost_usd` (or derived) across LLM spans. */
33
+ /**
34
+ * Sum of all reported cost attrs, falling back to a computed estimate
35
+ * (from token counts + the model pricing table) when no span reports
36
+ * cost directly. See `costSource` to tell which happened.
37
+ */
25
38
  totalCost: number;
39
+ /** Whether `totalCost` was reported by spans, computed, both, or neither. */
40
+ costSource: CostSource;
26
41
  /** Tool invocation summary derived from spans. */
27
42
  toolCalls: ReadonlyArray<{
28
43
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../../matchers/traces.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,+EAA+E;IAC/E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,6BAA6B;IAC7B,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAQpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAWxE;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,CA4CjE"}
1
+ {"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../../matchers/traces.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,UAAU,EAAE,UAAU,CAAC;IACvB,kDAAkD;IAClD,SAAS,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,+EAA+E;IAC/E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,6BAA6B;IAC7B,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CASpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAYxE;AAgCD,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,CA+IjE"}
@@ -2,6 +2,7 @@
2
2
  * Copyright OpenSearch Contributors
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
+ import { lookupModelRates, computeCost } from './tracesPricing.js';
5
6
  /**
6
7
  * Empty accessor — silent zeros. Used when the agent opted out of traces
7
8
  * (`useTraces: false`). Reading any accessor returns `0` / `[]` so user
@@ -11,6 +12,7 @@ export function emptyTracesAccessor() {
11
12
  return {
12
13
  totalTokens: 0,
13
14
  totalCost: 0,
15
+ costSource: 'none',
14
16
  toolCalls: [],
15
17
  spanDuration: () => 0,
16
18
  spans: [],
@@ -33,48 +35,166 @@ export function unavailableTracesAccessor(reason) {
33
35
  return {
34
36
  get totalTokens() { return fail(); },
35
37
  get totalCost() { return fail(); },
38
+ get costSource() { return fail(); },
36
39
  get toolCalls() { return fail(); },
37
40
  get spans() { return fail(); },
38
41
  spanDuration(_name) { return fail(); },
39
42
  };
40
43
  }
44
+ const PROMPT_TOKEN_KEYS = [
45
+ 'gen_ai.usage.prompt_tokens',
46
+ 'gen_ai.usage.input_tokens',
47
+ 'llm.usage.prompt_tokens',
48
+ 'input_tokens',
49
+ ];
50
+ const COMPLETION_TOKEN_KEYS = [
51
+ 'gen_ai.usage.completion_tokens',
52
+ 'gen_ai.usage.output_tokens',
53
+ 'llm.usage.completion_tokens',
54
+ 'output_tokens',
55
+ ];
56
+ const CACHE_READ_TOKEN_KEYS = [
57
+ 'gen_ai.usage.cache_read_input_tokens',
58
+ 'gen_ai.usage.cache_read_tokens',
59
+ 'cache_read_tokens',
60
+ ];
61
+ const CACHE_CREATION_TOKEN_KEYS = [
62
+ 'gen_ai.usage.cache_creation_input_tokens',
63
+ 'gen_ai.usage.cache_creation_tokens',
64
+ 'cache_creation_tokens',
65
+ ];
66
+ const MODEL_ID_KEYS = ['gen_ai.request.model', 'gen_ai.response.model', 'model'];
67
+ const ALL_TOKEN_KEYS = [
68
+ ...PROMPT_TOKEN_KEYS,
69
+ ...COMPLETION_TOKEN_KEYS,
70
+ ...CACHE_READ_TOKEN_KEYS,
71
+ ...CACHE_CREATION_TOKEN_KEYS,
72
+ ];
41
73
  /** Build a TracesAccessor from a flat list of spans. */
42
74
  export function buildTracesAccessor(spans) {
43
75
  let totalTokens = 0;
44
- let totalCost = 0;
76
+ let reportedCost = 0;
77
+ let computedCost = 0;
78
+ let hadReportedCost = false;
79
+ let hadComputedCost = false;
80
+ let tokenBearingSpanCount = 0;
81
+ // codex_review (PR #440): a span can have recognized tokens yet resolve
82
+ // NEITHER a reported cost NOR a computed one (unpriced/unresolved model) —
83
+ // this counter drives `costUnknowable` below so totalCost refuses to
84
+ // silently under-report just because *some other* span in the same trace
85
+ // happened to be priceable.
86
+ let unresolvedCostSpanCount = 0;
45
87
  const toolCalls = [];
46
88
  for (const span of spans) {
47
89
  const attrs = span.attributes ?? {};
48
- const promptTokens = pickNumber(attrs, [
49
- 'gen_ai.usage.prompt_tokens',
50
- 'gen_ai.usage.input_tokens',
51
- 'llm.usage.prompt_tokens',
52
- ]);
53
- const completionTokens = pickNumber(attrs, [
54
- 'gen_ai.usage.completion_tokens',
55
- 'gen_ai.usage.output_tokens',
56
- 'llm.usage.completion_tokens',
57
- ]);
90
+ const promptTokens = pickNumber(attrs, PROMPT_TOKEN_KEYS);
91
+ const completionTokens = pickNumber(attrs, COMPLETION_TOKEN_KEYS);
92
+ const cacheReadTokens = pickNumber(attrs, CACHE_READ_TOKEN_KEYS);
93
+ const cacheCreationTokens = pickNumber(attrs, CACHE_CREATION_TOKEN_KEYS);
94
+ const hasTokenAttr = promptTokens !== undefined ||
95
+ completionTokens !== undefined ||
96
+ cacheReadTokens !== undefined ||
97
+ cacheCreationTokens !== undefined;
98
+ if (hasTokenAttr)
99
+ tokenBearingSpanCount += 1;
58
100
  if (promptTokens)
59
101
  totalTokens += promptTokens;
60
102
  if (completionTokens)
61
103
  totalTokens += completionTokens;
62
- const cost = pickNumber(attrs, [
104
+ if (cacheReadTokens)
105
+ totalTokens += cacheReadTokens;
106
+ if (cacheCreationTokens)
107
+ totalTokens += cacheCreationTokens;
108
+ const reported = pickNumber(attrs, [
63
109
  'gen_ai.usage.cost_usd',
64
110
  'gen_ai.usage.cost',
65
111
  'llm.usage.cost_usd',
66
112
  ]);
67
- if (cost)
68
- totalCost += cost;
113
+ if (reported !== undefined) {
114
+ hadReportedCost = true;
115
+ reportedCost += reported;
116
+ }
117
+ else if (hasTokenAttr) {
118
+ // No reported cost attribute on this span — fall back to a computed
119
+ // estimate from token counts, if we know pricing for the model.
120
+ const modelId = pickString(attrs, MODEL_ID_KEYS);
121
+ const rates = lookupModelRates(modelId);
122
+ if (rates) {
123
+ hadComputedCost = true;
124
+ computedCost += computeCost({
125
+ input: promptTokens ?? 0,
126
+ output: completionTokens ?? 0,
127
+ cacheRead: cacheReadTokens ?? 0,
128
+ cacheCreation: cacheCreationTokens ?? 0,
129
+ }, rates);
130
+ }
131
+ else {
132
+ // This span's tokens contribute to totalTokens but NOT to totalCost —
133
+ // without this counter that would be a silent, plausible-looking
134
+ // undercount rather than the loud failure #230 asks for.
135
+ unresolvedCostSpanCount += 1;
136
+ }
137
+ }
69
138
  // Tool spans
70
139
  const toolName = pickString(attrs, ['gen_ai.tool.name', 'llm.tool.name']);
71
140
  if (toolName) {
72
141
  toolCalls.push({ name: toolName, durationMs: spanDurationMs(span) });
73
142
  }
74
143
  }
144
+ const totalCostValue = reportedCost + computedCost;
145
+ const costSource = hadReportedCost && hadComputedCost
146
+ ? 'mixed'
147
+ : hadReportedCost
148
+ ? 'reported'
149
+ : hadComputedCost
150
+ ? 'computed'
151
+ : 'none';
152
+ // Issue class: spans exist, but none of them carry any token-usage
153
+ // attribute we recognize. Reading totalTokens/totalCost would silently
154
+ // return 0, letting matchers like `expect(traces.totalCost).lessThan(2)`
155
+ // pass vacuously (mirrors the #230 fail-loud philosophy). A trace that
156
+ // genuinely has recognized-but-zero-valued attributes is fine and must
157
+ // NOT throw — only the "we found nothing we understand" case throws.
158
+ const noRecognizedTokenAttrs = spans.length > 0 && tokenBearingSpanCount === 0;
159
+ // Issue class: tokens were found, but AT LEAST ONE token-bearing span
160
+ // resolved neither a reported cost nor a computed one (unpriced model,
161
+ // no cost attribute). Pre-codex_review this only threw when EVERY span
162
+ // was unresolved — a trace with 9 priced spans and 1 unpriced span
163
+ // silently under-reported totalCost by exactly the unpriced span's real
164
+ // spend while still returning a plausible-looking number (PR #440 review
165
+ // finding, blocker). Throw only when reading totalCost (tokens themselves
166
+ // are legitimately known in this case) — any unresolved span makes the
167
+ // total untrustworthy, not just a total absence of resolved spans.
168
+ const costUnknowable = totalTokens > 0 && unresolvedCostSpanCount > 0;
75
169
  return {
76
- totalTokens,
77
- totalCost,
170
+ get totalTokens() {
171
+ if (noRecognizedTokenAttrs) {
172
+ throw new Error(`traces fixture: ${spans.length} span(s) present but none carried a recognized ` +
173
+ `token-usage attribute (checked ${ALL_TOKEN_KEYS.join(', ')}). Reading totalTokens ` +
174
+ `would silently return 0 — either this agent's spans use an unsupported attribute ` +
175
+ `shape (extend buildTracesAccessor() in lib/matchers/traces.ts), or this test case ` +
176
+ `has no LLM spans and should use traces.spans / traces.toolCalls instead of totalTokens.`);
177
+ }
178
+ return totalTokens;
179
+ },
180
+ get totalCost() {
181
+ if (noRecognizedTokenAttrs && !hadReportedCost) {
182
+ throw new Error(`traces fixture: ${spans.length} span(s) present but none carried a recognized ` +
183
+ `token-usage attribute or a cost attribute, so totalCost cannot be determined. ` +
184
+ `Reading totalCost would silently return 0.`);
185
+ }
186
+ if (costUnknowable) {
187
+ throw new Error(`traces fixture: ${unresolvedCostSpanCount} of ${tokenBearingSpanCount} span(s) with token usage ` +
188
+ `carried neither a cost attribute nor a model id matching the fallback pricing table (see ` +
189
+ `lib/matchers/tracesPricing.ts). Reading totalCost would silently UNDER-report real spend by ` +
190
+ `omitting those spans rather than reflect it -- add the model(s) to the pricing table or stamp a ` +
191
+ `cost attribute on the span(s), or use traces.spans to inspect per-span usage directly.`);
192
+ }
193
+ return totalCostValue;
194
+ },
195
+ get costSource() {
196
+ return costSource;
197
+ },
78
198
  toolCalls,
79
199
  spanDuration(name) {
80
200
  const hit = spans.find(s => s.name === name);
@@ -1 +1 @@
1
- {"version":3,"file":"traces.js","sourceRoot":"","sources":["../../../matchers/traces.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"traces.js","sourceRoot":"","sources":["../../../matchers/traces.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Fallback pricing table used by `buildTracesAccessor()` (see
3
+ * `lib/matchers/traces.ts`) to *compute* `totalCost` when spans carry
4
+ * token-usage attributes but no cost attribute (this is the common case —
5
+ * most OTel exporters, including Claude Code's, emit raw token counts and
6
+ * leave cost calculation to the consumer).
7
+ *
8
+ * Rates are USD per **million tokens** (MTok), converted to per-token below.
9
+ * These are point-in-time *estimates* maintained by hand — update them when
10
+ * pricing changes. They are intentionally separate from
11
+ * `server/services/codingAgents/pricing.ts` (used by the coding-agent
12
+ * analytics/leaderboard feature): that table silently falls back to Sonnet
13
+ * pricing for unknown models, which is the wrong behavior here — an eval
14
+ * reading `traces.totalCost` for an unpriced model must know the number is
15
+ * incomplete (see `buildTracesAccessor`'s fail-loud handling), not get a
16
+ * silently-substituted estimate.
17
+ */
18
+ export interface ModelRates {
19
+ /** USD per input (prompt) token. */
20
+ input: number;
21
+ /** USD per output (completion) token. */
22
+ output: number;
23
+ /** USD per cache-read token (cheaper than a fresh input token). */
24
+ cacheRead: number;
25
+ /** USD per cache-creation/write token (pricier than a fresh input token). */
26
+ cacheCreation: number;
27
+ }
28
+ /** Look up rates for a model id. Returns `undefined` when no rule matches. */
29
+ export declare function lookupModelRates(modelId: string | undefined): ModelRates | undefined;
30
+ export interface UsageTokens {
31
+ input: number;
32
+ output: number;
33
+ cacheRead: number;
34
+ cacheCreation: number;
35
+ }
36
+ /** Compute USD cost for a set of token counts against a model's rates. */
37
+ export declare function computeCost(tokens: UsageTokens, rates: ModelRates): number;
38
+ //# sourceMappingURL=tracesPricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracesPricing.d.ts","sourceRoot":"","sources":["../../../matchers/tracesPricing.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB;AA8CD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAMpF;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAO1E"}
@@ -0,0 +1,64 @@
1
+ /*
2
+ * Copyright OpenSearch Contributors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ const PER_MTOK = 1_000_000;
6
+ /**
7
+ * Estimated Bedrock/Anthropic Claude rates, USD per MTok:
8
+ * Sonnet class: $3 in / $15 out / $0.30 cache-read / $3.75 cache-write
9
+ * Opus class: $15 in / $75 out / $1.50 cache-read / $18.75 cache-write
10
+ * Haiku class: $0.80 in / $4 out / $0.08 cache-read / $1.00 cache-write
11
+ * Keyed by substrings matched case-insensitively against the span's model
12
+ * id (see `resolveModelId`), so both raw Anthropic ids
13
+ * (`claude-sonnet-4-6-...`) and Bedrock ids
14
+ * (`us.anthropic.claude-sonnet-4-6-...`) match.
15
+ */
16
+ const RATE_TABLE = [
17
+ {
18
+ match: /opus/i,
19
+ rates: {
20
+ input: 15 / PER_MTOK,
21
+ output: 75 / PER_MTOK,
22
+ cacheRead: 1.5 / PER_MTOK,
23
+ cacheCreation: 18.75 / PER_MTOK,
24
+ },
25
+ },
26
+ {
27
+ match: /haiku/i,
28
+ rates: {
29
+ input: 0.8 / PER_MTOK,
30
+ output: 4 / PER_MTOK,
31
+ cacheRead: 0.08 / PER_MTOK,
32
+ cacheCreation: 1.0 / PER_MTOK,
33
+ },
34
+ },
35
+ {
36
+ // Sonnet is the default Claude class; matched last so opus/haiku take
37
+ // priority, but also serves as the generic "claude" match.
38
+ match: /sonnet|claude/i,
39
+ rates: {
40
+ input: 3 / PER_MTOK,
41
+ output: 15 / PER_MTOK,
42
+ cacheRead: 0.3 / PER_MTOK,
43
+ cacheCreation: 3.75 / PER_MTOK,
44
+ },
45
+ },
46
+ ];
47
+ /** Look up rates for a model id. Returns `undefined` when no rule matches. */
48
+ export function lookupModelRates(modelId) {
49
+ if (!modelId)
50
+ return undefined;
51
+ for (const { match, rates } of RATE_TABLE) {
52
+ if (match.test(modelId))
53
+ return rates;
54
+ }
55
+ return undefined;
56
+ }
57
+ /** Compute USD cost for a set of token counts against a model's rates. */
58
+ export function computeCost(tokens, rates) {
59
+ return (tokens.input * rates.input +
60
+ tokens.output * rates.output +
61
+ tokens.cacheRead * rates.cacheRead +
62
+ tokens.cacheCreation * rates.cacheCreation);
63
+ }
64
+ //# sourceMappingURL=tracesPricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracesPricing.js","sourceRoot":"","sources":["../../../matchers/tracesPricing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH,MAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAgD;IAC9D;QACE,KAAK,EAAE,OAAO;QACd,KAAK,EAAE;YACL,KAAK,EAAE,EAAE,GAAG,QAAQ;YACpB,MAAM,EAAE,EAAE,GAAG,QAAQ;YACrB,SAAS,EAAE,GAAG,GAAG,QAAQ;YACzB,aAAa,EAAE,KAAK,GAAG,QAAQ;SAChC;KACF;IACD;QACE,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE;YACL,KAAK,EAAE,GAAG,GAAG,QAAQ;YACrB,MAAM,EAAE,CAAC,GAAG,QAAQ;YACpB,SAAS,EAAE,IAAI,GAAG,QAAQ;YAC1B,aAAa,EAAE,GAAG,GAAG,QAAQ;SAC9B;KACF;IACD;QACE,sEAAsE;QACtE,2DAA2D;QAC3D,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE;YACL,KAAK,EAAE,CAAC,GAAG,QAAQ;YACnB,MAAM,EAAE,EAAE,GAAG,QAAQ;YACrB,SAAS,EAAE,GAAG,GAAG,QAAQ;YACzB,aAAa,EAAE,IAAI,GAAG,QAAQ;SAC/B;KACF;CACF,CAAC;AAEF,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,OAA2B;IAC1D,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,UAAU,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AASD,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,MAAmB,EAAE,KAAiB;IAChE,OAAO,CACL,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;QAC1B,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAC5B,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS;QAClC,MAAM,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAC3C,CAAC;AACJ,CAAC"}
@@ -88,5 +88,29 @@ export declare function getReportIdsFromRun(run: BenchmarkRun): string[];
88
88
  * @param reports - Array of reports for this run
89
89
  * @returns Denormalized stats object (passed, failed, pending, total)
90
90
  */
91
+ /**
92
+ * Canonical "display" stats for a run detail/list view: prefer recomputing
93
+ * from the persisted per-test-case verdicts (`run.results`) via
94
+ * {@link bucketRunResults} — the single source of truth — and only fall
95
+ * back to the denormalized `run.stats` when `results` is empty (e.g. a run
96
+ * that hasn't started, or legacy data with no results map at all).
97
+ *
98
+ * This is THE single shared helper for pass/fail/errored/pending/total
99
+ * across every surface that renders run stats (runs list, benchmark runs
100
+ * list, run detail page, comparison page) — do not reimplement this logic
101
+ * locally in a component or add a differently-named twin. Historically this
102
+ * logic was duplicated per-page (each with its own subtly different bugs,
103
+ * e.g. `errored` not tracked, or `run.stats` trusted directly even when
104
+ * stale/inflated for trace-judged runs — a 66/84-real-passed run displaying
105
+ * as 84/84). Callers that only need `{passed, failed, errored, total}` can
106
+ * simply ignore the extra `pending` field.
107
+ */
108
+ export declare function computeRunStats(run: {
109
+ results?: Record<string, {
110
+ status?: string;
111
+ passFailStatus?: string;
112
+ }>;
113
+ stats?: Partial<RunStatsType> | null;
114
+ }): Pick<RunStatsType, 'passed' | 'failed' | 'errored' | 'pending' | 'total'>;
91
115
  export declare function computeRunStatsFromReports(run: BenchmarkRun, reports: EvaluationReport[]): RunStatsType;
92
116
  //# sourceMappingURL=runStats.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runStats.d.ts","sourceRoot":"","sources":["../../runStats.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjG;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,GAChF,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC,CAevE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,GAC/C,QAAQ,CAuEV;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAU/D;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,gBAAgB,EAAE,GAC1B,YAAY,CAed"}
1
+ {"version":3,"file":"runStats.d.ts","sourceRoot":"","sources":["../../runStats.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjG;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,GAChF,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC,CAevE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,GAC/C,QAAQ,CAuEV;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAU/D;AAED;;;;;;;GAOG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;CACtC,GACA,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC,CAc3E;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,gBAAgB,EAAE,GAC1B,YAAY,CAed"}
@@ -143,6 +143,38 @@ export function getReportIdsFromRun(run) {
143
143
  * @param reports - Array of reports for this run
144
144
  * @returns Denormalized stats object (passed, failed, pending, total)
145
145
  */
146
+ /**
147
+ * Canonical "display" stats for a run detail/list view: prefer recomputing
148
+ * from the persisted per-test-case verdicts (`run.results`) via
149
+ * {@link bucketRunResults} — the single source of truth — and only fall
150
+ * back to the denormalized `run.stats` when `results` is empty (e.g. a run
151
+ * that hasn't started, or legacy data with no results map at all).
152
+ *
153
+ * This is THE single shared helper for pass/fail/errored/pending/total
154
+ * across every surface that renders run stats (runs list, benchmark runs
155
+ * list, run detail page, comparison page) — do not reimplement this logic
156
+ * locally in a component or add a differently-named twin. Historically this
157
+ * logic was duplicated per-page (each with its own subtly different bugs,
158
+ * e.g. `errored` not tracked, or `run.stats` trusted directly even when
159
+ * stale/inflated for trace-judged runs — a 66/84-real-passed run displaying
160
+ * as 84/84). Callers that only need `{passed, failed, errored, total}` can
161
+ * simply ignore the extra `pending` field.
162
+ */
163
+ export function computeRunStats(run) {
164
+ if (run.results && Object.keys(run.results).length > 0) {
165
+ return bucketRunResults(run.results);
166
+ }
167
+ if (run.stats && (run.stats.total ?? 0) > 0) {
168
+ return {
169
+ passed: run.stats.passed ?? 0,
170
+ failed: run.stats.failed ?? 0,
171
+ errored: run.stats.errored ?? 0,
172
+ pending: run.stats.pending ?? 0,
173
+ total: run.stats.total ?? 0,
174
+ };
175
+ }
176
+ return { passed: 0, failed: 0, errored: 0, pending: 0, total: 0 };
177
+ }
146
178
  export function computeRunStatsFromReports(run, reports) {
147
179
  const reportsMap = {};
148
180
  reports.forEach(report => {
@@ -1 +1 @@
1
- {"version":3,"file":"runStats.js","sourceRoot":"","sources":["../../runStats.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runStats.js","sourceRoot":"","sources":["../../runStats.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { CodeTestCase, RegisteredHook } from './types.js';
2
2
  export declare function isCodeFile(filename: string): boolean;
3
- export declare function computeTestCaseHash(tc: CodeTestCase): string;
3
+ export { detectSourceLanguage } from '../../lib/utils.js';
4
+ export declare function computeTestCaseHash(tc: CodeTestCase, fileSource?: string): string;
4
5
  export interface LoadedTestCase extends CodeTestCase {
5
6
  hash: string;
6
7
  }
@@ -15,6 +16,15 @@ export interface LoadResult {
15
16
  * (CLI / server names it after the filename).
16
17
  */
17
18
  benchmarks: Map<string, string[]>;
19
+ /**
20
+ * Raw text of the eval file, read once up front (before execution) for
21
+ * BOTH .js and .ts/.mjs paths. Persisted verbatim as `sourceCode` on each
22
+ * imported TestCase (see cli/commands/benchmark.ts) so the Test Case
23
+ * detail page can render the whole file as an IDE-style code view --
24
+ * without this we'd only have the parsed `initialPrompt`/`expectedOutcomes`
25
+ * fields, not the `evaluate()` body that produced them.
26
+ */
27
+ fileSource: string;
18
28
  /**
19
29
  * All lifecycle hooks (`beforeEach`/`afterEach`/`beforeAll`/`afterAll`)
20
30
  * registered while loading this file. Empty when the file declares none.
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../testCases/loader.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW/D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,CAgB5D;AAED,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC;;;;OAIG;IACH,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkInF"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../testCases/loader.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW/D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAQD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBjF;AAED,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAiJnF"}