textopt 0.0.0 → 0.1.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.
- package/README.md +44 -22
- package/dist/bootstrap-search/index.cjs +153 -73
- package/dist/bootstrap-search/index.d.cts +32 -10
- package/dist/bootstrap-search/index.d.mts +32 -10
- package/dist/bootstrap-search/index.mjs +144 -66
- package/dist/{demos-B0pVQjYC.d.mts → demos-1b7JiUue.d.mts} +10 -3
- package/dist/{demos-BTuzFNsp.d.cts → demos-CU9dy2oT.d.cts} +10 -3
- package/dist/demos-D2o0qSSo.cjs +244 -0
- package/dist/demos-DE2oxNWX.mjs +215 -0
- package/dist/file-cache.cjs +11 -3
- package/dist/file-cache.mjs +11 -3
- package/dist/gepa/index.cjs +76 -71
- package/dist/gepa/index.d.cts +12 -6
- package/dist/gepa/index.d.mts +12 -6
- package/dist/gepa/index.mjs +49 -46
- package/dist/index.cjs +129 -27
- package/dist/index.d.cts +145 -7
- package/dist/index.d.mts +145 -7
- package/dist/index.mjs +113 -15
- package/dist/{math-COOofUyv.cjs → math-BhlziRPc.cjs} +60 -9
- package/dist/math-Dqme4rYz.mjs +123 -0
- package/dist/mipro/index.cjs +98 -70
- package/dist/mipro/index.d.cts +17 -14
- package/dist/mipro/index.d.mts +17 -14
- package/dist/mipro/index.mjs +84 -58
- package/dist/opro/index.cjs +130 -51
- package/dist/opro/index.d.cts +17 -9
- package/dist/opro/index.d.mts +17 -9
- package/dist/opro/index.mjs +115 -38
- package/dist/{optimizer-B7SpRwl7.d.cts → optimizer-Bh5DPRMH.d.cts} +50 -4
- package/dist/{optimizer-DqCoth_w.d.mts → optimizer-Ck6-e_8o.d.mts} +50 -4
- package/dist/random-search/index.cjs +93 -49
- package/dist/random-search/index.d.cts +15 -13
- package/dist/random-search/index.d.mts +15 -13
- package/dist/random-search/index.mjs +83 -41
- package/dist/{reflection-CQToe-5B.d.cts → reflection-Dt3QrXhM.d.cts} +7 -11
- package/dist/{reflection-Cr_upzU0.d.mts → reflection-LRaAZP4e.d.mts} +7 -11
- package/dist/{evaluation-OZOp6TB7.cjs → reporting-CNHzbJC-.cjs} +165 -5
- package/dist/reporting-DQbAohc9.d.cts +240 -0
- package/dist/reporting-DQbAohc9.d.mts +240 -0
- package/dist/{evaluation-BV0nSZVx.mjs → reporting-DY-DC4HG.mjs} +124 -6
- package/dist/simba/index.cjs +210 -83
- package/dist/simba/index.d.cts +32 -11
- package/dist/simba/index.d.mts +32 -11
- package/dist/simba/index.mjs +200 -75
- package/dist/testing.cjs +1 -0
- package/dist/testing.d.cts +5 -3
- package/dist/testing.d.mts +5 -3
- package/dist/testing.mjs +1 -1
- package/package.json +4 -3
- package/dist/demos-B9BJiNKz.cjs +0 -143
- package/dist/demos-Degx6UmP.mjs +0 -126
- package/dist/math-DhrDmpFS.mjs +0 -78
- package/dist/types-CWv4IQFF.d.cts +0 -129
- package/dist/types-CWv4IQFF.d.mts +0 -129
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ This package has no runtime dependencies. For an overview of the algorithms and
|
|
|
23
23
|
```ts
|
|
24
24
|
import {
|
|
25
25
|
assertResumable,
|
|
26
|
-
|
|
26
|
+
harvestFewShotExamples,
|
|
27
27
|
buildJudgePrompt,
|
|
28
28
|
compare,
|
|
29
29
|
componentNames,
|
|
@@ -32,11 +32,13 @@ import {
|
|
|
32
32
|
createJudge,
|
|
33
33
|
createMemoryCache,
|
|
34
34
|
formatDemos,
|
|
35
|
+
harvestRollouts,
|
|
35
36
|
mapWithConcurrency,
|
|
36
37
|
parseDemos,
|
|
37
38
|
parseProposedText,
|
|
38
39
|
priceUsage,
|
|
39
40
|
runFingerprint,
|
|
41
|
+
toTrainingJsonl,
|
|
40
42
|
} from "textopt";
|
|
41
43
|
import type {
|
|
42
44
|
Adapter,
|
|
@@ -93,9 +95,9 @@ evaluate(args: EvaluateArgs<Datum, K>): Promise<EvaluationBatch<Trajectory, Outp
|
|
|
93
95
|
|
|
94
96
|
**`transient`** marks scores caused by infrastructure failures such as rate limits, 5xx responses, or network errors. Transient scores are not cached.
|
|
95
97
|
|
|
96
|
-
**`Optimizer<Stop extends string>`** defines `optimize(task: OptimizerTask) => Promise<OptimizerResult>`. `OptimizerTask` contains the shared run inputs: `seedCandidate`, `trainingSet`, `validationSet`, `testSet`, `adapter`, `maxMetricCalls`, `maxCostUsd`, `maxWallClockMs`, `cacheNamespace`, `retry`, and `signal`. `OptimizerResult` contains `bestCandidate`, `bestScore`, `bestOutputs`, `metricCalls`, `usage`, `testScore`, `testMetricCalls`, and `stopReason`. Optimizer-specific task and result types extend these interfaces.
|
|
98
|
+
**`Optimizer<Stop extends string>`** defines `optimize(task: OptimizerTask) => Promise<OptimizerResult>`. `OptimizerTask` contains the shared run inputs: `seedCandidate`, `trainingSet`, `validationSet`, `testSet`, `adapter`, `maxMetricCalls`, `maxCostUsd`, `maxWallClockMs`, `cacheNamespace`, `retry`, and `signal`. `OptimizerResult` contains `bestCandidate`, `bestScore`, `bestOutputs`, `metricCalls`, `usage`, `testScore`, `testMetricCalls`, `testUsage`, and `stopReason`. Optimizer-specific task and result types extend these interfaces.
|
|
97
99
|
|
|
98
|
-
**`maxCostUsd`** and **`maxWallClockMs`** are checked between evaluations, so a run overruns by
|
|
100
|
+
**`maxCostUsd`** and **`maxWallClockMs`** are checked between evaluations, so a run overruns by whatever it had in flight when the ceiling was reached — one evaluation at the default concurrency, and up to `concurrency` of them above it. Neither bounds the held-out sweep, which runs once the search has already stopped: it is reported apart from the search as `testMetricCalls` and `testUsage`, and has to be budgeted for separately. Neither follows from `maxMetricCalls`: reflective search grows the text it optimizes, so late rollouts cost more than early ones, and a run behind a rate limit spends almost nothing while taking as long as the provider makes it take.
|
|
99
101
|
|
|
100
102
|
**`cacheNamespace`** scopes every cache key to the system the rollouts were measured under — model id, decoding settings, scorer version. Change it whenever anything outside the candidate text changes.
|
|
101
103
|
|
|
@@ -103,7 +105,7 @@ evaluate(args: EvaluateArgs<Datum, K>): Promise<EvaluationBatch<Trajectory, Outp
|
|
|
103
105
|
|
|
104
106
|
**`UsageTotals`** (`inputTokens`, `outputTokens`, `totalTokens`, `costUsd`, `rollouts`) is summed from the `RolloutUsage` entries an adapter reports. Zero throughout when the adapter reports none.
|
|
105
107
|
|
|
106
|
-
**`testSet`** is excluded from search and evaluated once against the winner. Because candidates are selected on `validationSet`, `bestScore` may be fitted to it. `testScore` measures held-out performance. Test rollouts are reported as `testMetricCalls`
|
|
108
|
+
**`testSet`** is excluded from search and evaluated once against the winner. Because candidates are selected on `validationSet`, `bestScore` may be fitted to it. `testScore` measures held-out performance. Test rollouts are reported as `testMetricCalls` costing `testUsage`, outside `maxMetricCalls` and `maxCostUsd` both.
|
|
107
109
|
|
|
108
110
|
**`TextModel`** is the provider-independent interface `({ prompt, signal }) => Promise<string>`.
|
|
109
111
|
|
|
@@ -117,9 +119,13 @@ For Redis, SQLite, or file-backed caching, implement **`EvaluationCache`** with
|
|
|
117
119
|
|
|
118
120
|
**`componentNames(candidate)`** returns `Object.keys(candidate)` while preserving the component-name union.
|
|
119
121
|
|
|
120
|
-
**`createEvaluator({ adapter, budget, cache, cacheNamespace, retry, trackOutputs, onEvaluation, signal, cacheHits })`** handles adapter calls, caching, budget accounting, transient scores, and evaluation events. `evaluate` returns a `ScoredBatch`. `evaluateTraced` returns an `EvaluationBatch`, or `null` when the remaining budget cannot cover the batch. A batch that exceeds the charged budget throws `BudgetExhausted`. All included optimizers use this evaluator.
|
|
122
|
+
**`createEvaluator({ adapter, budget, cache, cacheNamespace, retry, trackOutputs, onEvaluation, signal, cacheHits, usage })`** handles adapter calls, caching, budget accounting, transient scores, and evaluation events. `cacheHits` and `usage` seed the counters from a checkpoint, so a resumed run reports totals rather than deltas. `usage()` covers the charged rollouts a ceiling is checked against; `unchargedUsage()` covers what `charge: false` bought. `evaluate` returns a `ScoredBatch`. `evaluateTraced` returns an `EvaluationBatch`, or `null` when the remaining budget cannot cover the batch. A batch that exceeds the charged budget throws `BudgetExhausted`. All included optimizers use this evaluator.
|
|
121
123
|
|
|
122
|
-
**`
|
|
124
|
+
**`harvestRollouts({ adapter, candidate, data, minScore, maxRollouts, batchSize, maxMetricCalls, maxCostUsd, rng, signal })`** runs a candidate over `data` and returns the `Rollout`s the metric rewarded, alongside `metricCalls` and `attempted`. Omit `minScore` to keep any rollout scoring above zero; omit `maxRollouts` to sweep the whole pool. It carries its own budget and does not use the score cache, because it needs the outputs a cache hit cannot return. `maxCostUsd` bounds its dollars, checked between batches — a caller bounding spend cannot bound this pass from outside, since it runs on its own evaluator. Sweeping a validation set is the mistake to avoid — see [Distilling a run](../../docs/distillation.md).
|
|
125
|
+
|
|
126
|
+
**`toTrainingJsonl({ rollouts, render })`** serializes harvested rollouts as one chat-messages example per line. `render` turns a rollout into `{ messages }` or returns `null` to skip it, and decides how much of the optimized candidate stays in the training input. Returns the text; writing it is the caller's job.
|
|
127
|
+
|
|
128
|
+
**`harvestFewShotExamples({ adapter, candidate, trainingSet, minScore, maxDemos, batchSize, maxMetricCalls, maxCostUsd, rng, renderDemo, signal })`** evaluates a candidate on `trainingSet` and keeps the rollouts the metric rewarded. Omit `minScore` to keep any rollout scoring above zero, as MIPROv2 does without a `metric_threshold`; pass a number to require at least that score. It returns the selected `demos`, a formatted `block`, and the metric calls and usage it spent. It does not use the score cache because it needs rollout outputs.
|
|
123
129
|
|
|
124
130
|
**`formatDemos(demos, { render })`** and **`parseDemos(text)`** write and read the `<demo>`, `<input>`, and `<output>` block format.
|
|
125
131
|
|
|
@@ -137,6 +143,18 @@ For Redis, SQLite, or file-backed caching, implement **`EvaluationCache`** with
|
|
|
137
143
|
|
|
138
144
|
**`BatchSampler<Datum>`** and **`Rng`** are type-only exports. Their default implementations are internal.
|
|
139
145
|
|
|
146
|
+
### Reporting
|
|
147
|
+
|
|
148
|
+
Every optimizer takes **`reporters`**, an array of **`Reporter<Event>`** with an optional `onEvent` and an optional `flush`. `onEvent` is called synchronously on the search's hot path, so a reporter that ships anywhere over a network buffers there and uploads in `flush`, which is awaited once as the run ends — including when it ends by throwing. A reporter that throws is warned about and skipped: observability never fails a run. Persisting a run so it can be resumed is `onCheckpoint`, which is durability and a separate concern.
|
|
149
|
+
|
|
150
|
+
Each search emits its own discriminated union, but two members are shared. **`CandidateAccepted`** carries `candidateId`, the `candidate` text, its `aggregateScore`, its `instanceScores` over the validation set, and its `outputs` under `trackBestOutputs`. **`RunFinished`** carries `bestCandidateId`, `bestScore`, `metricCalls`, and the winner's held-out `testScore`, `testInstanceScores` and `testOutputs`. In both rows, `undefined` marks an instance nothing measured — the evaluation policy skipped it, or an infrastructure failure lost it — and reporting one as a zero shows as a regression that never happened.
|
|
151
|
+
|
|
152
|
+
`candidateAccepted` fires only when the incumbent moves and a full validation sweep measured it, so `instanceScores` never means "a subset, and you work out which". SIMBA accepts on a minibatch, which is too small a sample to name a row against; it reports those in its own `candidate` event and emits `candidateAccepted` when the sweep that confirms a step winner lands.
|
|
153
|
+
|
|
154
|
+
Every optimizer reports the seed as `candidateId` 0 once its own sweep lands, so a report opens with the baseline every later candidate is read against rather than with the first improvement. A resumed run does not re-emit it. Each search adds its own fields to the event — GEPA its `iteration`, `parentIds` and `source`, MIPRO its `trial`, SIMBA its `step`, OPRO and random search their `round`, bootstrap search its `source` and `demos` — and drops anything the seed could not honestly carry, since the events that name a component or a menu selection already carry it.
|
|
155
|
+
|
|
156
|
+
A reporter that reads only those two takes **`OptimizerEvent`** — `{ type: string }` — and narrows with **`isCandidateAccepted`** and **`isRunFinished`**. Because a literal tag is assignable to `string` and the parameter position is contravariant, one such reporter drops into any optimizer's `reporters` array. **`ReportableEvent`** names the union it reads. `@textopt/langsmith` is written this way.
|
|
157
|
+
|
|
140
158
|
## `textopt/gepa`
|
|
141
159
|
|
|
142
160
|
```ts
|
|
@@ -185,17 +203,17 @@ These options control search behavior and can be reused across runs.
|
|
|
185
203
|
|
|
186
204
|
Required: `seedCandidate`, `trainingSet`, `adapter` (a `GepaAdapter`), `reflect` (a `TextModel`), and `maxMetricCalls`.
|
|
187
205
|
|
|
188
|
-
| Option | Default
|
|
189
|
-
| --------------------- |
|
|
190
|
-
| `validationSet` | the trainingSet
|
|
191
|
-
| `testSet` | none. Held out of the search and scored once, on the winner
|
|
192
|
-
| `componentSelector` | `roundRobinComponentSelector()`
|
|
193
|
-
| `batchSampler` | an epoch-shuffled sampler over `minibatchSize`
|
|
194
|
-
| `valEvaluationPolicy` | `fullEvaluationPolicy()`
|
|
195
|
-
| `instanceId` | a content hash of the datum, falling back to its position when
|
|
196
|
-
| `cache` | a per-run memory cache. Pass `false` to disable
|
|
206
|
+
| Option | Default |
|
|
207
|
+
| --------------------- | ---------------------------------------------------------------------------------- |
|
|
208
|
+
| `validationSet` | the trainingSet |
|
|
209
|
+
| `testSet` | none. Held out of the search and scored once, on the winner |
|
|
210
|
+
| `componentSelector` | `roundRobinComponentSelector()` |
|
|
211
|
+
| `batchSampler` | an epoch-shuffled sampler over `minibatchSize` |
|
|
212
|
+
| `valEvaluationPolicy` | `fullEvaluationPolicy()` |
|
|
213
|
+
| `instanceId` | a content hash of the datum, falling back to its position when JSON cannot read it |
|
|
214
|
+
| `cache` | a per-run memory cache. Pass `false` to disable |
|
|
197
215
|
|
|
198
|
-
`
|
|
216
|
+
`reporters`, `onCheckpoint`, `resumeFrom`, and `signal` have no defaults.
|
|
199
217
|
|
|
200
218
|
TypeScript infers component names and the datum type from `seedCandidate` and `trainingSet`. Other fields use `NoInfer` and are checked against those inferred types.
|
|
201
219
|
|
|
@@ -230,7 +248,7 @@ When `proposeNewTexts` is implemented, the adapter generates proposals without c
|
|
|
230
248
|
|
|
231
249
|
Each export is a factory. Selector and acceptance interfaces accept custom functions. A `ValEvaluationPolicy` is an object with `selectInstances` and `bestCandidate` methods.
|
|
232
250
|
|
|
233
|
-
`pairedPermutationAcceptance` and `lowerBoundEvaluationPolicy` exist for metrics whose readings vary between runs of the same text. Both are strictly more conservative than the defaults
|
|
251
|
+
`pairedPermutationAcceptance` and `lowerBoundEvaluationPolicy` exist for metrics whose readings vary between runs of the same text. Both are strictly more conservative than the defaults. In the twenty-seed benchmark the pair scores 0.931 against plain GEPA's 0.920 on the noisy task and 0.945 against 0.947 on the noiseless one, neither gap significant over those seeds — conservative enough to be worth having on a metric that varies, cheap enough not to hurt on one that does not. A sign-flip test also needs a wide enough minibatch to say anything — over three instances the smallest p-value it can produce is 0.125, so at the default `minibatchSize` no proposal clears an `alpha` below that.
|
|
234
252
|
|
|
235
253
|
### Reflection prompts
|
|
236
254
|
|
|
@@ -270,7 +288,11 @@ Feedback is end-to-end and every module receives the same string. A metric score
|
|
|
270
288
|
|
|
271
289
|
**`stopReason`** is one of `"budgetExhausted"`, `"costExhausted"`, `"deadlineReached"`, `"reflectionBudgetExhausted"`, `"aborted"`, or `"maxIterations"`.
|
|
272
290
|
|
|
273
|
-
**`
|
|
291
|
+
**`reporters`** is an array of `Reporter<GepaEvent>`, each with an optional `onEvent` and an optional `flush`. `onEvent` receives a discriminated `GepaEvent`: `start`, `iterationStart`, `evaluation`, `proposal`, `candidateAccepted`, `candidateRejected` (with `reason: "worse" | "notSelected"`), `error`, and `finish`. It is called synchronously, on the search's hot path, so a reporter that ships anywhere over a network buffers in `onEvent` and uploads in `flush`, which is awaited once as the run ends — including when it ends by throwing. A reporter that throws is warned about and skipped: observability never fails a run.
|
|
292
|
+
|
|
293
|
+
`candidateAccepted` fires for the seed too, and carries the `candidate` text, its `instanceScores` over the validation set, and its `outputs` under `trackBestOutputs` — the row a candidate put on the frontier, not just the mean. `finish` carries the same for the held-out sweep in `testInstanceScores` and `testOutputs`. In both, `undefined` marks an instance nothing measured — the evaluation policy skipped it, or an infrastructure failure lost it — and reporting one as a zero shows as a regression that never happened.
|
|
294
|
+
|
|
295
|
+
Reporting is observability; persisting a run so it can be resumed is `onCheckpoint`, below.
|
|
274
296
|
|
|
275
297
|
**`onCheckpoint`** runs after seed evaluation and each iteration with a JSON-serializable `GepaSnapshot`. Pass it as `resumeFrom` to continue. A fingerprint prevents resuming with a different seed candidate, instance set, or random seed. Every optimizer here has the same three: `onCheckpoint`, `resumeFrom`, and a `snapshot` on the result. A snapshot handed back as `resumeFrom` is copied, never mutated by the run that continues from it.
|
|
276
298
|
|
|
@@ -280,7 +302,7 @@ Feedback is end-to-end and every module receives the same string. A metric score
|
|
|
280
302
|
import { SimbaOptimizer, buildAdvicePrompt, parseAdvice } from "textopt/simba";
|
|
281
303
|
```
|
|
282
304
|
|
|
283
|
-
SIMBA uses the base `Adapter`: it reads outputs, scores, and feedback and builds its own evidence, so it needs no `makeReflectiveDataset`. `SimbaTask` adds `reflect`, `demoComponents`, `instructionComponents`, `renderDemo`, `buildAdvicePrompt`, `sampler`, `instanceId`, `cache`, `
|
|
305
|
+
SIMBA uses the base `Adapter`: it reads outputs, scores, and feedback and builds its own evidence, so it needs no `makeReflectiveDataset`. `SimbaTask` adds `reflect`, `demoComponents`, `instructionComponents`, `renderDemo`, `buildAdvicePrompt`, `sampler`, `instanceId`, `cache`, `reporters`, `onCheckpoint`, and `resumeFrom`.
|
|
284
306
|
|
|
285
307
|
| Option | Default | Effect |
|
|
286
308
|
| ---------------------- | -------------------- | -------------------------------------------------------------------------- |
|
|
@@ -320,7 +342,7 @@ Ported from DSPy's SIMBA with two deliberate changes. A trajectory sample runs o
|
|
|
320
342
|
import { BootstrapSearchOptimizer } from "textopt/bootstrap-search";
|
|
321
343
|
```
|
|
322
344
|
|
|
323
|
-
DSPy's `BootstrapFewShotWithRandomSearch`. It uses the base `Adapter` and no reflection model at all: every candidate is assembled from outputs the system itself produced, so the search costs rollouts and nothing else. `BootstrapSearchTask` adds `demoComponents` (required), `renderDemo`, `goldOutput`, `instanceId`, `cache`, `
|
|
345
|
+
DSPy's `BootstrapFewShotWithRandomSearch`. It uses the base `Adapter` and no reflection model at all: every candidate is assembled from outputs the system itself produced, so the search costs rollouts and nothing else. `BootstrapSearchTask` adds `demoComponents` (required), `renderDemo`, `goldOutput`, `instanceId`, `cache`, `reporters`, `onCheckpoint`, and `resumeFrom`.
|
|
324
346
|
|
|
325
347
|
| Option | Default | Effect |
|
|
326
348
|
| ------------------ | ------- | -------------------------------------------------------------------- |
|
|
@@ -352,7 +374,7 @@ Unlike DSPy, which bootstraps each predictor separately from the traces of one p
|
|
|
352
374
|
import { OproOptimizer, buildOproPrompt } from "textopt/opro";
|
|
353
375
|
```
|
|
354
376
|
|
|
355
|
-
OPRO uses the base `Adapter`. `OproTask` adds `reflect` and optional `renderDatum`, `instanceId`, `cache`, and `
|
|
377
|
+
OPRO uses the base `Adapter`. `OproTask` adds `reflect` and optional `renderDatum`, `instanceId`, `cache`, and `reporters` fields.
|
|
356
378
|
|
|
357
379
|
| Option | Default | Effect |
|
|
358
380
|
| -------------------- | ----------------- | ---------------------------------------------------------- |
|
|
@@ -399,7 +421,7 @@ For multi-component candidates, each attempt records the other components presen
|
|
|
399
421
|
import { MiproOptimizer, proposeConfiguration } from "textopt/mipro";
|
|
400
422
|
```
|
|
401
423
|
|
|
402
|
-
MIPRO uses the base `Adapter`. `MiproTask` adds `reflect`, `componentOptions`, `renderDatum`, `batchSampler`, `instanceId`, `cache`, and `
|
|
424
|
+
MIPRO uses the base `Adapter`. `MiproTask` adds `reflect`, `componentOptions`, `renderDatum`, `batchSampler`, `instanceId`, `cache`, and `reporters`.
|
|
403
425
|
|
|
404
426
|
| Option | Default | Effect |
|
|
405
427
|
| -------------------------- | ------------------ | ---------------------------------------------------------------------------------- |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const require_demos = require("../demos-
|
|
2
|
+
const require_reporting = require("../reporting-CNHzbJC-.cjs");
|
|
3
|
+
const require_demos = require("../demos-D2o0qSSo.cjs");
|
|
4
4
|
const require_rng = require("../rng-DbA_rPIo.cjs");
|
|
5
5
|
//#region src/bootstrap-search/optimize.ts
|
|
6
6
|
const DEFAULT_CANDIDATES = 16;
|
|
@@ -34,21 +34,27 @@ const DEFAULT_MAX_LABELED_DEMOS = 16;
|
|
|
34
34
|
var BootstrapSearchOptimizer = class {
|
|
35
35
|
#config;
|
|
36
36
|
constructor(config = {}) {
|
|
37
|
+
assertBootstrapSearchConfig(config);
|
|
37
38
|
this.#config = config;
|
|
38
39
|
}
|
|
39
|
-
optimize(task) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
async optimize(task) {
|
|
41
|
+
try {
|
|
42
|
+
return await run({
|
|
43
|
+
config: this.#config,
|
|
44
|
+
task
|
|
45
|
+
});
|
|
46
|
+
} finally {
|
|
47
|
+
await require_reporting.flushReporters(task.reporters ?? []);
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
};
|
|
46
51
|
async function run(args) {
|
|
47
52
|
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,
|
|
50
|
-
const
|
|
51
|
-
const
|
|
53
|
+
const { candidates: shuffledHarvests = DEFAULT_CANDIDATES, maxDemos = DEFAULT_MAX_DEMOS, minDemos = DEFAULT_MIN_DEMOS, maxLabeledDemos = DEFAULT_MAX_LABELED_DEMOS, demoMinScore, stopAtScore, concurrency = 1, seed = 0, trackBestOutputs = false, checkpointCache = true } = config;
|
|
54
|
+
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, demoComponents, renderDemo, goldOutput, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_reporting.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
55
|
+
const emit = require_reporting.createEmitter(reporters);
|
|
56
|
+
const deadline = require_reporting.createDeadline({ maxWallClockMs });
|
|
57
|
+
const components = require_reporting.componentNames(seedCandidate);
|
|
52
58
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
53
59
|
if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
|
|
54
60
|
if (demoComponents.length === 0) throw new Error("optimize requires at least one demoComponent: this search has nothing to put demonstrations in otherwise");
|
|
@@ -61,7 +67,7 @@ async function run(args) {
|
|
|
61
67
|
datum,
|
|
62
68
|
index
|
|
63
69
|
})) ?? [];
|
|
64
|
-
const fingerprint =
|
|
70
|
+
const fingerprint = require_reporting.runFingerprint({
|
|
65
71
|
seedCandidate,
|
|
66
72
|
trainingIds: trainingSet.map((datum, index) => instanceId({
|
|
67
73
|
datum,
|
|
@@ -71,17 +77,17 @@ async function run(args) {
|
|
|
71
77
|
seed,
|
|
72
78
|
...cacheNamespace === void 0 ? {} : { cacheNamespace }
|
|
73
79
|
});
|
|
74
|
-
|
|
80
|
+
require_reporting.assertResumable({
|
|
75
81
|
fingerprint,
|
|
76
82
|
...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
|
|
77
83
|
});
|
|
78
84
|
const rng = require_rng.createSeededRng(seed, resumeFrom?.rngState);
|
|
79
|
-
const budget =
|
|
85
|
+
const budget = require_reporting.createBudget({
|
|
80
86
|
maxMetricCalls,
|
|
81
87
|
spent: resumeFrom?.metricCalls ?? 0
|
|
82
88
|
});
|
|
83
|
-
const evaluationCache = cache === false ? void 0 : cache ??
|
|
84
|
-
const evaluator =
|
|
89
|
+
const evaluationCache = cache === false ? void 0 : cache ?? require_reporting.createMemoryCache();
|
|
90
|
+
const evaluator = require_reporting.createEvaluator({
|
|
85
91
|
adapter,
|
|
86
92
|
budget,
|
|
87
93
|
...retry === void 0 ? {} : { retry },
|
|
@@ -89,8 +95,9 @@ async function run(args) {
|
|
|
89
95
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
90
96
|
trackOutputs: trackBestOutputs,
|
|
91
97
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
98
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
92
99
|
...signal === void 0 ? {} : { signal },
|
|
93
|
-
onEvaluation: (event) =>
|
|
100
|
+
onEvaluation: (event) => emit({
|
|
94
101
|
type: "evaluation",
|
|
95
102
|
...event
|
|
96
103
|
})
|
|
@@ -100,7 +107,7 @@ async function run(args) {
|
|
|
100
107
|
let drawn = resumeFrom?.drawn ?? 0;
|
|
101
108
|
let bootstrapMetricCalls = resumeFrom?.bootstrapMetricCalls ?? 0;
|
|
102
109
|
let stopReason = "candidatesExhausted";
|
|
103
|
-
|
|
110
|
+
emit({
|
|
104
111
|
type: "start",
|
|
105
112
|
components,
|
|
106
113
|
validationSetSize: validationSet.length
|
|
@@ -116,13 +123,25 @@ async function run(args) {
|
|
|
116
123
|
iteration: evaluated.length
|
|
117
124
|
});
|
|
118
125
|
}
|
|
119
|
-
const
|
|
120
|
-
|
|
126
|
+
const seedEvaluation = resumeFrom === void 0 ? await sweep(seedCandidate, "seed") : void 0;
|
|
127
|
+
const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore : require_reporting.requireMeasuredMean({
|
|
128
|
+
batch: seedEvaluation,
|
|
121
129
|
phase: "seed"
|
|
122
130
|
});
|
|
123
131
|
let best = resumeFrom?.best ?? seedCandidate;
|
|
124
132
|
let bestScore = resumeFrom?.bestScore ?? seedScore;
|
|
125
133
|
let bestOutputs;
|
|
134
|
+
let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
|
|
135
|
+
if (seedEvaluation !== void 0) emit({
|
|
136
|
+
type: "candidateAccepted",
|
|
137
|
+
source: "zeroShot",
|
|
138
|
+
demos: 0,
|
|
139
|
+
candidateId: 0,
|
|
140
|
+
candidate: seedCandidate,
|
|
141
|
+
aggregateScore: seedScore,
|
|
142
|
+
instanceScores: require_reporting.instanceRow(seedEvaluation),
|
|
143
|
+
...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
|
|
144
|
+
});
|
|
126
145
|
function takeSnapshot() {
|
|
127
146
|
const cached = checkpointCache ? evaluationCache?.entries?.() : void 0;
|
|
128
147
|
return {
|
|
@@ -136,6 +155,8 @@ async function run(args) {
|
|
|
136
155
|
metricCalls: budget.spent(),
|
|
137
156
|
bootstrapMetricCalls,
|
|
138
157
|
cacheHits: evaluator.cacheHits(),
|
|
158
|
+
usage: evaluator.usage(),
|
|
159
|
+
acceptedCandidates,
|
|
139
160
|
rngState: rng.state(),
|
|
140
161
|
...cached === void 0 ? {} : { cache: cached }
|
|
141
162
|
};
|
|
@@ -148,12 +169,13 @@ async function run(args) {
|
|
|
148
169
|
shuffledHarvests,
|
|
149
170
|
labeled: goldOutput !== void 0
|
|
150
171
|
});
|
|
151
|
-
|
|
172
|
+
const waveSize = stopAtScore === void 0 ? concurrency : 1;
|
|
173
|
+
while (drawn < plan.length) {
|
|
152
174
|
if (signal?.aborted) {
|
|
153
175
|
stopReason = "aborted";
|
|
154
176
|
break;
|
|
155
177
|
}
|
|
156
|
-
if (
|
|
178
|
+
if (require_reporting.costExhausted({
|
|
157
179
|
usage: evaluator.usage(),
|
|
158
180
|
maxCostUsd
|
|
159
181
|
})) {
|
|
@@ -164,58 +186,95 @@ async function run(args) {
|
|
|
164
186
|
stopReason = "deadlineReached";
|
|
165
187
|
break;
|
|
166
188
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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";
|
|
189
|
+
const wave = [];
|
|
190
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
191
|
+
let waveStop;
|
|
192
|
+
while (wave.length < waveSize && drawn + wave.length < plan.length) {
|
|
193
|
+
if (!budget.canAfford(validationSet.length + 1)) {
|
|
194
|
+
waveStop = "budgetExhausted";
|
|
184
195
|
break;
|
|
185
196
|
}
|
|
186
|
-
|
|
197
|
+
const source = plan[drawn + wave.length];
|
|
198
|
+
const block = await buildBlock(source);
|
|
199
|
+
const candidate = withDemos(block);
|
|
200
|
+
const key = require_reporting.candidateHash(candidate);
|
|
201
|
+
const prior = inFlight.get(key);
|
|
202
|
+
const sweeping = prior === void 0 ? settled(sweep(candidate, "validation")) : prior.then(() => settled(sweep(candidate, "validation")));
|
|
203
|
+
inFlight.set(key, sweeping);
|
|
204
|
+
wave.push({
|
|
205
|
+
source,
|
|
206
|
+
candidate,
|
|
207
|
+
block,
|
|
208
|
+
sweep: sweeping
|
|
209
|
+
});
|
|
187
210
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
let waveError;
|
|
212
|
+
let draining = false;
|
|
213
|
+
for (const entry of wave) {
|
|
214
|
+
const outcome = await entry.sweep;
|
|
215
|
+
if (draining) continue;
|
|
216
|
+
drawn += 1;
|
|
217
|
+
if (outcome.failed === true) {
|
|
218
|
+
draining = true;
|
|
219
|
+
if (outcome.err instanceof require_reporting.BudgetExhausted) {
|
|
220
|
+
waveStop = "budgetExhausted";
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (signal?.aborted) {
|
|
224
|
+
waveStop = "aborted";
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
waveError = outcome.err;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const evaluation = outcome.evaluation;
|
|
231
|
+
const score = require_reporting.measuredMean(evaluation);
|
|
232
|
+
if (score === void 0) continue;
|
|
233
|
+
const accepted = score > bestScore;
|
|
234
|
+
evaluated.push({
|
|
235
|
+
candidate: entry.candidate,
|
|
236
|
+
source: entry.source,
|
|
237
|
+
demos: countDemos(entry.block),
|
|
238
|
+
score
|
|
239
|
+
});
|
|
240
|
+
emit({
|
|
241
|
+
type: "candidate",
|
|
242
|
+
index: evaluated.length - 1,
|
|
243
|
+
source: entry.source,
|
|
244
|
+
demos: countDemos(entry.block),
|
|
245
|
+
score,
|
|
246
|
+
accepted
|
|
247
|
+
});
|
|
248
|
+
if (accepted) {
|
|
249
|
+
acceptedCandidates += 1;
|
|
250
|
+
emit({
|
|
251
|
+
type: "candidateAccepted",
|
|
252
|
+
source: entry.source,
|
|
253
|
+
demos: countDemos(entry.block),
|
|
254
|
+
candidateId: acceptedCandidates,
|
|
255
|
+
candidate: entry.candidate,
|
|
256
|
+
aggregateScore: score,
|
|
257
|
+
instanceScores: require_reporting.instanceRow(evaluation),
|
|
258
|
+
...trackBestOutputs ? { outputs: evaluation.outputs } : {}
|
|
259
|
+
});
|
|
260
|
+
best = entry.candidate;
|
|
261
|
+
bestScore = score;
|
|
262
|
+
bestOutputs = evaluation.outputs;
|
|
263
|
+
}
|
|
264
|
+
if (stopAtScore !== void 0 && score >= stopAtScore) {
|
|
265
|
+
waveStop = "scoreReached";
|
|
266
|
+
draining = true;
|
|
267
|
+
}
|
|
209
268
|
}
|
|
269
|
+
if (waveError !== void 0) throw waveError;
|
|
210
270
|
await checkpoint();
|
|
211
|
-
if (
|
|
212
|
-
stopReason =
|
|
213
|
-
drawn += 1;
|
|
271
|
+
if (waveStop !== void 0) {
|
|
272
|
+
stopReason = waveStop;
|
|
214
273
|
break;
|
|
215
274
|
}
|
|
216
275
|
}
|
|
217
276
|
if (signal?.aborted) stopReason = "aborted";
|
|
218
|
-
const
|
|
277
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
219
278
|
candidate: best,
|
|
220
279
|
batch: testSet,
|
|
221
280
|
ids: testIds,
|
|
@@ -224,13 +283,17 @@ async function run(args) {
|
|
|
224
283
|
candidateId: null,
|
|
225
284
|
iteration: evaluated.length,
|
|
226
285
|
charge: false
|
|
227
|
-
})
|
|
228
|
-
|
|
286
|
+
});
|
|
287
|
+
const testScore = heldOut === void 0 ? void 0 : require_reporting.measuredMean(heldOut);
|
|
288
|
+
emit({
|
|
229
289
|
type: "finish",
|
|
230
290
|
reason: stopReason,
|
|
291
|
+
bestCandidateId: acceptedCandidates,
|
|
231
292
|
bestScore,
|
|
232
293
|
metricCalls: budget.spent(),
|
|
233
|
-
...testScore === void 0 ? {} : { testScore }
|
|
294
|
+
...testScore === void 0 ? {} : { testScore },
|
|
295
|
+
...heldOut === void 0 ? {} : { testInstanceScores: require_reporting.instanceRow(heldOut) },
|
|
296
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
234
297
|
});
|
|
235
298
|
return {
|
|
236
299
|
bestCandidate: best,
|
|
@@ -245,7 +308,8 @@ async function run(args) {
|
|
|
245
308
|
...trackBestOutputs && bestOutputs !== void 0 ? { bestOutputs } : {},
|
|
246
309
|
...testScore === void 0 ? {} : {
|
|
247
310
|
testScore,
|
|
248
|
-
testMetricCalls:
|
|
311
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
312
|
+
testUsage: evaluator.unchargedUsage()
|
|
249
313
|
},
|
|
250
314
|
stopReason
|
|
251
315
|
};
|
|
@@ -260,19 +324,21 @@ async function run(args) {
|
|
|
260
324
|
const requested = source === "unshuffled" ? maxDemos : minDemos + rng.nextInt(Math.max(1, maxDemos - minDemos + 1));
|
|
261
325
|
const affordable = Math.min(trainingSet.length, budget.remaining() - validationSet.length);
|
|
262
326
|
if (affordable < 1) return "";
|
|
263
|
-
const harvest = await require_demos.
|
|
327
|
+
const harvest = await require_demos.harvestFewShotExamples({
|
|
264
328
|
adapter,
|
|
265
329
|
candidate: seedCandidate,
|
|
266
330
|
trainingSet,
|
|
267
331
|
...demoMinScore === void 0 ? {} : { minScore: demoMinScore },
|
|
268
332
|
maxDemos: requested,
|
|
269
333
|
maxMetricCalls: affordable,
|
|
334
|
+
...maxCostUsd === void 0 ? {} : { maxCostUsd: maxCostUsd - evaluator.usage().costUsd },
|
|
270
335
|
...source === "unshuffled" ? {} : { rng },
|
|
271
336
|
...renderDemo === void 0 ? {} : { renderDemo },
|
|
272
337
|
...signal === void 0 ? {} : { signal }
|
|
273
338
|
});
|
|
274
339
|
bootstrapMetricCalls += harvest.metricCalls;
|
|
275
340
|
budget.reserve(harvest.metricCalls);
|
|
341
|
+
evaluator.absorbUsage(harvest.usage);
|
|
276
342
|
return harvest.block;
|
|
277
343
|
}
|
|
278
344
|
function labeledBlock() {
|
|
@@ -298,11 +364,25 @@ function candidatePlan(args) {
|
|
|
298
364
|
...Array.from({ length: shuffledHarvests }, () => "bootstrapped")
|
|
299
365
|
];
|
|
300
366
|
}
|
|
367
|
+
/** Turns a sweep into a value, so a dispatched one never rejects unobserved. */
|
|
368
|
+
function settled(sweeping) {
|
|
369
|
+
return sweeping.then((evaluation) => ({ evaluation }), (err) => ({
|
|
370
|
+
failed: true,
|
|
371
|
+
err
|
|
372
|
+
}));
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Range checks on the search knobs, run at construction so a configuration
|
|
376
|
+
* that could never terminate is refused before a task is ever handed to it.
|
|
377
|
+
*/
|
|
378
|
+
function assertBootstrapSearchConfig(config) {
|
|
379
|
+
const { concurrency = 1 } = config;
|
|
380
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
381
|
+
}
|
|
301
382
|
function countDemos(block) {
|
|
302
383
|
return block.split("<demo>").length - 1;
|
|
303
384
|
}
|
|
304
|
-
function defaultInstanceId(args) {
|
|
305
|
-
return String(args.index);
|
|
306
|
-
}
|
|
307
385
|
//#endregion
|
|
308
386
|
exports.BootstrapSearchOptimizer = BootstrapSearchOptimizer;
|
|
387
|
+
exports.isCandidateAccepted = require_reporting.isCandidateAccepted;
|
|
388
|
+
exports.isRunFinished = require_reporting.isRunFinished;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as RunFinished, c as Adapter, i as Reporter, l as Candidate, n as OptimizerEvent, o as isCandidateAccepted, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, v as UsageTotals } from "../reporting-DQbAohc9.cjs";
|
|
2
2
|
import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.cjs";
|
|
3
|
-
import { r as DemoRenderer } from "../demos-
|
|
4
|
-
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-
|
|
3
|
+
import { r as DemoRenderer } from "../demos-CU9dy2oT.cjs";
|
|
4
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-Bh5DPRMH.cjs";
|
|
5
5
|
//#region src/bootstrap-search/optimize.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Where a candidate's demo block came from. `zeroShot` holds no demos at all,
|
|
@@ -35,6 +35,18 @@ interface BootstrapSearchConfig {
|
|
|
35
35
|
* every candidate, which is the reliable reading and the expensive one.
|
|
36
36
|
*/
|
|
37
37
|
stopAtScore?: number;
|
|
38
|
+
/**
|
|
39
|
+
* How many candidates may be swept at once. Default 1.
|
|
40
|
+
*
|
|
41
|
+
* Harvesting stays in plan order however this is set — every harvest draws
|
|
42
|
+
* from the same random stream, and reordering them would make a seeded run
|
|
43
|
+
* unreproducible — so what overlaps is a sweep with the harvest of the
|
|
44
|
+
* candidates behind it. Two costs come with raising it: a checkpoint is
|
|
45
|
+
* taken per wave rather than per candidate, so a killed run loses up to this
|
|
46
|
+
* many candidates instead of one, and `stopAtScore` is honoured by sweeping
|
|
47
|
+
* one at a time, since a wave cannot know it has already passed the target.
|
|
48
|
+
*/
|
|
49
|
+
concurrency?: number;
|
|
38
50
|
seed?: number;
|
|
39
51
|
trackBestOutputs?: boolean;
|
|
40
52
|
/**
|
|
@@ -61,6 +73,13 @@ interface BootstrapSearchSnapshot {
|
|
|
61
73
|
metricCalls: number;
|
|
62
74
|
bootstrapMetricCalls: number;
|
|
63
75
|
cacheHits: number;
|
|
76
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
77
|
+
usage?: UsageTotals;
|
|
78
|
+
/**
|
|
79
|
+
* Candidates accepted so far. Reporters key rows by this id, so restarting it
|
|
80
|
+
* at zero makes a resumed run collide with the run it continues.
|
|
81
|
+
*/
|
|
82
|
+
acceptedCandidates?: number;
|
|
64
83
|
rngState: number;
|
|
65
84
|
cache?: [string, CachedScore][];
|
|
66
85
|
}
|
|
@@ -92,7 +111,8 @@ interface BootstrapSearchTask<Datum, Trajectory = unknown, Output = unknown, K e
|
|
|
92
111
|
}) => string;
|
|
93
112
|
/** Pass `false` to disable caching entirely. */
|
|
94
113
|
cache?: EvaluationCache | false;
|
|
95
|
-
|
|
114
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
115
|
+
reporters?: readonly Reporter<BootstrapSearchEvent<NoInfer<K>>>[];
|
|
96
116
|
/** Called with a resumable snapshot after every candidate is scored. */
|
|
97
117
|
onCheckpoint?: (snapshot: BootstrapSearchSnapshot) => void | Promise<void>;
|
|
98
118
|
/** Snapshot to continue from. */
|
|
@@ -112,13 +132,15 @@ type BootstrapSearchEvent<K extends string = string> = {
|
|
|
112
132
|
demos: number;
|
|
113
133
|
score: number;
|
|
114
134
|
accepted: boolean;
|
|
115
|
-
} | {
|
|
135
|
+
} | ({
|
|
136
|
+
type: "candidateAccepted";
|
|
137
|
+
/** Which construction produced the demo block that won. */
|
|
138
|
+
source: DemoSource;
|
|
139
|
+
demos: number;
|
|
140
|
+
} & CandidateAccepted<K>) | ({
|
|
116
141
|
type: "finish";
|
|
117
142
|
reason: BootstrapSearchStopReason;
|
|
118
|
-
|
|
119
|
-
metricCalls: number;
|
|
120
|
-
testScore?: number;
|
|
121
|
-
};
|
|
143
|
+
} & RunFinished);
|
|
122
144
|
interface BootstrapSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, BootstrapSearchStopReason, Output> {
|
|
123
145
|
/** The seed's score, so the lift the demos bought is readable directly. */
|
|
124
146
|
seedScore: number;
|
|
@@ -159,4 +181,4 @@ declare class BootstrapSearchOptimizer implements Optimizer<BootstrapSearchStopR
|
|
|
159
181
|
optimize<Datum, Trajectory = unknown, Output = unknown, const K extends string = string>(task: BootstrapSearchTask<Datum, Trajectory, Output, K>): Promise<BootstrapSearchResult<K, Output>>;
|
|
160
182
|
}
|
|
161
183
|
//#endregion
|
|
162
|
-
export { type BootstrapCandidate, type BootstrapSearchConfig, type BootstrapSearchEvent, BootstrapSearchOptimizer, type BootstrapSearchResult, type BootstrapSearchSnapshot, type BootstrapSearchStopReason, type BootstrapSearchTask, type DemoSource };
|
|
184
|
+
export { type BootstrapCandidate, type BootstrapSearchConfig, type BootstrapSearchEvent, BootstrapSearchOptimizer, type BootstrapSearchResult, type BootstrapSearchSnapshot, type BootstrapSearchStopReason, type BootstrapSearchTask, type CandidateAccepted, type DemoSource, type OptimizerEvent, type ReportableEvent, type Reporter, type RunFinished, isCandidateAccepted, isRunFinished };
|