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
package/dist/opro/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as Candidate, t as
|
|
1
|
+
import { S as UsageTotals, a as RunFinished, f as Adapter, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, p as Candidate, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.cjs";
|
|
2
2
|
import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.cjs";
|
|
3
|
-
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-
|
|
3
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-4Zv-Zt2t.cjs";
|
|
4
4
|
//#region src/opro/optimize.d.ts
|
|
5
5
|
/** One instruction that was tried, and what it scored. */
|
|
6
6
|
/** A history entry plus the system state its score was measured in. */
|
|
@@ -111,6 +111,13 @@ interface OproSnapshot {
|
|
|
111
111
|
reflectionCalls: number;
|
|
112
112
|
metricCalls: number;
|
|
113
113
|
cacheHits: number;
|
|
114
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
115
|
+
usage?: UsageTotals;
|
|
116
|
+
/**
|
|
117
|
+
* Candidates accepted so far. Reporters key rows by this id, so restarting it
|
|
118
|
+
* at zero makes a resumed run collide with the run it continues.
|
|
119
|
+
*/
|
|
120
|
+
acceptedCandidates?: number;
|
|
114
121
|
rngState: number;
|
|
115
122
|
/** Component name -> every text tried for it, with what it scored. */
|
|
116
123
|
histories: Record<string, RecordedAttempt[]>;
|
|
@@ -133,7 +140,8 @@ interface OproTask<Datum, Trajectory = unknown, Output = unknown, K extends stri
|
|
|
133
140
|
}) => string;
|
|
134
141
|
/** Pass `false` to disable caching entirely. */
|
|
135
142
|
cache?: EvaluationCache | false;
|
|
136
|
-
|
|
143
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
144
|
+
reporters?: readonly Reporter<OproEvent<NoInfer<K>>>[];
|
|
137
145
|
/**
|
|
138
146
|
* Called with a resumable snapshot after the seed is scored and after every
|
|
139
147
|
* round. Persist it and a killed run costs the last round, not all of them.
|
|
@@ -161,13 +169,13 @@ type OproEvent<K extends string = string> = {
|
|
|
161
169
|
score: number;
|
|
162
170
|
/** True when this attempt became the new incumbent. */
|
|
163
171
|
accepted: boolean;
|
|
164
|
-
} | {
|
|
172
|
+
} | ({
|
|
173
|
+
type: "candidateAccepted";
|
|
174
|
+
round: number;
|
|
175
|
+
} & CandidateAccepted<K>) | ({
|
|
165
176
|
type: "finish";
|
|
166
177
|
reason: OproStopReason;
|
|
167
|
-
|
|
168
|
-
metricCalls: number;
|
|
169
|
-
testScore?: number;
|
|
170
|
-
};
|
|
178
|
+
} & RunFinished);
|
|
171
179
|
interface OproAttempt<K extends string = string> {
|
|
172
180
|
round: number;
|
|
173
181
|
component: K;
|
|
@@ -227,4 +235,4 @@ declare function buildOproPrompt(args: {
|
|
|
227
235
|
exemplars: readonly string[];
|
|
228
236
|
}): string;
|
|
229
237
|
//#endregion
|
|
230
|
-
export { type OproAttempt, type OproConfig, type OproEvent, OproOptimizer, type OproPromptBuilder, type OproResult, type OproSnapshot, type OproStopReason, type OproTask, type ScoredAttempt, buildOproPrompt };
|
|
238
|
+
export { type CandidateAccepted, type OproAttempt, type OproConfig, type OproEvent, OproOptimizer, type OproPromptBuilder, type OproResult, type OproSnapshot, type OproStopReason, type OproTask, type OptimizerEvent, type ReportableEvent, type Reporter, type RunFinished, type ScoredAttempt, buildOproPrompt, isCandidateAccepted, isRunFinished };
|
package/dist/opro/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as Candidate, t as
|
|
1
|
+
import { S as UsageTotals, a as RunFinished, f as Adapter, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, p as Candidate, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.mjs";
|
|
2
2
|
import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.mjs";
|
|
3
|
-
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-
|
|
3
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-Ds5mzYjz.mjs";
|
|
4
4
|
//#region src/opro/optimize.d.ts
|
|
5
5
|
/** One instruction that was tried, and what it scored. */
|
|
6
6
|
/** A history entry plus the system state its score was measured in. */
|
|
@@ -111,6 +111,13 @@ interface OproSnapshot {
|
|
|
111
111
|
reflectionCalls: number;
|
|
112
112
|
metricCalls: number;
|
|
113
113
|
cacheHits: number;
|
|
114
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
115
|
+
usage?: UsageTotals;
|
|
116
|
+
/**
|
|
117
|
+
* Candidates accepted so far. Reporters key rows by this id, so restarting it
|
|
118
|
+
* at zero makes a resumed run collide with the run it continues.
|
|
119
|
+
*/
|
|
120
|
+
acceptedCandidates?: number;
|
|
114
121
|
rngState: number;
|
|
115
122
|
/** Component name -> every text tried for it, with what it scored. */
|
|
116
123
|
histories: Record<string, RecordedAttempt[]>;
|
|
@@ -133,7 +140,8 @@ interface OproTask<Datum, Trajectory = unknown, Output = unknown, K extends stri
|
|
|
133
140
|
}) => string;
|
|
134
141
|
/** Pass `false` to disable caching entirely. */
|
|
135
142
|
cache?: EvaluationCache | false;
|
|
136
|
-
|
|
143
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
144
|
+
reporters?: readonly Reporter<OproEvent<NoInfer<K>>>[];
|
|
137
145
|
/**
|
|
138
146
|
* Called with a resumable snapshot after the seed is scored and after every
|
|
139
147
|
* round. Persist it and a killed run costs the last round, not all of them.
|
|
@@ -161,13 +169,13 @@ type OproEvent<K extends string = string> = {
|
|
|
161
169
|
score: number;
|
|
162
170
|
/** True when this attempt became the new incumbent. */
|
|
163
171
|
accepted: boolean;
|
|
164
|
-
} | {
|
|
172
|
+
} | ({
|
|
173
|
+
type: "candidateAccepted";
|
|
174
|
+
round: number;
|
|
175
|
+
} & CandidateAccepted<K>) | ({
|
|
165
176
|
type: "finish";
|
|
166
177
|
reason: OproStopReason;
|
|
167
|
-
|
|
168
|
-
metricCalls: number;
|
|
169
|
-
testScore?: number;
|
|
170
|
-
};
|
|
178
|
+
} & RunFinished);
|
|
171
179
|
interface OproAttempt<K extends string = string> {
|
|
172
180
|
round: number;
|
|
173
181
|
component: K;
|
|
@@ -227,4 +235,4 @@ declare function buildOproPrompt(args: {
|
|
|
227
235
|
exemplars: readonly string[];
|
|
228
236
|
}): string;
|
|
229
237
|
//#endregion
|
|
230
|
-
export { type OproAttempt, type OproConfig, type OproEvent, OproOptimizer, type OproPromptBuilder, type OproResult, type OproSnapshot, type OproStopReason, type OproTask, type ScoredAttempt, buildOproPrompt };
|
|
238
|
+
export { type CandidateAccepted, type OproAttempt, type OproConfig, type OproEvent, OproOptimizer, type OproPromptBuilder, type OproResult, type OproSnapshot, type OproStopReason, type OproTask, type OptimizerEvent, type ReportableEvent, type Reporter, type RunFinished, type ScoredAttempt, buildOproPrompt, isCandidateAccepted, isRunFinished };
|
package/dist/opro/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { S as createBudget, _ as componentNames, a as instanceRow, b as defaultInstanceId, c as BudgetExhausted, d as measuredMean, f as requireMeasuredMean, g as runFingerprint, i as flushReporters, l as costExhausted, m as assertResumable, o as isCandidateAccepted, p as createDeadline, r as createEmitter, s as isRunFinished, t as resolveValidationSet, u as createEvaluator, x as stableHash, y as createMemoryCache } from "../warnings-OxvDi9kN.mjs";
|
|
2
2
|
import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
|
|
3
3
|
import { t as parseProposedText } from "../text--v4Ffbus.mjs";
|
|
4
4
|
import { t as createSeededRng } from "../rng-Dtc5eZ_W.mjs";
|
|
@@ -42,10 +42,14 @@ var OproOptimizer = class {
|
|
|
42
42
|
this.#config = config;
|
|
43
43
|
}
|
|
44
44
|
async optimize(task) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
try {
|
|
46
|
+
return await runOpro({
|
|
47
|
+
config: this.#config,
|
|
48
|
+
task
|
|
49
|
+
});
|
|
50
|
+
} finally {
|
|
51
|
+
await flushReporters(task.reporters ?? []);
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
};
|
|
51
55
|
/**
|
|
@@ -90,7 +94,12 @@ function buildOproPrompt(args) {
|
|
|
90
94
|
async function runOpro(args) {
|
|
91
95
|
const { config, task } = args;
|
|
92
96
|
const { proposalsPerRound = DEFAULT_PROPOSALS_PER_ROUND, concurrency = 1, maxRounds = Number.POSITIVE_INFINITY, maxReflectionCalls = Number.POSITIVE_INFINITY, seed = 0, historySize = DEFAULT_HISTORY_SIZE, exemplars = DEFAULT_EXEMPLARS, scoringSetSize, fullEvalInterval = DEFAULT_FULL_EVAL_INTERVAL, scoreScale = DEFAULT_SCORE_SCALE, buildPrompt = buildOproPrompt, trackBestOutputs = false, checkpointCache = true } = config;
|
|
93
|
-
const { seedCandidate, trainingSet, validationSet
|
|
97
|
+
const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, maxMetricCalls, renderDatum = renderDefault, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
98
|
+
const { validationSet, warnings } = resolveValidationSet({
|
|
99
|
+
validationSet: requestedValidationSet,
|
|
100
|
+
trainingSet
|
|
101
|
+
});
|
|
102
|
+
const emit = createEmitter(reporters);
|
|
94
103
|
const deadline = createDeadline({ maxWallClockMs });
|
|
95
104
|
const components = componentNames(seedCandidate);
|
|
96
105
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
@@ -133,8 +142,9 @@ async function runOpro(args) {
|
|
|
133
142
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
134
143
|
trackOutputs: trackBestOutputs,
|
|
135
144
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
145
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
136
146
|
...signal === void 0 ? {} : { signal },
|
|
137
|
-
onEvaluation: (event) =>
|
|
147
|
+
onEvaluation: (event) => emit({
|
|
138
148
|
type: "evaluation",
|
|
139
149
|
...event
|
|
140
150
|
})
|
|
@@ -159,7 +169,7 @@ async function runOpro(args) {
|
|
|
159
169
|
let round = resumeFrom?.round ?? 0;
|
|
160
170
|
let reflectionCalls = resumeFrom?.reflectionCalls ?? 0;
|
|
161
171
|
let stopReason = "maxRounds";
|
|
162
|
-
|
|
172
|
+
emit({
|
|
163
173
|
type: "start",
|
|
164
174
|
components,
|
|
165
175
|
validationSetSize: validationSet.length
|
|
@@ -192,14 +202,36 @@ async function runOpro(args) {
|
|
|
192
202
|
iteration: round
|
|
193
203
|
});
|
|
194
204
|
}
|
|
205
|
+
/** What screening a candidate would cost, in rollouts nothing has cached. */
|
|
206
|
+
function screenCost(candidate) {
|
|
207
|
+
return scoringSet === void 0 ? evaluator.countUncached({
|
|
208
|
+
candidate,
|
|
209
|
+
ids: validationIds,
|
|
210
|
+
split: "val"
|
|
211
|
+
}) : evaluator.countUncached({
|
|
212
|
+
candidate,
|
|
213
|
+
ids: scoringIds,
|
|
214
|
+
split: "train"
|
|
215
|
+
});
|
|
216
|
+
}
|
|
195
217
|
const seedEvaluation = resumeFrom === void 0 ? await sweep(seedCandidate, "seed") : void 0;
|
|
196
218
|
const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore : requireMeasuredMean({
|
|
197
219
|
batch: seedEvaluation,
|
|
198
220
|
phase: "seed"
|
|
199
221
|
});
|
|
222
|
+
if (seedEvaluation !== void 0) emit({
|
|
223
|
+
type: "candidateAccepted",
|
|
224
|
+
round: 0,
|
|
225
|
+
candidateId: 0,
|
|
226
|
+
candidate: seedCandidate,
|
|
227
|
+
aggregateScore: seedScore,
|
|
228
|
+
instanceScores: instanceRow(seedEvaluation),
|
|
229
|
+
...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
|
|
230
|
+
});
|
|
200
231
|
let best = resumeFrom?.best ?? seedCandidate;
|
|
201
232
|
let bestScore = resumeFrom?.bestScore ?? seedScore;
|
|
202
233
|
/** Absent on a resumed run until a sweep wins: outputs are not checkpointed. */
|
|
234
|
+
let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
|
|
203
235
|
let bestOutputs = seedEvaluation?.outputs;
|
|
204
236
|
let reported = resumeFrom?.reported ?? seedCandidate;
|
|
205
237
|
let lastSwept = resumeFrom === void 0 ? seedCandidate : resumeFrom.incumbentSwept ? best : {};
|
|
@@ -214,6 +246,24 @@ async function runOpro(args) {
|
|
|
214
246
|
* what gets reported is the best candidate a full sweep has actually seen —
|
|
215
247
|
* never a subset number wearing a validation set label.
|
|
216
248
|
*/
|
|
249
|
+
/**
|
|
250
|
+
* The incumbent moved and a full sweep measured it. Emitted from the two
|
|
251
|
+
* places that can be true — a screening run with no scoring set, where the
|
|
252
|
+
* attempt's own evaluation is the sweep, and the cadence that confirms an
|
|
253
|
+
* incumbent later — because a payload assembled twice is one that drifts.
|
|
254
|
+
*/
|
|
255
|
+
function emitAccepted(args) {
|
|
256
|
+
acceptedCandidates += 1;
|
|
257
|
+
emit({
|
|
258
|
+
type: "candidateAccepted",
|
|
259
|
+
round,
|
|
260
|
+
candidateId: acceptedCandidates,
|
|
261
|
+
candidate: args.candidate,
|
|
262
|
+
aggregateScore: args.score,
|
|
263
|
+
instanceScores: instanceRow(args.evaluation),
|
|
264
|
+
...trackBestOutputs ? { outputs: args.evaluation.outputs } : {}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
217
267
|
async function refreshIncumbent() {
|
|
218
268
|
if (best === lastSwept || !budget.canAfford(validationSet.length)) return "ok";
|
|
219
269
|
try {
|
|
@@ -224,6 +274,11 @@ async function runOpro(args) {
|
|
|
224
274
|
reported = best;
|
|
225
275
|
bestScore = full;
|
|
226
276
|
bestOutputs = evaluation.outputs;
|
|
277
|
+
emitAccepted({
|
|
278
|
+
candidate: best,
|
|
279
|
+
evaluation,
|
|
280
|
+
score: full
|
|
281
|
+
});
|
|
227
282
|
}
|
|
228
283
|
} catch (err) {
|
|
229
284
|
if (err instanceof BudgetExhausted || signal?.aborted) return "stop";
|
|
@@ -259,6 +314,8 @@ async function runOpro(args) {
|
|
|
259
314
|
reflectionCalls,
|
|
260
315
|
metricCalls: budget.spent(),
|
|
261
316
|
cacheHits: evaluator.cacheHits(),
|
|
317
|
+
usage: evaluator.usage(),
|
|
318
|
+
acceptedCandidates,
|
|
262
319
|
rngState: rng.state(),
|
|
263
320
|
histories: Object.fromEntries([...histories].map(([name, attempts]) => [name, [...attempts]])),
|
|
264
321
|
...scoringIndices === void 0 ? {} : { scoringIndices },
|
|
@@ -303,7 +360,7 @@ async function runOpro(args) {
|
|
|
303
360
|
const history = histories.get(component);
|
|
304
361
|
const context = contextOf(best, component);
|
|
305
362
|
const comparable = history.filter((attempt) => attempt.context === context);
|
|
306
|
-
|
|
363
|
+
emit({
|
|
307
364
|
type: "roundStart",
|
|
308
365
|
round,
|
|
309
366
|
component,
|
|
@@ -332,25 +389,43 @@ async function runOpro(args) {
|
|
|
332
389
|
const unique = [...new Set(drawn)].filter((text) => text.length > 0 && !tried.has(text));
|
|
333
390
|
barrenRounds = unique.length === 0 ? barrenRounds + 1 : 0;
|
|
334
391
|
let roundStop;
|
|
392
|
+
const scheduled = [];
|
|
393
|
+
let owed = 0;
|
|
335
394
|
for (const text of unique) {
|
|
336
395
|
const candidate = {
|
|
337
396
|
...best,
|
|
338
397
|
[component]: text
|
|
339
398
|
};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
399
|
+
const cost = screenCost(candidate);
|
|
400
|
+
if (!budget.canAfford(owed + cost)) {
|
|
401
|
+
roundStop = "budgetExhausted";
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
owed += cost;
|
|
405
|
+
scheduled.push(candidate);
|
|
406
|
+
}
|
|
407
|
+
const screened = await mapWithConcurrency({
|
|
408
|
+
items: scheduled,
|
|
409
|
+
limit: concurrency,
|
|
410
|
+
task: async (candidate) => {
|
|
411
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
412
|
+
try {
|
|
413
|
+
return { evaluation: await screen(candidate, "validation") };
|
|
414
|
+
} catch (err) {
|
|
415
|
+
if (err instanceof BudgetExhausted) return { stop: "budgetExhausted" };
|
|
416
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
417
|
+
throw err;
|
|
351
418
|
}
|
|
352
|
-
throw err;
|
|
353
419
|
}
|
|
420
|
+
});
|
|
421
|
+
for (const [index, outcome] of screened.entries()) {
|
|
422
|
+
if (outcome.stop !== void 0) {
|
|
423
|
+
roundStop = outcome.stop;
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
const candidate = scheduled[index];
|
|
427
|
+
const text = candidate[component];
|
|
428
|
+
const evaluation = outcome.evaluation;
|
|
354
429
|
const score = measuredMean(evaluation);
|
|
355
430
|
if (score === void 0) continue;
|
|
356
431
|
const accepted = score > bestSearchScore;
|
|
@@ -365,7 +440,7 @@ async function runOpro(args) {
|
|
|
365
440
|
candidate,
|
|
366
441
|
score
|
|
367
442
|
});
|
|
368
|
-
|
|
443
|
+
emit({
|
|
369
444
|
type: "attempt",
|
|
370
445
|
round,
|
|
371
446
|
component,
|
|
@@ -380,6 +455,11 @@ async function runOpro(args) {
|
|
|
380
455
|
lastSwept = candidate;
|
|
381
456
|
bestScore = score;
|
|
382
457
|
bestOutputs = evaluation.outputs;
|
|
458
|
+
emitAccepted({
|
|
459
|
+
candidate,
|
|
460
|
+
evaluation,
|
|
461
|
+
score
|
|
462
|
+
});
|
|
383
463
|
}
|
|
384
464
|
const scaled = scaleScore(score, scoreScale);
|
|
385
465
|
for (const name of components) {
|
|
@@ -393,22 +473,22 @@ async function runOpro(args) {
|
|
|
393
473
|
}
|
|
394
474
|
}
|
|
395
475
|
round += 1;
|
|
476
|
+
let cadenceStop;
|
|
477
|
+
if (roundStop === void 0 && scoringSet !== void 0 && round % fullEvalInterval === 0 && await refreshIncumbent() === "stop") cadenceStop = signal?.aborted ? "aborted" : "budgetExhausted";
|
|
396
478
|
await checkpoint();
|
|
397
479
|
if (roundStop !== void 0) {
|
|
398
480
|
stopReason = roundStop;
|
|
399
481
|
break;
|
|
400
482
|
}
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
483
|
+
if (cadenceStop !== void 0) {
|
|
484
|
+
stopReason = cadenceStop;
|
|
485
|
+
break;
|
|
406
486
|
}
|
|
407
487
|
}
|
|
408
488
|
if (scoringSet !== void 0 && !signal?.aborted) await refreshIncumbent();
|
|
409
489
|
if (signal?.aborted) stopReason = "aborted";
|
|
410
|
-
const
|
|
411
|
-
candidate:
|
|
490
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
491
|
+
candidate: reported,
|
|
412
492
|
batch: testSet,
|
|
413
493
|
ids: testIds,
|
|
414
494
|
split: "test",
|
|
@@ -416,13 +496,18 @@ async function runOpro(args) {
|
|
|
416
496
|
candidateId: null,
|
|
417
497
|
iteration: round,
|
|
418
498
|
charge: false
|
|
419
|
-
})
|
|
420
|
-
|
|
499
|
+
});
|
|
500
|
+
const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
|
|
501
|
+
emit({
|
|
421
502
|
type: "finish",
|
|
422
503
|
reason: stopReason,
|
|
504
|
+
warnings,
|
|
505
|
+
bestCandidateId: acceptedCandidates,
|
|
423
506
|
bestScore,
|
|
424
507
|
metricCalls: budget.spent(),
|
|
425
|
-
...testScore === void 0 ? {} : { testScore }
|
|
508
|
+
...testScore === void 0 ? {} : { testScore },
|
|
509
|
+
...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
|
|
510
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
426
511
|
});
|
|
427
512
|
return {
|
|
428
513
|
snapshot: takeSnapshot(),
|
|
@@ -433,13 +518,15 @@ async function runOpro(args) {
|
|
|
433
518
|
...trackBestOutputs ? { bestOutputs } : {},
|
|
434
519
|
...testScore === void 0 ? {} : {
|
|
435
520
|
testScore,
|
|
436
|
-
testMetricCalls: evaluator.unchargedCalls()
|
|
521
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
522
|
+
testUsage: evaluator.unchargedUsage()
|
|
437
523
|
},
|
|
438
524
|
rounds: round,
|
|
439
525
|
trajectory,
|
|
440
526
|
metricCalls: budget.spent(),
|
|
441
527
|
reflectionCalls,
|
|
442
528
|
cacheHits: evaluator.cacheHits(),
|
|
529
|
+
warnings,
|
|
443
530
|
stopReason
|
|
444
531
|
};
|
|
445
532
|
}
|
|
@@ -477,9 +564,5 @@ function assertConfig(config) {
|
|
|
477
564
|
for (const [name, value] of positive) if (value !== void 0 && (!Number.isInteger(value) || value < 1)) throw new Error(`${name} must be a positive integer, received ${value}`);
|
|
478
565
|
if (config.exemplars !== void 0 && (!Number.isInteger(config.exemplars) || config.exemplars < 0)) throw new Error(`exemplars must be a non-negative integer, received ${config.exemplars}`);
|
|
479
566
|
}
|
|
480
|
-
function defaultInstanceId(args) {
|
|
481
|
-
const hash = stableHash(args.datum);
|
|
482
|
-
return hash === "" ? String(args.index) : hash;
|
|
483
|
-
}
|
|
484
567
|
//#endregion
|
|
485
|
-
export { OproOptimizer, buildOproPrompt };
|
|
568
|
+
export { OproOptimizer, buildOproPrompt, isCandidateAccepted, isRunFinished };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as UsageTotals, _ as EvaluationPhase, c as RunWarning, f as Adapter, h as EvaluationBatch, p as Candidate, v as EvaluationSplit } from "./reporting-bq007_2z.cjs";
|
|
2
2
|
import { n as EvaluationCache, t as CachedScore } from "./cache-CuSo0NJ8.cjs";
|
|
3
3
|
//#region src/budget.d.ts
|
|
4
4
|
interface Budget {
|
|
@@ -106,8 +106,24 @@ interface Evaluator<Datum, Trajectory, Output, K extends string> {
|
|
|
106
106
|
cacheHits(): number;
|
|
107
107
|
/** Rollouts made with `charge: false`, tracked apart from the budget. */
|
|
108
108
|
unchargedCalls(): number;
|
|
109
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Tokens and money the search has spent, as far as adapters have reported
|
|
111
|
+
* it. `maxCostUsd` is checked against this, so what the ceiling does not
|
|
112
|
+
* bound is not counted in it: rollouts made with `charge: false` are in
|
|
113
|
+
* `unchargedUsage` instead.
|
|
114
|
+
*/
|
|
110
115
|
usage(): UsageTotals;
|
|
116
|
+
/**
|
|
117
|
+
* What the rollouts made with `charge: false` cost. Held-out measurement is
|
|
118
|
+
* taken after the search has stopped, so no ceiling bounds it and reporting
|
|
119
|
+
* it inside `usage` would describe a run as having overrun one.
|
|
120
|
+
*/
|
|
121
|
+
unchargedUsage(): UsageTotals;
|
|
122
|
+
/**
|
|
123
|
+
* Folds in usage spent outside this evaluator — harvesting runs its own, and
|
|
124
|
+
* a cost ceiling that cannot see it bounds only part of the run.
|
|
125
|
+
*/
|
|
126
|
+
absorbUsage(spent: UsageTotals): void;
|
|
111
127
|
/** Cache contents for checkpointing, when the cache can enumerate them. */
|
|
112
128
|
entries(): [string, CachedScore][] | undefined;
|
|
113
129
|
restore(entries: Iterable<readonly [string, CachedScore]>): void;
|
|
@@ -140,6 +156,12 @@ declare function createEvaluator<Datum, Trajectory, Output, K extends string>(ar
|
|
|
140
156
|
signal?: AbortSignal;
|
|
141
157
|
/** Resumed counters, so a continued run reports totals rather than deltas. */
|
|
142
158
|
cacheHits?: number;
|
|
159
|
+
/**
|
|
160
|
+
* Usage the interrupted run had already spent. Without it `maxCostUsd` is a
|
|
161
|
+
* ceiling on the segment rather than on the run, and resuming repeatedly
|
|
162
|
+
* spends it again each time.
|
|
163
|
+
*/
|
|
164
|
+
usage?: UsageTotals;
|
|
143
165
|
/**
|
|
144
166
|
* Rate limits and 5xx responses are the common case in a long run, and a
|
|
145
167
|
* transient row costs the instance whichever optimizer is driving: it is
|
|
@@ -192,8 +214,28 @@ declare function requireMeasuredMean(args: {
|
|
|
192
214
|
*/
|
|
193
215
|
interface OptimizerTask<Datum, Trajectory = unknown, Output = unknown, K extends string = string> {
|
|
194
216
|
seedCandidate: Candidate<K>;
|
|
217
|
+
/**
|
|
218
|
+
* Instances the search draws evidence from. Reflective optimizers mine these
|
|
219
|
+
* for what a candidate got wrong, so a training row earns its place by being
|
|
220
|
+
* diagnostic — a row every candidate already passes teaches the rewriter
|
|
221
|
+
* nothing.
|
|
222
|
+
*
|
|
223
|
+
* @see docs/data-prep.md
|
|
224
|
+
*/
|
|
195
225
|
trainingSet: readonly Datum[];
|
|
196
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Instances the search selects candidates against. Defaults to
|
|
228
|
+
* `trainingSet`, which is the right default for a first run and the wrong
|
|
229
|
+
* number to report from one — the result carries a warning saying so.
|
|
230
|
+
* `"reuseTraining"` is that same default with the caller's name on it, and
|
|
231
|
+
* carries no warning.
|
|
232
|
+
*
|
|
233
|
+
* Split by group rather than by row: near-duplicate instances that straddle
|
|
234
|
+
* the boundary leak, and the run reports a score nothing earned.
|
|
235
|
+
*
|
|
236
|
+
* @see docs/data-prep.md
|
|
237
|
+
*/
|
|
238
|
+
validationSet?: readonly Datum[] | "reuseTraining";
|
|
197
239
|
/**
|
|
198
240
|
* `NoInfer` keeps the adapter out of `K`'s inference: an adapter built by a
|
|
199
241
|
* factory knows nothing about component names, and one inference candidate of
|
|
@@ -207,6 +249,18 @@ interface OptimizerTask<Datum, Trajectory = unknown, Output = unknown, K extends
|
|
|
207
249
|
* number in a result that no candidate was ever selected against.
|
|
208
250
|
*/
|
|
209
251
|
testSet?: readonly Datum[];
|
|
252
|
+
/**
|
|
253
|
+
* Scored rollouts the search may spend. Cache hits do not count, and test
|
|
254
|
+
* rollouts are outside it entirely.
|
|
255
|
+
*
|
|
256
|
+
* Spending it is only worth anything if the metric separates candidates
|
|
257
|
+
* first: a metric that scores every candidate alike turns the whole budget
|
|
258
|
+
* into ranked ties, and the run reports a stop reason that looks like any
|
|
259
|
+
* other.
|
|
260
|
+
*
|
|
261
|
+
* @see docs/metric-preflight.md
|
|
262
|
+
* @see docs/tuning.md
|
|
263
|
+
*/
|
|
210
264
|
maxMetricCalls: number;
|
|
211
265
|
/**
|
|
212
266
|
* Dollars the run may spend, as reported by the adapter's usage. Checked
|
|
@@ -252,8 +306,25 @@ interface OptimizerResult<K extends string, Stop extends string, Output = unknow
|
|
|
252
306
|
bestOutputs?: (Output | undefined)[];
|
|
253
307
|
metricCalls: number;
|
|
254
308
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
309
|
+
* Rollouts served from the cache rather than charged to `metricCalls`. Every
|
|
310
|
+
* optimizer here caches by default, so the same `maxMetricCalls` can buy a
|
|
311
|
+
* search that revisits scored candidates a longer effective run than one
|
|
312
|
+
* that never does — a comparison over `metricCalls` alone hides that. Zero
|
|
313
|
+
* for a run with caching disabled.
|
|
314
|
+
*/
|
|
315
|
+
cacheHits: number;
|
|
316
|
+
/**
|
|
317
|
+
* Calls made to a proposal or reflection model, which no metric budget
|
|
318
|
+
* covers — see each optimizer's own accounting for what a call costs there.
|
|
319
|
+
* Absent from a search that proposes nothing of its own: bootstrap search
|
|
320
|
+
* only accepts or rejects rollouts the metric already scored.
|
|
321
|
+
*/
|
|
322
|
+
reflectionCalls?: number;
|
|
323
|
+
/**
|
|
324
|
+
* Tokens and dollars the search spent, summed from what the adapter reported.
|
|
325
|
+
* Zero throughout when the adapter reports no usage. `maxCostUsd` is checked
|
|
326
|
+
* against this, so the held-out sweep — which runs after the search has
|
|
327
|
+
* stopped, under no ceiling — is reported apart from it, in `testUsage`.
|
|
257
328
|
*/
|
|
258
329
|
usage: UsageTotals;
|
|
259
330
|
/**
|
|
@@ -267,7 +338,21 @@ interface OptimizerResult<K extends string, Stop extends string, Output = unknow
|
|
|
267
338
|
* measurement rather than search, and so is not charged to `maxMetricCalls`.
|
|
268
339
|
*/
|
|
269
340
|
testMetricCalls?: number;
|
|
341
|
+
/**
|
|
342
|
+
* Tokens and dollars the held-out sweep cost, for the same reason: no ceiling
|
|
343
|
+
* bounds it, so a caller adding up what a run spent has to see it as its own
|
|
344
|
+
* number rather than find it folded into one `maxCostUsd` was supposed to
|
|
345
|
+
* hold. Absent when no testSet was given.
|
|
346
|
+
*/
|
|
347
|
+
testUsage?: UsageTotals;
|
|
270
348
|
stopReason: Stop;
|
|
349
|
+
/**
|
|
350
|
+
* What this run cannot say about itself from its own numbers — selection
|
|
351
|
+
* that reused the training instances, a seed the metric could not separate.
|
|
352
|
+
* Empty when there is nothing to say. Never fatal, and repeated on the
|
|
353
|
+
* `finish` event so a reporter sees them next to the score.
|
|
354
|
+
*/
|
|
355
|
+
warnings: RunWarning[];
|
|
271
356
|
}
|
|
272
357
|
/**
|
|
273
358
|
* An optimizer: a task in, the best candidate it found out. Exactly one method.
|