textopt 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -22
- package/dist/bootstrap-search/index.cjs +153 -73
- package/dist/bootstrap-search/index.d.cts +32 -10
- package/dist/bootstrap-search/index.d.mts +32 -10
- package/dist/bootstrap-search/index.mjs +144 -66
- package/dist/{demos-B0pVQjYC.d.mts → demos-1b7JiUue.d.mts} +10 -3
- package/dist/{demos-BTuzFNsp.d.cts → demos-CU9dy2oT.d.cts} +10 -3
- package/dist/demos-D2o0qSSo.cjs +244 -0
- package/dist/demos-DE2oxNWX.mjs +215 -0
- package/dist/file-cache.cjs +11 -3
- package/dist/file-cache.mjs +11 -3
- package/dist/gepa/index.cjs +76 -71
- package/dist/gepa/index.d.cts +12 -6
- package/dist/gepa/index.d.mts +12 -6
- package/dist/gepa/index.mjs +49 -46
- package/dist/index.cjs +129 -27
- package/dist/index.d.cts +145 -7
- package/dist/index.d.mts +145 -7
- package/dist/index.mjs +113 -15
- package/dist/{math-COOofUyv.cjs → math-BhlziRPc.cjs} +60 -9
- package/dist/math-Dqme4rYz.mjs +123 -0
- package/dist/mipro/index.cjs +98 -70
- package/dist/mipro/index.d.cts +17 -14
- package/dist/mipro/index.d.mts +17 -14
- package/dist/mipro/index.mjs +84 -58
- package/dist/opro/index.cjs +130 -51
- package/dist/opro/index.d.cts +17 -9
- package/dist/opro/index.d.mts +17 -9
- package/dist/opro/index.mjs +115 -38
- package/dist/{optimizer-B7SpRwl7.d.cts → optimizer-Bh5DPRMH.d.cts} +50 -4
- package/dist/{optimizer-DqCoth_w.d.mts → optimizer-Ck6-e_8o.d.mts} +50 -4
- package/dist/random-search/index.cjs +93 -49
- package/dist/random-search/index.d.cts +15 -13
- package/dist/random-search/index.d.mts +15 -13
- package/dist/random-search/index.mjs +83 -41
- package/dist/{reflection-CQToe-5B.d.cts → reflection-Dt3QrXhM.d.cts} +7 -11
- package/dist/{reflection-Cr_upzU0.d.mts → reflection-LRaAZP4e.d.mts} +7 -11
- package/dist/{evaluation-OZOp6TB7.cjs → reporting-CNHzbJC-.cjs} +165 -5
- package/dist/reporting-DQbAohc9.d.cts +240 -0
- package/dist/reporting-DQbAohc9.d.mts +240 -0
- package/dist/{evaluation-BV0nSZVx.mjs → reporting-DY-DC4HG.mjs} +124 -6
- package/dist/simba/index.cjs +210 -83
- package/dist/simba/index.d.cts +32 -11
- package/dist/simba/index.d.mts +32 -11
- package/dist/simba/index.mjs +200 -75
- package/dist/testing.cjs +1 -0
- package/dist/testing.d.cts +5 -3
- package/dist/testing.d.mts +5 -3
- package/dist/testing.mjs +1 -1
- package/package.json +4 -3
- package/dist/demos-B9BJiNKz.cjs +0 -143
- package/dist/demos-Degx6UmP.mjs +0 -126
- package/dist/math-DhrDmpFS.mjs +0 -78
- package/dist/types-CWv4IQFF.d.cts +0 -129
- package/dist/types-CWv4IQFF.d.mts +0 -129
package/dist/simba/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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
3
|
import { t as Rng } from "../rng-BR5MOedA.mjs";
|
|
4
|
-
import { r as DemoRenderer } from "../demos-
|
|
5
|
-
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-
|
|
4
|
+
import { r as DemoRenderer } from "../demos-1b7JiUue.mjs";
|
|
5
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-Ck6-e_8o.mjs";
|
|
6
6
|
import { t as BatchSampler } from "../sampling-DFo_7RNJ.mjs";
|
|
7
7
|
//#region src/simba/advice.d.ts
|
|
8
8
|
interface AdviceTrajectory<Output = unknown> {
|
|
@@ -13,6 +13,12 @@ interface AdviceTrajectory<Output = unknown> {
|
|
|
13
13
|
interface AdvicePromptArgs<Datum = unknown, Output = unknown> {
|
|
14
14
|
/** Components the advice is wanted for, named so each can be addressed. */
|
|
15
15
|
components: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* What each of those components currently says. Advice is appended to this
|
|
18
|
+
* text rather than replacing it, so a proposer that cannot see it writes
|
|
19
|
+
* blind: it restates what is already there, and cannot correct it.
|
|
20
|
+
*/
|
|
21
|
+
current: Record<string, string>;
|
|
16
22
|
input: Datum;
|
|
17
23
|
/** The higher scoring run of this instance, when there is one to contrast. */
|
|
18
24
|
better?: AdviceTrajectory<Output>;
|
|
@@ -56,6 +62,17 @@ interface SimbaConfig {
|
|
|
56
62
|
minibatchSize?: number;
|
|
57
63
|
/** Programs sampled per step, and candidates built from them. Default 6. */
|
|
58
64
|
candidates?: number;
|
|
65
|
+
/**
|
|
66
|
+
* How many evaluations may be in flight at once. Default 1.
|
|
67
|
+
*
|
|
68
|
+
* Covers the two places a step's work is independent — scoring the
|
|
69
|
+
* candidates a step built, and sweeping the finalists at the end of the run
|
|
70
|
+
* — and nothing else. The trajectory samples and the mutations that read
|
|
71
|
+
* them are deliberately left in sequence: each of those reads state the one
|
|
72
|
+
* before it wrote, so overlapping them would make a seeded run depend on
|
|
73
|
+
* which call returned first.
|
|
74
|
+
*/
|
|
75
|
+
concurrency?: number;
|
|
59
76
|
/** Steps to run. Default 8. */
|
|
60
77
|
maxSteps?: number;
|
|
61
78
|
/** Demos a candidate may hold before the loop starts dropping them. Default 4. */
|
|
@@ -97,6 +114,8 @@ interface SimbaSnapshot {
|
|
|
97
114
|
metricCalls: number;
|
|
98
115
|
reflectionCalls: number;
|
|
99
116
|
cacheHits: number;
|
|
117
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
118
|
+
usage?: UsageTotals;
|
|
100
119
|
rngState: number;
|
|
101
120
|
sampler?: unknown;
|
|
102
121
|
cache?: [string, CachedScore][];
|
|
@@ -121,7 +140,8 @@ interface SimbaTask<Datum, Trajectory = unknown, Output = unknown, K extends str
|
|
|
121
140
|
index: number;
|
|
122
141
|
}) => string;
|
|
123
142
|
cache?: EvaluationCache | false;
|
|
124
|
-
|
|
143
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
144
|
+
reporters?: readonly Reporter<SimbaEvent<NoInfer<K>>>[];
|
|
125
145
|
onCheckpoint?: (snapshot: SimbaSnapshot) => void | Promise<void>;
|
|
126
146
|
resumeFrom?: SimbaSnapshot;
|
|
127
147
|
}
|
|
@@ -143,17 +163,18 @@ type SimbaEvent<K extends string = string> = {
|
|
|
143
163
|
/** The program it was mutated from; 0 is the seed. */
|
|
144
164
|
sourceProgram: number;
|
|
145
165
|
minibatchScore: number;
|
|
146
|
-
} | {
|
|
166
|
+
} | ({
|
|
167
|
+
type: "candidateAccepted";
|
|
168
|
+
/** The step whose winner this was. */
|
|
169
|
+
step: number;
|
|
170
|
+
} & CandidateAccepted<K>) | {
|
|
147
171
|
type: "error";
|
|
148
172
|
step: number;
|
|
149
173
|
err: unknown;
|
|
150
|
-
} | {
|
|
174
|
+
} | ({
|
|
151
175
|
type: "finish";
|
|
152
176
|
reason: SimbaStopReason;
|
|
153
|
-
|
|
154
|
-
metricCalls: number;
|
|
155
|
-
testScore?: number;
|
|
156
|
-
};
|
|
177
|
+
} & RunFinished);
|
|
157
178
|
interface SimbaResult<K extends string = string, Output = unknown> extends OptimizerResult<K, SimbaStopReason, Output> {
|
|
158
179
|
/** The seed's own validation score, so the lift the run bought is readable. */
|
|
159
180
|
seedScore: number;
|
|
@@ -286,4 +307,4 @@ declare function evenlySpacedIndices(args: {
|
|
|
286
307
|
count: number;
|
|
287
308
|
}): number[];
|
|
288
309
|
//#endregion
|
|
289
|
-
export { type AdvicePromptArgs, type AdvicePromptBuilder, type AdviceTrajectory, type SimbaBucket, type SimbaConfig, type SimbaEvent, type SimbaFinalist, SimbaOptimizer, type SimbaResult, type SimbaRollout, type SimbaSample, type SimbaSnapshot, type SimbaStopReason, type SimbaStrategy, type SimbaTask, buildAdvicePrompt, buildBuckets, evenlySpacedIndices, parseAdvice, percentile, samplePoisson, softmaxWeights, topKPlusBaseline };
|
|
310
|
+
export { type AdvicePromptArgs, type AdvicePromptBuilder, type AdviceTrajectory, type CandidateAccepted, type OptimizerEvent, type ReportableEvent, type Reporter, type RunFinished, type SimbaBucket, type SimbaConfig, type SimbaEvent, type SimbaFinalist, SimbaOptimizer, type SimbaResult, type SimbaRollout, type SimbaSample, type SimbaSnapshot, type SimbaStopReason, type SimbaStrategy, type SimbaTask, buildAdvicePrompt, buildBuckets, evenlySpacedIndices, isCandidateAccepted, isRunFinished, parseAdvice, percentile, samplePoisson, softmaxWeights, topKPlusBaseline };
|
package/dist/simba/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { _ as createMemoryCache, a as isRunFinished, b as createBudget, c as createEvaluator, d as createDeadline, f as assertResumable, g as candidateHash, 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, v as defaultInstanceId } from "../reporting-DY-DC4HG.mjs";
|
|
2
|
+
import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
|
|
3
|
+
import { i as replaceDemos, r as parseDemos } from "../demos-DE2oxNWX.mjs";
|
|
3
4
|
import { t as createSeededRng } from "../rng-Dtc5eZ_W.mjs";
|
|
4
5
|
import { t as createEpochShuffledSampler } from "../sampling-CfHt7Gue.mjs";
|
|
5
6
|
//#region src/simba/advice.ts
|
|
@@ -14,7 +15,7 @@ const ADVICE_BLOCK = /<advice\s+component="([^"]+)"\s*>([\s\S]*?)<\/advice>/g;
|
|
|
14
15
|
* behaviour rather than to the instance being easier.
|
|
15
16
|
*/
|
|
16
17
|
function buildAdvicePrompt(args) {
|
|
17
|
-
const { components, input, better, worse } = args;
|
|
18
|
+
const { components, current, input, better, worse } = args;
|
|
18
19
|
return [
|
|
19
20
|
"Two runs of the same system on the same input are shown below, with the reward each earned.",
|
|
20
21
|
"Your job is to write advice that would make the system behave like the higher scoring run the next time it sees a similar input.",
|
|
@@ -25,12 +26,18 @@ function buildAdvicePrompt(args) {
|
|
|
25
26
|
...worse === void 0 ? [] : trajectoryBlock("worse", worse),
|
|
26
27
|
...better === void 0 ? [] : trajectoryBlock("better", better),
|
|
27
28
|
"",
|
|
28
|
-
"Write advice for each of these components:",
|
|
29
|
+
"Write advice for each of these components, shown with what it says now:",
|
|
29
30
|
"",
|
|
30
31
|
"<components>",
|
|
31
|
-
components.
|
|
32
|
+
components.map((component) => [
|
|
33
|
+
`<component name="${component}">`,
|
|
34
|
+
current[component] ?? "",
|
|
35
|
+
"</component>"
|
|
36
|
+
].join("\n")).join("\n"),
|
|
32
37
|
"</components>",
|
|
33
38
|
"",
|
|
39
|
+
"Your advice is appended to what the component already says. Do not restate advice it already carries; add what is missing or correct what is wrong.",
|
|
40
|
+
"",
|
|
34
41
|
"The component will not have access to this example, so advice that only covers this input is wasted. State the general behaviour it should adopt, and be concrete about when it applies.",
|
|
35
42
|
"Address each component's own sub-task rather than the system as a whole.",
|
|
36
43
|
"Base the advice on what actually differed between the two runs. If nothing useful can be said for a component, leave it out.",
|
|
@@ -228,21 +235,28 @@ const DEMO_DROP_SCALE = 3;
|
|
|
228
235
|
var SimbaOptimizer = class {
|
|
229
236
|
#config;
|
|
230
237
|
constructor(config = {}) {
|
|
238
|
+
assertSimbaConfig(config);
|
|
231
239
|
this.#config = config;
|
|
232
240
|
}
|
|
233
|
-
optimize(task) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
241
|
+
async optimize(task) {
|
|
242
|
+
try {
|
|
243
|
+
return await run({
|
|
244
|
+
config: this.#config,
|
|
245
|
+
task
|
|
246
|
+
});
|
|
247
|
+
} finally {
|
|
248
|
+
await flushReporters(task.reporters ?? []);
|
|
249
|
+
}
|
|
238
250
|
}
|
|
239
251
|
};
|
|
240
252
|
async function run(args) {
|
|
241
253
|
const { config, task } = args;
|
|
242
|
-
const { minibatchSize = DEFAULT_MINIBATCH_SIZE, candidates: candidateCount = DEFAULT_CANDIDATES, maxSteps = DEFAULT_MAX_STEPS, maxDemos = DEFAULT_MAX_DEMOS, samplingTemperature = DEFAULT_TEMPERATURE, candidateTemperature = DEFAULT_TEMPERATURE, strategies, maxReflectionCalls, seed = 0, trackBestOutputs = false, checkpointCache = true } = config;
|
|
243
|
-
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, demoComponents = [], instructionComponents, renderDemo, buildAdvicePrompt: buildPrompt = buildAdvicePrompt, sampler = createEpochShuffledSampler({ minibatchSize }), maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId,
|
|
254
|
+
const { minibatchSize = DEFAULT_MINIBATCH_SIZE, candidates: candidateCount = DEFAULT_CANDIDATES, concurrency = 1, maxSteps = DEFAULT_MAX_STEPS, maxDemos = DEFAULT_MAX_DEMOS, samplingTemperature = DEFAULT_TEMPERATURE, candidateTemperature = DEFAULT_TEMPERATURE, strategies, maxReflectionCalls, seed = 0, trackBestOutputs = false, checkpointCache = true } = config;
|
|
255
|
+
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, demoComponents = [], instructionComponents, renderDemo, buildAdvicePrompt: buildPrompt = buildAdvicePrompt, sampler = createEpochShuffledSampler({ minibatchSize }), maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
256
|
+
const emit = createEmitter(reporters);
|
|
244
257
|
const components = componentNames(seedCandidate);
|
|
245
|
-
const
|
|
258
|
+
const nonDemo = components.filter((name) => !demoComponents.includes(name));
|
|
259
|
+
const ruleComponents = instructionComponents ?? (nonDemo.length > 0 ? nonDemo : components);
|
|
246
260
|
const enabled = strategies ?? (demoComponents.length > 0 ? ["appendDemo", "appendRule"] : ["appendRule"]);
|
|
247
261
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
248
262
|
if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
|
|
@@ -251,6 +265,10 @@ async function run(args) {
|
|
|
251
265
|
if (enabled.includes("appendDemo") && demoComponents.length === 0) throw new Error("the appendDemo strategy requires at least one entry in demoComponents");
|
|
252
266
|
if (enabled.includes("appendRule") && ruleComponents.length === 0) throw new Error("the appendRule strategy requires at least one instruction component to write into");
|
|
253
267
|
if (testSet !== void 0 && testSet.length === 0) throw new Error("optimize requires a non-empty testSet when one is given; omit it to skip held-out evaluation");
|
|
268
|
+
const trainingIds = trainingSet.map((datum, index) => instanceId({
|
|
269
|
+
datum,
|
|
270
|
+
index
|
|
271
|
+
}));
|
|
254
272
|
const validationIds = validationSet.map((datum, index) => instanceId({
|
|
255
273
|
datum,
|
|
256
274
|
index
|
|
@@ -261,10 +279,7 @@ async function run(args) {
|
|
|
261
279
|
})) ?? [];
|
|
262
280
|
const fingerprint = runFingerprint({
|
|
263
281
|
seedCandidate,
|
|
264
|
-
trainingIds
|
|
265
|
-
datum,
|
|
266
|
-
index
|
|
267
|
-
})),
|
|
282
|
+
trainingIds,
|
|
268
283
|
validationIds,
|
|
269
284
|
seed,
|
|
270
285
|
...cacheNamespace === void 0 ? {} : { cacheNamespace }
|
|
@@ -288,8 +303,9 @@ async function run(args) {
|
|
|
288
303
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
289
304
|
trackOutputs: true,
|
|
290
305
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
306
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
291
307
|
...signal === void 0 ? {} : { signal },
|
|
292
|
-
onEvaluation: (event) =>
|
|
308
|
+
onEvaluation: (event) => emit({
|
|
293
309
|
type: "evaluation",
|
|
294
310
|
...event
|
|
295
311
|
})
|
|
@@ -305,7 +321,7 @@ async function run(args) {
|
|
|
305
321
|
let step = resumeFrom?.step ?? 0;
|
|
306
322
|
let reflectionCalls = resumeFrom?.reflectionCalls ?? 0;
|
|
307
323
|
let stopReason = "maxSteps";
|
|
308
|
-
|
|
324
|
+
emit({
|
|
309
325
|
type: "start",
|
|
310
326
|
components,
|
|
311
327
|
validationSetSize: validationSet.length
|
|
@@ -340,16 +356,17 @@ async function run(args) {
|
|
|
340
356
|
scores: programs.map((_, index) => averageScore(index)),
|
|
341
357
|
k: candidateCount
|
|
342
358
|
});
|
|
343
|
-
|
|
359
|
+
emit({
|
|
344
360
|
type: "stepStart",
|
|
345
361
|
step,
|
|
346
362
|
poolSize: pool.length
|
|
347
363
|
});
|
|
348
|
-
const
|
|
364
|
+
const batchIndices = sampler({
|
|
349
365
|
trainingSet,
|
|
350
366
|
iteration: step,
|
|
351
367
|
rng
|
|
352
|
-
})
|
|
368
|
+
});
|
|
369
|
+
const batch = batchIndices.map((index) => trainingSet[index]);
|
|
353
370
|
const samples = [];
|
|
354
371
|
for (let slot = 0; slot < candidateCount; slot += 1) {
|
|
355
372
|
const programIndex = softmaxSample({
|
|
@@ -411,7 +428,7 @@ async function run(args) {
|
|
|
411
428
|
high
|
|
412
429
|
});
|
|
413
430
|
} catch (err) {
|
|
414
|
-
|
|
431
|
+
emit({
|
|
415
432
|
type: "error",
|
|
416
433
|
step,
|
|
417
434
|
err
|
|
@@ -426,36 +443,54 @@ async function run(args) {
|
|
|
426
443
|
});
|
|
427
444
|
}
|
|
428
445
|
let stepBest;
|
|
446
|
+
const batchIds = batchIndices.map((index) => trainingIds[index]);
|
|
447
|
+
const scheduled = [];
|
|
448
|
+
let owed = 0;
|
|
429
449
|
for (const entry of built) {
|
|
430
|
-
if (!budget.canAfford(batch.length)) {
|
|
450
|
+
if (!budget.canAfford(owed + batch.length)) {
|
|
431
451
|
stopReason = "budgetExhausted";
|
|
432
452
|
break;
|
|
433
453
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
454
|
+
owed += evaluator.countUncached({
|
|
455
|
+
candidate: entry.candidate,
|
|
456
|
+
ids: batchIds,
|
|
457
|
+
split: "train"
|
|
458
|
+
});
|
|
459
|
+
scheduled.push(entry);
|
|
460
|
+
}
|
|
461
|
+
const poolBase = programs.length;
|
|
462
|
+
const scored = await mapDistinct({
|
|
463
|
+
items: scheduled,
|
|
464
|
+
limit: concurrency,
|
|
465
|
+
key: (entry) => candidateHash(entry.candidate),
|
|
466
|
+
task: async (entry, index) => {
|
|
467
|
+
try {
|
|
468
|
+
return { score: measuredMean(await evaluator.evaluate({
|
|
469
|
+
candidate: entry.candidate,
|
|
470
|
+
batch,
|
|
471
|
+
ids: batchIds,
|
|
472
|
+
split: "train",
|
|
473
|
+
phase: "minibatch",
|
|
474
|
+
candidateId: poolBase + index,
|
|
475
|
+
iteration: step
|
|
476
|
+
})) };
|
|
477
|
+
} catch (err) {
|
|
478
|
+
if (err instanceof BudgetExhausted) return { stop: true };
|
|
479
|
+
throw err;
|
|
452
480
|
}
|
|
453
|
-
throw err;
|
|
454
481
|
}
|
|
482
|
+
});
|
|
483
|
+
for (const [index, outcome] of scored.entries()) {
|
|
484
|
+
if (outcome.stop === true) {
|
|
485
|
+
stopReason = "budgetExhausted";
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
const { score } = outcome;
|
|
455
489
|
if (score === void 0) continue;
|
|
490
|
+
const entry = scheduled[index];
|
|
456
491
|
programs.push(entry.candidate);
|
|
457
492
|
programScores.push([score]);
|
|
458
|
-
|
|
493
|
+
emit({
|
|
459
494
|
type: "candidate",
|
|
460
495
|
step,
|
|
461
496
|
strategy: entry.strategy,
|
|
@@ -478,38 +513,78 @@ async function run(args) {
|
|
|
478
513
|
}
|
|
479
514
|
}
|
|
480
515
|
if (signal?.aborted) stopReason = "aborted";
|
|
481
|
-
const
|
|
516
|
+
const contenders = [];
|
|
517
|
+
let owedForFinalists = 0;
|
|
482
518
|
for (const index of evenlySpacedIndices({
|
|
483
519
|
length: winners.length,
|
|
484
520
|
count: candidateCount + 1
|
|
485
521
|
})) {
|
|
486
522
|
const winner = winners[index];
|
|
487
|
-
|
|
523
|
+
const uncached = evaluator.countUncached({
|
|
488
524
|
candidate: winner.candidate,
|
|
489
525
|
ids: validationIds,
|
|
490
526
|
split: "val"
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
batch: validationSet,
|
|
497
|
-
ids: validationIds,
|
|
498
|
-
split: "val",
|
|
499
|
-
phase: index === 0 ? "seed" : "validation",
|
|
500
|
-
candidateId: index,
|
|
501
|
-
iteration: step
|
|
502
|
-
}));
|
|
503
|
-
} catch (err) {
|
|
504
|
-
if (err instanceof BudgetExhausted || signal?.aborted) break;
|
|
505
|
-
throw err;
|
|
506
|
-
}
|
|
507
|
-
if (score !== void 0) finalists.push({
|
|
527
|
+
});
|
|
528
|
+
if (!budget.canAfford(owedForFinalists + uncached)) break;
|
|
529
|
+
owedForFinalists += uncached;
|
|
530
|
+
contenders.push({
|
|
531
|
+
index,
|
|
508
532
|
candidate: winner.candidate,
|
|
509
|
-
score,
|
|
510
533
|
step: winner.step
|
|
511
534
|
});
|
|
512
535
|
}
|
|
536
|
+
const sweeps = await mapDistinct({
|
|
537
|
+
items: contenders,
|
|
538
|
+
limit: concurrency,
|
|
539
|
+
key: (contender) => candidateHash(contender.candidate),
|
|
540
|
+
task: async (contender) => {
|
|
541
|
+
if (signal?.aborted) return { stop: true };
|
|
542
|
+
try {
|
|
543
|
+
const evaluation = await evaluator.evaluate({
|
|
544
|
+
candidate: contender.candidate,
|
|
545
|
+
batch: validationSet,
|
|
546
|
+
ids: validationIds,
|
|
547
|
+
split: "val",
|
|
548
|
+
phase: contender.index === 0 ? "seed" : "validation",
|
|
549
|
+
candidateId: contender.index,
|
|
550
|
+
iteration: step
|
|
551
|
+
});
|
|
552
|
+
return {
|
|
553
|
+
score: measuredMean(evaluation),
|
|
554
|
+
evaluation
|
|
555
|
+
};
|
|
556
|
+
} catch (err) {
|
|
557
|
+
if (err instanceof BudgetExhausted || signal?.aborted) return { stop: true };
|
|
558
|
+
throw err;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
const finalists = [];
|
|
563
|
+
let acceptedCandidates = -1;
|
|
564
|
+
let acceptedScore = Number.NEGATIVE_INFINITY;
|
|
565
|
+
for (const [position, outcome] of sweeps.entries()) {
|
|
566
|
+
if (outcome.stop === true) break;
|
|
567
|
+
if (outcome.score === void 0) continue;
|
|
568
|
+
const contender = contenders[position];
|
|
569
|
+
finalists.push({
|
|
570
|
+
candidate: contender.candidate,
|
|
571
|
+
score: outcome.score,
|
|
572
|
+
step: contender.step
|
|
573
|
+
});
|
|
574
|
+
if (outcome.score > acceptedScore) {
|
|
575
|
+
acceptedScore = outcome.score;
|
|
576
|
+
acceptedCandidates += 1;
|
|
577
|
+
emit({
|
|
578
|
+
type: "candidateAccepted",
|
|
579
|
+
step: contender.step,
|
|
580
|
+
candidateId: acceptedCandidates,
|
|
581
|
+
candidate: contender.candidate,
|
|
582
|
+
aggregateScore: outcome.score,
|
|
583
|
+
instanceScores: instanceRow(outcome.evaluation),
|
|
584
|
+
...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
513
588
|
const seedScore = finalists[0]?.score ?? 0;
|
|
514
589
|
finalists.sort((a, b) => b.score - a.score);
|
|
515
590
|
const best = finalists[0] ?? {
|
|
@@ -525,7 +600,7 @@ async function run(args) {
|
|
|
525
600
|
candidateId: null,
|
|
526
601
|
iteration: step
|
|
527
602
|
}))?.outputs;
|
|
528
|
-
const
|
|
603
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
529
604
|
candidate: best.candidate,
|
|
530
605
|
batch: testSet,
|
|
531
606
|
ids: testIds,
|
|
@@ -534,13 +609,17 @@ async function run(args) {
|
|
|
534
609
|
candidateId: null,
|
|
535
610
|
iteration: step,
|
|
536
611
|
charge: false
|
|
537
|
-
})
|
|
538
|
-
|
|
612
|
+
});
|
|
613
|
+
const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
|
|
614
|
+
emit({
|
|
539
615
|
type: "finish",
|
|
540
616
|
reason: stopReason,
|
|
617
|
+
bestCandidateId: acceptedCandidates,
|
|
541
618
|
bestScore: best.score,
|
|
542
619
|
metricCalls: budget.spent(),
|
|
543
|
-
...testScore === void 0 ? {} : { testScore }
|
|
620
|
+
...testScore === void 0 ? {} : { testScore },
|
|
621
|
+
...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
|
|
622
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
544
623
|
});
|
|
545
624
|
return {
|
|
546
625
|
bestCandidate: best.candidate,
|
|
@@ -556,7 +635,8 @@ async function run(args) {
|
|
|
556
635
|
...bestOutputs === void 0 ? {} : { bestOutputs },
|
|
557
636
|
...testScore === void 0 ? {} : {
|
|
558
637
|
testScore,
|
|
559
|
-
testMetricCalls:
|
|
638
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
639
|
+
testUsage: evaluator.unchargedUsage()
|
|
560
640
|
},
|
|
561
641
|
stopReason
|
|
562
642
|
};
|
|
@@ -593,7 +673,11 @@ async function run(args) {
|
|
|
593
673
|
const drops = /* @__PURE__ */ new Set();
|
|
594
674
|
for (let draw = 0; draw < Math.min(wanted, held); draw += 1) drops.add(rng.nextInt(held));
|
|
595
675
|
const next = { ...candidate };
|
|
596
|
-
for (const [name, demos] of blocks) next[name] =
|
|
676
|
+
for (const [name, demos] of blocks) next[name] = replaceDemos({
|
|
677
|
+
text: next[name] ?? "",
|
|
678
|
+
demos: demos.filter((_, index) => !drops.has(index)),
|
|
679
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
680
|
+
});
|
|
597
681
|
return next;
|
|
598
682
|
}
|
|
599
683
|
/**
|
|
@@ -614,8 +698,12 @@ async function run(args) {
|
|
|
614
698
|
};
|
|
615
699
|
const next = { ...candidate };
|
|
616
700
|
for (const name of demoComponents) {
|
|
617
|
-
const
|
|
618
|
-
next[name] =
|
|
701
|
+
const text = next[name] ?? "";
|
|
702
|
+
next[name] = replaceDemos({
|
|
703
|
+
text,
|
|
704
|
+
demos: [...parseDemos(text), demo],
|
|
705
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
706
|
+
});
|
|
619
707
|
}
|
|
620
708
|
return next;
|
|
621
709
|
}
|
|
@@ -643,6 +731,7 @@ async function run(args) {
|
|
|
643
731
|
const response = await reflect({
|
|
644
732
|
prompt: buildPrompt({
|
|
645
733
|
components: ruleComponents,
|
|
734
|
+
current: Object.fromEntries(ruleComponents.map((name) => [name, candidate[name] ?? ""])),
|
|
646
735
|
input: bucket.datum,
|
|
647
736
|
...better === void 0 ? {} : { better },
|
|
648
737
|
...worse === void 0 ? {} : { worse }
|
|
@@ -683,6 +772,7 @@ async function run(args) {
|
|
|
683
772
|
metricCalls: budget.spent(),
|
|
684
773
|
reflectionCalls,
|
|
685
774
|
cacheHits: evaluator.cacheHits(),
|
|
775
|
+
usage: evaluator.usage(),
|
|
686
776
|
rngState: rng.state(),
|
|
687
777
|
...sampler.state === void 0 ? {} : { sampler: sampler.state() },
|
|
688
778
|
...cached === void 0 ? {} : { cache: cached }
|
|
@@ -693,8 +783,43 @@ async function run(args) {
|
|
|
693
783
|
await onCheckpoint(takeSnapshot(completed));
|
|
694
784
|
}
|
|
695
785
|
}
|
|
696
|
-
|
|
697
|
-
|
|
786
|
+
/**
|
|
787
|
+
* Runs `items` concurrently, except that items sharing a key run one after the
|
|
788
|
+
* other.
|
|
789
|
+
*
|
|
790
|
+
* Two identical candidates cost one evaluation and one cache hit when they are
|
|
791
|
+
* scored in sequence, and two evaluations when they overlap: the cache is only
|
|
792
|
+
* written once a rollout returns. Serializing the duplicates is what keeps a
|
|
793
|
+
* fan-out from buying a second copy of a score the run has already paid for.
|
|
794
|
+
*/
|
|
795
|
+
async function mapDistinct(args) {
|
|
796
|
+
const { items, limit, key, task } = args;
|
|
797
|
+
const groups = /* @__PURE__ */ new Map();
|
|
798
|
+
items.forEach((item, index) => {
|
|
799
|
+
const group = groups.get(key(item));
|
|
800
|
+
if (group === void 0) {
|
|
801
|
+
groups.set(key(item), [index]);
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
group.push(index);
|
|
805
|
+
});
|
|
806
|
+
const results = new Array(items.length);
|
|
807
|
+
await mapWithConcurrency({
|
|
808
|
+
items: [...groups.values()],
|
|
809
|
+
limit,
|
|
810
|
+
task: async (indices) => {
|
|
811
|
+
for (const index of indices) results[index] = await task(items[index], index);
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
return results;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Range checks on the search knobs, run at construction so a configuration
|
|
818
|
+
* that could never terminate is refused before a task is ever handed to it.
|
|
819
|
+
*/
|
|
820
|
+
function assertSimbaConfig(config) {
|
|
821
|
+
const { concurrency = 1 } = config;
|
|
822
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
698
823
|
}
|
|
699
824
|
//#endregion
|
|
700
|
-
export { SimbaOptimizer, buildAdvicePrompt, buildBuckets, evenlySpacedIndices, parseAdvice, percentile, samplePoisson, softmaxWeights, topKPlusBaseline };
|
|
825
|
+
export { SimbaOptimizer, buildAdvicePrompt, buildBuckets, evenlySpacedIndices, isCandidateAccepted, isRunFinished, parseAdvice, percentile, samplePoisson, softmaxWeights, topKPlusBaseline };
|
package/dist/testing.cjs
CHANGED
|
@@ -147,6 +147,7 @@ function extractMissingTerms(prompt) {
|
|
|
147
147
|
}
|
|
148
148
|
//#endregion
|
|
149
149
|
exports.KEYWORD_EXAMPLES = KEYWORD_EXAMPLES;
|
|
150
|
+
exports.SAMPLING_POOL = SAMPLING_POOL;
|
|
150
151
|
exports.buildReflectionPrompt = require_reflection.buildReflectionPrompt;
|
|
151
152
|
exports.createDegradingReflector = createDegradingReflector;
|
|
152
153
|
exports.createHillClimbingReflector = createHillClimbingReflector;
|
package/dist/testing.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-
|
|
1
|
+
import { _ as TextModel } from "./reporting-DQbAohc9.cjs";
|
|
2
|
+
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-Dt3QrXhM.cjs";
|
|
3
3
|
//#region src/testing.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A deterministic, LLM-free system under optimization.
|
|
@@ -20,6 +20,8 @@ interface KeywordTrajectory {
|
|
|
20
20
|
missing: string[];
|
|
21
21
|
}
|
|
22
22
|
declare const KEYWORD_EXAMPLES: KeywordExample[];
|
|
23
|
+
/** Terms the keyword metric rewards, interleaved with terms it ignores. */
|
|
24
|
+
declare const SAMPLING_POOL: string[];
|
|
23
25
|
declare function createKeywordAdapter(): GepaAdapter<KeywordExample, KeywordTrajectory, string>;
|
|
24
26
|
/**
|
|
25
27
|
* A deterministic stand-in for a reflection model: it reads the feedback in the
|
|
@@ -50,4 +52,4 @@ declare function createHillClimbingReflector(args?: {
|
|
|
50
52
|
/** A reflection model that always proposes something strictly worse. */
|
|
51
53
|
declare function createDegradingReflector(): TextModel;
|
|
52
54
|
//#endregion
|
|
53
|
-
export { KEYWORD_EXAMPLES, KeywordExample, KeywordTrajectory, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
|
55
|
+
export { KEYWORD_EXAMPLES, KeywordExample, KeywordTrajectory, SAMPLING_POOL, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-
|
|
1
|
+
import { _ as TextModel } from "./reporting-DQbAohc9.mjs";
|
|
2
|
+
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-LRaAZP4e.mjs";
|
|
3
3
|
//#region src/testing.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A deterministic, LLM-free system under optimization.
|
|
@@ -20,6 +20,8 @@ interface KeywordTrajectory {
|
|
|
20
20
|
missing: string[];
|
|
21
21
|
}
|
|
22
22
|
declare const KEYWORD_EXAMPLES: KeywordExample[];
|
|
23
|
+
/** Terms the keyword metric rewards, interleaved with terms it ignores. */
|
|
24
|
+
declare const SAMPLING_POOL: string[];
|
|
23
25
|
declare function createKeywordAdapter(): GepaAdapter<KeywordExample, KeywordTrajectory, string>;
|
|
24
26
|
/**
|
|
25
27
|
* A deterministic stand-in for a reflection model: it reads the feedback in the
|
|
@@ -50,4 +52,4 @@ declare function createHillClimbingReflector(args?: {
|
|
|
50
52
|
/** A reflection model that always proposes something strictly worse. */
|
|
51
53
|
declare function createDegradingReflector(): TextModel;
|
|
52
54
|
//#endregion
|
|
53
|
-
export { KEYWORD_EXAMPLES, KeywordExample, KeywordTrajectory, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
|
55
|
+
export { KEYWORD_EXAMPLES, KeywordExample, KeywordTrajectory, SAMPLING_POOL, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
package/dist/testing.mjs
CHANGED
|
@@ -145,4 +145,4 @@ function extractMissingTerms(prompt) {
|
|
|
145
145
|
return [...terms];
|
|
146
146
|
}
|
|
147
147
|
//#endregion
|
|
148
|
-
export { KEYWORD_EXAMPLES, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
|
148
|
+
export { KEYWORD_EXAMPLES, SAMPLING_POOL, buildReflectionPrompt, createDegradingReflector, createHillClimbingReflector, createKeywordAdapter, createKeywordReflector, createSamplingReflector };
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "textopt",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Prompt optimization for TypeScript, with GEPA, OPRO, MIPRO, and random search behind a shared interface",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Prompt optimization for TypeScript, with GEPA, SIMBA, OPRO, MIPRO, bootstrapped few-shot search, and random search behind a shared interface",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"prompt-optimization",
|
|
7
7
|
"gepa",
|
|
8
|
+
"simba",
|
|
8
9
|
"mipro",
|
|
9
10
|
"opro",
|
|
10
11
|
"llm",
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
"type": "module",
|
|
26
27
|
"sideEffects": false,
|
|
27
28
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
29
|
+
"node": ">=22"
|
|
29
30
|
},
|
|
30
31
|
"publishConfig": {
|
|
31
32
|
"access": "public"
|