textopt 0.0.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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +509 -0
  3. package/dist/bootstrap-search/index.cjs +308 -0
  4. package/dist/bootstrap-search/index.d.cts +162 -0
  5. package/dist/bootstrap-search/index.d.mts +162 -0
  6. package/dist/bootstrap-search/index.mjs +307 -0
  7. package/dist/cache-CuSo0NJ8.d.cts +24 -0
  8. package/dist/cache-CuSo0NJ8.d.mts +24 -0
  9. package/dist/concurrency-C-cFzWW2.cjs +44 -0
  10. package/dist/concurrency-D58PWeSk.mjs +39 -0
  11. package/dist/demos-B0pVQjYC.d.mts +88 -0
  12. package/dist/demos-B9BJiNKz.cjs +143 -0
  13. package/dist/demos-BTuzFNsp.d.cts +88 -0
  14. package/dist/demos-Degx6UmP.mjs +126 -0
  15. package/dist/evaluation-BV0nSZVx.mjs +521 -0
  16. package/dist/evaluation-OZOp6TB7.cjs +598 -0
  17. package/dist/file-cache.cjs +70 -0
  18. package/dist/file-cache.d.cts +21 -0
  19. package/dist/file-cache.d.mts +21 -0
  20. package/dist/file-cache.mjs +69 -0
  21. package/dist/gepa/index.cjs +1671 -0
  22. package/dist/gepa/index.d.cts +385 -0
  23. package/dist/gepa/index.d.mts +385 -0
  24. package/dist/gepa/index.mjs +1652 -0
  25. package/dist/index.cjs +266 -0
  26. package/dist/index.d.cts +221 -0
  27. package/dist/index.d.mts +221 -0
  28. package/dist/index.mjs +245 -0
  29. package/dist/math-COOofUyv.cjs +101 -0
  30. package/dist/math-DhrDmpFS.mjs +78 -0
  31. package/dist/mipro/index.cjs +739 -0
  32. package/dist/mipro/index.d.cts +372 -0
  33. package/dist/mipro/index.d.mts +372 -0
  34. package/dist/mipro/index.mjs +736 -0
  35. package/dist/opro/index.cjs +487 -0
  36. package/dist/opro/index.d.cts +230 -0
  37. package/dist/opro/index.d.mts +230 -0
  38. package/dist/opro/index.mjs +485 -0
  39. package/dist/optimizer-B7SpRwl7.d.cts +288 -0
  40. package/dist/optimizer-DqCoth_w.d.mts +288 -0
  41. package/dist/random-search/index.cjs +321 -0
  42. package/dist/random-search/index.d.cts +156 -0
  43. package/dist/random-search/index.d.mts +156 -0
  44. package/dist/random-search/index.mjs +319 -0
  45. package/dist/reflection-CQToe-5B.d.cts +283 -0
  46. package/dist/reflection-Cr_upzU0.d.mts +283 -0
  47. package/dist/reflection-DRfbk6hu.cjs +249 -0
  48. package/dist/reflection-mwMhrjs_.mjs +214 -0
  49. package/dist/rng-BR5MOedA.d.cts +22 -0
  50. package/dist/rng-BR5MOedA.d.mts +22 -0
  51. package/dist/rng-DbA_rPIo.cjs +67 -0
  52. package/dist/rng-Dtc5eZ_W.mjs +62 -0
  53. package/dist/sampling-CfHt7Gue.mjs +59 -0
  54. package/dist/sampling-DFo_7RNJ.d.mts +23 -0
  55. package/dist/sampling-Dars7ctR.cjs +64 -0
  56. package/dist/sampling-axOwfZf5.d.cts +23 -0
  57. package/dist/simba/index.cjs +709 -0
  58. package/dist/simba/index.d.cts +289 -0
  59. package/dist/simba/index.d.mts +289 -0
  60. package/dist/simba/index.mjs +700 -0
  61. package/dist/testing.cjs +155 -0
  62. package/dist/testing.d.cts +53 -0
  63. package/dist/testing.d.mts +53 -0
  64. package/dist/testing.mjs +148 -0
  65. package/dist/text--v4Ffbus.mjs +21 -0
  66. package/dist/text-CK_HB3su.cjs +26 -0
  67. package/dist/types-CWv4IQFF.d.cts +129 -0
  68. package/dist/types-CWv4IQFF.d.mts +129 -0
  69. package/package.json +135 -0
@@ -0,0 +1,308 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_evaluation = require("../evaluation-OZOp6TB7.cjs");
3
+ const require_demos = require("../demos-B9BJiNKz.cjs");
4
+ const require_rng = require("../rng-DbA_rPIo.cjs");
5
+ //#region src/bootstrap-search/optimize.ts
6
+ const DEFAULT_CANDIDATES = 16;
7
+ const DEFAULT_MAX_DEMOS = 4;
8
+ const DEFAULT_MIN_DEMOS = 1;
9
+ const DEFAULT_MAX_LABELED_DEMOS = 16;
10
+ /**
11
+ * Bootstrapped few-shot search: harvest demonstrations from rollouts the metric
12
+ * already rewarded, and pick the set that scores best.
13
+ *
14
+ * DSPy's `BootstrapFewShotWithRandomSearch`, which is what the literature
15
+ * usually means by "random search" over prompts. It is the only optimizer here
16
+ * that calls no model to write text: every candidate is assembled from outputs
17
+ * the system itself produced, so the search costs rollouts and nothing else.
18
+ * That makes it the right first thing to try — it is cheap, it needs no
19
+ * frontier model, and on tasks where the instruction is already adequate and
20
+ * the format is not, it is often the whole win.
21
+ *
22
+ * The fixed candidates come first and in DSPy's order: zero-shot (seed -3),
23
+ * labels-only when gold outputs exist (seed -2), and one unshuffled harvest at
24
+ * full size (seed -1). Shuffled harvests of random size follow. Keeping
25
+ * zero-shot in the running is not a formality — demonstrations can hurt, and a
26
+ * search that cannot return "no demos" has no baseline to report against.
27
+ *
28
+ * One deviation, deliberate: DSPy bootstraps each predictor separately from the
29
+ * traces of one pass. This library's adapter runs the whole system, so a
30
+ * harvest is a set of end-to-end rollouts and every demo component is given the
31
+ * same block. For per-module demos, use `createPipelineAdapter` with GEPA,
32
+ * which sees each module's own inputs and outputs.
33
+ */
34
+ var BootstrapSearchOptimizer = class {
35
+ #config;
36
+ constructor(config = {}) {
37
+ this.#config = config;
38
+ }
39
+ optimize(task) {
40
+ return run({
41
+ config: this.#config,
42
+ task
43
+ });
44
+ }
45
+ };
46
+ async function run(args) {
47
+ const { config, task } = args;
48
+ const { candidates: shuffledHarvests = DEFAULT_CANDIDATES, maxDemos = DEFAULT_MAX_DEMOS, minDemos = DEFAULT_MIN_DEMOS, maxLabeledDemos = DEFAULT_MAX_LABELED_DEMOS, demoMinScore, stopAtScore, seed = 0, trackBestOutputs = false, checkpointCache = true } = config;
49
+ const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, demoComponents, renderDemo, goldOutput, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, onEvent, onCheckpoint, resumeFrom, signal } = task;
50
+ const deadline = require_evaluation.createDeadline({ maxWallClockMs });
51
+ const components = require_evaluation.componentNames(seedCandidate);
52
+ if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
53
+ if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
54
+ if (demoComponents.length === 0) throw new Error("optimize requires at least one demoComponent: this search has nothing to put demonstrations in otherwise");
55
+ if (testSet !== void 0 && testSet.length === 0) throw new Error("optimize requires a non-empty testSet when one is given; omit it to skip held-out evaluation");
56
+ const validationIds = validationSet.map((datum, index) => instanceId({
57
+ datum,
58
+ index
59
+ }));
60
+ const testIds = testSet?.map((datum, index) => instanceId({
61
+ datum,
62
+ index
63
+ })) ?? [];
64
+ const fingerprint = require_evaluation.runFingerprint({
65
+ seedCandidate,
66
+ trainingIds: trainingSet.map((datum, index) => instanceId({
67
+ datum,
68
+ index
69
+ })),
70
+ validationIds,
71
+ seed,
72
+ ...cacheNamespace === void 0 ? {} : { cacheNamespace }
73
+ });
74
+ require_evaluation.assertResumable({
75
+ fingerprint,
76
+ ...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
77
+ });
78
+ const rng = require_rng.createSeededRng(seed, resumeFrom?.rngState);
79
+ const budget = require_evaluation.createBudget({
80
+ maxMetricCalls,
81
+ spent: resumeFrom?.metricCalls ?? 0
82
+ });
83
+ const evaluationCache = cache === false ? void 0 : cache ?? require_evaluation.createMemoryCache();
84
+ const evaluator = require_evaluation.createEvaluator({
85
+ adapter,
86
+ budget,
87
+ ...retry === void 0 ? {} : { retry },
88
+ ...cacheNamespace === void 0 ? {} : { cacheNamespace },
89
+ ...evaluationCache === void 0 ? {} : { cache: evaluationCache },
90
+ trackOutputs: trackBestOutputs,
91
+ cacheHits: resumeFrom?.cacheHits ?? 0,
92
+ ...signal === void 0 ? {} : { signal },
93
+ onEvaluation: (event) => onEvent?.({
94
+ type: "evaluation",
95
+ ...event
96
+ })
97
+ });
98
+ evaluator.restore(resumeFrom?.cache ?? []);
99
+ const evaluated = [...resumeFrom?.candidates ?? []];
100
+ let drawn = resumeFrom?.drawn ?? 0;
101
+ let bootstrapMetricCalls = resumeFrom?.bootstrapMetricCalls ?? 0;
102
+ let stopReason = "candidatesExhausted";
103
+ onEvent?.({
104
+ type: "start",
105
+ components,
106
+ validationSetSize: validationSet.length
107
+ });
108
+ async function sweep(candidate, phase) {
109
+ return evaluator.evaluate({
110
+ candidate,
111
+ batch: validationSet,
112
+ ids: validationIds,
113
+ split: "val",
114
+ phase,
115
+ candidateId: null,
116
+ iteration: evaluated.length
117
+ });
118
+ }
119
+ const seedScore = resumeFrom?.seedScore ?? require_evaluation.requireMeasuredMean({
120
+ batch: await sweep(seedCandidate, "seed"),
121
+ phase: "seed"
122
+ });
123
+ let best = resumeFrom?.best ?? seedCandidate;
124
+ let bestScore = resumeFrom?.bestScore ?? seedScore;
125
+ let bestOutputs;
126
+ function takeSnapshot() {
127
+ const cached = checkpointCache ? evaluationCache?.entries?.() : void 0;
128
+ return {
129
+ version: 1,
130
+ fingerprint,
131
+ candidates: [...evaluated],
132
+ best,
133
+ bestScore,
134
+ seedScore,
135
+ drawn,
136
+ metricCalls: budget.spent(),
137
+ bootstrapMetricCalls,
138
+ cacheHits: evaluator.cacheHits(),
139
+ rngState: rng.state(),
140
+ ...cached === void 0 ? {} : { cache: cached }
141
+ };
142
+ }
143
+ async function checkpoint() {
144
+ if (onCheckpoint === void 0) return;
145
+ await onCheckpoint(takeSnapshot());
146
+ }
147
+ const plan = candidatePlan({
148
+ shuffledHarvests,
149
+ labeled: goldOutput !== void 0
150
+ });
151
+ for (; drawn < plan.length; drawn += 1) {
152
+ if (signal?.aborted) {
153
+ stopReason = "aborted";
154
+ break;
155
+ }
156
+ if (require_evaluation.costExhausted({
157
+ usage: evaluator.usage(),
158
+ maxCostUsd
159
+ })) {
160
+ stopReason = "costExhausted";
161
+ break;
162
+ }
163
+ if (deadline.exceeded()) {
164
+ stopReason = "deadlineReached";
165
+ break;
166
+ }
167
+ if (!budget.canAfford(validationSet.length + 1)) {
168
+ stopReason = "budgetExhausted";
169
+ break;
170
+ }
171
+ const source = plan[drawn];
172
+ const block = await buildBlock(source);
173
+ const candidate = withDemos(block);
174
+ let evaluation;
175
+ try {
176
+ evaluation = await sweep(candidate, "validation");
177
+ } catch (err) {
178
+ if (err instanceof require_evaluation.BudgetExhausted) {
179
+ stopReason = "budgetExhausted";
180
+ break;
181
+ }
182
+ if (signal?.aborted) {
183
+ stopReason = "aborted";
184
+ break;
185
+ }
186
+ throw err;
187
+ }
188
+ const score = require_evaluation.measuredMean(evaluation);
189
+ if (score === void 0) continue;
190
+ const accepted = score > bestScore;
191
+ evaluated.push({
192
+ candidate,
193
+ source,
194
+ demos: countDemos(block),
195
+ score
196
+ });
197
+ onEvent?.({
198
+ type: "candidate",
199
+ index: evaluated.length - 1,
200
+ source,
201
+ demos: countDemos(block),
202
+ score,
203
+ accepted
204
+ });
205
+ if (accepted) {
206
+ best = candidate;
207
+ bestScore = score;
208
+ bestOutputs = evaluation.outputs;
209
+ }
210
+ await checkpoint();
211
+ if (stopAtScore !== void 0 && score >= stopAtScore) {
212
+ stopReason = "scoreReached";
213
+ drawn += 1;
214
+ break;
215
+ }
216
+ }
217
+ if (signal?.aborted) stopReason = "aborted";
218
+ const testScore = testSet === void 0 ? void 0 : require_evaluation.measuredMean(await evaluator.evaluate({
219
+ candidate: best,
220
+ batch: testSet,
221
+ ids: testIds,
222
+ split: "test",
223
+ phase: "test",
224
+ candidateId: null,
225
+ iteration: evaluated.length,
226
+ charge: false
227
+ }));
228
+ onEvent?.({
229
+ type: "finish",
230
+ reason: stopReason,
231
+ bestScore,
232
+ metricCalls: budget.spent(),
233
+ ...testScore === void 0 ? {} : { testScore }
234
+ });
235
+ return {
236
+ bestCandidate: best,
237
+ bestScore,
238
+ usage: evaluator.usage(),
239
+ seedScore,
240
+ candidates: evaluated,
241
+ bootstrapMetricCalls,
242
+ cacheHits: evaluator.cacheHits(),
243
+ metricCalls: budget.spent(),
244
+ snapshot: takeSnapshot(),
245
+ ...trackBestOutputs && bestOutputs !== void 0 ? { bestOutputs } : {},
246
+ ...testScore === void 0 ? {} : {
247
+ testScore,
248
+ testMetricCalls: testSet?.length ?? 0
249
+ },
250
+ stopReason
251
+ };
252
+ function withDemos(block) {
253
+ const candidate = { ...seedCandidate };
254
+ for (const name of demoComponents) candidate[name] = block;
255
+ return candidate;
256
+ }
257
+ async function buildBlock(source) {
258
+ if (source === "zeroShot") return "";
259
+ if (source === "labeled") return labeledBlock();
260
+ const requested = source === "unshuffled" ? maxDemos : minDemos + rng.nextInt(Math.max(1, maxDemos - minDemos + 1));
261
+ const affordable = Math.min(trainingSet.length, budget.remaining() - validationSet.length);
262
+ if (affordable < 1) return "";
263
+ const harvest = await require_demos.bootstrapDemos({
264
+ adapter,
265
+ candidate: seedCandidate,
266
+ trainingSet,
267
+ ...demoMinScore === void 0 ? {} : { minScore: demoMinScore },
268
+ maxDemos: requested,
269
+ maxMetricCalls: affordable,
270
+ ...source === "unshuffled" ? {} : { rng },
271
+ ...renderDemo === void 0 ? {} : { renderDemo },
272
+ ...signal === void 0 ? {} : { signal }
273
+ });
274
+ bootstrapMetricCalls += harvest.metricCalls;
275
+ budget.reserve(harvest.metricCalls);
276
+ return harvest.block;
277
+ }
278
+ function labeledBlock() {
279
+ const labelled = trainingSet.map((datum) => ({
280
+ input: datum,
281
+ output: goldOutput?.(datum)
282
+ })).filter((demo) => demo.output !== void 0).slice(0, maxLabeledDemos);
283
+ return require_demos.formatDemos(labelled, renderDemo === void 0 ? {} : { render: renderDemo });
284
+ }
285
+ }
286
+ /**
287
+ * The order candidates are tried in, following DSPy's special seeds: zero-shot
288
+ * first, then labels-only, then one unshuffled full-size harvest, then the
289
+ * shuffled ones. Cheapest and most reliable first, so a run cut short by its
290
+ * budget still has the baseline it needs to report against.
291
+ */
292
+ function candidatePlan(args) {
293
+ const { shuffledHarvests, labeled } = args;
294
+ return [
295
+ "zeroShot",
296
+ ...labeled ? ["labeled"] : [],
297
+ "unshuffled",
298
+ ...Array.from({ length: shuffledHarvests }, () => "bootstrapped")
299
+ ];
300
+ }
301
+ function countDemos(block) {
302
+ return block.split("<demo>").length - 1;
303
+ }
304
+ function defaultInstanceId(args) {
305
+ return String(args.index);
306
+ }
307
+ //#endregion
308
+ exports.BootstrapSearchOptimizer = BootstrapSearchOptimizer;
@@ -0,0 +1,162 @@
1
+ import { n as Candidate, t as Adapter } from "../types-CWv4IQFF.cjs";
2
+ import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.cjs";
3
+ import { r as DemoRenderer } from "../demos-BTuzFNsp.cjs";
4
+ import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-B7SpRwl7.cjs";
5
+ //#region src/bootstrap-search/optimize.d.ts
6
+ /**
7
+ * Where a candidate's demo block came from. `zeroShot` holds no demos at all,
8
+ * `labeled` is built from gold outputs and costs no rollouts, `unshuffled`
9
+ * takes the training set in order, and `bootstrapped` is one shuffled harvest.
10
+ */
11
+ type DemoSource = "zeroShot" | "labeled" | "unshuffled" | "bootstrapped";
12
+ interface BootstrapCandidate<K extends string = string> {
13
+ candidate: Candidate<K>;
14
+ source: DemoSource;
15
+ /** Demos in the block, so a win can be read against how much it carried. */
16
+ demos: number;
17
+ score: number;
18
+ }
19
+ interface BootstrapSearchConfig {
20
+ /**
21
+ * Shuffled harvests attempted, beyond the fixed candidates every run tries.
22
+ * Default 16, DSPy's `num_candidate_programs`.
23
+ */
24
+ candidates?: number;
25
+ /** Most demos a harvested set may hold. Default 4. */
26
+ maxDemos?: number;
27
+ /** Fewest demos a shuffled harvest may ask for. Default 1. */
28
+ minDemos?: number;
29
+ /** Most demos the labels-only candidate may hold. Default 16. */
30
+ maxLabeledDemos?: number;
31
+ /** Score a rollout must reach to be kept as a demo. */
32
+ demoMinScore?: number;
33
+ /**
34
+ * Stop as soon as a candidate reaches this validation score. Unset evaluates
35
+ * every candidate, which is the reliable reading and the expensive one.
36
+ */
37
+ stopAtScore?: number;
38
+ seed?: number;
39
+ trackBestOutputs?: boolean;
40
+ /**
41
+ * Include cached instance scores in every checkpoint. Leaving them out keeps
42
+ * snapshots small at the cost of a resumed run re-paying for rollouts it
43
+ * cannot look up. Default true.
44
+ */
45
+ checkpointCache?: boolean;
46
+ }
47
+ /**
48
+ * Everything needed to continue a run: which candidates have been evaluated,
49
+ * what they scored, and the budget already spent. Plain JSON — persist it and
50
+ * hand it back as `resumeFrom`.
51
+ */
52
+ interface BootstrapSearchSnapshot {
53
+ version: 1;
54
+ fingerprint: string;
55
+ candidates: BootstrapCandidate[];
56
+ best: Candidate;
57
+ bestScore: number;
58
+ seedScore: number;
59
+ /** How many candidates have been drawn, including the fixed ones. */
60
+ drawn: number;
61
+ metricCalls: number;
62
+ bootstrapMetricCalls: number;
63
+ cacheHits: number;
64
+ rngState: number;
65
+ cache?: [string, CachedScore][];
66
+ }
67
+ interface BootstrapSearchTask<Datum, Trajectory = unknown, Output = unknown, K extends string = string> extends OptimizerTask<Datum, Trajectory, Output, K> {
68
+ /**
69
+ * The base adapter, and no reflection model: this search writes no text at
70
+ * all, so there is nothing for a model to propose.
71
+ */
72
+ adapter: Adapter<Datum, Trajectory, Output, NoInfer<K>>;
73
+ /**
74
+ * Components holding few-shot demo blocks. Every one of them receives the
75
+ * same harvest, because a harvest is a pass over the training set and one
76
+ * pass per component would multiply the cost of the search by the number of
77
+ * components for evidence that came from the same rollouts.
78
+ */
79
+ demoComponents: readonly NoInfer<K>[];
80
+ /** Renders a harvested rollout as demo text. Defaults to JSON. */
81
+ renderDemo?: DemoRenderer<NoInfer<Datum>, NoInfer<Output>>;
82
+ /**
83
+ * The gold output for a training datum, where the caller has labels. Supply
84
+ * it and the run tries a labels-only candidate, which costs no rollouts to
85
+ * build and is the only candidate available at all to a system too weak to
86
+ * bootstrap from.
87
+ */
88
+ goldOutput?: (datum: NoInfer<Datum>) => NoInfer<Output> | undefined;
89
+ instanceId?: (args: {
90
+ datum: NoInfer<Datum>;
91
+ index: number;
92
+ }) => string;
93
+ /** Pass `false` to disable caching entirely. */
94
+ cache?: EvaluationCache | false;
95
+ onEvent?: (event: BootstrapSearchEvent<NoInfer<K>>) => void;
96
+ /** Called with a resumable snapshot after every candidate is scored. */
97
+ onCheckpoint?: (snapshot: BootstrapSearchSnapshot) => void | Promise<void>;
98
+ /** Snapshot to continue from. */
99
+ resumeFrom?: BootstrapSearchSnapshot;
100
+ }
101
+ type BootstrapSearchStopReason = "budgetExhausted" | "costExhausted" | "deadlineReached" | "scoreReached" | "candidatesExhausted" | "aborted";
102
+ type BootstrapSearchEvent<K extends string = string> = {
103
+ type: "start";
104
+ components: K[];
105
+ validationSetSize: number;
106
+ } | ({
107
+ type: "evaluation";
108
+ } & EvaluationEvent) | {
109
+ type: "candidate";
110
+ index: number;
111
+ source: DemoSource;
112
+ demos: number;
113
+ score: number;
114
+ accepted: boolean;
115
+ } | {
116
+ type: "finish";
117
+ reason: BootstrapSearchStopReason;
118
+ bestScore: number;
119
+ metricCalls: number;
120
+ testScore?: number;
121
+ };
122
+ interface BootstrapSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, BootstrapSearchStopReason, Output> {
123
+ /** The seed's score, so the lift the demos bought is readable directly. */
124
+ seedScore: number;
125
+ /** Every candidate evaluated, in the order it was tried. */
126
+ candidates: BootstrapCandidate<K>[];
127
+ /** Rollouts spent harvesting demos, included in `metricCalls`. */
128
+ bootstrapMetricCalls: number;
129
+ cacheHits: number;
130
+ snapshot: BootstrapSearchSnapshot;
131
+ }
132
+ /**
133
+ * Bootstrapped few-shot search: harvest demonstrations from rollouts the metric
134
+ * already rewarded, and pick the set that scores best.
135
+ *
136
+ * DSPy's `BootstrapFewShotWithRandomSearch`, which is what the literature
137
+ * usually means by "random search" over prompts. It is the only optimizer here
138
+ * that calls no model to write text: every candidate is assembled from outputs
139
+ * the system itself produced, so the search costs rollouts and nothing else.
140
+ * That makes it the right first thing to try — it is cheap, it needs no
141
+ * frontier model, and on tasks where the instruction is already adequate and
142
+ * the format is not, it is often the whole win.
143
+ *
144
+ * The fixed candidates come first and in DSPy's order: zero-shot (seed -3),
145
+ * labels-only when gold outputs exist (seed -2), and one unshuffled harvest at
146
+ * full size (seed -1). Shuffled harvests of random size follow. Keeping
147
+ * zero-shot in the running is not a formality — demonstrations can hurt, and a
148
+ * search that cannot return "no demos" has no baseline to report against.
149
+ *
150
+ * One deviation, deliberate: DSPy bootstraps each predictor separately from the
151
+ * traces of one pass. This library's adapter runs the whole system, so a
152
+ * harvest is a set of end-to-end rollouts and every demo component is given the
153
+ * same block. For per-module demos, use `createPipelineAdapter` with GEPA,
154
+ * which sees each module's own inputs and outputs.
155
+ */
156
+ declare class BootstrapSearchOptimizer implements Optimizer<BootstrapSearchStopReason> {
157
+ #private;
158
+ constructor(config?: BootstrapSearchConfig);
159
+ optimize<Datum, Trajectory = unknown, Output = unknown, const K extends string = string>(task: BootstrapSearchTask<Datum, Trajectory, Output, K>): Promise<BootstrapSearchResult<K, Output>>;
160
+ }
161
+ //#endregion
162
+ export { type BootstrapCandidate, type BootstrapSearchConfig, type BootstrapSearchEvent, BootstrapSearchOptimizer, type BootstrapSearchResult, type BootstrapSearchSnapshot, type BootstrapSearchStopReason, type BootstrapSearchTask, type DemoSource };
@@ -0,0 +1,162 @@
1
+ import { n as Candidate, t as Adapter } from "../types-CWv4IQFF.mjs";
2
+ import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.mjs";
3
+ import { r as DemoRenderer } from "../demos-B0pVQjYC.mjs";
4
+ import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-DqCoth_w.mjs";
5
+ //#region src/bootstrap-search/optimize.d.ts
6
+ /**
7
+ * Where a candidate's demo block came from. `zeroShot` holds no demos at all,
8
+ * `labeled` is built from gold outputs and costs no rollouts, `unshuffled`
9
+ * takes the training set in order, and `bootstrapped` is one shuffled harvest.
10
+ */
11
+ type DemoSource = "zeroShot" | "labeled" | "unshuffled" | "bootstrapped";
12
+ interface BootstrapCandidate<K extends string = string> {
13
+ candidate: Candidate<K>;
14
+ source: DemoSource;
15
+ /** Demos in the block, so a win can be read against how much it carried. */
16
+ demos: number;
17
+ score: number;
18
+ }
19
+ interface BootstrapSearchConfig {
20
+ /**
21
+ * Shuffled harvests attempted, beyond the fixed candidates every run tries.
22
+ * Default 16, DSPy's `num_candidate_programs`.
23
+ */
24
+ candidates?: number;
25
+ /** Most demos a harvested set may hold. Default 4. */
26
+ maxDemos?: number;
27
+ /** Fewest demos a shuffled harvest may ask for. Default 1. */
28
+ minDemos?: number;
29
+ /** Most demos the labels-only candidate may hold. Default 16. */
30
+ maxLabeledDemos?: number;
31
+ /** Score a rollout must reach to be kept as a demo. */
32
+ demoMinScore?: number;
33
+ /**
34
+ * Stop as soon as a candidate reaches this validation score. Unset evaluates
35
+ * every candidate, which is the reliable reading and the expensive one.
36
+ */
37
+ stopAtScore?: number;
38
+ seed?: number;
39
+ trackBestOutputs?: boolean;
40
+ /**
41
+ * Include cached instance scores in every checkpoint. Leaving them out keeps
42
+ * snapshots small at the cost of a resumed run re-paying for rollouts it
43
+ * cannot look up. Default true.
44
+ */
45
+ checkpointCache?: boolean;
46
+ }
47
+ /**
48
+ * Everything needed to continue a run: which candidates have been evaluated,
49
+ * what they scored, and the budget already spent. Plain JSON — persist it and
50
+ * hand it back as `resumeFrom`.
51
+ */
52
+ interface BootstrapSearchSnapshot {
53
+ version: 1;
54
+ fingerprint: string;
55
+ candidates: BootstrapCandidate[];
56
+ best: Candidate;
57
+ bestScore: number;
58
+ seedScore: number;
59
+ /** How many candidates have been drawn, including the fixed ones. */
60
+ drawn: number;
61
+ metricCalls: number;
62
+ bootstrapMetricCalls: number;
63
+ cacheHits: number;
64
+ rngState: number;
65
+ cache?: [string, CachedScore][];
66
+ }
67
+ interface BootstrapSearchTask<Datum, Trajectory = unknown, Output = unknown, K extends string = string> extends OptimizerTask<Datum, Trajectory, Output, K> {
68
+ /**
69
+ * The base adapter, and no reflection model: this search writes no text at
70
+ * all, so there is nothing for a model to propose.
71
+ */
72
+ adapter: Adapter<Datum, Trajectory, Output, NoInfer<K>>;
73
+ /**
74
+ * Components holding few-shot demo blocks. Every one of them receives the
75
+ * same harvest, because a harvest is a pass over the training set and one
76
+ * pass per component would multiply the cost of the search by the number of
77
+ * components for evidence that came from the same rollouts.
78
+ */
79
+ demoComponents: readonly NoInfer<K>[];
80
+ /** Renders a harvested rollout as demo text. Defaults to JSON. */
81
+ renderDemo?: DemoRenderer<NoInfer<Datum>, NoInfer<Output>>;
82
+ /**
83
+ * The gold output for a training datum, where the caller has labels. Supply
84
+ * it and the run tries a labels-only candidate, which costs no rollouts to
85
+ * build and is the only candidate available at all to a system too weak to
86
+ * bootstrap from.
87
+ */
88
+ goldOutput?: (datum: NoInfer<Datum>) => NoInfer<Output> | undefined;
89
+ instanceId?: (args: {
90
+ datum: NoInfer<Datum>;
91
+ index: number;
92
+ }) => string;
93
+ /** Pass `false` to disable caching entirely. */
94
+ cache?: EvaluationCache | false;
95
+ onEvent?: (event: BootstrapSearchEvent<NoInfer<K>>) => void;
96
+ /** Called with a resumable snapshot after every candidate is scored. */
97
+ onCheckpoint?: (snapshot: BootstrapSearchSnapshot) => void | Promise<void>;
98
+ /** Snapshot to continue from. */
99
+ resumeFrom?: BootstrapSearchSnapshot;
100
+ }
101
+ type BootstrapSearchStopReason = "budgetExhausted" | "costExhausted" | "deadlineReached" | "scoreReached" | "candidatesExhausted" | "aborted";
102
+ type BootstrapSearchEvent<K extends string = string> = {
103
+ type: "start";
104
+ components: K[];
105
+ validationSetSize: number;
106
+ } | ({
107
+ type: "evaluation";
108
+ } & EvaluationEvent) | {
109
+ type: "candidate";
110
+ index: number;
111
+ source: DemoSource;
112
+ demos: number;
113
+ score: number;
114
+ accepted: boolean;
115
+ } | {
116
+ type: "finish";
117
+ reason: BootstrapSearchStopReason;
118
+ bestScore: number;
119
+ metricCalls: number;
120
+ testScore?: number;
121
+ };
122
+ interface BootstrapSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, BootstrapSearchStopReason, Output> {
123
+ /** The seed's score, so the lift the demos bought is readable directly. */
124
+ seedScore: number;
125
+ /** Every candidate evaluated, in the order it was tried. */
126
+ candidates: BootstrapCandidate<K>[];
127
+ /** Rollouts spent harvesting demos, included in `metricCalls`. */
128
+ bootstrapMetricCalls: number;
129
+ cacheHits: number;
130
+ snapshot: BootstrapSearchSnapshot;
131
+ }
132
+ /**
133
+ * Bootstrapped few-shot search: harvest demonstrations from rollouts the metric
134
+ * already rewarded, and pick the set that scores best.
135
+ *
136
+ * DSPy's `BootstrapFewShotWithRandomSearch`, which is what the literature
137
+ * usually means by "random search" over prompts. It is the only optimizer here
138
+ * that calls no model to write text: every candidate is assembled from outputs
139
+ * the system itself produced, so the search costs rollouts and nothing else.
140
+ * That makes it the right first thing to try — it is cheap, it needs no
141
+ * frontier model, and on tasks where the instruction is already adequate and
142
+ * the format is not, it is often the whole win.
143
+ *
144
+ * The fixed candidates come first and in DSPy's order: zero-shot (seed -3),
145
+ * labels-only when gold outputs exist (seed -2), and one unshuffled harvest at
146
+ * full size (seed -1). Shuffled harvests of random size follow. Keeping
147
+ * zero-shot in the running is not a formality — demonstrations can hurt, and a
148
+ * search that cannot return "no demos" has no baseline to report against.
149
+ *
150
+ * One deviation, deliberate: DSPy bootstraps each predictor separately from the
151
+ * traces of one pass. This library's adapter runs the whole system, so a
152
+ * harvest is a set of end-to-end rollouts and every demo component is given the
153
+ * same block. For per-module demos, use `createPipelineAdapter` with GEPA,
154
+ * which sees each module's own inputs and outputs.
155
+ */
156
+ declare class BootstrapSearchOptimizer implements Optimizer<BootstrapSearchStopReason> {
157
+ #private;
158
+ constructor(config?: BootstrapSearchConfig);
159
+ optimize<Datum, Trajectory = unknown, Output = unknown, const K extends string = string>(task: BootstrapSearchTask<Datum, Trajectory, Output, K>): Promise<BootstrapSearchResult<K, Output>>;
160
+ }
161
+ //#endregion
162
+ export { type BootstrapCandidate, type BootstrapSearchConfig, type BootstrapSearchEvent, BootstrapSearchOptimizer, type BootstrapSearchResult, type BootstrapSearchSnapshot, type BootstrapSearchStopReason, type BootstrapSearchTask, type DemoSource };