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.
- package/README.md +65 -25
- package/dist/bootstrap-search/index.cjs +159 -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 +150 -66
- package/dist/demos-9v5ts7F3.cjs +244 -0
- package/dist/{demos-B0pVQjYC.d.mts → demos-ASsSXYXA.d.mts} +10 -3
- package/dist/demos-Brobjfuc.mjs +215 -0
- package/dist/{demos-BTuzFNsp.d.cts → demos-ByaLZy-Z.d.cts} +10 -3
- package/dist/file-cache.cjs +27 -8
- package/dist/file-cache.d.cts +13 -0
- package/dist/file-cache.d.mts +13 -0
- package/dist/file-cache.mjs +27 -8
- package/dist/gepa/index.cjs +128 -80
- package/dist/gepa/index.d.cts +15 -7
- package/dist/gepa/index.d.mts +15 -7
- package/dist/gepa/index.mjs +101 -55
- package/dist/index.cjs +157 -30
- package/dist/index.d.cts +177 -7
- package/dist/index.d.mts +177 -7
- package/dist/index.mjs +139 -18
- package/dist/{math-COOofUyv.cjs → math-BhlziRPc.cjs} +60 -9
- package/dist/math-Dqme4rYz.mjs +123 -0
- package/dist/mipro/index.cjs +104 -70
- package/dist/mipro/index.d.cts +17 -14
- package/dist/mipro/index.d.mts +17 -14
- package/dist/mipro/index.mjs +90 -58
- package/dist/opro/index.cjs +136 -51
- package/dist/opro/index.d.cts +17 -9
- package/dist/opro/index.d.mts +17 -9
- package/dist/opro/index.mjs +121 -38
- package/dist/{optimizer-B7SpRwl7.d.cts → optimizer-4Zv-Zt2t.d.cts} +90 -5
- package/dist/{optimizer-DqCoth_w.d.mts → optimizer-Ds5mzYjz.d.mts} +90 -5
- package/dist/random-search/index.cjs +99 -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 +89 -41
- package/dist/{reflection-Cr_upzU0.d.mts → reflection-CMezGu6u.d.mts} +38 -14
- package/dist/{reflection-CQToe-5B.d.cts → reflection-D0A7eahD.d.cts} +38 -14
- package/dist/reporting-bq007_2z.d.cts +294 -0
- package/dist/reporting-bq007_2z.d.mts +294 -0
- package/dist/simba/index.cjs +216 -83
- package/dist/simba/index.d.cts +53 -13
- package/dist/simba/index.d.mts +53 -13
- package/dist/simba/index.mjs +206 -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/dist/{evaluation-OZOp6TB7.cjs → warnings-CWRJF-jA.cjs} +228 -5
- package/dist/{evaluation-BV0nSZVx.mjs → warnings-OxvDi9kN.mjs} +175 -6
- package/docs/adapters.md +169 -0
- package/docs/benchmark.md +90 -0
- package/docs/data-prep.md +113 -0
- package/docs/distillation.md +128 -0
- package/docs/evaluation.md +87 -0
- package/docs/metric-preflight.md +132 -0
- package/docs/optimizers.md +293 -0
- package/docs/tuning.md +130 -0
- package/package.json +6 -4
- 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
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A candidate is a map of named text components to their current text. This is
|
|
4
|
+
* the unit of optimization — prompts, instructions, code, tool descriptions,
|
|
5
|
+
* anything expressible as a named string.
|
|
6
|
+
*
|
|
7
|
+
* `K` is the union of component names, inferred from the seed candidate, so a
|
|
8
|
+
* misspelled component is a compile error rather than a silent no-op.
|
|
9
|
+
*/
|
|
10
|
+
type Candidate<K extends string = string> = Record<K, string>;
|
|
11
|
+
/**
|
|
12
|
+
* What one rollout consumed. Every field is optional because providers report
|
|
13
|
+
* different subsets, and a partial reading is still worth more than none.
|
|
14
|
+
*/
|
|
15
|
+
interface RolloutUsage {
|
|
16
|
+
inputTokens?: number;
|
|
17
|
+
outputTokens?: number;
|
|
18
|
+
/** Defaults to the sum of the two token counts when they are reported. */
|
|
19
|
+
totalTokens?: number;
|
|
20
|
+
costUsd?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Usage summed over a run, alongside the rollouts that produced it. */
|
|
23
|
+
interface UsageTotals {
|
|
24
|
+
inputTokens: number;
|
|
25
|
+
outputTokens: number;
|
|
26
|
+
totalTokens: number;
|
|
27
|
+
costUsd: number;
|
|
28
|
+
/** Fresh rollouts counted here. Cached instances buy nothing. */
|
|
29
|
+
rollouts: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of running a candidate over a batch of data instances.
|
|
33
|
+
*
|
|
34
|
+
* `scores` is the load-bearing field: one number per instance, higher is
|
|
35
|
+
* better. `feedback` is a per-instance textual diagnosis of what went wrong,
|
|
36
|
+
* which a reflective optimizer reads to write a better candidate.
|
|
37
|
+
*/
|
|
38
|
+
interface EvaluationBatch<Trajectory = unknown, Output = unknown> {
|
|
39
|
+
outputs: Output[];
|
|
40
|
+
scores: number[];
|
|
41
|
+
/**
|
|
42
|
+
* What each rollout consumed. Rollout counts are the budget, but they are a
|
|
43
|
+
* poor proxy for spend: reflective search grows the text it optimizes, so
|
|
44
|
+
* the same rollout costs more late in a run than early in it.
|
|
45
|
+
*/
|
|
46
|
+
usage?: RolloutUsage[];
|
|
47
|
+
feedback?: string[];
|
|
48
|
+
trajectories?: Trajectory[];
|
|
49
|
+
objectiveScores?: Record<string, number>[];
|
|
50
|
+
/**
|
|
51
|
+
* Per-instance: true when the score reflects an infrastructure failure
|
|
52
|
+
* rather than the candidate's behaviour. Transient scores are never written
|
|
53
|
+
* to the evaluation cache.
|
|
54
|
+
*/
|
|
55
|
+
transient?: boolean[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What a per-instance scorer returns. Shared by every adapter so scorers are
|
|
59
|
+
* portable between them — a Braintrust scorer works in a LangChain run.
|
|
60
|
+
*/
|
|
61
|
+
interface ScoreResult {
|
|
62
|
+
score: number;
|
|
63
|
+
feedback?: string;
|
|
64
|
+
objectiveScores?: Record<string, number>;
|
|
65
|
+
/** What this rollout consumed, when the caller can see it. */
|
|
66
|
+
usage?: RolloutUsage;
|
|
67
|
+
/**
|
|
68
|
+
* Marks a score produced by an infrastructure failure — a rate limit, a
|
|
69
|
+
* network blip, a provider 5xx — rather than by the candidate. Without this
|
|
70
|
+
* the engine cannot tell such a zero from a genuine one, and would cache it
|
|
71
|
+
* permanently against the candidate.
|
|
72
|
+
*/
|
|
73
|
+
transient?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface EvaluateArgs<Datum, K extends string = string> {
|
|
76
|
+
batch: readonly Datum[];
|
|
77
|
+
candidate: Candidate<K>;
|
|
78
|
+
captureTraces: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Where this batch sits in the run. Forward it to whatever tracing the
|
|
81
|
+
* system under optimization already has — without it a run is thousands of
|
|
82
|
+
* indistinguishable rollouts, and no trace can be tied back to the iteration
|
|
83
|
+
* whose score moved.
|
|
84
|
+
*/
|
|
85
|
+
run: EvaluationContext;
|
|
86
|
+
signal?: AbortSignal;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Identifies one evaluation within a run. `candidateId` is null while the
|
|
90
|
+
* candidate is still a proposal being screened on a minibatch: it has no
|
|
91
|
+
* record, and inventing an id for it would collide with the one it gets if it
|
|
92
|
+
* is accepted.
|
|
93
|
+
*/
|
|
94
|
+
interface EvaluationContext {
|
|
95
|
+
iteration: number;
|
|
96
|
+
phase: EvaluationPhase;
|
|
97
|
+
split: EvaluationSplit;
|
|
98
|
+
candidateId: number | null;
|
|
99
|
+
}
|
|
100
|
+
type EvaluationPhase = "seed" | "minibatch" | "validation" | "test";
|
|
101
|
+
/**
|
|
102
|
+
* Which dataset an instance id was drawn from. Each split numbers its ids
|
|
103
|
+
* independently, so the same id can name three different instances; the cache
|
|
104
|
+
* key has to keep them apart.
|
|
105
|
+
*/
|
|
106
|
+
type EvaluationSplit = "train" | "val" | "test";
|
|
107
|
+
/**
|
|
108
|
+
* The single integration seam between an optimizer and a system under
|
|
109
|
+
* optimization. Everything framework-specific — LangChain, the AI SDK,
|
|
110
|
+
* Braintrust — lives in an implementation of this interface.
|
|
111
|
+
*/
|
|
112
|
+
interface Adapter<Datum, Trajectory = unknown, Output = unknown, K extends string = string> {
|
|
113
|
+
evaluate(args: EvaluateArgs<Datum, K>): Promise<EvaluationBatch<Trajectory, Output>> | EvaluationBatch<Trajectory, Output>;
|
|
114
|
+
}
|
|
115
|
+
/** Provider-agnostic text model: text in, text out. */
|
|
116
|
+
type TextModel = (args: {
|
|
117
|
+
prompt: string;
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
}) => Promise<string>;
|
|
120
|
+
/**
|
|
121
|
+
* The component names of a candidate, as the union they were inferred from.
|
|
122
|
+
*
|
|
123
|
+
* `Object.keys` widens a closed key union back to `string`. This is the one
|
|
124
|
+
* place that narrowing happens, so every other caller stays assertion-free.
|
|
125
|
+
* Accepts a partial so it also names the components of a component patch.
|
|
126
|
+
*/
|
|
127
|
+
declare function componentNames<K extends string>(candidate: Partial<Candidate<K>>): K[];
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/warnings.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Something about a run that its own numbers cannot say.
|
|
132
|
+
*
|
|
133
|
+
* A search reports a score, a stop reason and what it spent, and every one of
|
|
134
|
+
* those reads the same whether the run measured what the caller thinks it did
|
|
135
|
+
* or not. These are the conditions under which a normal-looking result means
|
|
136
|
+
* less than it appears to: they never stop a run, and they are carried on the
|
|
137
|
+
* result and the `finish` event so a report can say so next to the number.
|
|
138
|
+
*/
|
|
139
|
+
interface RunWarning {
|
|
140
|
+
code: RunWarningCode;
|
|
141
|
+
message: string;
|
|
142
|
+
}
|
|
143
|
+
type RunWarningCode = "validationSetReusesTraining" | "seedScoreSaturated" | "seedScoreFloored";
|
|
144
|
+
/**
|
|
145
|
+
* The validation set a run will actually select against, and whatever the
|
|
146
|
+
* choice costs it.
|
|
147
|
+
*
|
|
148
|
+
* Defaulting to the training set is the right default for a first run and the
|
|
149
|
+
* wrong number to report from one. It is worse than ordinary overfitting under
|
|
150
|
+
* reflective search: the reflection prompt asks the model to mine domain facts
|
|
151
|
+
* out of the traces it is shown, so those facts come out of the very instances
|
|
152
|
+
* that then select the candidate carrying them. `"reuseTraining"` is the same
|
|
153
|
+
* behaviour with the caller's name on it, and silences the warning.
|
|
154
|
+
*/
|
|
155
|
+
declare function resolveValidationSet<Datum>(args: {
|
|
156
|
+
validationSet: readonly Datum[] | "reuseTraining" | undefined;
|
|
157
|
+
trainingSet: readonly Datum[];
|
|
158
|
+
}): {
|
|
159
|
+
validationSet: readonly Datum[];
|
|
160
|
+
warnings: RunWarning[];
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* What the seed's own validation row says about whether the run could have
|
|
164
|
+
* learned anything.
|
|
165
|
+
*
|
|
166
|
+
* A search ranks candidates by how they differ across instances, so a seed row
|
|
167
|
+
* with no spread leaves nothing to rank: at the ceiling every proposal is a tie
|
|
168
|
+
* the acceptance test resolves by noise, and at the floor no proposal has a
|
|
169
|
+
* partial improvement to build on. Both produce a run that spends its whole
|
|
170
|
+
* budget and reports a stop reason that looks like any other.
|
|
171
|
+
*/
|
|
172
|
+
declare function seedScoreWarnings(args: {
|
|
173
|
+
scores: readonly (number | undefined)[];
|
|
174
|
+
perfectScore: number;
|
|
175
|
+
}): RunWarning[];
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/reporting.d.ts
|
|
178
|
+
/**
|
|
179
|
+
* Where a run's progress goes, for any optimizer. Observability only:
|
|
180
|
+
* persisting a run so it can be resumed is `onCheckpoint`, which is durability
|
|
181
|
+
* and a separate concern.
|
|
182
|
+
*
|
|
183
|
+
* Generic over the event union rather than one union covering every optimizer:
|
|
184
|
+
* a search emits what it actually has, and a reporter written against one
|
|
185
|
+
* optimizer still type-checks against the events it reads.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* The least an optimizer's event satisfies. A reporter typed against this
|
|
189
|
+
* accepts every optimizer's union, because a literal tag is assignable to
|
|
190
|
+
* `string` and the parameter position is contravariant.
|
|
191
|
+
*/
|
|
192
|
+
interface OptimizerEvent {
|
|
193
|
+
type: string;
|
|
194
|
+
}
|
|
195
|
+
interface Reporter<Event> {
|
|
196
|
+
/**
|
|
197
|
+
* Called on the search's hot path, synchronously. A reporter that ships
|
|
198
|
+
* anywhere over a network buffers here and uploads in `flush`, or it charges
|
|
199
|
+
* every iteration for its latency.
|
|
200
|
+
*
|
|
201
|
+
* A reporter that throws is warned about and skipped: observability never
|
|
202
|
+
* fails a run.
|
|
203
|
+
*/
|
|
204
|
+
onEvent?: (event: Event) => void;
|
|
205
|
+
/** Awaited once as the run ends, including when it ends by throwing. */
|
|
206
|
+
flush?: () => Promise<void>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The payload every optimizer's `candidateAccepted` carries, so one reporter
|
|
210
|
+
* can read an acceptance without knowing which search produced it. Each
|
|
211
|
+
* optimizer intersects its own fields onto this — GEPA its lineage, MIPRO its
|
|
212
|
+
* menu choices — the way the event unions already intersect `EvaluationEvent`.
|
|
213
|
+
*
|
|
214
|
+
* Emitted only when the incumbent moves and a full validation sweep measured
|
|
215
|
+
* it. An optimizer that accepts on a minibatch reports the acceptance in its
|
|
216
|
+
* own event and emits this one once the sweep that confirms it lands, so
|
|
217
|
+
* `instanceScores` never means "a subset, and you work out which".
|
|
218
|
+
*/
|
|
219
|
+
interface CandidateAccepted<K extends string = string> {
|
|
220
|
+
/** Identifies the candidate within the run. */
|
|
221
|
+
candidateId: number;
|
|
222
|
+
/** The text that scored, so a move is readable next to the edit. */
|
|
223
|
+
candidate: Candidate<K>;
|
|
224
|
+
/** Mean over the validation set, which is what selection is decided on. */
|
|
225
|
+
aggregateScore: number;
|
|
226
|
+
/**
|
|
227
|
+
* Per-instance scores, aligned with the validation set. `undefined` marks an
|
|
228
|
+
* instance an infrastructure failure left unmeasured — unknown, not zero.
|
|
229
|
+
*
|
|
230
|
+
* Handed out by reference rather than copied: a run emits this once per
|
|
231
|
+
* accepted candidate, and copying a validation-set-sized array that often to
|
|
232
|
+
* guard against a listener that writes to it costs every run to protect a
|
|
233
|
+
* listener that should not exist.
|
|
234
|
+
*/
|
|
235
|
+
instanceScores: readonly (number | undefined)[];
|
|
236
|
+
/** Aligned with `instanceScores`. Present only under `trackBestOutputs`. */
|
|
237
|
+
outputs?: readonly unknown[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The payload every optimizer's `finish` carries. `reason` stays per-optimizer
|
|
241
|
+
* because the stop reasons genuinely differ — only GEPA can exhaust a
|
|
242
|
+
* reflection budget.
|
|
243
|
+
*/
|
|
244
|
+
interface RunFinished {
|
|
245
|
+
/** The winner, named the way `CandidateAccepted.candidateId` names it. */
|
|
246
|
+
bestCandidateId: number;
|
|
247
|
+
bestScore: number;
|
|
248
|
+
metricCalls: number;
|
|
249
|
+
/** The winner's held-out score, when a testSet was given. */
|
|
250
|
+
testScore?: number;
|
|
251
|
+
/**
|
|
252
|
+
* The winner's per-instance held-out scores, aligned with the testSet and
|
|
253
|
+
* present whenever `testScore` is. `undefined` marks an instance an
|
|
254
|
+
* infrastructure failure left unmeasured, which is what `testScore` averages
|
|
255
|
+
* over too.
|
|
256
|
+
*
|
|
257
|
+
* The mean is the number selection never saw; this is where the gap below
|
|
258
|
+
* `bestScore` came from.
|
|
259
|
+
*/
|
|
260
|
+
testInstanceScores?: readonly (number | undefined)[];
|
|
261
|
+
/** Aligned with `testInstanceScores`. Only under `trackBestOutputs`. */
|
|
262
|
+
testOutputs?: readonly unknown[];
|
|
263
|
+
/**
|
|
264
|
+
* What the run cannot say about itself from its own numbers. A reporter that
|
|
265
|
+
* writes the score somewhere permanent writes these beside it, or the record
|
|
266
|
+
* outlives the only place the caveat was ever stated.
|
|
267
|
+
*/
|
|
268
|
+
warnings: readonly RunWarning[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* The two events every optimizer emits with a payload a reporter can read
|
|
272
|
+
* without knowing which search produced it. A cross-optimizer reporter takes
|
|
273
|
+
* this as its event type: it is a supertype of every optimizer's own union, so
|
|
274
|
+
* one reporter drops into any optimizer's `reporters` array.
|
|
275
|
+
*/
|
|
276
|
+
type ReportableEvent<K extends string = string> = ({
|
|
277
|
+
type: "candidateAccepted";
|
|
278
|
+
} & CandidateAccepted<K>) | ({
|
|
279
|
+
type: "finish";
|
|
280
|
+
} & RunFinished);
|
|
281
|
+
/**
|
|
282
|
+
* Narrows an event off any optimizer's union to an acceptance. The tag is
|
|
283
|
+
* enough: every optimizer's `candidateAccepted` intersects `CandidateAccepted`,
|
|
284
|
+
* so carrying the payload is a compile-time obligation rather than a hope.
|
|
285
|
+
*/
|
|
286
|
+
declare function isCandidateAccepted<K extends string = string>(event: OptimizerEvent): event is {
|
|
287
|
+
type: "candidateAccepted";
|
|
288
|
+
} & CandidateAccepted<K>;
|
|
289
|
+
/** Narrows an event off any optimizer's union to the end of the run. */
|
|
290
|
+
declare function isRunFinished(event: OptimizerEvent): event is {
|
|
291
|
+
type: "finish";
|
|
292
|
+
} & RunFinished;
|
|
293
|
+
//#endregion
|
|
294
|
+
export { componentNames as C, UsageTotals as S, EvaluationPhase as _, RunFinished as a, ScoreResult as b, RunWarning as c, seedScoreWarnings as d, Adapter as f, EvaluationContext as g, EvaluationBatch as h, Reporter as i, RunWarningCode as l, EvaluateArgs as m, OptimizerEvent as n, isCandidateAccepted as o, Candidate as p, ReportableEvent as r, isRunFinished as s, CandidateAccepted as t, resolveValidationSet as u, EvaluationSplit as v, TextModel as x, RolloutUsage as y };
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A candidate is a map of named text components to their current text. This is
|
|
4
|
+
* the unit of optimization — prompts, instructions, code, tool descriptions,
|
|
5
|
+
* anything expressible as a named string.
|
|
6
|
+
*
|
|
7
|
+
* `K` is the union of component names, inferred from the seed candidate, so a
|
|
8
|
+
* misspelled component is a compile error rather than a silent no-op.
|
|
9
|
+
*/
|
|
10
|
+
type Candidate<K extends string = string> = Record<K, string>;
|
|
11
|
+
/**
|
|
12
|
+
* What one rollout consumed. Every field is optional because providers report
|
|
13
|
+
* different subsets, and a partial reading is still worth more than none.
|
|
14
|
+
*/
|
|
15
|
+
interface RolloutUsage {
|
|
16
|
+
inputTokens?: number;
|
|
17
|
+
outputTokens?: number;
|
|
18
|
+
/** Defaults to the sum of the two token counts when they are reported. */
|
|
19
|
+
totalTokens?: number;
|
|
20
|
+
costUsd?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Usage summed over a run, alongside the rollouts that produced it. */
|
|
23
|
+
interface UsageTotals {
|
|
24
|
+
inputTokens: number;
|
|
25
|
+
outputTokens: number;
|
|
26
|
+
totalTokens: number;
|
|
27
|
+
costUsd: number;
|
|
28
|
+
/** Fresh rollouts counted here. Cached instances buy nothing. */
|
|
29
|
+
rollouts: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of running a candidate over a batch of data instances.
|
|
33
|
+
*
|
|
34
|
+
* `scores` is the load-bearing field: one number per instance, higher is
|
|
35
|
+
* better. `feedback` is a per-instance textual diagnosis of what went wrong,
|
|
36
|
+
* which a reflective optimizer reads to write a better candidate.
|
|
37
|
+
*/
|
|
38
|
+
interface EvaluationBatch<Trajectory = unknown, Output = unknown> {
|
|
39
|
+
outputs: Output[];
|
|
40
|
+
scores: number[];
|
|
41
|
+
/**
|
|
42
|
+
* What each rollout consumed. Rollout counts are the budget, but they are a
|
|
43
|
+
* poor proxy for spend: reflective search grows the text it optimizes, so
|
|
44
|
+
* the same rollout costs more late in a run than early in it.
|
|
45
|
+
*/
|
|
46
|
+
usage?: RolloutUsage[];
|
|
47
|
+
feedback?: string[];
|
|
48
|
+
trajectories?: Trajectory[];
|
|
49
|
+
objectiveScores?: Record<string, number>[];
|
|
50
|
+
/**
|
|
51
|
+
* Per-instance: true when the score reflects an infrastructure failure
|
|
52
|
+
* rather than the candidate's behaviour. Transient scores are never written
|
|
53
|
+
* to the evaluation cache.
|
|
54
|
+
*/
|
|
55
|
+
transient?: boolean[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What a per-instance scorer returns. Shared by every adapter so scorers are
|
|
59
|
+
* portable between them — a Braintrust scorer works in a LangChain run.
|
|
60
|
+
*/
|
|
61
|
+
interface ScoreResult {
|
|
62
|
+
score: number;
|
|
63
|
+
feedback?: string;
|
|
64
|
+
objectiveScores?: Record<string, number>;
|
|
65
|
+
/** What this rollout consumed, when the caller can see it. */
|
|
66
|
+
usage?: RolloutUsage;
|
|
67
|
+
/**
|
|
68
|
+
* Marks a score produced by an infrastructure failure — a rate limit, a
|
|
69
|
+
* network blip, a provider 5xx — rather than by the candidate. Without this
|
|
70
|
+
* the engine cannot tell such a zero from a genuine one, and would cache it
|
|
71
|
+
* permanently against the candidate.
|
|
72
|
+
*/
|
|
73
|
+
transient?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface EvaluateArgs<Datum, K extends string = string> {
|
|
76
|
+
batch: readonly Datum[];
|
|
77
|
+
candidate: Candidate<K>;
|
|
78
|
+
captureTraces: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Where this batch sits in the run. Forward it to whatever tracing the
|
|
81
|
+
* system under optimization already has — without it a run is thousands of
|
|
82
|
+
* indistinguishable rollouts, and no trace can be tied back to the iteration
|
|
83
|
+
* whose score moved.
|
|
84
|
+
*/
|
|
85
|
+
run: EvaluationContext;
|
|
86
|
+
signal?: AbortSignal;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Identifies one evaluation within a run. `candidateId` is null while the
|
|
90
|
+
* candidate is still a proposal being screened on a minibatch: it has no
|
|
91
|
+
* record, and inventing an id for it would collide with the one it gets if it
|
|
92
|
+
* is accepted.
|
|
93
|
+
*/
|
|
94
|
+
interface EvaluationContext {
|
|
95
|
+
iteration: number;
|
|
96
|
+
phase: EvaluationPhase;
|
|
97
|
+
split: EvaluationSplit;
|
|
98
|
+
candidateId: number | null;
|
|
99
|
+
}
|
|
100
|
+
type EvaluationPhase = "seed" | "minibatch" | "validation" | "test";
|
|
101
|
+
/**
|
|
102
|
+
* Which dataset an instance id was drawn from. Each split numbers its ids
|
|
103
|
+
* independently, so the same id can name three different instances; the cache
|
|
104
|
+
* key has to keep them apart.
|
|
105
|
+
*/
|
|
106
|
+
type EvaluationSplit = "train" | "val" | "test";
|
|
107
|
+
/**
|
|
108
|
+
* The single integration seam between an optimizer and a system under
|
|
109
|
+
* optimization. Everything framework-specific — LangChain, the AI SDK,
|
|
110
|
+
* Braintrust — lives in an implementation of this interface.
|
|
111
|
+
*/
|
|
112
|
+
interface Adapter<Datum, Trajectory = unknown, Output = unknown, K extends string = string> {
|
|
113
|
+
evaluate(args: EvaluateArgs<Datum, K>): Promise<EvaluationBatch<Trajectory, Output>> | EvaluationBatch<Trajectory, Output>;
|
|
114
|
+
}
|
|
115
|
+
/** Provider-agnostic text model: text in, text out. */
|
|
116
|
+
type TextModel = (args: {
|
|
117
|
+
prompt: string;
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
}) => Promise<string>;
|
|
120
|
+
/**
|
|
121
|
+
* The component names of a candidate, as the union they were inferred from.
|
|
122
|
+
*
|
|
123
|
+
* `Object.keys` widens a closed key union back to `string`. This is the one
|
|
124
|
+
* place that narrowing happens, so every other caller stays assertion-free.
|
|
125
|
+
* Accepts a partial so it also names the components of a component patch.
|
|
126
|
+
*/
|
|
127
|
+
declare function componentNames<K extends string>(candidate: Partial<Candidate<K>>): K[];
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/warnings.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Something about a run that its own numbers cannot say.
|
|
132
|
+
*
|
|
133
|
+
* A search reports a score, a stop reason and what it spent, and every one of
|
|
134
|
+
* those reads the same whether the run measured what the caller thinks it did
|
|
135
|
+
* or not. These are the conditions under which a normal-looking result means
|
|
136
|
+
* less than it appears to: they never stop a run, and they are carried on the
|
|
137
|
+
* result and the `finish` event so a report can say so next to the number.
|
|
138
|
+
*/
|
|
139
|
+
interface RunWarning {
|
|
140
|
+
code: RunWarningCode;
|
|
141
|
+
message: string;
|
|
142
|
+
}
|
|
143
|
+
type RunWarningCode = "validationSetReusesTraining" | "seedScoreSaturated" | "seedScoreFloored";
|
|
144
|
+
/**
|
|
145
|
+
* The validation set a run will actually select against, and whatever the
|
|
146
|
+
* choice costs it.
|
|
147
|
+
*
|
|
148
|
+
* Defaulting to the training set is the right default for a first run and the
|
|
149
|
+
* wrong number to report from one. It is worse than ordinary overfitting under
|
|
150
|
+
* reflective search: the reflection prompt asks the model to mine domain facts
|
|
151
|
+
* out of the traces it is shown, so those facts come out of the very instances
|
|
152
|
+
* that then select the candidate carrying them. `"reuseTraining"` is the same
|
|
153
|
+
* behaviour with the caller's name on it, and silences the warning.
|
|
154
|
+
*/
|
|
155
|
+
declare function resolveValidationSet<Datum>(args: {
|
|
156
|
+
validationSet: readonly Datum[] | "reuseTraining" | undefined;
|
|
157
|
+
trainingSet: readonly Datum[];
|
|
158
|
+
}): {
|
|
159
|
+
validationSet: readonly Datum[];
|
|
160
|
+
warnings: RunWarning[];
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* What the seed's own validation row says about whether the run could have
|
|
164
|
+
* learned anything.
|
|
165
|
+
*
|
|
166
|
+
* A search ranks candidates by how they differ across instances, so a seed row
|
|
167
|
+
* with no spread leaves nothing to rank: at the ceiling every proposal is a tie
|
|
168
|
+
* the acceptance test resolves by noise, and at the floor no proposal has a
|
|
169
|
+
* partial improvement to build on. Both produce a run that spends its whole
|
|
170
|
+
* budget and reports a stop reason that looks like any other.
|
|
171
|
+
*/
|
|
172
|
+
declare function seedScoreWarnings(args: {
|
|
173
|
+
scores: readonly (number | undefined)[];
|
|
174
|
+
perfectScore: number;
|
|
175
|
+
}): RunWarning[];
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/reporting.d.ts
|
|
178
|
+
/**
|
|
179
|
+
* Where a run's progress goes, for any optimizer. Observability only:
|
|
180
|
+
* persisting a run so it can be resumed is `onCheckpoint`, which is durability
|
|
181
|
+
* and a separate concern.
|
|
182
|
+
*
|
|
183
|
+
* Generic over the event union rather than one union covering every optimizer:
|
|
184
|
+
* a search emits what it actually has, and a reporter written against one
|
|
185
|
+
* optimizer still type-checks against the events it reads.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* The least an optimizer's event satisfies. A reporter typed against this
|
|
189
|
+
* accepts every optimizer's union, because a literal tag is assignable to
|
|
190
|
+
* `string` and the parameter position is contravariant.
|
|
191
|
+
*/
|
|
192
|
+
interface OptimizerEvent {
|
|
193
|
+
type: string;
|
|
194
|
+
}
|
|
195
|
+
interface Reporter<Event> {
|
|
196
|
+
/**
|
|
197
|
+
* Called on the search's hot path, synchronously. A reporter that ships
|
|
198
|
+
* anywhere over a network buffers here and uploads in `flush`, or it charges
|
|
199
|
+
* every iteration for its latency.
|
|
200
|
+
*
|
|
201
|
+
* A reporter that throws is warned about and skipped: observability never
|
|
202
|
+
* fails a run.
|
|
203
|
+
*/
|
|
204
|
+
onEvent?: (event: Event) => void;
|
|
205
|
+
/** Awaited once as the run ends, including when it ends by throwing. */
|
|
206
|
+
flush?: () => Promise<void>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The payload every optimizer's `candidateAccepted` carries, so one reporter
|
|
210
|
+
* can read an acceptance without knowing which search produced it. Each
|
|
211
|
+
* optimizer intersects its own fields onto this — GEPA its lineage, MIPRO its
|
|
212
|
+
* menu choices — the way the event unions already intersect `EvaluationEvent`.
|
|
213
|
+
*
|
|
214
|
+
* Emitted only when the incumbent moves and a full validation sweep measured
|
|
215
|
+
* it. An optimizer that accepts on a minibatch reports the acceptance in its
|
|
216
|
+
* own event and emits this one once the sweep that confirms it lands, so
|
|
217
|
+
* `instanceScores` never means "a subset, and you work out which".
|
|
218
|
+
*/
|
|
219
|
+
interface CandidateAccepted<K extends string = string> {
|
|
220
|
+
/** Identifies the candidate within the run. */
|
|
221
|
+
candidateId: number;
|
|
222
|
+
/** The text that scored, so a move is readable next to the edit. */
|
|
223
|
+
candidate: Candidate<K>;
|
|
224
|
+
/** Mean over the validation set, which is what selection is decided on. */
|
|
225
|
+
aggregateScore: number;
|
|
226
|
+
/**
|
|
227
|
+
* Per-instance scores, aligned with the validation set. `undefined` marks an
|
|
228
|
+
* instance an infrastructure failure left unmeasured — unknown, not zero.
|
|
229
|
+
*
|
|
230
|
+
* Handed out by reference rather than copied: a run emits this once per
|
|
231
|
+
* accepted candidate, and copying a validation-set-sized array that often to
|
|
232
|
+
* guard against a listener that writes to it costs every run to protect a
|
|
233
|
+
* listener that should not exist.
|
|
234
|
+
*/
|
|
235
|
+
instanceScores: readonly (number | undefined)[];
|
|
236
|
+
/** Aligned with `instanceScores`. Present only under `trackBestOutputs`. */
|
|
237
|
+
outputs?: readonly unknown[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The payload every optimizer's `finish` carries. `reason` stays per-optimizer
|
|
241
|
+
* because the stop reasons genuinely differ — only GEPA can exhaust a
|
|
242
|
+
* reflection budget.
|
|
243
|
+
*/
|
|
244
|
+
interface RunFinished {
|
|
245
|
+
/** The winner, named the way `CandidateAccepted.candidateId` names it. */
|
|
246
|
+
bestCandidateId: number;
|
|
247
|
+
bestScore: number;
|
|
248
|
+
metricCalls: number;
|
|
249
|
+
/** The winner's held-out score, when a testSet was given. */
|
|
250
|
+
testScore?: number;
|
|
251
|
+
/**
|
|
252
|
+
* The winner's per-instance held-out scores, aligned with the testSet and
|
|
253
|
+
* present whenever `testScore` is. `undefined` marks an instance an
|
|
254
|
+
* infrastructure failure left unmeasured, which is what `testScore` averages
|
|
255
|
+
* over too.
|
|
256
|
+
*
|
|
257
|
+
* The mean is the number selection never saw; this is where the gap below
|
|
258
|
+
* `bestScore` came from.
|
|
259
|
+
*/
|
|
260
|
+
testInstanceScores?: readonly (number | undefined)[];
|
|
261
|
+
/** Aligned with `testInstanceScores`. Only under `trackBestOutputs`. */
|
|
262
|
+
testOutputs?: readonly unknown[];
|
|
263
|
+
/**
|
|
264
|
+
* What the run cannot say about itself from its own numbers. A reporter that
|
|
265
|
+
* writes the score somewhere permanent writes these beside it, or the record
|
|
266
|
+
* outlives the only place the caveat was ever stated.
|
|
267
|
+
*/
|
|
268
|
+
warnings: readonly RunWarning[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* The two events every optimizer emits with a payload a reporter can read
|
|
272
|
+
* without knowing which search produced it. A cross-optimizer reporter takes
|
|
273
|
+
* this as its event type: it is a supertype of every optimizer's own union, so
|
|
274
|
+
* one reporter drops into any optimizer's `reporters` array.
|
|
275
|
+
*/
|
|
276
|
+
type ReportableEvent<K extends string = string> = ({
|
|
277
|
+
type: "candidateAccepted";
|
|
278
|
+
} & CandidateAccepted<K>) | ({
|
|
279
|
+
type: "finish";
|
|
280
|
+
} & RunFinished);
|
|
281
|
+
/**
|
|
282
|
+
* Narrows an event off any optimizer's union to an acceptance. The tag is
|
|
283
|
+
* enough: every optimizer's `candidateAccepted` intersects `CandidateAccepted`,
|
|
284
|
+
* so carrying the payload is a compile-time obligation rather than a hope.
|
|
285
|
+
*/
|
|
286
|
+
declare function isCandidateAccepted<K extends string = string>(event: OptimizerEvent): event is {
|
|
287
|
+
type: "candidateAccepted";
|
|
288
|
+
} & CandidateAccepted<K>;
|
|
289
|
+
/** Narrows an event off any optimizer's union to the end of the run. */
|
|
290
|
+
declare function isRunFinished(event: OptimizerEvent): event is {
|
|
291
|
+
type: "finish";
|
|
292
|
+
} & RunFinished;
|
|
293
|
+
//#endregion
|
|
294
|
+
export { componentNames as C, UsageTotals as S, EvaluationPhase as _, RunFinished as a, ScoreResult as b, RunWarning as c, seedScoreWarnings as d, Adapter as f, EvaluationContext as g, EvaluationBatch as h, Reporter as i, RunWarningCode as l, EvaluateArgs as m, OptimizerEvent as n, isCandidateAccepted as o, Candidate as p, ReportableEvent as r, isRunFinished as s, CandidateAccepted as t, resolveValidationSet as u, EvaluationSplit as v, TextModel as x, RolloutUsage as y };
|