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
|
@@ -0,0 +1,240 @@
|
|
|
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/reporting.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Where a run's progress goes, for any optimizer. Observability only:
|
|
132
|
+
* persisting a run so it can be resumed is `onCheckpoint`, which is durability
|
|
133
|
+
* and a separate concern.
|
|
134
|
+
*
|
|
135
|
+
* Generic over the event union rather than one union covering every optimizer:
|
|
136
|
+
* a search emits what it actually has, and a reporter written against one
|
|
137
|
+
* optimizer still type-checks against the events it reads.
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* The least an optimizer's event satisfies. A reporter typed against this
|
|
141
|
+
* accepts every optimizer's union, because a literal tag is assignable to
|
|
142
|
+
* `string` and the parameter position is contravariant.
|
|
143
|
+
*/
|
|
144
|
+
interface OptimizerEvent {
|
|
145
|
+
type: string;
|
|
146
|
+
}
|
|
147
|
+
interface Reporter<Event> {
|
|
148
|
+
/**
|
|
149
|
+
* Called on the search's hot path, synchronously. A reporter that ships
|
|
150
|
+
* anywhere over a network buffers here and uploads in `flush`, or it charges
|
|
151
|
+
* every iteration for its latency.
|
|
152
|
+
*
|
|
153
|
+
* A reporter that throws is warned about and skipped: observability never
|
|
154
|
+
* fails a run.
|
|
155
|
+
*/
|
|
156
|
+
onEvent?: (event: Event) => void;
|
|
157
|
+
/** Awaited once as the run ends, including when it ends by throwing. */
|
|
158
|
+
flush?: () => Promise<void>;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The payload every optimizer's `candidateAccepted` carries, so one reporter
|
|
162
|
+
* can read an acceptance without knowing which search produced it. Each
|
|
163
|
+
* optimizer intersects its own fields onto this — GEPA its lineage, MIPRO its
|
|
164
|
+
* menu choices — the way the event unions already intersect `EvaluationEvent`.
|
|
165
|
+
*
|
|
166
|
+
* Emitted only when the incumbent moves and a full validation sweep measured
|
|
167
|
+
* it. An optimizer that accepts on a minibatch reports the acceptance in its
|
|
168
|
+
* own event and emits this one once the sweep that confirms it lands, so
|
|
169
|
+
* `instanceScores` never means "a subset, and you work out which".
|
|
170
|
+
*/
|
|
171
|
+
interface CandidateAccepted<K extends string = string> {
|
|
172
|
+
/** Identifies the candidate within the run. */
|
|
173
|
+
candidateId: number;
|
|
174
|
+
/** The text that scored, so a move is readable next to the edit. */
|
|
175
|
+
candidate: Candidate<K>;
|
|
176
|
+
/** Mean over the validation set, which is what selection is decided on. */
|
|
177
|
+
aggregateScore: number;
|
|
178
|
+
/**
|
|
179
|
+
* Per-instance scores, aligned with the validation set. `undefined` marks an
|
|
180
|
+
* instance an infrastructure failure left unmeasured — unknown, not zero.
|
|
181
|
+
*
|
|
182
|
+
* Handed out by reference rather than copied: a run emits this once per
|
|
183
|
+
* accepted candidate, and copying a validation-set-sized array that often to
|
|
184
|
+
* guard against a listener that writes to it costs every run to protect a
|
|
185
|
+
* listener that should not exist.
|
|
186
|
+
*/
|
|
187
|
+
instanceScores: readonly (number | undefined)[];
|
|
188
|
+
/** Aligned with `instanceScores`. Present only under `trackBestOutputs`. */
|
|
189
|
+
outputs?: readonly unknown[];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The payload every optimizer's `finish` carries. `reason` stays per-optimizer
|
|
193
|
+
* because the stop reasons genuinely differ — only GEPA can exhaust a
|
|
194
|
+
* reflection budget.
|
|
195
|
+
*/
|
|
196
|
+
interface RunFinished {
|
|
197
|
+
/** The winner, named the way `CandidateAccepted.candidateId` names it. */
|
|
198
|
+
bestCandidateId: number;
|
|
199
|
+
bestScore: number;
|
|
200
|
+
metricCalls: number;
|
|
201
|
+
/** The winner's held-out score, when a testSet was given. */
|
|
202
|
+
testScore?: number;
|
|
203
|
+
/**
|
|
204
|
+
* The winner's per-instance held-out scores, aligned with the testSet and
|
|
205
|
+
* present whenever `testScore` is. `undefined` marks an instance an
|
|
206
|
+
* infrastructure failure left unmeasured, which is what `testScore` averages
|
|
207
|
+
* over too.
|
|
208
|
+
*
|
|
209
|
+
* The mean is the number selection never saw; this is where the gap below
|
|
210
|
+
* `bestScore` came from.
|
|
211
|
+
*/
|
|
212
|
+
testInstanceScores?: readonly (number | undefined)[];
|
|
213
|
+
/** Aligned with `testInstanceScores`. Only under `trackBestOutputs`. */
|
|
214
|
+
testOutputs?: readonly unknown[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The two events every optimizer emits with a payload a reporter can read
|
|
218
|
+
* without knowing which search produced it. A cross-optimizer reporter takes
|
|
219
|
+
* this as its event type: it is a supertype of every optimizer's own union, so
|
|
220
|
+
* one reporter drops into any optimizer's `reporters` array.
|
|
221
|
+
*/
|
|
222
|
+
type ReportableEvent<K extends string = string> = ({
|
|
223
|
+
type: "candidateAccepted";
|
|
224
|
+
} & CandidateAccepted<K>) | ({
|
|
225
|
+
type: "finish";
|
|
226
|
+
} & RunFinished);
|
|
227
|
+
/**
|
|
228
|
+
* Narrows an event off any optimizer's union to an acceptance. The tag is
|
|
229
|
+
* enough: every optimizer's `candidateAccepted` intersects `CandidateAccepted`,
|
|
230
|
+
* so carrying the payload is a compile-time obligation rather than a hope.
|
|
231
|
+
*/
|
|
232
|
+
declare function isCandidateAccepted<K extends string = string>(event: OptimizerEvent): event is {
|
|
233
|
+
type: "candidateAccepted";
|
|
234
|
+
} & CandidateAccepted<K>;
|
|
235
|
+
/** Narrows an event off any optimizer's union to the end of the run. */
|
|
236
|
+
declare function isRunFinished(event: OptimizerEvent): event is {
|
|
237
|
+
type: "finish";
|
|
238
|
+
} & RunFinished;
|
|
239
|
+
//#endregion
|
|
240
|
+
export { TextModel as _, RunFinished as a, Adapter as c, EvaluationBatch as d, EvaluationContext as f, ScoreResult as g, RolloutUsage as h, Reporter as i, Candidate as l, EvaluationSplit as m, OptimizerEvent as n, isCandidateAccepted as o, EvaluationPhase as p, ReportableEvent as r, isRunFinished as s, CandidateAccepted as t, EvaluateArgs as u, UsageTotals as v, componentNames as y };
|
|
@@ -30,6 +30,8 @@ function createBudget(args) {
|
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/cache.ts
|
|
33
|
+
/** What `stableHash` returns for any value that serializes to `{}`. */
|
|
34
|
+
const EMPTY_OBJECT_HASH = stableHash({});
|
|
33
35
|
/**
|
|
34
36
|
* The loop re-evaluates unchanged candidates against the same validation
|
|
35
37
|
* instances constantly (every accepted child inherits most of its parent's
|
|
@@ -65,6 +67,20 @@ function stableHash(value) {
|
|
|
65
67
|
}
|
|
66
68
|
return `${hash32(serialized, 2166136261)}${hash32(serialized, 16777619)}`;
|
|
67
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Names one data instance for the evaluation cache: a content hash, so the same
|
|
72
|
+
* row is the same instance wherever it appears in a run.
|
|
73
|
+
*
|
|
74
|
+
* Falls back to the row's position when the datum carries nothing the hash can
|
|
75
|
+
* read. A Map, a Set and a class instance holding its state privately all
|
|
76
|
+
* serialize to `{}`, and an id two rows share serves each of them the score the
|
|
77
|
+
* other measured. Position is a weaker id — it is only stable while the data is
|
|
78
|
+
* — but it is one instance per row, which is what the cache needs to be sound.
|
|
79
|
+
*/
|
|
80
|
+
function defaultInstanceId(args) {
|
|
81
|
+
const hash = stableHash(args.datum);
|
|
82
|
+
return hash === "" || hash === EMPTY_OBJECT_HASH ? String(args.index) : hash;
|
|
83
|
+
}
|
|
68
84
|
function createMemoryCache(args = {}) {
|
|
69
85
|
const { maxEntries = 1e5, entries: initial = [] } = args;
|
|
70
86
|
const entries = new Map(initial);
|
|
@@ -172,25 +188,39 @@ const DEFAULT_RETRY = {
|
|
|
172
188
|
*/
|
|
173
189
|
var BudgetExhausted = class extends Error {};
|
|
174
190
|
function createEvaluator(args) {
|
|
175
|
-
const { adapter, budget, cache, trackOutputs = false, onEvaluation, signal, cacheHits: initialCacheHits = 0, retry, cacheNamespace } = args;
|
|
191
|
+
const { adapter, budget, cache, trackOutputs = false, onEvaluation, signal, cacheHits: initialCacheHits = 0, usage: initialUsage, retry, cacheNamespace } = args;
|
|
176
192
|
const { attempts: retryAttempts, delayMs: retryDelayMs } = {
|
|
177
193
|
...DEFAULT_RETRY,
|
|
178
194
|
...retry
|
|
179
195
|
};
|
|
196
|
+
if (initialUsage !== void 0) assertUsage({
|
|
197
|
+
reading: initialUsage,
|
|
198
|
+
source: "Checkpoint carries"
|
|
199
|
+
});
|
|
180
200
|
let cacheHits = initialCacheHits;
|
|
181
201
|
let unchargedCalls = 0;
|
|
182
202
|
const usage = {
|
|
203
|
+
inputTokens: 0,
|
|
204
|
+
outputTokens: 0,
|
|
205
|
+
totalTokens: 0,
|
|
206
|
+
costUsd: 0,
|
|
207
|
+
rollouts: 0,
|
|
208
|
+
...initialUsage
|
|
209
|
+
};
|
|
210
|
+
/** What `charge: false` bought, kept out of the totals a ceiling reads. */
|
|
211
|
+
const unchargedUsage = {
|
|
183
212
|
inputTokens: 0,
|
|
184
213
|
outputTokens: 0,
|
|
185
214
|
totalTokens: 0,
|
|
186
215
|
costUsd: 0,
|
|
187
216
|
rollouts: 0
|
|
188
217
|
};
|
|
189
|
-
/** Folds one adapter call's reported usage into the
|
|
218
|
+
/** Folds one adapter call's reported usage into the totals that bought it. */
|
|
190
219
|
function recordUsage(args) {
|
|
191
|
-
|
|
220
|
+
const totals = args.charge ? usage : unchargedUsage;
|
|
221
|
+
totals.rollouts += args.rollouts;
|
|
192
222
|
for (const rollout of args.evaluation.usage ?? []) addUsage({
|
|
193
|
-
totals
|
|
223
|
+
totals,
|
|
194
224
|
rollout
|
|
195
225
|
});
|
|
196
226
|
}
|
|
@@ -252,7 +282,8 @@ function createEvaluator(args) {
|
|
|
252
282
|
});
|
|
253
283
|
recordUsage({
|
|
254
284
|
evaluation,
|
|
255
|
-
rollouts: rows.length
|
|
285
|
+
rollouts: rows.length,
|
|
286
|
+
charge
|
|
256
287
|
});
|
|
257
288
|
return evaluation;
|
|
258
289
|
} catch (err) {
|
|
@@ -395,6 +426,18 @@ function createEvaluator(args) {
|
|
|
395
426
|
cacheHits: () => cacheHits,
|
|
396
427
|
unchargedCalls: () => unchargedCalls,
|
|
397
428
|
usage: () => ({ ...usage }),
|
|
429
|
+
unchargedUsage: () => ({ ...unchargedUsage }),
|
|
430
|
+
absorbUsage: (spent) => {
|
|
431
|
+
assertUsage({
|
|
432
|
+
reading: spent,
|
|
433
|
+
source: "Absorbed usage carries"
|
|
434
|
+
});
|
|
435
|
+
usage.inputTokens += spent.inputTokens;
|
|
436
|
+
usage.outputTokens += spent.outputTokens;
|
|
437
|
+
usage.totalTokens += spent.totalTokens;
|
|
438
|
+
usage.costUsd += spent.costUsd;
|
|
439
|
+
usage.rollouts += spent.rollouts;
|
|
440
|
+
},
|
|
398
441
|
entries: () => cache?.entries?.(),
|
|
399
442
|
restore: (entries) => {
|
|
400
443
|
for (const [key, cached] of entries) cache?.set(key, cached);
|
|
@@ -471,11 +514,29 @@ function requireMeasuredMean(args) {
|
|
|
471
514
|
function addUsage(args) {
|
|
472
515
|
const { totals, rollout } = args;
|
|
473
516
|
const { inputTokens = 0, outputTokens = 0, costUsd = 0 } = rollout;
|
|
517
|
+
assertUsage({
|
|
518
|
+
reading: rollout,
|
|
519
|
+
source: "Adapter reported"
|
|
520
|
+
});
|
|
474
521
|
totals.inputTokens += inputTokens;
|
|
475
522
|
totals.outputTokens += outputTokens;
|
|
476
523
|
totals.totalTokens += rollout.totalTokens ?? inputTokens + outputTokens;
|
|
477
524
|
totals.costUsd += costUsd;
|
|
478
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Refuses a reading the totals cannot hold, where it enters rather than once it
|
|
528
|
+
* has been absorbed. A NaN folded in makes every later `maxCostUsd` comparison
|
|
529
|
+
* false, so the ceiling stops holding without saying so, and a value that is
|
|
530
|
+
* not a number at all concatenates onto the totals instead of adding to them.
|
|
531
|
+
* `RolloutUsage` binds TypeScript callers and nothing else.
|
|
532
|
+
*/
|
|
533
|
+
function assertUsage(args) {
|
|
534
|
+
const { reading, source } = args;
|
|
535
|
+
for (const [field, value] of Object.entries(reading)) {
|
|
536
|
+
if (value === void 0) continue;
|
|
537
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error(`${source} ${field} as ${value}; usage must be a non-negative finite number`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
479
540
|
function transientIndices(evaluation) {
|
|
480
541
|
const { transient } = evaluation;
|
|
481
542
|
if (transient === void 0) return [];
|
|
@@ -518,4 +579,61 @@ function delay(milliseconds) {
|
|
|
518
579
|
});
|
|
519
580
|
}
|
|
520
581
|
//#endregion
|
|
521
|
-
|
|
582
|
+
//#region src/reporting.ts
|
|
583
|
+
/**
|
|
584
|
+
* Narrows an event off any optimizer's union to an acceptance. The tag is
|
|
585
|
+
* enough: every optimizer's `candidateAccepted` intersects `CandidateAccepted`,
|
|
586
|
+
* so carrying the payload is a compile-time obligation rather than a hope.
|
|
587
|
+
*/
|
|
588
|
+
function isCandidateAccepted(event) {
|
|
589
|
+
return event.type === "candidateAccepted";
|
|
590
|
+
}
|
|
591
|
+
/** Narrows an event off any optimizer's union to the end of the run. */
|
|
592
|
+
function isRunFinished(event) {
|
|
593
|
+
return event.type === "finish";
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Fans one event out to every reporter, absorbing whatever they throw. A
|
|
597
|
+
* reporter is an observer of the search, never a participant in it: a logging
|
|
598
|
+
* endpoint that is down must not decide a run's outcome.
|
|
599
|
+
*/
|
|
600
|
+
function createEmitter(reporters) {
|
|
601
|
+
return function emit(event) {
|
|
602
|
+
for (const reporter of reporters) try {
|
|
603
|
+
reporter.onEvent?.(event);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
console.warn("[textopt] reporter threw while handling an event", {
|
|
606
|
+
type: event.type,
|
|
607
|
+
err
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Gives every reporter its one chance to upload what it buffered. Called from
|
|
614
|
+
* a `finally` rather than after the run: a reporter that buffers has the most
|
|
615
|
+
* to say about a run that aborted or threw, and that is exactly the run that
|
|
616
|
+
* never reaches its last line.
|
|
617
|
+
*/
|
|
618
|
+
async function flushReporters(reporters) {
|
|
619
|
+
await Promise.all(reporters.map(async (reporter) => {
|
|
620
|
+
try {
|
|
621
|
+
await reporter.flush?.();
|
|
622
|
+
} catch (err) {
|
|
623
|
+
console.warn("[textopt] reporter threw while flushing", { err });
|
|
624
|
+
}
|
|
625
|
+
}));
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* A scored batch as the per-instance row a reporter should read: an instance
|
|
629
|
+
* an infrastructure failure left unmeasured becomes `undefined` rather than
|
|
630
|
+
* the zero the adapter reported for it.
|
|
631
|
+
*
|
|
632
|
+
* The same distinction `measuredMean` makes when it averages — a row of zeros
|
|
633
|
+
* and a row of unknowns describe very different runs.
|
|
634
|
+
*/
|
|
635
|
+
function instanceRow(batch) {
|
|
636
|
+
return batch.scores.map((score, index) => batch.transient[index] === true ? void 0 : score);
|
|
637
|
+
}
|
|
638
|
+
//#endregion
|
|
639
|
+
export { createMemoryCache as _, isRunFinished as a, createBudget as b, createEvaluator as c, createDeadline as d, assertResumable as f, candidateHash as g, componentNames as h, isCandidateAccepted as i, measuredMean as l, runFingerprint as m, flushReporters as n, BudgetExhausted as o, candidateFingerprint as p, instanceRow as r, costExhausted as s, createEmitter as t, requireMeasuredMean as u, defaultInstanceId as v, stableHash as y };
|