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 { d as UsageTotals, i as EvaluationBatch, n as Candidate, o as EvaluationPhase, s as EvaluationSplit, t as Adapter } from "./types-CWv4IQFF.mjs";
1
+ import { S as UsageTotals, _ as EvaluationPhase, c as RunWarning, f as Adapter, h as EvaluationBatch, p as Candidate, v as EvaluationSplit } from "./reporting-bq007_2z.mjs";
2
2
  import { n as EvaluationCache, t as CachedScore } from "./cache-CuSo0NJ8.mjs";
3
3
  //#region src/budget.d.ts
4
4
  interface Budget {
@@ -106,8 +106,24 @@ interface Evaluator<Datum, Trajectory, Output, K extends string> {
106
106
  cacheHits(): number;
107
107
  /** Rollouts made with `charge: false`, tracked apart from the budget. */
108
108
  unchargedCalls(): number;
109
- /** Tokens and money the run has spent, as far as adapters have reported it. */
109
+ /**
110
+ * Tokens and money the search has spent, as far as adapters have reported
111
+ * it. `maxCostUsd` is checked against this, so what the ceiling does not
112
+ * bound is not counted in it: rollouts made with `charge: false` are in
113
+ * `unchargedUsage` instead.
114
+ */
110
115
  usage(): UsageTotals;
116
+ /**
117
+ * What the rollouts made with `charge: false` cost. Held-out measurement is
118
+ * taken after the search has stopped, so no ceiling bounds it and reporting
119
+ * it inside `usage` would describe a run as having overrun one.
120
+ */
121
+ unchargedUsage(): UsageTotals;
122
+ /**
123
+ * Folds in usage spent outside this evaluator — harvesting runs its own, and
124
+ * a cost ceiling that cannot see it bounds only part of the run.
125
+ */
126
+ absorbUsage(spent: UsageTotals): void;
111
127
  /** Cache contents for checkpointing, when the cache can enumerate them. */
112
128
  entries(): [string, CachedScore][] | undefined;
113
129
  restore(entries: Iterable<readonly [string, CachedScore]>): void;
@@ -140,6 +156,12 @@ declare function createEvaluator<Datum, Trajectory, Output, K extends string>(ar
140
156
  signal?: AbortSignal;
141
157
  /** Resumed counters, so a continued run reports totals rather than deltas. */
142
158
  cacheHits?: number;
159
+ /**
160
+ * Usage the interrupted run had already spent. Without it `maxCostUsd` is a
161
+ * ceiling on the segment rather than on the run, and resuming repeatedly
162
+ * spends it again each time.
163
+ */
164
+ usage?: UsageTotals;
143
165
  /**
144
166
  * Rate limits and 5xx responses are the common case in a long run, and a
145
167
  * transient row costs the instance whichever optimizer is driving: it is
@@ -192,8 +214,28 @@ declare function requireMeasuredMean(args: {
192
214
  */
193
215
  interface OptimizerTask<Datum, Trajectory = unknown, Output = unknown, K extends string = string> {
194
216
  seedCandidate: Candidate<K>;
217
+ /**
218
+ * Instances the search draws evidence from. Reflective optimizers mine these
219
+ * for what a candidate got wrong, so a training row earns its place by being
220
+ * diagnostic — a row every candidate already passes teaches the rewriter
221
+ * nothing.
222
+ *
223
+ * @see docs/data-prep.md
224
+ */
195
225
  trainingSet: readonly Datum[];
196
- validationSet?: readonly Datum[];
226
+ /**
227
+ * Instances the search selects candidates against. Defaults to
228
+ * `trainingSet`, which is the right default for a first run and the wrong
229
+ * number to report from one — the result carries a warning saying so.
230
+ * `"reuseTraining"` is that same default with the caller's name on it, and
231
+ * carries no warning.
232
+ *
233
+ * Split by group rather than by row: near-duplicate instances that straddle
234
+ * the boundary leak, and the run reports a score nothing earned.
235
+ *
236
+ * @see docs/data-prep.md
237
+ */
238
+ validationSet?: readonly Datum[] | "reuseTraining";
197
239
  /**
198
240
  * `NoInfer` keeps the adapter out of `K`'s inference: an adapter built by a
199
241
  * factory knows nothing about component names, and one inference candidate of
@@ -207,6 +249,18 @@ interface OptimizerTask<Datum, Trajectory = unknown, Output = unknown, K extends
207
249
  * number in a result that no candidate was ever selected against.
208
250
  */
209
251
  testSet?: readonly Datum[];
252
+ /**
253
+ * Scored rollouts the search may spend. Cache hits do not count, and test
254
+ * rollouts are outside it entirely.
255
+ *
256
+ * Spending it is only worth anything if the metric separates candidates
257
+ * first: a metric that scores every candidate alike turns the whole budget
258
+ * into ranked ties, and the run reports a stop reason that looks like any
259
+ * other.
260
+ *
261
+ * @see docs/metric-preflight.md
262
+ * @see docs/tuning.md
263
+ */
210
264
  maxMetricCalls: number;
211
265
  /**
212
266
  * Dollars the run may spend, as reported by the adapter's usage. Checked
@@ -252,8 +306,25 @@ interface OptimizerResult<K extends string, Stop extends string, Output = unknow
252
306
  bestOutputs?: (Output | undefined)[];
253
307
  metricCalls: number;
254
308
  /**
255
- * Tokens and dollars the run spent, summed from what the adapter reported.
256
- * Zero throughout when the adapter reports no usage.
309
+ * Rollouts served from the cache rather than charged to `metricCalls`. Every
310
+ * optimizer here caches by default, so the same `maxMetricCalls` can buy a
311
+ * search that revisits scored candidates a longer effective run than one
312
+ * that never does — a comparison over `metricCalls` alone hides that. Zero
313
+ * for a run with caching disabled.
314
+ */
315
+ cacheHits: number;
316
+ /**
317
+ * Calls made to a proposal or reflection model, which no metric budget
318
+ * covers — see each optimizer's own accounting for what a call costs there.
319
+ * Absent from a search that proposes nothing of its own: bootstrap search
320
+ * only accepts or rejects rollouts the metric already scored.
321
+ */
322
+ reflectionCalls?: number;
323
+ /**
324
+ * Tokens and dollars the search spent, summed from what the adapter reported.
325
+ * Zero throughout when the adapter reports no usage. `maxCostUsd` is checked
326
+ * against this, so the held-out sweep — which runs after the search has
327
+ * stopped, under no ceiling — is reported apart from it, in `testUsage`.
257
328
  */
258
329
  usage: UsageTotals;
259
330
  /**
@@ -267,7 +338,21 @@ interface OptimizerResult<K extends string, Stop extends string, Output = unknow
267
338
  * measurement rather than search, and so is not charged to `maxMetricCalls`.
268
339
  */
269
340
  testMetricCalls?: number;
341
+ /**
342
+ * Tokens and dollars the held-out sweep cost, for the same reason: no ceiling
343
+ * bounds it, so a caller adding up what a run spent has to see it as its own
344
+ * number rather than find it folded into one `maxCostUsd` was supposed to
345
+ * hold. Absent when no testSet was given.
346
+ */
347
+ testUsage?: UsageTotals;
270
348
  stopReason: Stop;
349
+ /**
350
+ * What this run cannot say about itself from its own numbers — selection
351
+ * that reused the training instances, a seed the metric could not separate.
352
+ * Empty when there is nothing to say. Never fatal, and repeated on the
353
+ * `finish` event so a reporter sees them next to the score.
354
+ */
355
+ warnings: RunWarning[];
271
356
  }
272
357
  /**
273
358
  * An optimizer: a task in, the best candidate it found out. Exactly one method.
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_evaluation = require("../evaluation-OZOp6TB7.cjs");
2
+ const require_warnings = require("../warnings-CWRJF-jA.cjs");
3
3
  const require_concurrency = require("../concurrency-C-cFzWW2.cjs");
4
4
  const require_text = require("../text-CK_HB3su.cjs");
5
5
  //#region src/random-search/optimize.ts
@@ -33,10 +33,14 @@ var RandomSearchOptimizer = class {
33
33
  this.#config = config;
34
34
  }
35
35
  async optimize(task) {
36
- return runRandomSearch({
37
- config: this.#config,
38
- task
39
- });
36
+ try {
37
+ return await runRandomSearch({
38
+ config: this.#config,
39
+ task
40
+ });
41
+ } finally {
42
+ await require_warnings.flushReporters(task.reporters ?? []);
43
+ }
40
44
  }
41
45
  };
42
46
  /**
@@ -63,9 +67,14 @@ function buildParaphrasePrompt(args) {
63
67
  async function runRandomSearch(args) {
64
68
  const { config, task } = args;
65
69
  const { variants = DEFAULT_VARIANTS, concurrency = 1, maxRounds = Number.POSITIVE_INFINITY, buildPrompt = buildParaphrasePrompt, trackBestOutputs = false, checkpointCache = true } = config;
66
- const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, onEvent, onCheckpoint, resumeFrom, signal } = task;
67
- const deadline = require_evaluation.createDeadline({ maxWallClockMs });
68
- const components = require_evaluation.componentNames(seedCandidate);
70
+ const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_warnings.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
71
+ const { validationSet, warnings } = require_warnings.resolveValidationSet({
72
+ validationSet: requestedValidationSet,
73
+ trainingSet
74
+ });
75
+ const emit = require_warnings.createEmitter(reporters);
76
+ const deadline = require_warnings.createDeadline({ maxWallClockMs });
77
+ const components = require_warnings.componentNames(seedCandidate);
69
78
  if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
70
79
  if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
71
80
  if (components.length === 0) throw new Error("optimize requires a seed candidate with at least one component");
@@ -78,7 +87,7 @@ async function runRandomSearch(args) {
78
87
  datum,
79
88
  index
80
89
  })) ?? [];
81
- const fingerprint = require_evaluation.runFingerprint({
90
+ const fingerprint = require_warnings.runFingerprint({
82
91
  seedCandidate,
83
92
  trainingIds: trainingSet.map((datum, index) => instanceId({
84
93
  datum,
@@ -87,16 +96,16 @@ async function runRandomSearch(args) {
87
96
  validationIds,
88
97
  ...cacheNamespace === void 0 ? {} : { cacheNamespace }
89
98
  });
90
- require_evaluation.assertResumable({
99
+ require_warnings.assertResumable({
91
100
  fingerprint,
92
101
  ...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
93
102
  });
94
- const budget = require_evaluation.createBudget({
103
+ const budget = require_warnings.createBudget({
95
104
  maxMetricCalls,
96
105
  spent: resumeFrom?.metricCalls ?? 0
97
106
  });
98
- const evaluationCache = cache === false ? void 0 : cache ?? require_evaluation.createMemoryCache();
99
- const evaluator = require_evaluation.createEvaluator({
107
+ const evaluationCache = cache === false ? void 0 : cache ?? require_warnings.createMemoryCache();
108
+ const evaluator = require_warnings.createEvaluator({
100
109
  adapter,
101
110
  budget,
102
111
  ...retry === void 0 ? {} : { retry },
@@ -104,8 +113,9 @@ async function runRandomSearch(args) {
104
113
  ...evaluationCache === void 0 ? {} : { cache: evaluationCache },
105
114
  trackOutputs: trackBestOutputs,
106
115
  cacheHits: resumeFrom?.cacheHits ?? 0,
116
+ ...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
107
117
  ...signal === void 0 ? {} : { signal },
108
- onEvaluation: (event) => onEvent?.({
118
+ onEvaluation: (event) => emit({
109
119
  type: "evaluation",
110
120
  ...event
111
121
  })
@@ -123,7 +133,7 @@ async function runRandomSearch(args) {
123
133
  * no budget here bounds.
124
134
  */
125
135
  let stalledRounds = 0;
126
- onEvent?.({
136
+ emit({
127
137
  type: "start",
128
138
  components,
129
139
  validationSetSize: validationSet.length
@@ -152,6 +162,8 @@ async function runRandomSearch(args) {
152
162
  reflectionCalls,
153
163
  metricCalls: budget.spent(),
154
164
  cacheHits: evaluator.cacheHits(),
165
+ usage: evaluator.usage(),
166
+ acceptedCandidates,
155
167
  ...cached === void 0 ? {} : { cache: cached }
156
168
  };
157
169
  }
@@ -163,21 +175,31 @@ async function runRandomSearch(args) {
163
175
  candidate: seedCandidate,
164
176
  phase: "seed"
165
177
  }) : void 0;
166
- const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore : require_evaluation.requireMeasuredMean({
178
+ const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore : require_warnings.requireMeasuredMean({
167
179
  batch: seedEvaluation,
168
180
  phase: "seed"
169
181
  });
182
+ if (seedEvaluation !== void 0) emit({
183
+ type: "candidateAccepted",
184
+ round: 0,
185
+ candidateId: 0,
186
+ candidate: seedCandidate,
187
+ aggregateScore: seedScore,
188
+ instanceScores: require_warnings.instanceRow(seedEvaluation),
189
+ ...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
190
+ });
170
191
  let best = resumeFrom?.best ?? seedCandidate;
171
192
  let bestScore = resumeFrom?.bestScore ?? seedScore;
172
193
  /** Absent on a resumed run until a variant wins: outputs are not checkpointed. */
173
194
  let bestOutputs = seedEvaluation?.outputs;
195
+ let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
174
196
  await checkpoint();
175
197
  while (round < maxRounds) {
176
198
  if (signal?.aborted) {
177
199
  stopReason = "aborted";
178
200
  break;
179
201
  }
180
- if (require_evaluation.costExhausted({
202
+ if (require_warnings.costExhausted({
181
203
  usage: evaluator.usage(),
182
204
  maxCostUsd
183
205
  })) {
@@ -188,12 +210,12 @@ async function runRandomSearch(args) {
188
210
  stopReason = "deadlineReached";
189
211
  break;
190
212
  }
191
- if (!budget.canAfford(variants * validationSet.length)) {
213
+ if (!budget.canAfford(validationSet.length)) {
192
214
  stopReason = "budgetExhausted";
193
215
  break;
194
216
  }
195
217
  const component = components[round % components.length];
196
- onEvent?.({
218
+ emit({
197
219
  type: "roundStart",
198
220
  round,
199
221
  component
@@ -220,41 +242,64 @@ async function runRandomSearch(args) {
220
242
  });
221
243
  const unique = [...new Set(drawn)].filter((text) => text.length > 0 && text !== currentText);
222
244
  let roundStop;
245
+ const scheduled = [];
246
+ let owed = 0;
223
247
  for (const text of unique) {
224
248
  const candidate = {
225
249
  ...best,
226
250
  [component]: text
227
251
  };
228
- let evaluation;
229
- try {
230
- evaluation = await sweep({
231
- candidate,
232
- phase: "validation"
233
- });
234
- } catch (err) {
235
- if (err instanceof require_evaluation.BudgetExhausted) {
236
- roundStop = "budgetExhausted";
237
- break;
238
- }
239
- if (signal?.aborted) {
240
- roundStop = "aborted";
241
- break;
252
+ const uncached = evaluator.countUncached({
253
+ candidate,
254
+ ids: validationIds,
255
+ split: "val"
256
+ });
257
+ if (!budget.canAfford(owed + uncached)) {
258
+ roundStop = "budgetExhausted";
259
+ break;
260
+ }
261
+ owed += uncached;
262
+ scheduled.push(candidate);
263
+ }
264
+ const swept = await require_concurrency.mapWithConcurrency({
265
+ items: scheduled,
266
+ limit: concurrency,
267
+ task: async (candidate) => {
268
+ if (signal?.aborted) return { stop: "aborted" };
269
+ try {
270
+ return { evaluation: await sweep({
271
+ candidate,
272
+ phase: "validation"
273
+ }) };
274
+ } catch (err) {
275
+ if (err instanceof require_warnings.BudgetExhausted) return { stop: "budgetExhausted" };
276
+ if (signal?.aborted) return { stop: "aborted" };
277
+ throw err;
242
278
  }
243
- throw err;
279
+ }
280
+ });
281
+ for (const [index, outcome] of swept.entries()) {
282
+ if (outcome.stop !== void 0) {
283
+ roundStop = outcome.stop;
284
+ break;
244
285
  }
245
286
  variantsEvaluated += 1;
246
- const score = require_evaluation.measuredMean(evaluation);
287
+ const score = require_warnings.measuredMean(outcome.evaluation);
247
288
  if (score !== void 0 && score > bestScore) {
248
- onEvent?.({
289
+ const candidate = scheduled[index];
290
+ acceptedCandidates += 1;
291
+ emit({
249
292
  type: "candidateAccepted",
250
293
  round,
251
- component,
252
- score,
253
- previousScore: bestScore
294
+ candidateId: acceptedCandidates,
295
+ candidate,
296
+ aggregateScore: score,
297
+ instanceScores: require_warnings.instanceRow(outcome.evaluation),
298
+ ...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
254
299
  });
255
300
  best = candidate;
256
301
  bestScore = score;
257
- bestOutputs = evaluation.outputs;
302
+ bestOutputs = outcome.evaluation.outputs;
258
303
  }
259
304
  }
260
305
  round += 1;
@@ -270,7 +315,7 @@ async function runRandomSearch(args) {
270
315
  }
271
316
  }
272
317
  if (signal?.aborted) stopReason = "aborted";
273
- const testScore = testSet === void 0 ? void 0 : require_evaluation.measuredMean(await evaluator.evaluate({
318
+ const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
274
319
  candidate: best,
275
320
  batch: testSet,
276
321
  ids: testIds,
@@ -279,13 +324,18 @@ async function runRandomSearch(args) {
279
324
  candidateId: null,
280
325
  iteration: round,
281
326
  charge: false
282
- }));
283
- onEvent?.({
327
+ });
328
+ const testScore = heldOut === void 0 ? void 0 : require_warnings.measuredMean(heldOut);
329
+ emit({
284
330
  type: "finish",
285
331
  reason: stopReason,
332
+ warnings,
333
+ bestCandidateId: acceptedCandidates,
286
334
  bestScore,
287
335
  metricCalls: budget.spent(),
288
- ...testScore === void 0 ? {} : { testScore }
336
+ ...testScore === void 0 ? {} : { testScore },
337
+ ...heldOut === void 0 ? {} : { testInstanceScores: require_warnings.instanceRow(heldOut) },
338
+ ...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
289
339
  });
290
340
  return {
291
341
  bestCandidate: best,
@@ -296,13 +346,15 @@ async function runRandomSearch(args) {
296
346
  ...trackBestOutputs ? { bestOutputs } : {},
297
347
  ...testScore === void 0 ? {} : {
298
348
  testScore,
299
- testMetricCalls: evaluator.unchargedCalls()
349
+ testMetricCalls: evaluator.unchargedCalls(),
350
+ testUsage: evaluator.unchargedUsage()
300
351
  },
301
352
  rounds: round,
302
353
  variantsEvaluated,
303
354
  metricCalls: budget.spent(),
304
355
  reflectionCalls,
305
356
  cacheHits: evaluator.cacheHits(),
357
+ warnings,
306
358
  stopReason
307
359
  };
308
360
  }
@@ -312,10 +364,8 @@ function assertConfig(config) {
312
364
  if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
313
365
  if (maxRounds !== void 0 && (!Number.isInteger(maxRounds) || maxRounds < 1)) throw new Error(`maxRounds must be a positive integer, received ${maxRounds}`);
314
366
  }
315
- function defaultInstanceId(args) {
316
- const hash = require_evaluation.stableHash(args.datum);
317
- return hash === "" ? String(args.index) : hash;
318
- }
319
367
  //#endregion
320
368
  exports.RandomSearchOptimizer = RandomSearchOptimizer;
321
369
  exports.buildParaphrasePrompt = buildParaphrasePrompt;
370
+ exports.isCandidateAccepted = require_warnings.isCandidateAccepted;
371
+ exports.isRunFinished = require_warnings.isRunFinished;
@@ -1,6 +1,6 @@
1
- import { n as Candidate, t as Adapter, u as TextModel } from "../types-CWv4IQFF.cjs";
1
+ import { S as UsageTotals, a as RunFinished, f as Adapter, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, p as Candidate, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.cjs";
2
2
  import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.cjs";
3
- import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-B7SpRwl7.cjs";
3
+ import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-4Zv-Zt2t.cjs";
4
4
  //#region src/random-search/optimize.d.ts
5
5
  /** Builds the prompt one variant is drawn from. */
6
6
  type ParaphrasePromptBuilder = (args: {
@@ -54,6 +54,13 @@ interface RandomSearchSnapshot {
54
54
  reflectionCalls: number;
55
55
  metricCalls: number;
56
56
  cacheHits: number;
57
+ /** Usage already spent, so a resumed run reports totals and honours ceilings. */
58
+ usage?: UsageTotals;
59
+ /**
60
+ * Candidates accepted so far. Reporters key rows by this id, so restarting it
61
+ * at zero makes a resumed run collide with the run it continues.
62
+ */
63
+ acceptedCandidates?: number;
57
64
  /** Cached instance scores, when the cache can enumerate them. */
58
65
  cache?: [string, CachedScore][];
59
66
  }
@@ -71,7 +78,8 @@ interface RandomSearchTask<Datum, Trajectory = unknown, Output = unknown, K exte
71
78
  }) => string;
72
79
  /** Pass `false` to disable caching entirely. */
73
80
  cache?: EvaluationCache | false;
74
- onEvent?: (event: RandomSearchEvent<NoInfer<K>>) => void;
81
+ /** Observers of the run. Every one sees every event; none can fail it. */
82
+ reporters?: readonly Reporter<RandomSearchEvent<NoInfer<K>>>[];
75
83
  /**
76
84
  * Called with a resumable snapshot after the seed is scored and after every
77
85
  * round. Persist it and a killed run costs the last round, not all of them.
@@ -91,19 +99,13 @@ type RandomSearchEvent<K extends string = string> = {
91
99
  component: K;
92
100
  } | ({
93
101
  type: "evaluation";
94
- } & EvaluationEvent) | {
102
+ } & EvaluationEvent) | ({
95
103
  type: "candidateAccepted";
96
104
  round: number;
97
- component: K;
98
- score: number;
99
- previousScore: number;
100
- } | {
105
+ } & CandidateAccepted<K>) | ({
101
106
  type: "finish";
102
107
  reason: RandomSearchStopReason;
103
- bestScore: number;
104
- metricCalls: number;
105
- testScore?: number;
106
- };
108
+ } & RunFinished);
107
109
  interface RandomSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, RandomSearchStopReason, Output> {
108
110
  /** The seed's score, so the lift the search bought is readable directly. */
109
111
  seedScore: number;
@@ -153,4 +155,4 @@ declare function buildParaphrasePrompt(args: {
153
155
  attempt: number;
154
156
  }): string;
155
157
  //#endregion
156
- export { type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, buildParaphrasePrompt };
158
+ export { type CandidateAccepted, type OptimizerEvent, type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, type ReportableEvent, type Reporter, type RunFinished, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };
@@ -1,6 +1,6 @@
1
- import { n as Candidate, t as Adapter, u as TextModel } from "../types-CWv4IQFF.mjs";
1
+ import { S as UsageTotals, a as RunFinished, f as Adapter, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, p as Candidate, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.mjs";
2
2
  import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.mjs";
3
- import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-DqCoth_w.mjs";
3
+ import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-Ds5mzYjz.mjs";
4
4
  //#region src/random-search/optimize.d.ts
5
5
  /** Builds the prompt one variant is drawn from. */
6
6
  type ParaphrasePromptBuilder = (args: {
@@ -54,6 +54,13 @@ interface RandomSearchSnapshot {
54
54
  reflectionCalls: number;
55
55
  metricCalls: number;
56
56
  cacheHits: number;
57
+ /** Usage already spent, so a resumed run reports totals and honours ceilings. */
58
+ usage?: UsageTotals;
59
+ /**
60
+ * Candidates accepted so far. Reporters key rows by this id, so restarting it
61
+ * at zero makes a resumed run collide with the run it continues.
62
+ */
63
+ acceptedCandidates?: number;
57
64
  /** Cached instance scores, when the cache can enumerate them. */
58
65
  cache?: [string, CachedScore][];
59
66
  }
@@ -71,7 +78,8 @@ interface RandomSearchTask<Datum, Trajectory = unknown, Output = unknown, K exte
71
78
  }) => string;
72
79
  /** Pass `false` to disable caching entirely. */
73
80
  cache?: EvaluationCache | false;
74
- onEvent?: (event: RandomSearchEvent<NoInfer<K>>) => void;
81
+ /** Observers of the run. Every one sees every event; none can fail it. */
82
+ reporters?: readonly Reporter<RandomSearchEvent<NoInfer<K>>>[];
75
83
  /**
76
84
  * Called with a resumable snapshot after the seed is scored and after every
77
85
  * round. Persist it and a killed run costs the last round, not all of them.
@@ -91,19 +99,13 @@ type RandomSearchEvent<K extends string = string> = {
91
99
  component: K;
92
100
  } | ({
93
101
  type: "evaluation";
94
- } & EvaluationEvent) | {
102
+ } & EvaluationEvent) | ({
95
103
  type: "candidateAccepted";
96
104
  round: number;
97
- component: K;
98
- score: number;
99
- previousScore: number;
100
- } | {
105
+ } & CandidateAccepted<K>) | ({
101
106
  type: "finish";
102
107
  reason: RandomSearchStopReason;
103
- bestScore: number;
104
- metricCalls: number;
105
- testScore?: number;
106
- };
108
+ } & RunFinished);
107
109
  interface RandomSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, RandomSearchStopReason, Output> {
108
110
  /** The seed's score, so the lift the search bought is readable directly. */
109
111
  seedScore: number;
@@ -153,4 +155,4 @@ declare function buildParaphrasePrompt(args: {
153
155
  attempt: number;
154
156
  }): string;
155
157
  //#endregion
156
- export { type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, buildParaphrasePrompt };
158
+ export { type CandidateAccepted, type OptimizerEvent, type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, type ReportableEvent, type Reporter, type RunFinished, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };