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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_reporting = require("../reporting-CNHzbJC-.cjs");
|
|
3
3
|
const require_concurrency = require("../concurrency-C-cFzWW2.cjs");
|
|
4
4
|
const require_text = require("../text-CK_HB3su.cjs");
|
|
5
5
|
//#region src/random-search/optimize.ts
|
|
@@ -33,10 +33,14 @@ var RandomSearchOptimizer = class {
|
|
|
33
33
|
this.#config = config;
|
|
34
34
|
}
|
|
35
35
|
async optimize(task) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
try {
|
|
37
|
+
return await runRandomSearch({
|
|
38
|
+
config: this.#config,
|
|
39
|
+
task
|
|
40
|
+
});
|
|
41
|
+
} finally {
|
|
42
|
+
await require_reporting.flushReporters(task.reporters ?? []);
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
};
|
|
42
46
|
/**
|
|
@@ -63,9 +67,10 @@ function buildParaphrasePrompt(args) {
|
|
|
63
67
|
async function runRandomSearch(args) {
|
|
64
68
|
const { config, task } = args;
|
|
65
69
|
const { variants = DEFAULT_VARIANTS, concurrency = 1, maxRounds = Number.POSITIVE_INFINITY, buildPrompt = buildParaphrasePrompt, trackBestOutputs = false, checkpointCache = true } = config;
|
|
66
|
-
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId,
|
|
67
|
-
const
|
|
68
|
-
const
|
|
70
|
+
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_reporting.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
71
|
+
const emit = require_reporting.createEmitter(reporters);
|
|
72
|
+
const deadline = require_reporting.createDeadline({ maxWallClockMs });
|
|
73
|
+
const components = require_reporting.componentNames(seedCandidate);
|
|
69
74
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
70
75
|
if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
|
|
71
76
|
if (components.length === 0) throw new Error("optimize requires a seed candidate with at least one component");
|
|
@@ -78,7 +83,7 @@ async function runRandomSearch(args) {
|
|
|
78
83
|
datum,
|
|
79
84
|
index
|
|
80
85
|
})) ?? [];
|
|
81
|
-
const fingerprint =
|
|
86
|
+
const fingerprint = require_reporting.runFingerprint({
|
|
82
87
|
seedCandidate,
|
|
83
88
|
trainingIds: trainingSet.map((datum, index) => instanceId({
|
|
84
89
|
datum,
|
|
@@ -87,16 +92,16 @@ async function runRandomSearch(args) {
|
|
|
87
92
|
validationIds,
|
|
88
93
|
...cacheNamespace === void 0 ? {} : { cacheNamespace }
|
|
89
94
|
});
|
|
90
|
-
|
|
95
|
+
require_reporting.assertResumable({
|
|
91
96
|
fingerprint,
|
|
92
97
|
...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
|
|
93
98
|
});
|
|
94
|
-
const budget =
|
|
99
|
+
const budget = require_reporting.createBudget({
|
|
95
100
|
maxMetricCalls,
|
|
96
101
|
spent: resumeFrom?.metricCalls ?? 0
|
|
97
102
|
});
|
|
98
|
-
const evaluationCache = cache === false ? void 0 : cache ??
|
|
99
|
-
const evaluator =
|
|
103
|
+
const evaluationCache = cache === false ? void 0 : cache ?? require_reporting.createMemoryCache();
|
|
104
|
+
const evaluator = require_reporting.createEvaluator({
|
|
100
105
|
adapter,
|
|
101
106
|
budget,
|
|
102
107
|
...retry === void 0 ? {} : { retry },
|
|
@@ -104,8 +109,9 @@ async function runRandomSearch(args) {
|
|
|
104
109
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
105
110
|
trackOutputs: trackBestOutputs,
|
|
106
111
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
112
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
107
113
|
...signal === void 0 ? {} : { signal },
|
|
108
|
-
onEvaluation: (event) =>
|
|
114
|
+
onEvaluation: (event) => emit({
|
|
109
115
|
type: "evaluation",
|
|
110
116
|
...event
|
|
111
117
|
})
|
|
@@ -123,7 +129,7 @@ async function runRandomSearch(args) {
|
|
|
123
129
|
* no budget here bounds.
|
|
124
130
|
*/
|
|
125
131
|
let stalledRounds = 0;
|
|
126
|
-
|
|
132
|
+
emit({
|
|
127
133
|
type: "start",
|
|
128
134
|
components,
|
|
129
135
|
validationSetSize: validationSet.length
|
|
@@ -152,6 +158,8 @@ async function runRandomSearch(args) {
|
|
|
152
158
|
reflectionCalls,
|
|
153
159
|
metricCalls: budget.spent(),
|
|
154
160
|
cacheHits: evaluator.cacheHits(),
|
|
161
|
+
usage: evaluator.usage(),
|
|
162
|
+
acceptedCandidates,
|
|
155
163
|
...cached === void 0 ? {} : { cache: cached }
|
|
156
164
|
};
|
|
157
165
|
}
|
|
@@ -163,21 +171,31 @@ async function runRandomSearch(args) {
|
|
|
163
171
|
candidate: seedCandidate,
|
|
164
172
|
phase: "seed"
|
|
165
173
|
}) : void 0;
|
|
166
|
-
const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore :
|
|
174
|
+
const seedScore = seedEvaluation === void 0 ? resumeFrom.seedScore : require_reporting.requireMeasuredMean({
|
|
167
175
|
batch: seedEvaluation,
|
|
168
176
|
phase: "seed"
|
|
169
177
|
});
|
|
178
|
+
if (seedEvaluation !== void 0) emit({
|
|
179
|
+
type: "candidateAccepted",
|
|
180
|
+
round: 0,
|
|
181
|
+
candidateId: 0,
|
|
182
|
+
candidate: seedCandidate,
|
|
183
|
+
aggregateScore: seedScore,
|
|
184
|
+
instanceScores: require_reporting.instanceRow(seedEvaluation),
|
|
185
|
+
...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
|
|
186
|
+
});
|
|
170
187
|
let best = resumeFrom?.best ?? seedCandidate;
|
|
171
188
|
let bestScore = resumeFrom?.bestScore ?? seedScore;
|
|
172
189
|
/** Absent on a resumed run until a variant wins: outputs are not checkpointed. */
|
|
173
190
|
let bestOutputs = seedEvaluation?.outputs;
|
|
191
|
+
let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
|
|
174
192
|
await checkpoint();
|
|
175
193
|
while (round < maxRounds) {
|
|
176
194
|
if (signal?.aborted) {
|
|
177
195
|
stopReason = "aborted";
|
|
178
196
|
break;
|
|
179
197
|
}
|
|
180
|
-
if (
|
|
198
|
+
if (require_reporting.costExhausted({
|
|
181
199
|
usage: evaluator.usage(),
|
|
182
200
|
maxCostUsd
|
|
183
201
|
})) {
|
|
@@ -188,12 +206,12 @@ async function runRandomSearch(args) {
|
|
|
188
206
|
stopReason = "deadlineReached";
|
|
189
207
|
break;
|
|
190
208
|
}
|
|
191
|
-
if (!budget.canAfford(
|
|
209
|
+
if (!budget.canAfford(validationSet.length)) {
|
|
192
210
|
stopReason = "budgetExhausted";
|
|
193
211
|
break;
|
|
194
212
|
}
|
|
195
213
|
const component = components[round % components.length];
|
|
196
|
-
|
|
214
|
+
emit({
|
|
197
215
|
type: "roundStart",
|
|
198
216
|
round,
|
|
199
217
|
component
|
|
@@ -220,41 +238,64 @@ async function runRandomSearch(args) {
|
|
|
220
238
|
});
|
|
221
239
|
const unique = [...new Set(drawn)].filter((text) => text.length > 0 && text !== currentText);
|
|
222
240
|
let roundStop;
|
|
241
|
+
const scheduled = [];
|
|
242
|
+
let owed = 0;
|
|
223
243
|
for (const text of unique) {
|
|
224
244
|
const candidate = {
|
|
225
245
|
...best,
|
|
226
246
|
[component]: text
|
|
227
247
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
248
|
+
const uncached = evaluator.countUncached({
|
|
249
|
+
candidate,
|
|
250
|
+
ids: validationIds,
|
|
251
|
+
split: "val"
|
|
252
|
+
});
|
|
253
|
+
if (!budget.canAfford(owed + uncached)) {
|
|
254
|
+
roundStop = "budgetExhausted";
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
owed += uncached;
|
|
258
|
+
scheduled.push(candidate);
|
|
259
|
+
}
|
|
260
|
+
const swept = await require_concurrency.mapWithConcurrency({
|
|
261
|
+
items: scheduled,
|
|
262
|
+
limit: concurrency,
|
|
263
|
+
task: async (candidate) => {
|
|
264
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
265
|
+
try {
|
|
266
|
+
return { evaluation: await sweep({
|
|
267
|
+
candidate,
|
|
268
|
+
phase: "validation"
|
|
269
|
+
}) };
|
|
270
|
+
} catch (err) {
|
|
271
|
+
if (err instanceof require_reporting.BudgetExhausted) return { stop: "budgetExhausted" };
|
|
272
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
273
|
+
throw err;
|
|
242
274
|
}
|
|
243
|
-
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
for (const [index, outcome] of swept.entries()) {
|
|
278
|
+
if (outcome.stop !== void 0) {
|
|
279
|
+
roundStop = outcome.stop;
|
|
280
|
+
break;
|
|
244
281
|
}
|
|
245
282
|
variantsEvaluated += 1;
|
|
246
|
-
const score =
|
|
283
|
+
const score = require_reporting.measuredMean(outcome.evaluation);
|
|
247
284
|
if (score !== void 0 && score > bestScore) {
|
|
248
|
-
|
|
285
|
+
const candidate = scheduled[index];
|
|
286
|
+
acceptedCandidates += 1;
|
|
287
|
+
emit({
|
|
249
288
|
type: "candidateAccepted",
|
|
250
289
|
round,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
290
|
+
candidateId: acceptedCandidates,
|
|
291
|
+
candidate,
|
|
292
|
+
aggregateScore: score,
|
|
293
|
+
instanceScores: require_reporting.instanceRow(outcome.evaluation),
|
|
294
|
+
...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
|
|
254
295
|
});
|
|
255
296
|
best = candidate;
|
|
256
297
|
bestScore = score;
|
|
257
|
-
bestOutputs = evaluation.outputs;
|
|
298
|
+
bestOutputs = outcome.evaluation.outputs;
|
|
258
299
|
}
|
|
259
300
|
}
|
|
260
301
|
round += 1;
|
|
@@ -270,7 +311,7 @@ async function runRandomSearch(args) {
|
|
|
270
311
|
}
|
|
271
312
|
}
|
|
272
313
|
if (signal?.aborted) stopReason = "aborted";
|
|
273
|
-
const
|
|
314
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
274
315
|
candidate: best,
|
|
275
316
|
batch: testSet,
|
|
276
317
|
ids: testIds,
|
|
@@ -279,13 +320,17 @@ async function runRandomSearch(args) {
|
|
|
279
320
|
candidateId: null,
|
|
280
321
|
iteration: round,
|
|
281
322
|
charge: false
|
|
282
|
-
})
|
|
283
|
-
|
|
323
|
+
});
|
|
324
|
+
const testScore = heldOut === void 0 ? void 0 : require_reporting.measuredMean(heldOut);
|
|
325
|
+
emit({
|
|
284
326
|
type: "finish",
|
|
285
327
|
reason: stopReason,
|
|
328
|
+
bestCandidateId: acceptedCandidates,
|
|
286
329
|
bestScore,
|
|
287
330
|
metricCalls: budget.spent(),
|
|
288
|
-
...testScore === void 0 ? {} : { testScore }
|
|
331
|
+
...testScore === void 0 ? {} : { testScore },
|
|
332
|
+
...heldOut === void 0 ? {} : { testInstanceScores: require_reporting.instanceRow(heldOut) },
|
|
333
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
289
334
|
});
|
|
290
335
|
return {
|
|
291
336
|
bestCandidate: best,
|
|
@@ -296,7 +341,8 @@ async function runRandomSearch(args) {
|
|
|
296
341
|
...trackBestOutputs ? { bestOutputs } : {},
|
|
297
342
|
...testScore === void 0 ? {} : {
|
|
298
343
|
testScore,
|
|
299
|
-
testMetricCalls: evaluator.unchargedCalls()
|
|
344
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
345
|
+
testUsage: evaluator.unchargedUsage()
|
|
300
346
|
},
|
|
301
347
|
rounds: round,
|
|
302
348
|
variantsEvaluated,
|
|
@@ -312,10 +358,8 @@ function assertConfig(config) {
|
|
|
312
358
|
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
313
359
|
if (maxRounds !== void 0 && (!Number.isInteger(maxRounds) || maxRounds < 1)) throw new Error(`maxRounds must be a positive integer, received ${maxRounds}`);
|
|
314
360
|
}
|
|
315
|
-
function defaultInstanceId(args) {
|
|
316
|
-
const hash = require_evaluation.stableHash(args.datum);
|
|
317
|
-
return hash === "" ? String(args.index) : hash;
|
|
318
|
-
}
|
|
319
361
|
//#endregion
|
|
320
362
|
exports.RandomSearchOptimizer = RandomSearchOptimizer;
|
|
321
363
|
exports.buildParaphrasePrompt = buildParaphrasePrompt;
|
|
364
|
+
exports.isCandidateAccepted = require_reporting.isCandidateAccepted;
|
|
365
|
+
exports.isRunFinished = require_reporting.isRunFinished;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as TextModel, 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 { 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-Bh5DPRMH.cjs";
|
|
4
4
|
//#region src/random-search/optimize.d.ts
|
|
5
5
|
/** Builds the prompt one variant is drawn from. */
|
|
6
6
|
type ParaphrasePromptBuilder = (args: {
|
|
@@ -54,6 +54,13 @@ interface RandomSearchSnapshot {
|
|
|
54
54
|
reflectionCalls: number;
|
|
55
55
|
metricCalls: number;
|
|
56
56
|
cacheHits: number;
|
|
57
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
58
|
+
usage?: UsageTotals;
|
|
59
|
+
/**
|
|
60
|
+
* Candidates accepted so far. Reporters key rows by this id, so restarting it
|
|
61
|
+
* at zero makes a resumed run collide with the run it continues.
|
|
62
|
+
*/
|
|
63
|
+
acceptedCandidates?: number;
|
|
57
64
|
/** Cached instance scores, when the cache can enumerate them. */
|
|
58
65
|
cache?: [string, CachedScore][];
|
|
59
66
|
}
|
|
@@ -71,7 +78,8 @@ interface RandomSearchTask<Datum, Trajectory = unknown, Output = unknown, K exte
|
|
|
71
78
|
}) => string;
|
|
72
79
|
/** Pass `false` to disable caching entirely. */
|
|
73
80
|
cache?: EvaluationCache | false;
|
|
74
|
-
|
|
81
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
82
|
+
reporters?: readonly Reporter<RandomSearchEvent<NoInfer<K>>>[];
|
|
75
83
|
/**
|
|
76
84
|
* Called with a resumable snapshot after the seed is scored and after every
|
|
77
85
|
* round. Persist it and a killed run costs the last round, not all of them.
|
|
@@ -91,19 +99,13 @@ type RandomSearchEvent<K extends string = string> = {
|
|
|
91
99
|
component: K;
|
|
92
100
|
} | ({
|
|
93
101
|
type: "evaluation";
|
|
94
|
-
} & EvaluationEvent) | {
|
|
102
|
+
} & EvaluationEvent) | ({
|
|
95
103
|
type: "candidateAccepted";
|
|
96
104
|
round: number;
|
|
97
|
-
|
|
98
|
-
score: number;
|
|
99
|
-
previousScore: number;
|
|
100
|
-
} | {
|
|
105
|
+
} & CandidateAccepted<K>) | ({
|
|
101
106
|
type: "finish";
|
|
102
107
|
reason: RandomSearchStopReason;
|
|
103
|
-
|
|
104
|
-
metricCalls: number;
|
|
105
|
-
testScore?: number;
|
|
106
|
-
};
|
|
108
|
+
} & RunFinished);
|
|
107
109
|
interface RandomSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, RandomSearchStopReason, Output> {
|
|
108
110
|
/** The seed's score, so the lift the search bought is readable directly. */
|
|
109
111
|
seedScore: number;
|
|
@@ -153,4 +155,4 @@ declare function buildParaphrasePrompt(args: {
|
|
|
153
155
|
attempt: number;
|
|
154
156
|
}): string;
|
|
155
157
|
//#endregion
|
|
156
|
-
export { type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, buildParaphrasePrompt };
|
|
158
|
+
export { type CandidateAccepted, type OptimizerEvent, type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, type ReportableEvent, type Reporter, type RunFinished, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as TextModel, 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.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-Ck6-e_8o.mjs";
|
|
4
4
|
//#region src/random-search/optimize.d.ts
|
|
5
5
|
/** Builds the prompt one variant is drawn from. */
|
|
6
6
|
type ParaphrasePromptBuilder = (args: {
|
|
@@ -54,6 +54,13 @@ interface RandomSearchSnapshot {
|
|
|
54
54
|
reflectionCalls: number;
|
|
55
55
|
metricCalls: number;
|
|
56
56
|
cacheHits: number;
|
|
57
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
58
|
+
usage?: UsageTotals;
|
|
59
|
+
/**
|
|
60
|
+
* Candidates accepted so far. Reporters key rows by this id, so restarting it
|
|
61
|
+
* at zero makes a resumed run collide with the run it continues.
|
|
62
|
+
*/
|
|
63
|
+
acceptedCandidates?: number;
|
|
57
64
|
/** Cached instance scores, when the cache can enumerate them. */
|
|
58
65
|
cache?: [string, CachedScore][];
|
|
59
66
|
}
|
|
@@ -71,7 +78,8 @@ interface RandomSearchTask<Datum, Trajectory = unknown, Output = unknown, K exte
|
|
|
71
78
|
}) => string;
|
|
72
79
|
/** Pass `false` to disable caching entirely. */
|
|
73
80
|
cache?: EvaluationCache | false;
|
|
74
|
-
|
|
81
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
82
|
+
reporters?: readonly Reporter<RandomSearchEvent<NoInfer<K>>>[];
|
|
75
83
|
/**
|
|
76
84
|
* Called with a resumable snapshot after the seed is scored and after every
|
|
77
85
|
* round. Persist it and a killed run costs the last round, not all of them.
|
|
@@ -91,19 +99,13 @@ type RandomSearchEvent<K extends string = string> = {
|
|
|
91
99
|
component: K;
|
|
92
100
|
} | ({
|
|
93
101
|
type: "evaluation";
|
|
94
|
-
} & EvaluationEvent) | {
|
|
102
|
+
} & EvaluationEvent) | ({
|
|
95
103
|
type: "candidateAccepted";
|
|
96
104
|
round: number;
|
|
97
|
-
|
|
98
|
-
score: number;
|
|
99
|
-
previousScore: number;
|
|
100
|
-
} | {
|
|
105
|
+
} & CandidateAccepted<K>) | ({
|
|
101
106
|
type: "finish";
|
|
102
107
|
reason: RandomSearchStopReason;
|
|
103
|
-
|
|
104
|
-
metricCalls: number;
|
|
105
|
-
testScore?: number;
|
|
106
|
-
};
|
|
108
|
+
} & RunFinished);
|
|
107
109
|
interface RandomSearchResult<K extends string = string, Output = unknown> extends OptimizerResult<K, RandomSearchStopReason, Output> {
|
|
108
110
|
/** The seed's score, so the lift the search bought is readable directly. */
|
|
109
111
|
seedScore: number;
|
|
@@ -153,4 +155,4 @@ declare function buildParaphrasePrompt(args: {
|
|
|
153
155
|
attempt: number;
|
|
154
156
|
}): string;
|
|
155
157
|
//#endregion
|
|
156
|
-
export { type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, buildParaphrasePrompt };
|
|
158
|
+
export { type CandidateAccepted, type OptimizerEvent, type ParaphrasePromptBuilder, type RandomSearchConfig, type RandomSearchEvent, RandomSearchOptimizer, type RandomSearchResult, type RandomSearchSnapshot, type RandomSearchStopReason, type RandomSearchTask, type ReportableEvent, type Reporter, type RunFinished, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { _ as createMemoryCache, a as isRunFinished, b as createBudget, c as createEvaluator, d as createDeadline, f as assertResumable, h as componentNames, i as isCandidateAccepted, l as measuredMean, m as runFingerprint, n as flushReporters, o as BudgetExhausted, r as instanceRow, s as costExhausted, t as createEmitter, u as requireMeasuredMean, v as defaultInstanceId } from "../reporting-DY-DC4HG.mjs";
|
|
2
2
|
import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
|
|
3
3
|
import { t as parseProposedText } from "../text--v4Ffbus.mjs";
|
|
4
4
|
//#region src/random-search/optimize.ts
|
|
@@ -32,10 +32,14 @@ var RandomSearchOptimizer = class {
|
|
|
32
32
|
this.#config = config;
|
|
33
33
|
}
|
|
34
34
|
async optimize(task) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
try {
|
|
36
|
+
return await runRandomSearch({
|
|
37
|
+
config: this.#config,
|
|
38
|
+
task
|
|
39
|
+
});
|
|
40
|
+
} finally {
|
|
41
|
+
await flushReporters(task.reporters ?? []);
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
};
|
|
41
45
|
/**
|
|
@@ -62,7 +66,8 @@ function buildParaphrasePrompt(args) {
|
|
|
62
66
|
async function runRandomSearch(args) {
|
|
63
67
|
const { config, task } = args;
|
|
64
68
|
const { variants = DEFAULT_VARIANTS, concurrency = 1, maxRounds = Number.POSITIVE_INFINITY, buildPrompt = buildParaphrasePrompt, trackBestOutputs = false, checkpointCache = true } = config;
|
|
65
|
-
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId,
|
|
69
|
+
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
70
|
+
const emit = createEmitter(reporters);
|
|
66
71
|
const deadline = createDeadline({ maxWallClockMs });
|
|
67
72
|
const components = componentNames(seedCandidate);
|
|
68
73
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
@@ -103,8 +108,9 @@ async function runRandomSearch(args) {
|
|
|
103
108
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
104
109
|
trackOutputs: trackBestOutputs,
|
|
105
110
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
111
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
106
112
|
...signal === void 0 ? {} : { signal },
|
|
107
|
-
onEvaluation: (event) =>
|
|
113
|
+
onEvaluation: (event) => emit({
|
|
108
114
|
type: "evaluation",
|
|
109
115
|
...event
|
|
110
116
|
})
|
|
@@ -122,7 +128,7 @@ async function runRandomSearch(args) {
|
|
|
122
128
|
* no budget here bounds.
|
|
123
129
|
*/
|
|
124
130
|
let stalledRounds = 0;
|
|
125
|
-
|
|
131
|
+
emit({
|
|
126
132
|
type: "start",
|
|
127
133
|
components,
|
|
128
134
|
validationSetSize: validationSet.length
|
|
@@ -151,6 +157,8 @@ async function runRandomSearch(args) {
|
|
|
151
157
|
reflectionCalls,
|
|
152
158
|
metricCalls: budget.spent(),
|
|
153
159
|
cacheHits: evaluator.cacheHits(),
|
|
160
|
+
usage: evaluator.usage(),
|
|
161
|
+
acceptedCandidates,
|
|
154
162
|
...cached === void 0 ? {} : { cache: cached }
|
|
155
163
|
};
|
|
156
164
|
}
|
|
@@ -166,10 +174,20 @@ async function runRandomSearch(args) {
|
|
|
166
174
|
batch: seedEvaluation,
|
|
167
175
|
phase: "seed"
|
|
168
176
|
});
|
|
177
|
+
if (seedEvaluation !== void 0) emit({
|
|
178
|
+
type: "candidateAccepted",
|
|
179
|
+
round: 0,
|
|
180
|
+
candidateId: 0,
|
|
181
|
+
candidate: seedCandidate,
|
|
182
|
+
aggregateScore: seedScore,
|
|
183
|
+
instanceScores: instanceRow(seedEvaluation),
|
|
184
|
+
...trackBestOutputs ? { outputs: seedEvaluation.outputs } : {}
|
|
185
|
+
});
|
|
169
186
|
let best = resumeFrom?.best ?? seedCandidate;
|
|
170
187
|
let bestScore = resumeFrom?.bestScore ?? seedScore;
|
|
171
188
|
/** Absent on a resumed run until a variant wins: outputs are not checkpointed. */
|
|
172
189
|
let bestOutputs = seedEvaluation?.outputs;
|
|
190
|
+
let acceptedCandidates = resumeFrom?.acceptedCandidates ?? 0;
|
|
173
191
|
await checkpoint();
|
|
174
192
|
while (round < maxRounds) {
|
|
175
193
|
if (signal?.aborted) {
|
|
@@ -187,12 +205,12 @@ async function runRandomSearch(args) {
|
|
|
187
205
|
stopReason = "deadlineReached";
|
|
188
206
|
break;
|
|
189
207
|
}
|
|
190
|
-
if (!budget.canAfford(
|
|
208
|
+
if (!budget.canAfford(validationSet.length)) {
|
|
191
209
|
stopReason = "budgetExhausted";
|
|
192
210
|
break;
|
|
193
211
|
}
|
|
194
212
|
const component = components[round % components.length];
|
|
195
|
-
|
|
213
|
+
emit({
|
|
196
214
|
type: "roundStart",
|
|
197
215
|
round,
|
|
198
216
|
component
|
|
@@ -219,41 +237,64 @@ async function runRandomSearch(args) {
|
|
|
219
237
|
});
|
|
220
238
|
const unique = [...new Set(drawn)].filter((text) => text.length > 0 && text !== currentText);
|
|
221
239
|
let roundStop;
|
|
240
|
+
const scheduled = [];
|
|
241
|
+
let owed = 0;
|
|
222
242
|
for (const text of unique) {
|
|
223
243
|
const candidate = {
|
|
224
244
|
...best,
|
|
225
245
|
[component]: text
|
|
226
246
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
const uncached = evaluator.countUncached({
|
|
248
|
+
candidate,
|
|
249
|
+
ids: validationIds,
|
|
250
|
+
split: "val"
|
|
251
|
+
});
|
|
252
|
+
if (!budget.canAfford(owed + uncached)) {
|
|
253
|
+
roundStop = "budgetExhausted";
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
owed += uncached;
|
|
257
|
+
scheduled.push(candidate);
|
|
258
|
+
}
|
|
259
|
+
const swept = await mapWithConcurrency({
|
|
260
|
+
items: scheduled,
|
|
261
|
+
limit: concurrency,
|
|
262
|
+
task: async (candidate) => {
|
|
263
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
264
|
+
try {
|
|
265
|
+
return { evaluation: await sweep({
|
|
266
|
+
candidate,
|
|
267
|
+
phase: "validation"
|
|
268
|
+
}) };
|
|
269
|
+
} catch (err) {
|
|
270
|
+
if (err instanceof BudgetExhausted) return { stop: "budgetExhausted" };
|
|
271
|
+
if (signal?.aborted) return { stop: "aborted" };
|
|
272
|
+
throw err;
|
|
241
273
|
}
|
|
242
|
-
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
for (const [index, outcome] of swept.entries()) {
|
|
277
|
+
if (outcome.stop !== void 0) {
|
|
278
|
+
roundStop = outcome.stop;
|
|
279
|
+
break;
|
|
243
280
|
}
|
|
244
281
|
variantsEvaluated += 1;
|
|
245
|
-
const score = measuredMean(evaluation);
|
|
282
|
+
const score = measuredMean(outcome.evaluation);
|
|
246
283
|
if (score !== void 0 && score > bestScore) {
|
|
247
|
-
|
|
284
|
+
const candidate = scheduled[index];
|
|
285
|
+
acceptedCandidates += 1;
|
|
286
|
+
emit({
|
|
248
287
|
type: "candidateAccepted",
|
|
249
288
|
round,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
289
|
+
candidateId: acceptedCandidates,
|
|
290
|
+
candidate,
|
|
291
|
+
aggregateScore: score,
|
|
292
|
+
instanceScores: instanceRow(outcome.evaluation),
|
|
293
|
+
...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
|
|
253
294
|
});
|
|
254
295
|
best = candidate;
|
|
255
296
|
bestScore = score;
|
|
256
|
-
bestOutputs = evaluation.outputs;
|
|
297
|
+
bestOutputs = outcome.evaluation.outputs;
|
|
257
298
|
}
|
|
258
299
|
}
|
|
259
300
|
round += 1;
|
|
@@ -269,7 +310,7 @@ async function runRandomSearch(args) {
|
|
|
269
310
|
}
|
|
270
311
|
}
|
|
271
312
|
if (signal?.aborted) stopReason = "aborted";
|
|
272
|
-
const
|
|
313
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
273
314
|
candidate: best,
|
|
274
315
|
batch: testSet,
|
|
275
316
|
ids: testIds,
|
|
@@ -278,13 +319,17 @@ async function runRandomSearch(args) {
|
|
|
278
319
|
candidateId: null,
|
|
279
320
|
iteration: round,
|
|
280
321
|
charge: false
|
|
281
|
-
})
|
|
282
|
-
|
|
322
|
+
});
|
|
323
|
+
const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
|
|
324
|
+
emit({
|
|
283
325
|
type: "finish",
|
|
284
326
|
reason: stopReason,
|
|
327
|
+
bestCandidateId: acceptedCandidates,
|
|
285
328
|
bestScore,
|
|
286
329
|
metricCalls: budget.spent(),
|
|
287
|
-
...testScore === void 0 ? {} : { testScore }
|
|
330
|
+
...testScore === void 0 ? {} : { testScore },
|
|
331
|
+
...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
|
|
332
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
288
333
|
});
|
|
289
334
|
return {
|
|
290
335
|
bestCandidate: best,
|
|
@@ -295,7 +340,8 @@ async function runRandomSearch(args) {
|
|
|
295
340
|
...trackBestOutputs ? { bestOutputs } : {},
|
|
296
341
|
...testScore === void 0 ? {} : {
|
|
297
342
|
testScore,
|
|
298
|
-
testMetricCalls: evaluator.unchargedCalls()
|
|
343
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
344
|
+
testUsage: evaluator.unchargedUsage()
|
|
299
345
|
},
|
|
300
346
|
rounds: round,
|
|
301
347
|
variantsEvaluated,
|
|
@@ -311,9 +357,5 @@ function assertConfig(config) {
|
|
|
311
357
|
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
312
358
|
if (maxRounds !== void 0 && (!Number.isInteger(maxRounds) || maxRounds < 1)) throw new Error(`maxRounds must be a positive integer, received ${maxRounds}`);
|
|
313
359
|
}
|
|
314
|
-
function defaultInstanceId(args) {
|
|
315
|
-
const hash = stableHash(args.datum);
|
|
316
|
-
return hash === "" ? String(args.index) : hash;
|
|
317
|
-
}
|
|
318
360
|
//#endregion
|
|
319
|
-
export { RandomSearchOptimizer, buildParaphrasePrompt };
|
|
361
|
+
export { RandomSearchOptimizer, buildParaphrasePrompt, isCandidateAccepted, isRunFinished };
|