textopt 0.0.0 → 0.2.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 (65) hide show
  1. package/README.md +65 -25
  2. package/dist/bootstrap-search/index.cjs +159 -73
  3. package/dist/bootstrap-search/index.d.cts +32 -10
  4. package/dist/bootstrap-search/index.d.mts +32 -10
  5. package/dist/bootstrap-search/index.mjs +150 -66
  6. package/dist/demos-9v5ts7F3.cjs +244 -0
  7. package/dist/{demos-B0pVQjYC.d.mts → demos-ASsSXYXA.d.mts} +10 -3
  8. package/dist/demos-Brobjfuc.mjs +215 -0
  9. package/dist/{demos-BTuzFNsp.d.cts → demos-ByaLZy-Z.d.cts} +10 -3
  10. package/dist/file-cache.cjs +27 -8
  11. package/dist/file-cache.d.cts +13 -0
  12. package/dist/file-cache.d.mts +13 -0
  13. package/dist/file-cache.mjs +27 -8
  14. package/dist/gepa/index.cjs +128 -80
  15. package/dist/gepa/index.d.cts +15 -7
  16. package/dist/gepa/index.d.mts +15 -7
  17. package/dist/gepa/index.mjs +101 -55
  18. package/dist/index.cjs +157 -30
  19. package/dist/index.d.cts +177 -7
  20. package/dist/index.d.mts +177 -7
  21. package/dist/index.mjs +139 -18
  22. package/dist/{math-COOofUyv.cjs → math-BhlziRPc.cjs} +60 -9
  23. package/dist/math-Dqme4rYz.mjs +123 -0
  24. package/dist/mipro/index.cjs +104 -70
  25. package/dist/mipro/index.d.cts +17 -14
  26. package/dist/mipro/index.d.mts +17 -14
  27. package/dist/mipro/index.mjs +90 -58
  28. package/dist/opro/index.cjs +136 -51
  29. package/dist/opro/index.d.cts +17 -9
  30. package/dist/opro/index.d.mts +17 -9
  31. package/dist/opro/index.mjs +121 -38
  32. package/dist/{optimizer-B7SpRwl7.d.cts → optimizer-4Zv-Zt2t.d.cts} +90 -5
  33. package/dist/{optimizer-DqCoth_w.d.mts → optimizer-Ds5mzYjz.d.mts} +90 -5
  34. package/dist/random-search/index.cjs +99 -49
  35. package/dist/random-search/index.d.cts +15 -13
  36. package/dist/random-search/index.d.mts +15 -13
  37. package/dist/random-search/index.mjs +89 -41
  38. package/dist/{reflection-Cr_upzU0.d.mts → reflection-CMezGu6u.d.mts} +38 -14
  39. package/dist/{reflection-CQToe-5B.d.cts → reflection-D0A7eahD.d.cts} +38 -14
  40. package/dist/reporting-bq007_2z.d.cts +294 -0
  41. package/dist/reporting-bq007_2z.d.mts +294 -0
  42. package/dist/simba/index.cjs +216 -83
  43. package/dist/simba/index.d.cts +53 -13
  44. package/dist/simba/index.d.mts +53 -13
  45. package/dist/simba/index.mjs +206 -75
  46. package/dist/testing.cjs +1 -0
  47. package/dist/testing.d.cts +5 -3
  48. package/dist/testing.d.mts +5 -3
  49. package/dist/testing.mjs +1 -1
  50. package/dist/{evaluation-OZOp6TB7.cjs → warnings-CWRJF-jA.cjs} +228 -5
  51. package/dist/{evaluation-BV0nSZVx.mjs → warnings-OxvDi9kN.mjs} +175 -6
  52. package/docs/adapters.md +169 -0
  53. package/docs/benchmark.md +90 -0
  54. package/docs/data-prep.md +113 -0
  55. package/docs/distillation.md +128 -0
  56. package/docs/evaluation.md +87 -0
  57. package/docs/metric-preflight.md +132 -0
  58. package/docs/optimizers.md +293 -0
  59. package/docs/tuning.md +130 -0
  60. package/package.json +6 -4
  61. package/dist/demos-B9BJiNKz.cjs +0 -143
  62. package/dist/demos-Degx6UmP.mjs +0 -126
  63. package/dist/math-DhrDmpFS.mjs +0 -78
  64. package/dist/types-CWv4IQFF.d.cts +0 -129
  65. package/dist/types-CWv4IQFF.d.mts +0 -129
@@ -1,4 +1,4 @@
1
- import { a as requireMeasuredMean, d as createMemoryCache, f as stableHash, i as measuredMean, l as runFingerprint, n as costExhausted, o as createDeadline, p as createBudget, r as createEvaluator, s as assertResumable, t as BudgetExhausted, u as componentNames } from "../evaluation-BV0nSZVx.mjs";
1
+ import { S as createBudget, _ as componentNames, a as instanceRow, b as defaultInstanceId, c as BudgetExhausted, d as measuredMean, f as requireMeasuredMean, g as runFingerprint, i as flushReporters, l as costExhausted, m as assertResumable, o as isCandidateAccepted, p as createDeadline, r as createEmitter, s as isRunFinished, t as resolveValidationSet, u as createEvaluator, y as createMemoryCache } from "../warnings-OxvDi9kN.mjs";
2
2
  import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
3
3
  import { t as parseProposedText } from "../text--v4Ffbus.mjs";
4
4
  //#region src/random-search/optimize.ts
@@ -32,10 +32,14 @@ var RandomSearchOptimizer = class {
32
32
  this.#config = config;
33
33
  }
34
34
  async optimize(task) {
35
- return runRandomSearch({
36
- config: this.#config,
37
- task
38
- });
35
+ try {
36
+ return await runRandomSearch({
37
+ config: this.#config,
38
+ task
39
+ });
40
+ } finally {
41
+ await flushReporters(task.reporters ?? []);
42
+ }
39
43
  }
40
44
  };
41
45
  /**
@@ -62,7 +66,12 @@ function buildParaphrasePrompt(args) {
62
66
  async function runRandomSearch(args) {
63
67
  const { config, task } = args;
64
68
  const { variants = DEFAULT_VARIANTS, concurrency = 1, maxRounds = Number.POSITIVE_INFINITY, buildPrompt = buildParaphrasePrompt, trackBestOutputs = false, checkpointCache = true } = config;
65
- const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, onEvent, onCheckpoint, resumeFrom, signal } = task;
69
+ const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
70
+ const { validationSet, warnings } = resolveValidationSet({
71
+ validationSet: requestedValidationSet,
72
+ trainingSet
73
+ });
74
+ const emit = createEmitter(reporters);
66
75
  const deadline = createDeadline({ maxWallClockMs });
67
76
  const components = componentNames(seedCandidate);
68
77
  if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
@@ -103,8 +112,9 @@ async function runRandomSearch(args) {
103
112
  ...evaluationCache === void 0 ? {} : { cache: evaluationCache },
104
113
  trackOutputs: trackBestOutputs,
105
114
  cacheHits: resumeFrom?.cacheHits ?? 0,
115
+ ...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
106
116
  ...signal === void 0 ? {} : { signal },
107
- onEvaluation: (event) => onEvent?.({
117
+ onEvaluation: (event) => emit({
108
118
  type: "evaluation",
109
119
  ...event
110
120
  })
@@ -122,7 +132,7 @@ async function runRandomSearch(args) {
122
132
  * no budget here bounds.
123
133
  */
124
134
  let stalledRounds = 0;
125
- onEvent?.({
135
+ emit({
126
136
  type: "start",
127
137
  components,
128
138
  validationSetSize: validationSet.length
@@ -151,6 +161,8 @@ async function runRandomSearch(args) {
151
161
  reflectionCalls,
152
162
  metricCalls: budget.spent(),
153
163
  cacheHits: evaluator.cacheHits(),
164
+ usage: evaluator.usage(),
165
+ acceptedCandidates,
154
166
  ...cached === void 0 ? {} : { cache: cached }
155
167
  };
156
168
  }
@@ -166,10 +178,20 @@ async function runRandomSearch(args) {
166
178
  batch: seedEvaluation,
167
179
  phase: "seed"
168
180
  });
181
+ if (seedEvaluation !== void 0) emit({
182
+ type: "candidateAccepted",
183
+ round: 0,
184
+ candidateId: 0,
185
+ candidate: seedCandidate,
186
+ aggregateScore: seedScore,
187
+ instanceScores: instanceRow(seedEvaluation),
188
+ ...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
189
+ });
169
190
  let best = resumeFrom?.best ?? seedCandidate;
170
191
  let bestScore = resumeFrom?.bestScore ?? seedScore;
171
192
  /** Absent on a resumed run until a variant wins: outputs are not checkpointed. */
172
193
  let bestOutputs = seedEvaluation?.outputs;
194
+ let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
173
195
  await checkpoint();
174
196
  while (round < maxRounds) {
175
197
  if (signal?.aborted) {
@@ -187,12 +209,12 @@ async function runRandomSearch(args) {
187
209
  stopReason = "deadlineReached";
188
210
  break;
189
211
  }
190
- if (!budget.canAfford(variants * validationSet.length)) {
212
+ if (!budget.canAfford(validationSet.length)) {
191
213
  stopReason = "budgetExhausted";
192
214
  break;
193
215
  }
194
216
  const component = components[round % components.length];
195
- onEvent?.({
217
+ emit({
196
218
  type: "roundStart",
197
219
  round,
198
220
  component
@@ -219,41 +241,64 @@ async function runRandomSearch(args) {
219
241
  });
220
242
  const unique = [...new Set(drawn)].filter((text) => text.length > 0 && text !== currentText);
221
243
  let roundStop;
244
+ const scheduled = [];
245
+ let owed = 0;
222
246
  for (const text of unique) {
223
247
  const candidate = {
224
248
  ...best,
225
249
  [component]: text
226
250
  };
227
- let evaluation;
228
- try {
229
- evaluation = await sweep({
230
- candidate,
231
- phase: "validation"
232
- });
233
- } catch (err) {
234
- if (err instanceof BudgetExhausted) {
235
- roundStop = "budgetExhausted";
236
- break;
237
- }
238
- if (signal?.aborted) {
239
- roundStop = "aborted";
240
- break;
251
+ const uncached = evaluator.countUncached({
252
+ candidate,
253
+ ids: validationIds,
254
+ split: "val"
255
+ });
256
+ if (!budget.canAfford(owed + uncached)) {
257
+ roundStop = "budgetExhausted";
258
+ break;
259
+ }
260
+ owed += uncached;
261
+ scheduled.push(candidate);
262
+ }
263
+ const swept = await mapWithConcurrency({
264
+ items: scheduled,
265
+ limit: concurrency,
266
+ task: async (candidate) => {
267
+ if (signal?.aborted) return { stop: "aborted" };
268
+ try {
269
+ return { evaluation: await sweep({
270
+ candidate,
271
+ phase: "validation"
272
+ }) };
273
+ } catch (err) {
274
+ if (err instanceof BudgetExhausted) return { stop: "budgetExhausted" };
275
+ if (signal?.aborted) return { stop: "aborted" };
276
+ throw err;
241
277
  }
242
- throw err;
278
+ }
279
+ });
280
+ for (const [index, outcome] of swept.entries()) {
281
+ if (outcome.stop !== void 0) {
282
+ roundStop = outcome.stop;
283
+ break;
243
284
  }
244
285
  variantsEvaluated += 1;
245
- const score = measuredMean(evaluation);
286
+ const score = measuredMean(outcome.evaluation);
246
287
  if (score !== void 0 && score > bestScore) {
247
- onEvent?.({
288
+ const candidate = scheduled[index];
289
+ acceptedCandidates += 1;
290
+ emit({
248
291
  type: "candidateAccepted",
249
292
  round,
250
- component,
251
- score,
252
- previousScore: bestScore
293
+ candidateId: acceptedCandidates,
294
+ candidate,
295
+ aggregateScore: score,
296
+ instanceScores: instanceRow(outcome.evaluation),
297
+ ...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
253
298
  });
254
299
  best = candidate;
255
300
  bestScore = score;
256
- bestOutputs = evaluation.outputs;
301
+ bestOutputs = outcome.evaluation.outputs;
257
302
  }
258
303
  }
259
304
  round += 1;
@@ -269,7 +314,7 @@ async function runRandomSearch(args) {
269
314
  }
270
315
  }
271
316
  if (signal?.aborted) stopReason = "aborted";
272
- const testScore = testSet === void 0 ? void 0 : measuredMean(await evaluator.evaluate({
317
+ const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
273
318
  candidate: best,
274
319
  batch: testSet,
275
320
  ids: testIds,
@@ -278,13 +323,18 @@ async function runRandomSearch(args) {
278
323
  candidateId: null,
279
324
  iteration: round,
280
325
  charge: false
281
- }));
282
- onEvent?.({
326
+ });
327
+ const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
328
+ emit({
283
329
  type: "finish",
284
330
  reason: stopReason,
331
+ warnings,
332
+ bestCandidateId: acceptedCandidates,
285
333
  bestScore,
286
334
  metricCalls: budget.spent(),
287
- ...testScore === void 0 ? {} : { testScore }
335
+ ...testScore === void 0 ? {} : { testScore },
336
+ ...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
337
+ ...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
288
338
  });
289
339
  return {
290
340
  bestCandidate: best,
@@ -295,13 +345,15 @@ async function runRandomSearch(args) {
295
345
  ...trackBestOutputs ? { bestOutputs } : {},
296
346
  ...testScore === void 0 ? {} : {
297
347
  testScore,
298
- testMetricCalls: evaluator.unchargedCalls()
348
+ testMetricCalls: evaluator.unchargedCalls(),
349
+ testUsage: evaluator.unchargedUsage()
299
350
  },
300
351
  rounds: round,
301
352
  variantsEvaluated,
302
353
  metricCalls: budget.spent(),
303
354
  reflectionCalls,
304
355
  cacheHits: evaluator.cacheHits(),
356
+ warnings,
305
357
  stopReason
306
358
  };
307
359
  }
@@ -311,9 +363,5 @@ function assertConfig(config) {
311
363
  if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
312
364
  if (maxRounds !== void 0 && (!Number.isInteger(maxRounds) || maxRounds < 1)) throw new Error(`maxRounds must be a positive integer, received ${maxRounds}`);
313
365
  }
314
- function defaultInstanceId(args) {
315
- const hash = stableHash(args.datum);
316
- return hash === "" ? String(args.index) : hash;
317
- }
318
366
  //#endregion
319
- export { RandomSearchOptimizer, buildParaphrasePrompt };
367
+ export { RandomSearchOptimizer, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };
@@ -1,4 +1,4 @@
1
- import { i as EvaluationBatch, n as Candidate, o as EvaluationPhase, s as EvaluationSplit, t as Adapter, u as TextModel } from "./types-CWv4IQFF.mjs";
1
+ import { S as UsageTotals, _ as EvaluationPhase, a as RunFinished, f as Adapter, h as EvaluationBatch, m as EvaluateArgs, p as Candidate, t as CandidateAccepted, v as EvaluationSplit, x as TextModel } from "./reporting-bq007_2z.mjs";
2
2
  import { t as CachedScore } from "./cache-CuSo0NJ8.mjs";
3
3
  import { t as Rng } from "./rng-BR5MOedA.mjs";
4
4
  //#region src/gepa/types.d.ts
@@ -56,12 +56,29 @@ interface ProposeArgs<K extends string = string> {
56
56
  reflect: TextModel;
57
57
  signal?: AbortSignal;
58
58
  }
59
+ /**
60
+ * A scored batch that carries the per-instance diagnosis reflection is written
61
+ * from. `feedback` is optional on `EvaluationBatch` because the searches that
62
+ * never reflect have no use for it; here it is the input to the whole method,
63
+ * so it is required.
64
+ *
65
+ * An adapter that returns scores and no prose reduces every rollout to a
66
+ * number, and reflection then rewrites the instruction from a prompt whose
67
+ * feedback blocks are empty. That run spends its whole budget, reports a
68
+ * normal-looking `stopReason`, and has been doing blind search — which is
69
+ * exactly the failure no reading of the result can distinguish from a hard
70
+ * task. It is a type error instead.
71
+ */
72
+ type ReflectiveBatch<Trajectory = unknown, Output = unknown> = EvaluationBatch<Trajectory, Output> & {
73
+ feedback: string[];
74
+ };
59
75
  /**
60
76
  * An adapter GEPA can reflect against: evaluation, plus the traces reflection
61
77
  * reads. `makeReflectiveDataset` is what turns a scored batch into the
62
78
  * per-component evidence a reflection call is written from.
63
79
  */
64
80
  interface GepaAdapter<Datum, Trajectory = unknown, Output = unknown, K extends string = string> extends Adapter<Datum, Trajectory, Output, K> {
81
+ evaluate(args: EvaluateArgs<Datum, K>): Promise<ReflectiveBatch<Trajectory, Output>> | ReflectiveBatch<Trajectory, Output>;
65
82
  makeReflectiveDataset(args: MakeReflectiveDatasetArgs<Datum, Trajectory, Output, K>): Promise<ReflectiveDataset<K>> | ReflectiveDataset<K>;
66
83
  proposeNewTexts?(args: ProposeArgs<K>): Promise<ComponentPatch<K>> | ComponentPatch<K>;
67
84
  }
@@ -111,10 +128,21 @@ type ComponentSelector<K extends string = string> = (args: {
111
128
  iteration: number;
112
129
  rng: Rng;
113
130
  }) => K[];
114
- type AcceptancePolicy = (args: {
131
+ type AcceptancePolicy = ((args: {
115
132
  parentScores: readonly number[];
116
133
  childScores: readonly number[];
117
- }) => boolean;
134
+ }) => boolean) & {
135
+ /**
136
+ * Smallest minibatch this policy could ever accept on, when it has one. A
137
+ * policy that tests for significance has a floor its batch size must clear
138
+ * — a sign-flip test over three instances cannot report a p-value below
139
+ * 0.125 — and below it every proposal is rejected on arithmetic rather than
140
+ * on evidence. The run that results looks exactly like a search that found
141
+ * nothing, so the optimizer refuses the combination at construction instead
142
+ * of spending the budget discovering it.
143
+ */
144
+ minimumPairs?: number;
145
+ };
118
146
  /**
119
147
  * Which validation instances a candidate is scored on, and how the best
120
148
  * candidate is read back out of possibly partial coverage.
@@ -158,14 +186,12 @@ type GepaEvent<K extends string = string> = {
158
186
  parentId: number;
159
187
  componentsToUpdate: K[];
160
188
  changed: boolean;
161
- } | {
189
+ } | ({
162
190
  type: "candidateAccepted";
163
191
  iteration: number;
164
- candidateId: number;
165
192
  parentIds: number[];
166
- aggregateScore: number;
167
193
  source: CandidateSource;
168
- } | {
194
+ } & CandidateAccepted<K>) | {
169
195
  type: "candidateRejected";
170
196
  iteration: number;
171
197
  parentId: number;
@@ -182,14 +208,10 @@ type GepaEvent<K extends string = string> = {
182
208
  type: "error";
183
209
  iteration: number;
184
210
  err: unknown;
185
- } | {
211
+ } | ({
186
212
  type: "finish";
187
213
  reason: GepaStopReason;
188
- bestCandidateId: number;
189
- metricCalls: number;
190
- /** The winner's held-out score, when a testSet was given. */
191
- testScore?: number;
192
- };
214
+ } & RunFinished);
193
215
  /**
194
216
  * Everything needed to continue a run: the candidate pool with its scores, the
195
217
  * budget already spent, the position of the random stream, and the bookkeeping
@@ -219,6 +241,8 @@ interface GepaSnapshot {
219
241
  metricCalls: number;
220
242
  reflectionCalls: number;
221
243
  cacheHits: number;
244
+ /** Usage already spent, so a resumed run reports totals and honours ceilings. */
245
+ usage?: UsageTotals;
222
246
  rngState: number;
223
247
  /** Whatever the batch sampler reports from `state()`, when it has one. */
224
248
  sampler?: unknown;
@@ -280,4 +304,4 @@ declare function buildRewritePrompt(args: ReflectionPromptArgs): string;
280
304
  */
281
305
  declare function diverseReflectionStrategies(): ReflectionPromptBuilder[];
282
306
  //#endregion
283
- export { RejectedProposal as C, ReflectiveRecord as S, ValEvaluationPolicy as T, GepaStopReason as _, buildRewritePrompt as a, ProposeArgs as b, AcceptancePolicy as c, CandidateSource as d, ComponentPatch as f, GepaSnapshot as g, GepaEvent as h, buildReflectionPrompt as i, CandidateRecord as l, GepaAdapter as m, ReflectionPromptBuilder as n, buildSimplifyPrompt as o, ComponentSelector as p, buildGeneralizePrompt as r, diverseReflectionStrategies as s, ReflectionPromptArgs as t, CandidateSelector as u, MakeReflectiveDatasetArgs as v, SelectionState as w, ReflectiveDataset as x, ParetoFrontier as y };
307
+ export { ReflectiveRecord as C, ValEvaluationPolicy as E, ReflectiveDataset as S, SelectionState as T, GepaStopReason as _, buildRewritePrompt as a, ProposeArgs as b, AcceptancePolicy as c, CandidateSource as d, ComponentPatch as f, GepaSnapshot as g, GepaEvent as h, buildReflectionPrompt as i, CandidateRecord as l, GepaAdapter as m, ReflectionPromptBuilder as n, buildSimplifyPrompt as o, ComponentSelector as p, buildGeneralizePrompt as r, diverseReflectionStrategies as s, ReflectionPromptArgs as t, CandidateSelector as u, MakeReflectiveDatasetArgs as v, RejectedProposal as w, ReflectiveBatch as x, ParetoFrontier as y };
@@ -1,4 +1,4 @@
1
- import { i as EvaluationBatch, n as Candidate, o as EvaluationPhase, s as EvaluationSplit, t as Adapter, u as TextModel } from "./types-CWv4IQFF.cjs";
1
+ import { S as UsageTotals, _ as EvaluationPhase, a as RunFinished, f as Adapter, h as EvaluationBatch, m as EvaluateArgs, p as Candidate, t as CandidateAccepted, v as EvaluationSplit, x as TextModel } from "./reporting-bq007_2z.cjs";
2
2
  import { t as CachedScore } from "./cache-CuSo0NJ8.cjs";
3
3
  import { t as Rng } from "./rng-BR5MOedA.cjs";
4
4
  //#region src/gepa/types.d.ts
@@ -56,12 +56,29 @@ interface ProposeArgs<K extends string = string> {
56
56
  reflect: TextModel;
57
57
  signal?: AbortSignal;
58
58
  }
59
+ /**
60
+ * A scored batch that carries the per-instance diagnosis reflection is written
61
+ * from. `feedback` is optional on `EvaluationBatch` because the searches that
62
+ * never reflect have no use for it; here it is the input to the whole method,
63
+ * so it is required.
64
+ *
65
+ * An adapter that returns scores and no prose reduces every rollout to a
66
+ * number, and reflection then rewrites the instruction from a prompt whose
67
+ * feedback blocks are empty. That run spends its whole budget, reports a
68
+ * normal-looking `stopReason`, and has been doing blind search — which is
69
+ * exactly the failure no reading of the result can distinguish from a hard
70
+ * task. It is a type error instead.
71
+ */
72
+ type ReflectiveBatch<Trajectory = unknown, Output = unknown> = EvaluationBatch<Trajectory, Output> & {
73
+ feedback: string[];
74
+ };
59
75
  /**
60
76
  * An adapter GEPA can reflect against: evaluation, plus the traces reflection
61
77
  * reads. `makeReflectiveDataset` is what turns a scored batch into the
62
78
  * per-component evidence a reflection call is written from.
63
79
  */
64
80
  interface GepaAdapter<Datum, Trajectory = unknown, Output = unknown, K extends string = string> extends Adapter<Datum, Trajectory, Output, K> {
81
+ evaluate(args: EvaluateArgs<Datum, K>): Promise<ReflectiveBatch<Trajectory, Output>> | ReflectiveBatch<Trajectory, Output>;
65
82
  makeReflectiveDataset(args: MakeReflectiveDatasetArgs<Datum, Trajectory, Output, K>): Promise<ReflectiveDataset<K>> | ReflectiveDataset<K>;
66
83
  proposeNewTexts?(args: ProposeArgs<K>): Promise<ComponentPatch<K>> | ComponentPatch<K>;
67
84
  }
@@ -111,10 +128,21 @@ type ComponentSelector<K extends string = string> = (args: {
111
128
  iteration: number;
112
129
  rng: Rng;
113
130
  }) => K[];
114
- type AcceptancePolicy = (args: {
131
+ type AcceptancePolicy = ((args: {
115
132
  parentScores: readonly number[];
116
133
  childScores: readonly number[];
117
- }) => boolean;
134
+ }) => boolean) & {
135
+ /**
136
+ * Smallest minibatch this policy could ever accept on, when it has one. A
137
+ * policy that tests for significance has a floor its batch size must clear
138
+ * — a sign-flip test over three instances cannot report a p-value below
139
+ * 0.125 — and below it every proposal is rejected on arithmetic rather than
140
+ * on evidence. The run that results looks exactly like a search that found
141
+ * nothing, so the optimizer refuses the combination at construction instead
142
+ * of spending the budget discovering it.
143
+ */
144
+ minimumPairs?: number;
145
+ };
118
146
  /**
119
147
  * Which validation instances a candidate is scored on, and how the best
120
148
  * candidate is read back out of possibly partial coverage.
@@ -158,14 +186,12 @@ type GepaEvent<K extends string = string> = {
158
186
  parentId: number;
159
187
  componentsToUpdate: K[];
160
188
  changed: boolean;
161
- } | {
189
+ } | ({
162
190
  type: "candidateAccepted";
163
191
  iteration: number;
164
- candidateId: number;
165
192
  parentIds: number[];
166
- aggregateScore: number;
167
193
  source: CandidateSource;
168
- } | {
194
+ } & CandidateAccepted<K>) | {
169
195
  type: "candidateRejected";
170
196
  iteration: number;
171
197
  parentId: number;
@@ -182,14 +208,10 @@ type GepaEvent<K extends string = string> = {
182
208
  type: "error";
183
209
  iteration: number;
184
210
  err: unknown;
185
- } | {
211
+ } | ({
186
212
  type: "finish";
187
213
  reason: GepaStopReason;
188
- bestCandidateId: number;
189
- metricCalls: number;
190
- /** The winner's held-out score, when a testSet was given. */
191
- testScore?: number;
192
- };
214
+ } & RunFinished);
193
215
  /**
194
216
  * Everything needed to continue a run: the candidate pool with its scores, the
195
217
  * budget already spent, the position of the random stream, and the bookkeeping
@@ -219,6 +241,8 @@ interface GepaSnapshot {
219
241
  metricCalls: number;
220
242
  reflectionCalls: number;
221
243
  cacheHits: number;
244
+ /** Usage already spent, so a resumed run reports totals and honours ceilings. */
245
+ usage?: UsageTotals;
222
246
  rngState: number;
223
247
  /** Whatever the batch sampler reports from `state()`, when it has one. */
224
248
  sampler?: unknown;
@@ -280,4 +304,4 @@ declare function buildRewritePrompt(args: ReflectionPromptArgs): string;
280
304
  */
281
305
  declare function diverseReflectionStrategies(): ReflectionPromptBuilder[];
282
306
  //#endregion
283
- export { RejectedProposal as C, ReflectiveRecord as S, ValEvaluationPolicy as T, GepaStopReason as _, buildRewritePrompt as a, ProposeArgs as b, AcceptancePolicy as c, CandidateSource as d, ComponentPatch as f, GepaSnapshot as g, GepaEvent as h, buildReflectionPrompt as i, CandidateRecord as l, GepaAdapter as m, ReflectionPromptBuilder as n, buildSimplifyPrompt as o, ComponentSelector as p, buildGeneralizePrompt as r, diverseReflectionStrategies as s, ReflectionPromptArgs as t, CandidateSelector as u, MakeReflectiveDatasetArgs as v, SelectionState as w, ReflectiveDataset as x, ParetoFrontier as y };
307
+ export { ReflectiveRecord as C, ValEvaluationPolicy as E, ReflectiveDataset as S, SelectionState as T, GepaStopReason as _, buildRewritePrompt as a, ProposeArgs as b, AcceptancePolicy as c, CandidateSource as d, ComponentPatch as f, GepaSnapshot as g, GepaEvent as h, buildReflectionPrompt as i, CandidateRecord as l, GepaAdapter as m, ReflectionPromptBuilder as n, buildSimplifyPrompt as o, ComponentSelector as p, buildGeneralizePrompt as r, diverseReflectionStrategies as s, ReflectionPromptArgs as t, CandidateSelector as u, MakeReflectiveDatasetArgs as v, RejectedProposal as w, ReflectiveBatch as x, ParetoFrontier as y };