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/simba/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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
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-ASsSXYXA.mjs";
|
|
5
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-Ds5mzYjz.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>;
|
|
@@ -52,11 +58,41 @@ interface SimbaFinalist<K extends string = string> {
|
|
|
52
58
|
step: number;
|
|
53
59
|
}
|
|
54
60
|
interface SimbaConfig {
|
|
55
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Instances per step. Default 32.
|
|
63
|
+
*
|
|
64
|
+
* Minibatch defaults do not transfer between optimizers — GEPA's 3 and
|
|
65
|
+
* MIPRO's 35 mean different things, and SIMBA's ranks instances within a
|
|
66
|
+
* batch by how much its programs disagreed.
|
|
67
|
+
*
|
|
68
|
+
* @see docs/tuning.md
|
|
69
|
+
*/
|
|
56
70
|
minibatchSize?: number;
|
|
57
71
|
/** Programs sampled per step, and candidates built from them. Default 6. */
|
|
58
72
|
candidates?: number;
|
|
59
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* How many evaluations may be in flight at once. Default 1.
|
|
75
|
+
*
|
|
76
|
+
* Covers the two places a step's work is independent — scoring the
|
|
77
|
+
* candidates a step built, and sweeping the finalists at the end of the run
|
|
78
|
+
* — and nothing else. The trajectory samples and the mutations that read
|
|
79
|
+
* them are deliberately left in sequence: each of those reads state the one
|
|
80
|
+
* before it wrote, so overlapping them would make a seeded run depend on
|
|
81
|
+
* which call returned first.
|
|
82
|
+
*/
|
|
83
|
+
concurrency?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Steps to run. Default 8.
|
|
86
|
+
*
|
|
87
|
+
* A run has to be funded past the finalist reserve before any step happens:
|
|
88
|
+
* `min(candidates + 1, maxSteps + 1) * validationSet.length` rollouts are
|
|
89
|
+
* held back to sweep the finalists, and each step then costs
|
|
90
|
+
* `candidates * minibatchSize + minibatchSize`. Fund only the steps and the
|
|
91
|
+
* run stops after one, reporting `budgetExhausted` like any other exhausted
|
|
92
|
+
* budget.
|
|
93
|
+
*
|
|
94
|
+
* @see docs/tuning.md
|
|
95
|
+
*/
|
|
60
96
|
maxSteps?: number;
|
|
61
97
|
/** Demos a candidate may hold before the loop starts dropping them. Default 4. */
|
|
62
98
|
maxDemos?: number;
|
|
@@ -97,6 +133,8 @@ interface SimbaSnapshot {
|
|
|
97
133
|
metricCalls: number;
|
|
98
134
|
reflectionCalls: number;
|
|
99
135
|
cacheHits: number;
|
|
136
|
+
/** Usage already spent, so a resumed run reports totals and honours ceilings. */
|
|
137
|
+
usage?: UsageTotals;
|
|
100
138
|
rngState: number;
|
|
101
139
|
sampler?: unknown;
|
|
102
140
|
cache?: [string, CachedScore][];
|
|
@@ -121,7 +159,8 @@ interface SimbaTask<Datum, Trajectory = unknown, Output = unknown, K extends str
|
|
|
121
159
|
index: number;
|
|
122
160
|
}) => string;
|
|
123
161
|
cache?: EvaluationCache | false;
|
|
124
|
-
|
|
162
|
+
/** Observers of the run. Every one sees every event; none can fail it. */
|
|
163
|
+
reporters?: readonly Reporter<SimbaEvent<NoInfer<K>>>[];
|
|
125
164
|
onCheckpoint?: (snapshot: SimbaSnapshot) => void | Promise<void>;
|
|
126
165
|
resumeFrom?: SimbaSnapshot;
|
|
127
166
|
}
|
|
@@ -143,17 +182,18 @@ type SimbaEvent<K extends string = string> = {
|
|
|
143
182
|
/** The program it was mutated from; 0 is the seed. */
|
|
144
183
|
sourceProgram: number;
|
|
145
184
|
minibatchScore: number;
|
|
146
|
-
} | {
|
|
185
|
+
} | ({
|
|
186
|
+
type: "candidateAccepted";
|
|
187
|
+
/** The step whose winner this was. */
|
|
188
|
+
step: number;
|
|
189
|
+
} & CandidateAccepted<K>) | {
|
|
147
190
|
type: "error";
|
|
148
191
|
step: number;
|
|
149
192
|
err: unknown;
|
|
150
|
-
} | {
|
|
193
|
+
} | ({
|
|
151
194
|
type: "finish";
|
|
152
195
|
reason: SimbaStopReason;
|
|
153
|
-
|
|
154
|
-
metricCalls: number;
|
|
155
|
-
testScore?: number;
|
|
156
|
-
};
|
|
196
|
+
} & RunFinished);
|
|
157
197
|
interface SimbaResult<K extends string = string, Output = unknown> extends OptimizerResult<K, SimbaStopReason, Output> {
|
|
158
198
|
/** The seed's own validation score, so the lift the run bought is readable. */
|
|
159
199
|
seedScore: number;
|
|
@@ -286,4 +326,4 @@ declare function evenlySpacedIndices(args: {
|
|
|
286
326
|
count: number;
|
|
287
327
|
}): number[];
|
|
288
328
|
//#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 };
|
|
329
|
+
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 { S as createBudget, _ as componentNames, a as instanceRow, b as defaultInstanceId, c as BudgetExhausted, d as measuredMean, 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, v as candidateHash, y as createMemoryCache } from "../warnings-OxvDi9kN.mjs";
|
|
2
|
+
import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
|
|
3
|
+
import { i as replaceDemos, r as parseDemos } from "../demos-Brobjfuc.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,32 @@ 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
|
|
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: requestedValidationSet, 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 { validationSet, warnings } = resolveValidationSet({
|
|
257
|
+
validationSet: requestedValidationSet,
|
|
258
|
+
trainingSet
|
|
259
|
+
});
|
|
260
|
+
const emit = createEmitter(reporters);
|
|
244
261
|
const components = componentNames(seedCandidate);
|
|
245
|
-
const
|
|
262
|
+
const nonDemo = components.filter((name) => !demoComponents.includes(name));
|
|
263
|
+
const ruleComponents = instructionComponents ?? (nonDemo.length > 0 ? nonDemo : components);
|
|
246
264
|
const enabled = strategies ?? (demoComponents.length > 0 ? ["appendDemo", "appendRule"] : ["appendRule"]);
|
|
247
265
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
248
266
|
if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
|
|
@@ -251,6 +269,10 @@ async function run(args) {
|
|
|
251
269
|
if (enabled.includes("appendDemo") && demoComponents.length === 0) throw new Error("the appendDemo strategy requires at least one entry in demoComponents");
|
|
252
270
|
if (enabled.includes("appendRule") && ruleComponents.length === 0) throw new Error("the appendRule strategy requires at least one instruction component to write into");
|
|
253
271
|
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");
|
|
272
|
+
const trainingIds = trainingSet.map((datum, index) => instanceId({
|
|
273
|
+
datum,
|
|
274
|
+
index
|
|
275
|
+
}));
|
|
254
276
|
const validationIds = validationSet.map((datum, index) => instanceId({
|
|
255
277
|
datum,
|
|
256
278
|
index
|
|
@@ -261,10 +283,7 @@ async function run(args) {
|
|
|
261
283
|
})) ?? [];
|
|
262
284
|
const fingerprint = runFingerprint({
|
|
263
285
|
seedCandidate,
|
|
264
|
-
trainingIds
|
|
265
|
-
datum,
|
|
266
|
-
index
|
|
267
|
-
})),
|
|
286
|
+
trainingIds,
|
|
268
287
|
validationIds,
|
|
269
288
|
seed,
|
|
270
289
|
...cacheNamespace === void 0 ? {} : { cacheNamespace }
|
|
@@ -288,8 +307,9 @@ async function run(args) {
|
|
|
288
307
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
289
308
|
trackOutputs: true,
|
|
290
309
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
310
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
291
311
|
...signal === void 0 ? {} : { signal },
|
|
292
|
-
onEvaluation: (event) =>
|
|
312
|
+
onEvaluation: (event) => emit({
|
|
293
313
|
type: "evaluation",
|
|
294
314
|
...event
|
|
295
315
|
})
|
|
@@ -305,7 +325,7 @@ async function run(args) {
|
|
|
305
325
|
let step = resumeFrom?.step ?? 0;
|
|
306
326
|
let reflectionCalls = resumeFrom?.reflectionCalls ?? 0;
|
|
307
327
|
let stopReason = "maxSteps";
|
|
308
|
-
|
|
328
|
+
emit({
|
|
309
329
|
type: "start",
|
|
310
330
|
components,
|
|
311
331
|
validationSetSize: validationSet.length
|
|
@@ -340,16 +360,17 @@ async function run(args) {
|
|
|
340
360
|
scores: programs.map((_, index) => averageScore(index)),
|
|
341
361
|
k: candidateCount
|
|
342
362
|
});
|
|
343
|
-
|
|
363
|
+
emit({
|
|
344
364
|
type: "stepStart",
|
|
345
365
|
step,
|
|
346
366
|
poolSize: pool.length
|
|
347
367
|
});
|
|
348
|
-
const
|
|
368
|
+
const batchIndices = sampler({
|
|
349
369
|
trainingSet,
|
|
350
370
|
iteration: step,
|
|
351
371
|
rng
|
|
352
|
-
})
|
|
372
|
+
});
|
|
373
|
+
const batch = batchIndices.map((index) => trainingSet[index]);
|
|
353
374
|
const samples = [];
|
|
354
375
|
for (let slot = 0; slot < candidateCount; slot += 1) {
|
|
355
376
|
const programIndex = softmaxSample({
|
|
@@ -411,7 +432,7 @@ async function run(args) {
|
|
|
411
432
|
high
|
|
412
433
|
});
|
|
413
434
|
} catch (err) {
|
|
414
|
-
|
|
435
|
+
emit({
|
|
415
436
|
type: "error",
|
|
416
437
|
step,
|
|
417
438
|
err
|
|
@@ -426,36 +447,54 @@ async function run(args) {
|
|
|
426
447
|
});
|
|
427
448
|
}
|
|
428
449
|
let stepBest;
|
|
450
|
+
const batchIds = batchIndices.map((index) => trainingIds[index]);
|
|
451
|
+
const scheduled = [];
|
|
452
|
+
let owed = 0;
|
|
429
453
|
for (const entry of built) {
|
|
430
|
-
if (!budget.canAfford(batch.length)) {
|
|
454
|
+
if (!budget.canAfford(owed + batch.length)) {
|
|
431
455
|
stopReason = "budgetExhausted";
|
|
432
456
|
break;
|
|
433
457
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
458
|
+
owed += evaluator.countUncached({
|
|
459
|
+
candidate: entry.candidate,
|
|
460
|
+
ids: batchIds,
|
|
461
|
+
split: "train"
|
|
462
|
+
});
|
|
463
|
+
scheduled.push(entry);
|
|
464
|
+
}
|
|
465
|
+
const poolBase = programs.length;
|
|
466
|
+
const scored = await mapDistinct({
|
|
467
|
+
items: scheduled,
|
|
468
|
+
limit: concurrency,
|
|
469
|
+
key: (entry) => candidateHash(entry.candidate),
|
|
470
|
+
task: async (entry, index) => {
|
|
471
|
+
try {
|
|
472
|
+
return { score: measuredMean(await evaluator.evaluate({
|
|
473
|
+
candidate: entry.candidate,
|
|
474
|
+
batch,
|
|
475
|
+
ids: batchIds,
|
|
476
|
+
split: "train",
|
|
477
|
+
phase: "minibatch",
|
|
478
|
+
candidateId: poolBase + index,
|
|
479
|
+
iteration: step
|
|
480
|
+
})) };
|
|
481
|
+
} catch (err) {
|
|
482
|
+
if (err instanceof BudgetExhausted) return { stop: true };
|
|
483
|
+
throw err;
|
|
452
484
|
}
|
|
453
|
-
throw err;
|
|
454
485
|
}
|
|
486
|
+
});
|
|
487
|
+
for (const [index, outcome] of scored.entries()) {
|
|
488
|
+
if (outcome.stop === true) {
|
|
489
|
+
stopReason = "budgetExhausted";
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
const { score } = outcome;
|
|
455
493
|
if (score === void 0) continue;
|
|
494
|
+
const entry = scheduled[index];
|
|
456
495
|
programs.push(entry.candidate);
|
|
457
496
|
programScores.push([score]);
|
|
458
|
-
|
|
497
|
+
emit({
|
|
459
498
|
type: "candidate",
|
|
460
499
|
step,
|
|
461
500
|
strategy: entry.strategy,
|
|
@@ -478,38 +517,78 @@ async function run(args) {
|
|
|
478
517
|
}
|
|
479
518
|
}
|
|
480
519
|
if (signal?.aborted) stopReason = "aborted";
|
|
481
|
-
const
|
|
520
|
+
const contenders = [];
|
|
521
|
+
let owedForFinalists = 0;
|
|
482
522
|
for (const index of evenlySpacedIndices({
|
|
483
523
|
length: winners.length,
|
|
484
524
|
count: candidateCount + 1
|
|
485
525
|
})) {
|
|
486
526
|
const winner = winners[index];
|
|
487
|
-
|
|
527
|
+
const uncached = evaluator.countUncached({
|
|
488
528
|
candidate: winner.candidate,
|
|
489
529
|
ids: validationIds,
|
|
490
530
|
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({
|
|
531
|
+
});
|
|
532
|
+
if (!budget.canAfford(owedForFinalists + uncached)) break;
|
|
533
|
+
owedForFinalists += uncached;
|
|
534
|
+
contenders.push({
|
|
535
|
+
index,
|
|
508
536
|
candidate: winner.candidate,
|
|
509
|
-
score,
|
|
510
537
|
step: winner.step
|
|
511
538
|
});
|
|
512
539
|
}
|
|
540
|
+
const sweeps = await mapDistinct({
|
|
541
|
+
items: contenders,
|
|
542
|
+
limit: concurrency,
|
|
543
|
+
key: (contender) => candidateHash(contender.candidate),
|
|
544
|
+
task: async (contender) => {
|
|
545
|
+
if (signal?.aborted) return { stop: true };
|
|
546
|
+
try {
|
|
547
|
+
const evaluation = await evaluator.evaluate({
|
|
548
|
+
candidate: contender.candidate,
|
|
549
|
+
batch: validationSet,
|
|
550
|
+
ids: validationIds,
|
|
551
|
+
split: "val",
|
|
552
|
+
phase: contender.index === 0 ? "seed" : "validation",
|
|
553
|
+
candidateId: contender.index,
|
|
554
|
+
iteration: step
|
|
555
|
+
});
|
|
556
|
+
return {
|
|
557
|
+
score: measuredMean(evaluation),
|
|
558
|
+
evaluation
|
|
559
|
+
};
|
|
560
|
+
} catch (err) {
|
|
561
|
+
if (err instanceof BudgetExhausted || signal?.aborted) return { stop: true };
|
|
562
|
+
throw err;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
const finalists = [];
|
|
567
|
+
let acceptedCandidates = -1;
|
|
568
|
+
let acceptedScore = Number.NEGATIVE_INFINITY;
|
|
569
|
+
for (const [position, outcome] of sweeps.entries()) {
|
|
570
|
+
if (outcome.stop === true) break;
|
|
571
|
+
if (outcome.score === void 0) continue;
|
|
572
|
+
const contender = contenders[position];
|
|
573
|
+
finalists.push({
|
|
574
|
+
candidate: contender.candidate,
|
|
575
|
+
score: outcome.score,
|
|
576
|
+
step: contender.step
|
|
577
|
+
});
|
|
578
|
+
if (outcome.score > acceptedScore) {
|
|
579
|
+
acceptedScore = outcome.score;
|
|
580
|
+
acceptedCandidates += 1;
|
|
581
|
+
emit({
|
|
582
|
+
type: "candidateAccepted",
|
|
583
|
+
step: contender.step,
|
|
584
|
+
candidateId: acceptedCandidates,
|
|
585
|
+
candidate: contender.candidate,
|
|
586
|
+
aggregateScore: outcome.score,
|
|
587
|
+
instanceScores: instanceRow(outcome.evaluation),
|
|
588
|
+
...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
513
592
|
const seedScore = finalists[0]?.score ?? 0;
|
|
514
593
|
finalists.sort((a, b) => b.score - a.score);
|
|
515
594
|
const best = finalists[0] ?? {
|
|
@@ -525,7 +604,7 @@ async function run(args) {
|
|
|
525
604
|
candidateId: null,
|
|
526
605
|
iteration: step
|
|
527
606
|
}))?.outputs;
|
|
528
|
-
const
|
|
607
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
529
608
|
candidate: best.candidate,
|
|
530
609
|
batch: testSet,
|
|
531
610
|
ids: testIds,
|
|
@@ -534,13 +613,18 @@ async function run(args) {
|
|
|
534
613
|
candidateId: null,
|
|
535
614
|
iteration: step,
|
|
536
615
|
charge: false
|
|
537
|
-
})
|
|
538
|
-
|
|
616
|
+
});
|
|
617
|
+
const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
|
|
618
|
+
emit({
|
|
539
619
|
type: "finish",
|
|
540
620
|
reason: stopReason,
|
|
621
|
+
warnings,
|
|
622
|
+
bestCandidateId: acceptedCandidates,
|
|
541
623
|
bestScore: best.score,
|
|
542
624
|
metricCalls: budget.spent(),
|
|
543
|
-
...testScore === void 0 ? {} : { testScore }
|
|
625
|
+
...testScore === void 0 ? {} : { testScore },
|
|
626
|
+
...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
|
|
627
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
544
628
|
});
|
|
545
629
|
return {
|
|
546
630
|
bestCandidate: best.candidate,
|
|
@@ -556,8 +640,10 @@ async function run(args) {
|
|
|
556
640
|
...bestOutputs === void 0 ? {} : { bestOutputs },
|
|
557
641
|
...testScore === void 0 ? {} : {
|
|
558
642
|
testScore,
|
|
559
|
-
testMetricCalls:
|
|
643
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
644
|
+
testUsage: evaluator.unchargedUsage()
|
|
560
645
|
},
|
|
646
|
+
warnings,
|
|
561
647
|
stopReason
|
|
562
648
|
};
|
|
563
649
|
/**
|
|
@@ -593,7 +679,11 @@ async function run(args) {
|
|
|
593
679
|
const drops = /* @__PURE__ */ new Set();
|
|
594
680
|
for (let draw = 0; draw < Math.min(wanted, held); draw += 1) drops.add(rng.nextInt(held));
|
|
595
681
|
const next = { ...candidate };
|
|
596
|
-
for (const [name, demos] of blocks) next[name] =
|
|
682
|
+
for (const [name, demos] of blocks) next[name] = replaceDemos({
|
|
683
|
+
text: next[name] ?? "",
|
|
684
|
+
demos: demos.filter((_, index) => !drops.has(index)),
|
|
685
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
686
|
+
});
|
|
597
687
|
return next;
|
|
598
688
|
}
|
|
599
689
|
/**
|
|
@@ -614,8 +704,12 @@ async function run(args) {
|
|
|
614
704
|
};
|
|
615
705
|
const next = { ...candidate };
|
|
616
706
|
for (const name of demoComponents) {
|
|
617
|
-
const
|
|
618
|
-
next[name] =
|
|
707
|
+
const text = next[name] ?? "";
|
|
708
|
+
next[name] = replaceDemos({
|
|
709
|
+
text,
|
|
710
|
+
demos: [...parseDemos(text), demo],
|
|
711
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
712
|
+
});
|
|
619
713
|
}
|
|
620
714
|
return next;
|
|
621
715
|
}
|
|
@@ -643,6 +737,7 @@ async function run(args) {
|
|
|
643
737
|
const response = await reflect({
|
|
644
738
|
prompt: buildPrompt({
|
|
645
739
|
components: ruleComponents,
|
|
740
|
+
current: Object.fromEntries(ruleComponents.map((name) => [name, candidate[name] ?? ""])),
|
|
646
741
|
input: bucket.datum,
|
|
647
742
|
...better === void 0 ? {} : { better },
|
|
648
743
|
...worse === void 0 ? {} : { worse }
|
|
@@ -683,6 +778,7 @@ async function run(args) {
|
|
|
683
778
|
metricCalls: budget.spent(),
|
|
684
779
|
reflectionCalls,
|
|
685
780
|
cacheHits: evaluator.cacheHits(),
|
|
781
|
+
usage: evaluator.usage(),
|
|
686
782
|
rngState: rng.state(),
|
|
687
783
|
...sampler.state === void 0 ? {} : { sampler: sampler.state() },
|
|
688
784
|
...cached === void 0 ? {} : { cache: cached }
|
|
@@ -693,8 +789,43 @@ async function run(args) {
|
|
|
693
789
|
await onCheckpoint(takeSnapshot(completed));
|
|
694
790
|
}
|
|
695
791
|
}
|
|
696
|
-
|
|
697
|
-
|
|
792
|
+
/**
|
|
793
|
+
* Runs `items` concurrently, except that items sharing a key run one after the
|
|
794
|
+
* other.
|
|
795
|
+
*
|
|
796
|
+
* Two identical candidates cost one evaluation and one cache hit when they are
|
|
797
|
+
* scored in sequence, and two evaluations when they overlap: the cache is only
|
|
798
|
+
* written once a rollout returns. Serializing the duplicates is what keeps a
|
|
799
|
+
* fan-out from buying a second copy of a score the run has already paid for.
|
|
800
|
+
*/
|
|
801
|
+
async function mapDistinct(args) {
|
|
802
|
+
const { items, limit, key, task } = args;
|
|
803
|
+
const groups = /* @__PURE__ */ new Map();
|
|
804
|
+
items.forEach((item, index) => {
|
|
805
|
+
const group = groups.get(key(item));
|
|
806
|
+
if (group === void 0) {
|
|
807
|
+
groups.set(key(item), [index]);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
group.push(index);
|
|
811
|
+
});
|
|
812
|
+
const results = new Array(items.length);
|
|
813
|
+
await mapWithConcurrency({
|
|
814
|
+
items: [...groups.values()],
|
|
815
|
+
limit,
|
|
816
|
+
task: async (indices) => {
|
|
817
|
+
for (const index of indices) results[index] = await task(items[index], index);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
return results;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Range checks on the search knobs, run at construction so a configuration
|
|
824
|
+
* that could never terminate is refused before a task is ever handed to it.
|
|
825
|
+
*/
|
|
826
|
+
function assertSimbaConfig(config) {
|
|
827
|
+
const { concurrency = 1 } = config;
|
|
828
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
698
829
|
}
|
|
699
830
|
//#endregion
|
|
700
|
-
export { SimbaOptimizer, buildAdvicePrompt, buildBuckets, evenlySpacedIndices, parseAdvice, percentile, samplePoisson, softmaxWeights, topKPlusBaseline };
|
|
831
|
+
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 { x as TextModel } from "./reporting-bq007_2z.cjs";
|
|
2
|
+
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-D0A7eahD.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 { x as TextModel } from "./reporting-bq007_2z.mjs";
|
|
2
|
+
import { i as buildReflectionPrompt, m as GepaAdapter } from "./reflection-CMezGu6u.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 };
|