@tangle-network/agent-eval 0.123.2 → 0.123.3

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 (65) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +48 -4
  3. package/dist/analyst/index.d.ts +18 -3
  4. package/dist/analyst/index.js +5 -5
  5. package/dist/benchmarks/index.d.ts +10 -3
  6. package/dist/benchmarks/index.js +7 -7
  7. package/dist/campaign/index.d.ts +241 -221
  8. package/dist/campaign/index.js +20 -16
  9. package/dist/{chunk-RQP5UTK5.js → chunk-22VO7T2I.js} +689 -356
  10. package/dist/chunk-22VO7T2I.js.map +1 -0
  11. package/dist/{chunk-EBDOTTZJ.js → chunk-4KO6D2BO.js} +3 -3
  12. package/dist/{chunk-3FCG7FBV.js → chunk-D5JZ7UDZ.js} +2 -2
  13. package/dist/{chunk-BGVTIE2C.js → chunk-GRCDRKII.js} +13 -2
  14. package/dist/chunk-GRCDRKII.js.map +1 -0
  15. package/dist/{chunk-FHFTYX2Q.js → chunk-JKDNAOF5.js} +2 -2
  16. package/dist/{chunk-WXQTVEKM.js → chunk-MHPEGJHC.js} +3 -3
  17. package/dist/{chunk-J7S4YM27.js → chunk-MK3Q6GCL.js} +28 -14
  18. package/dist/chunk-MK3Q6GCL.js.map +1 -0
  19. package/dist/{chunk-DNVPOYUS.js → chunk-N7ZS6PEF.js} +2 -2
  20. package/dist/{chunk-4SOQ4ND2.js → chunk-QFQZ3U3X.js} +1 -1
  21. package/dist/chunk-QFQZ3U3X.js.map +1 -0
  22. package/dist/{chunk-WMJR67FX.js → chunk-QNVBALEX.js} +4 -4
  23. package/dist/{chunk-E3HAD4A3.js → chunk-SJT4OBVL.js} +2 -2
  24. package/dist/{chunk-HQY7LBV2.js → chunk-SUN7QLPB.js} +6 -6
  25. package/dist/{chunk-HQY7LBV2.js.map → chunk-SUN7QLPB.js.map} +1 -1
  26. package/dist/{chunk-5YMKIFYP.js → chunk-WP3GEXKI.js} +3 -3
  27. package/dist/cli.js +3 -3
  28. package/dist/contract/index.d.ts +18 -3
  29. package/dist/contract/index.js +7 -7
  30. package/dist/fuzz.d.ts +8 -1
  31. package/dist/fuzz.js +4 -4
  32. package/dist/index.d.ts +27 -9
  33. package/dist/index.js +14 -12
  34. package/dist/index.js.map +1 -1
  35. package/dist/multishot/index.d.ts +2 -2
  36. package/dist/openapi.json +1 -1
  37. package/dist/rl.d.ts +15 -2
  38. package/dist/rl.js +4 -2
  39. package/dist/rl.js.map +1 -1
  40. package/dist/{run-campaign-ZKR5MQMQ.js → run-campaign-I3JXKVAK.js} +3 -3
  41. package/dist/traces.d.ts +2 -2
  42. package/dist/traces.js +1 -1
  43. package/dist/wire/index.d.ts +16 -1
  44. package/dist/wire/index.js +3 -3
  45. package/docs/campaign-proposers.md +142 -131
  46. package/docs/concepts.md +30 -34
  47. package/docs/design/loop-taxonomy.md +2 -3
  48. package/docs/design.md +0 -1
  49. package/docs/multi-shot-optimization.md +41 -49
  50. package/package.json +1 -1
  51. package/dist/chunk-4SOQ4ND2.js.map +0 -1
  52. package/dist/chunk-BGVTIE2C.js.map +0 -1
  53. package/dist/chunk-J7S4YM27.js.map +0 -1
  54. package/dist/chunk-RQP5UTK5.js.map +0 -1
  55. package/docs/improvement-glossary.md +0 -204
  56. package/docs/self-improvement-map.md +0 -115
  57. /package/dist/{chunk-EBDOTTZJ.js.map → chunk-4KO6D2BO.js.map} +0 -0
  58. /package/dist/{chunk-3FCG7FBV.js.map → chunk-D5JZ7UDZ.js.map} +0 -0
  59. /package/dist/{chunk-FHFTYX2Q.js.map → chunk-JKDNAOF5.js.map} +0 -0
  60. /package/dist/{chunk-WXQTVEKM.js.map → chunk-MHPEGJHC.js.map} +0 -0
  61. /package/dist/{chunk-DNVPOYUS.js.map → chunk-N7ZS6PEF.js.map} +0 -0
  62. /package/dist/{chunk-WMJR67FX.js.map → chunk-QNVBALEX.js.map} +0 -0
  63. /package/dist/{chunk-E3HAD4A3.js.map → chunk-SJT4OBVL.js.map} +0 -0
  64. /package/dist/{chunk-5YMKIFYP.js.map → chunk-WP3GEXKI.js.map} +0 -0
  65. /package/dist/{run-campaign-ZKR5MQMQ.js.map → run-campaign-I3JXKVAK.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,7 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
8
8
 
9
9
  ### Added
10
10
 
11
+ - `callLlmJson()` accepts `jsonPayloadMode: 'exact'` when callers must reject fenced, prose-wrapped, or multi-root responses instead of extracting a JSON value.
11
12
  - `llmPolicyEditProposer({ redactCurrentSurfaceForModel })` can remove credentials and unrelated fields from the current surface sent to the model while applying validated edits to the complete original surface.
12
13
  - `CostLedger.listPending()` exposes immutable pending paid calls and distinguishes calls that are active, late after cancellation, or interrupted by a prior process so durable workflows can reconcile exact reservations before resuming.
13
14
  - `traceAnalystProposer()` accepts an opt-in `resolvePriorFindings` callback that forwards canonical prior findings into the existing analyst registry.
@@ -21,12 +22,35 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
21
22
 
22
23
  ### Fixed
23
24
 
25
+ - `llmPolicyEditProposer()` asks the model only for semantic edit choices, deterministically binds caller-owned policy fields, and requires one exact whole-response JSON value before admission.
26
+ - `compareOptimizationMethods()` owns three non-empty, pairwise-disjoint scenario sets.
27
+ Methods receive independent copies of train and selection data, every method finishes before final test scoring starts, and final test data is absent from `OptimizationMethodInput`.
28
+ Built-in GEPA, SkillOpt, and FAPO methods read the shared baseline, runner, judges, directories, and execution settings from the comparison call instead of duplicating them in each method config.
29
+ Final test scoring uses one shared spend limit.
30
+ Reused cost ledgers isolate comparison receipts from unrelated calls, and final test scenarios are copied for each measured surface.
31
+ Results report optimization, test, and total cost with accounting completeness and reasons for unknown charges.
32
+ Per-method results include the paired scenario values used to compute lift.
33
+ Cost orders a tied-lift group only when every method in that group has complete accounting.
34
+ Bootstrap intervals adjust across all method contrasts, and the default resample count increases when needed to represent the adjusted tails.
35
+ `optimizationConcurrency` can run independent methods in parallel.
36
+ - `runSkillOpt()` validates every numeric control before scoring, enforces `patchesPerEpoch` for custom proposers, and no longer mutates the caller's `runDir`.
24
37
  - Include the complete required response shape in `llmPolicyEditProposer` model instructions, and add `LlmClientOptions.jsonSchemaTransport: 'json-object'` for providers that do not implement native JSON Schema enforcement.
25
38
  - `compositeProposer` restores each member's original labels when replaying history, so stateful members do not repeat candidates whose labels were decorated for provenance.
26
39
  - `InsightReport.interRater.kappa` now reports quadratic weighted kappa instead of Pearson correlation.
27
40
  Read `interRater.pearson` for the previous correlation measure; `icc` and `spearman` are now reported separately.
28
41
  - Compute contextual-bandit doubly robust estimates with separate logged-action and target-policy value terms, expose how many rows use DR versus IPS or the deprecated scalar path, and carry both values through belief-state records.
29
42
 
43
+ ### Breaking
44
+
45
+ - `compareProposers()` is replaced by `compareOptimizationMethods()`.
46
+ Rename `proposers` to `methods`, `ProposerEntry` to `OptimizationMethod`, and the built-in `*Entry` factories to `*Method`.
47
+ Pass `trainScenarios`, `selectionScenarios`, and `testScenarios`; the ambiguous `holdoutScenarios` option is rejected.
48
+ `OptimizerEntryConfig` is replaced by `BuiltinOptimizationMethodConfig`, which contains method-specific settings only.
49
+ Move shared method execution settings to `optimizationRunOptions` on the comparison call.
50
+ Read `optimizationCost`, `testCost`, and `totalCost`; each includes `totalCostUsd`, `accountingComplete`, and `incompleteReasons`.
51
+ - `runSkillOpt({ holdoutScenarios })` fails closed because those rows are adaptively reused.
52
+ Pass `selectionScenarios`; selection result fields now use `Selection` instead of `Holdout`, and `lift` is now `selectionLift`.
53
+
30
54
  ## [0.122.2] — 2026-07-17 — premeasured optimization continuation
31
55
 
32
56
  ### Added
package/README.md CHANGED
@@ -8,7 +8,7 @@ A TypeScript library that measures whether your AI agent got better or worse, us
8
8
  [![license: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
9
9
 
10
10
  You give it agent runs: outputs, traces, scores, and production feedback.
11
- It gives you numbers you can act on: did the new prompt beat the old one, is the difference statistically real, what failed and why, and whether the change should ship.
11
+ It gives you numbers you can act on: how much the new prompt changed outcomes, how uncertain that estimate is, what failed and why, and whether the change meets your release rule.
12
12
 
13
13
  Use it when you need to:
14
14
 
@@ -132,11 +132,53 @@ const judge = llmJudge<string, SupportScenario>(
132
132
  Pass `judge` to `defineAgentEval()` in place of the offline judge.
133
133
  The model receives the scenario, artifact, scoring prompt, and dimension descriptions.
134
134
 
135
+ ### Compare Optimization Methods
136
+
137
+ Use `compareOptimizationMethods()` when you need to compare complete search procedures rather than individual prompts.
138
+ The function gives every method the same baseline, runner, judges, train data, and selection data, then ranks their selected surfaces on separate final test data.
139
+
140
+ ```ts
141
+ import {
142
+ type BuiltinOptimizationMethodConfig,
143
+ compareOptimizationMethods,
144
+ gepaParetoMethod,
145
+ gepaReflectionMethod,
146
+ skillOptMethod,
147
+ } from '@tangle-network/agent-eval/campaign'
148
+
149
+ const methodConfig: BuiltinOptimizationMethodConfig<MyScenario, MyArtifact> = {
150
+ llm,
151
+ model,
152
+ target: 'the complete prompt being improved',
153
+ }
154
+
155
+ const result = await compareOptimizationMethods<MyScenario, MyArtifact>({
156
+ methods: [
157
+ gepaReflectionMethod(methodConfig),
158
+ gepaParetoMethod(methodConfig),
159
+ skillOptMethod(methodConfig),
160
+ ],
161
+ baselineSurface,
162
+ trainScenarios,
163
+ selectionScenarios,
164
+ testScenarios,
165
+ dispatchWithSurface,
166
+ judges,
167
+ runDir,
168
+ })
169
+ ```
170
+
171
+ Read `result.scores` for lift and intervals.
172
+ Read `result.totalCost` for dollars plus whether every charge was known.
173
+ Ranks follow estimated lift; use the intervals and pairwise results to determine whether the observed difference excludes zero.
174
+ See the [method-comparison guide](./docs/campaign-proposers.md) and [runnable example](./examples/compare-optimization-methods/).
175
+
135
176
  ---
136
177
 
137
- ## What's in the box
178
+ ## Core APIs
138
179
 
139
- One-line tour of the primitives. All of these are plain functions and interfaces you compose; start from `/contract` and pull in more only when you need it.
180
+ Start from `/contract` for the common path.
181
+ Use `/campaign` when you need direct control over runs, candidate generation, or method comparison.
140
182
 
141
183
  | Primitive | What it does |
142
184
  |---|---|
@@ -144,6 +186,7 @@ One-line tour of the primitives. All of these are plain functions and interfaces
144
186
  | **Scoring** (`JudgeConfig`, `llmJudge`, calibration) | Score one output on weighted dimensions with code or a model, then compare model scores against human ratings. |
145
187
  | **Release rules** (`heldOutGate`, `paretoSignificanceGate`, `composeGate`, …) | Decide whether a candidate ships, such as requiring an improvement on scenarios that candidate generation never saw. |
146
188
  | **Candidate generation** (`gepaProposer`, `evolutionaryProposer`, …) | Generate candidate prompts or configs from prior failures. |
189
+ | **Method comparison** (`compareOptimizationMethods`) | Run complete optimization methods on shared train and selection data, then rank them on separate final test data. |
147
190
  | **Run analysis** (`analyzeRuns`, `diffRuns`) | Turn any set of `RunRecord`s into a report: score distributions, baseline-vs-candidate lift with confidence intervals, failure clusters, cost breakdown, recommendations. |
148
191
  | **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Convert data you already have, such as human ratings tables and OpenTelemetry spans, into `RunRecord`s. |
149
192
  | **Cost tracking** | Attribute every model call's tokens and dollars to the run, phase, and judge that spent them, including interrupted calls. |
@@ -159,6 +202,7 @@ Our own experiments with these primitives live in [`examples/`](./examples/READM
159
202
  | [`examples/selfimprove-quickstart/`](./examples/selfimprove-quickstart/) | The closed improve-and-verify loop, fully offline |
160
203
  | [`examples/customer-feedback-loop/`](./examples/customer-feedback-loop/) | Multi-rater human feedback (CSV/Sheets/Obsidian) → per-rater judges → report |
161
204
  | [`examples/customer-otel-traces/`](./examples/customer-otel-traces/) | Production OpenTelemetry traces → report, no closed loop required |
205
+ | [`examples/compare-optimization-methods/`](./examples/compare-optimization-methods/) | Compare complete optimization methods with separate train, selection, and test data |
162
206
 
163
207
  Each is a single `index.ts` you run with `pnpm tsx`.
164
208
 
@@ -192,7 +236,7 @@ The root export (`@tangle-network/agent-eval`) remains broad for compatibility;
192
236
  - [`docs/concepts.md`](./docs/concepts.md): the mental model for runs, judges, verifiers, traces, and the top-level functions (5-minute read)
193
237
  - [`docs/customer-journeys.md`](./docs/customer-journeys.md): three complete adoption paths with code
194
238
  - [`docs/insight-report.md`](./docs/insight-report.md): annotated walkthrough of every section of the `analyzeRuns()` report
195
- - [`docs/campaign-proposers.md`](./docs/campaign-proposers.md): which proposer to use and when
239
+ - [`docs/campaign-proposers.md`](./docs/campaign-proposers.md): candidate generation and fair comparison of complete optimization methods
196
240
  - [`docs/adapters-observability.md`](./docs/adapters-observability.md): composing with LangSmith, Langfuse, Phoenix, and OpenLLMetry
197
241
  - [`docs/wire-protocol.md`](./docs/wire-protocol.md): the HTTP/RPC contract for other languages
198
242
  - [`docs/design.md`](./docs/design.md): how this package relates to the rest of the Tangle agent stack, and the dependency rules that keep it reusable
@@ -844,9 +844,16 @@ interface CostReceiptInput extends CostUsage {
844
844
  costUnknown?: boolean;
845
845
  usageUnknown?: boolean;
846
846
  }
847
+ /** Per-million token rates for a model or endpoint not covered by package pricing. */
848
+ interface CustomTokenPricing {
849
+ inputUsdPerMillion: number;
850
+ outputUsdPerMillion: number;
851
+ }
847
852
  type MaximumCharge = {
848
853
  externallyEnforcedMaximumUsd: number;
849
854
  } | ({
855
+ customTokenPricing: CustomTokenPricing;
856
+ } & Pick<CostUsage, 'inputTokens' | 'outputTokens'>) | ({
850
857
  model: string;
851
858
  } & CostUsage);
852
859
  interface RunPaidCallInput<T> {
@@ -858,7 +865,7 @@ interface RunPaidCallInput<T> {
858
865
  model?: string;
859
866
  tags?: Record<string, string>;
860
867
  signal?: AbortSignal;
861
- /** Provider-enforced dollar maximum, or maximum priced token usage. Required when capped. */
868
+ /** Provider-enforced dollar maximum, or maximum token usage with known pricing. Required when capped. */
862
869
  maximumCharge?: MaximumCharge;
863
870
  /** `callId` can be forwarded as the provider's idempotency key. */
864
871
  execute(signal: AbortSignal, callId: string): Promise<T>;
@@ -1042,8 +1049,8 @@ interface LlmCallResult {
1042
1049
  content: string;
1043
1050
  usage: LlmUsage;
1044
1051
  /**
1045
- * Cost in USD. Pulled from proxy's `_response_cost` field when present;
1046
- * `null` when neither the proxy nor the caller can derive it.
1052
+ * Cost in USD. Uses the provider's reported cost when present, otherwise
1053
+ * caller-supplied token pricing. `null` when neither is available.
1047
1054
  */
1048
1055
  costUsd: number | null;
1049
1056
  /** Model name actually used (echoed from response). */
@@ -1101,6 +1108,8 @@ interface LlmClientOptions {
1101
1108
  deadlineMs?: number;
1102
1109
  /** Total provider attempts. Legacy option name; default 3 (1 initial + 2 retries). */
1103
1110
  maxRetries?: number;
1111
+ /** Token rates used when the provider omits cost or package pricing does not cover the model. */
1112
+ customTokenPricing?: CustomTokenPricing;
1104
1113
  /**
1105
1114
  * Transport for requests that declare `jsonSchema`. `native` sends
1106
1115
  * `response_format: json_schema`; `json-object` sends the broadly supported
@@ -1108,6 +1117,12 @@ interface LlmClientOptions {
1108
1117
  * instructions. Default: `native`.
1109
1118
  */
1110
1119
  jsonSchemaTransport?: 'native' | 'json-object';
1120
+ /**
1121
+ * JSON payload parsing policy. `extract` accepts fenced or prose-prefixed JSON.
1122
+ * `exact` requires the complete response content to be one JSON value.
1123
+ * Default: `extract`.
1124
+ */
1125
+ jsonPayloadMode?: 'extract' | 'exact';
1111
1126
  /** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */
1112
1127
  fetch?: typeof fetch;
1113
1128
  /**
@@ -9,13 +9,13 @@ import {
9
9
  diffFindings,
10
10
  emitSkillUsageFindings,
11
11
  runSemanticConceptJudge
12
- } from "../chunk-WMJR67FX.js";
12
+ } from "../chunk-QNVBALEX.js";
13
13
  import {
14
14
  behavioralAnalyst,
15
15
  buildDefaultAnalystRegistry,
16
16
  createChatClient,
17
17
  deriveEfficiencyFindings
18
- } from "../chunk-5YMKIFYP.js";
18
+ } from "../chunk-WP3GEXKI.js";
19
19
  import "../chunk-HHWE3POT.js";
20
20
  import {
21
21
  ANALYST_SEVERITIES,
@@ -71,11 +71,11 @@ import {
71
71
  validatePolicyEdit,
72
72
  validatePolicyEditCandidateRecord,
73
73
  validateUsageSettlementTimeout
74
- } from "../chunk-WXQTVEKM.js";
75
- import "../chunk-J7S4YM27.js";
74
+ } from "../chunk-MHPEGJHC.js";
75
+ import "../chunk-MK3Q6GCL.js";
76
76
  import {
77
77
  CostLedger
78
- } from "../chunk-BGVTIE2C.js";
78
+ } from "../chunk-GRCDRKII.js";
79
79
  import "../chunk-VI2UW6B6.js";
80
80
  import "../chunk-IR3KBHOY.js";
81
81
  import "../chunk-PC4UYEBM.js";
@@ -66,9 +66,16 @@ interface CostReceiptInput extends CostUsage {
66
66
  costUnknown?: boolean;
67
67
  usageUnknown?: boolean;
68
68
  }
69
+ /** Per-million token rates for a model or endpoint not covered by package pricing. */
70
+ interface CustomTokenPricing {
71
+ inputUsdPerMillion: number;
72
+ outputUsdPerMillion: number;
73
+ }
69
74
  type MaximumCharge = {
70
75
  externallyEnforcedMaximumUsd: number;
71
76
  } | ({
77
+ customTokenPricing: CustomTokenPricing;
78
+ } & Pick<CostUsage, 'inputTokens' | 'outputTokens'>) | ({
72
79
  model: string;
73
80
  } & CostUsage);
74
81
  interface RunPaidCallInput<T> {
@@ -80,7 +87,7 @@ interface RunPaidCallInput<T> {
80
87
  model?: string;
81
88
  tags?: Record<string, string>;
82
89
  signal?: AbortSignal;
83
- /** Provider-enforced dollar maximum, or maximum priced token usage. Required when capped. */
90
+ /** Provider-enforced dollar maximum, or maximum token usage with known pricing. Required when capped. */
84
91
  maximumCharge?: MaximumCharge;
85
92
  /** `callId` can be forwarded as the provider's idempotency key. */
86
93
  execute(signal: AbortSignal, callId: string): Promise<T>;
@@ -205,8 +212,8 @@ interface LlmCallResult {
205
212
  content: string;
206
213
  usage: LlmUsage;
207
214
  /**
208
- * Cost in USD. Pulled from proxy's `_response_cost` field when present;
209
- * `null` when neither the proxy nor the caller can derive it.
215
+ * Cost in USD. Uses the provider's reported cost when present, otherwise
216
+ * caller-supplied token pricing. `null` when neither is available.
210
217
  */
211
218
  costUsd: number | null;
212
219
  /** Model name actually used (echoed from response). */
@@ -16,15 +16,15 @@ import {
16
16
  routing_exports,
17
17
  runBenchmarkAdapter,
18
18
  summarizeBenchmarkCampaign
19
- } from "../chunk-FHFTYX2Q.js";
20
- import "../chunk-RQP5UTK5.js";
21
- import "../chunk-HQY7LBV2.js";
22
- import "../chunk-3FCG7FBV.js";
23
- import "../chunk-WXQTVEKM.js";
19
+ } from "../chunk-JKDNAOF5.js";
20
+ import "../chunk-22VO7T2I.js";
21
+ import "../chunk-SUN7QLPB.js";
22
+ import "../chunk-D5JZ7UDZ.js";
23
+ import "../chunk-MHPEGJHC.js";
24
24
  import "../chunk-ARU2PZFM.js";
25
- import "../chunk-J7S4YM27.js";
25
+ import "../chunk-MK3Q6GCL.js";
26
26
  import "../chunk-PJQFMIOX.js";
27
- import "../chunk-BGVTIE2C.js";
27
+ import "../chunk-GRCDRKII.js";
28
28
  import "../chunk-VI2UW6B6.js";
29
29
  import "../chunk-NUKSVU3W.js";
30
30
  import "../chunk-GGE4NNQT.js";