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.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
2
|
+
const require_warnings = require("../warnings-CWRJF-jA.cjs");
|
|
3
|
+
const require_concurrency = require("../concurrency-C-cFzWW2.cjs");
|
|
4
|
+
const require_demos = require("../demos-9v5ts7F3.cjs");
|
|
4
5
|
const require_rng = require("../rng-DbA_rPIo.cjs");
|
|
5
6
|
const require_sampling = require("../sampling-Dars7ctR.cjs");
|
|
6
7
|
//#region src/simba/advice.ts
|
|
@@ -15,7 +16,7 @@ const ADVICE_BLOCK = /<advice\s+component="([^"]+)"\s*>([\s\S]*?)<\/advice>/g;
|
|
|
15
16
|
* behaviour rather than to the instance being easier.
|
|
16
17
|
*/
|
|
17
18
|
function buildAdvicePrompt(args) {
|
|
18
|
-
const { components, input, better, worse } = args;
|
|
19
|
+
const { components, current, input, better, worse } = args;
|
|
19
20
|
return [
|
|
20
21
|
"Two runs of the same system on the same input are shown below, with the reward each earned.",
|
|
21
22
|
"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.",
|
|
@@ -26,12 +27,18 @@ function buildAdvicePrompt(args) {
|
|
|
26
27
|
...worse === void 0 ? [] : trajectoryBlock("worse", worse),
|
|
27
28
|
...better === void 0 ? [] : trajectoryBlock("better", better),
|
|
28
29
|
"",
|
|
29
|
-
"Write advice for each of these components:",
|
|
30
|
+
"Write advice for each of these components, shown with what it says now:",
|
|
30
31
|
"",
|
|
31
32
|
"<components>",
|
|
32
|
-
components.
|
|
33
|
+
components.map((component) => [
|
|
34
|
+
`<component name="${component}">`,
|
|
35
|
+
current[component] ?? "",
|
|
36
|
+
"</component>"
|
|
37
|
+
].join("\n")).join("\n"),
|
|
33
38
|
"</components>",
|
|
34
39
|
"",
|
|
40
|
+
"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.",
|
|
41
|
+
"",
|
|
35
42
|
"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.",
|
|
36
43
|
"Address each component's own sub-task rather than the system as a whole.",
|
|
37
44
|
"Base the advice on what actually differed between the two runs. If nothing useful can be said for a component, leave it out.",
|
|
@@ -229,21 +236,32 @@ const DEMO_DROP_SCALE = 3;
|
|
|
229
236
|
var SimbaOptimizer = class {
|
|
230
237
|
#config;
|
|
231
238
|
constructor(config = {}) {
|
|
239
|
+
assertSimbaConfig(config);
|
|
232
240
|
this.#config = config;
|
|
233
241
|
}
|
|
234
|
-
optimize(task) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
async optimize(task) {
|
|
243
|
+
try {
|
|
244
|
+
return await run({
|
|
245
|
+
config: this.#config,
|
|
246
|
+
task
|
|
247
|
+
});
|
|
248
|
+
} finally {
|
|
249
|
+
await require_warnings.flushReporters(task.reporters ?? []);
|
|
250
|
+
}
|
|
239
251
|
}
|
|
240
252
|
};
|
|
241
253
|
async function run(args) {
|
|
242
254
|
const { config, task } = args;
|
|
243
|
-
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;
|
|
244
|
-
const { seedCandidate, trainingSet, validationSet
|
|
245
|
-
const
|
|
246
|
-
|
|
255
|
+
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;
|
|
256
|
+
const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, demoComponents = [], instructionComponents, renderDemo, buildAdvicePrompt: buildPrompt = buildAdvicePrompt, sampler = require_sampling.createEpochShuffledSampler({ minibatchSize }), maxMetricCalls, cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_warnings.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
257
|
+
const { validationSet, warnings } = require_warnings.resolveValidationSet({
|
|
258
|
+
validationSet: requestedValidationSet,
|
|
259
|
+
trainingSet
|
|
260
|
+
});
|
|
261
|
+
const emit = require_warnings.createEmitter(reporters);
|
|
262
|
+
const components = require_warnings.componentNames(seedCandidate);
|
|
263
|
+
const nonDemo = components.filter((name) => !demoComponents.includes(name));
|
|
264
|
+
const ruleComponents = instructionComponents ?? (nonDemo.length > 0 ? nonDemo : components);
|
|
247
265
|
const enabled = strategies ?? (demoComponents.length > 0 ? ["appendDemo", "appendRule"] : ["appendRule"]);
|
|
248
266
|
if (trainingSet.length === 0) throw new Error("optimize requires a non-empty trainingSet");
|
|
249
267
|
if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet");
|
|
@@ -252,6 +270,10 @@ async function run(args) {
|
|
|
252
270
|
if (enabled.includes("appendDemo") && demoComponents.length === 0) throw new Error("the appendDemo strategy requires at least one entry in demoComponents");
|
|
253
271
|
if (enabled.includes("appendRule") && ruleComponents.length === 0) throw new Error("the appendRule strategy requires at least one instruction component to write into");
|
|
254
272
|
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");
|
|
273
|
+
const trainingIds = trainingSet.map((datum, index) => instanceId({
|
|
274
|
+
datum,
|
|
275
|
+
index
|
|
276
|
+
}));
|
|
255
277
|
const validationIds = validationSet.map((datum, index) => instanceId({
|
|
256
278
|
datum,
|
|
257
279
|
index
|
|
@@ -260,28 +282,25 @@ async function run(args) {
|
|
|
260
282
|
datum,
|
|
261
283
|
index
|
|
262
284
|
})) ?? [];
|
|
263
|
-
const fingerprint =
|
|
285
|
+
const fingerprint = require_warnings.runFingerprint({
|
|
264
286
|
seedCandidate,
|
|
265
|
-
trainingIds
|
|
266
|
-
datum,
|
|
267
|
-
index
|
|
268
|
-
})),
|
|
287
|
+
trainingIds,
|
|
269
288
|
validationIds,
|
|
270
289
|
seed,
|
|
271
290
|
...cacheNamespace === void 0 ? {} : { cacheNamespace }
|
|
272
291
|
});
|
|
273
|
-
|
|
292
|
+
require_warnings.assertResumable({
|
|
274
293
|
fingerprint,
|
|
275
294
|
...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
|
|
276
295
|
});
|
|
277
|
-
const deadline =
|
|
296
|
+
const deadline = require_warnings.createDeadline({ maxWallClockMs });
|
|
278
297
|
const rng = require_rng.createSeededRng(seed, resumeFrom?.rngState);
|
|
279
|
-
const budget =
|
|
298
|
+
const budget = require_warnings.createBudget({
|
|
280
299
|
maxMetricCalls,
|
|
281
300
|
spent: resumeFrom?.metricCalls ?? 0
|
|
282
301
|
});
|
|
283
|
-
const evaluationCache = cache === false ? void 0 : cache ??
|
|
284
|
-
const evaluator =
|
|
302
|
+
const evaluationCache = cache === false ? void 0 : cache ?? require_warnings.createMemoryCache();
|
|
303
|
+
const evaluator = require_warnings.createEvaluator({
|
|
285
304
|
adapter,
|
|
286
305
|
budget,
|
|
287
306
|
...retry === void 0 ? {} : { retry },
|
|
@@ -289,8 +308,9 @@ async function run(args) {
|
|
|
289
308
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
290
309
|
trackOutputs: true,
|
|
291
310
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
311
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
292
312
|
...signal === void 0 ? {} : { signal },
|
|
293
|
-
onEvaluation: (event) =>
|
|
313
|
+
onEvaluation: (event) => emit({
|
|
294
314
|
type: "evaluation",
|
|
295
315
|
...event
|
|
296
316
|
})
|
|
@@ -306,7 +326,7 @@ async function run(args) {
|
|
|
306
326
|
let step = resumeFrom?.step ?? 0;
|
|
307
327
|
let reflectionCalls = resumeFrom?.reflectionCalls ?? 0;
|
|
308
328
|
let stopReason = "maxSteps";
|
|
309
|
-
|
|
329
|
+
emit({
|
|
310
330
|
type: "start",
|
|
311
331
|
components,
|
|
312
332
|
validationSetSize: validationSet.length
|
|
@@ -318,7 +338,7 @@ async function run(args) {
|
|
|
318
338
|
stopReason = "aborted";
|
|
319
339
|
break;
|
|
320
340
|
}
|
|
321
|
-
if (
|
|
341
|
+
if (require_warnings.costExhausted({
|
|
322
342
|
usage: evaluator.usage(),
|
|
323
343
|
maxCostUsd
|
|
324
344
|
})) {
|
|
@@ -341,16 +361,17 @@ async function run(args) {
|
|
|
341
361
|
scores: programs.map((_, index) => averageScore(index)),
|
|
342
362
|
k: candidateCount
|
|
343
363
|
});
|
|
344
|
-
|
|
364
|
+
emit({
|
|
345
365
|
type: "stepStart",
|
|
346
366
|
step,
|
|
347
367
|
poolSize: pool.length
|
|
348
368
|
});
|
|
349
|
-
const
|
|
369
|
+
const batchIndices = sampler({
|
|
350
370
|
trainingSet,
|
|
351
371
|
iteration: step,
|
|
352
372
|
rng
|
|
353
|
-
})
|
|
373
|
+
});
|
|
374
|
+
const batch = batchIndices.map((index) => trainingSet[index]);
|
|
354
375
|
const samples = [];
|
|
355
376
|
for (let slot = 0; slot < candidateCount; slot += 1) {
|
|
356
377
|
const programIndex = softmaxSample({
|
|
@@ -372,7 +393,7 @@ async function run(args) {
|
|
|
372
393
|
outputs: evaluation.outputs,
|
|
373
394
|
...evaluation.feedback === void 0 ? {} : { feedback: evaluation.feedback }
|
|
374
395
|
});
|
|
375
|
-
programScores[programIndex].push(
|
|
396
|
+
programScores[programIndex].push(require_warnings.measuredMean(evaluation) ?? 0);
|
|
376
397
|
}
|
|
377
398
|
if (samples.length === 0) {
|
|
378
399
|
stopReason = "budgetExhausted";
|
|
@@ -412,7 +433,7 @@ async function run(args) {
|
|
|
412
433
|
high
|
|
413
434
|
});
|
|
414
435
|
} catch (err) {
|
|
415
|
-
|
|
436
|
+
emit({
|
|
416
437
|
type: "error",
|
|
417
438
|
step,
|
|
418
439
|
err
|
|
@@ -427,36 +448,54 @@ async function run(args) {
|
|
|
427
448
|
});
|
|
428
449
|
}
|
|
429
450
|
let stepBest;
|
|
451
|
+
const batchIds = batchIndices.map((index) => trainingIds[index]);
|
|
452
|
+
const scheduled = [];
|
|
453
|
+
let owed = 0;
|
|
430
454
|
for (const entry of built) {
|
|
431
|
-
if (!budget.canAfford(batch.length)) {
|
|
455
|
+
if (!budget.canAfford(owed + batch.length)) {
|
|
432
456
|
stopReason = "budgetExhausted";
|
|
433
457
|
break;
|
|
434
458
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
459
|
+
owed += evaluator.countUncached({
|
|
460
|
+
candidate: entry.candidate,
|
|
461
|
+
ids: batchIds,
|
|
462
|
+
split: "train"
|
|
463
|
+
});
|
|
464
|
+
scheduled.push(entry);
|
|
465
|
+
}
|
|
466
|
+
const poolBase = programs.length;
|
|
467
|
+
const scored = await mapDistinct({
|
|
468
|
+
items: scheduled,
|
|
469
|
+
limit: concurrency,
|
|
470
|
+
key: (entry) => require_warnings.candidateHash(entry.candidate),
|
|
471
|
+
task: async (entry, index) => {
|
|
472
|
+
try {
|
|
473
|
+
return { score: require_warnings.measuredMean(await evaluator.evaluate({
|
|
474
|
+
candidate: entry.candidate,
|
|
475
|
+
batch,
|
|
476
|
+
ids: batchIds,
|
|
477
|
+
split: "train",
|
|
478
|
+
phase: "minibatch",
|
|
479
|
+
candidateId: poolBase + index,
|
|
480
|
+
iteration: step
|
|
481
|
+
})) };
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof require_warnings.BudgetExhausted) return { stop: true };
|
|
484
|
+
throw err;
|
|
453
485
|
}
|
|
454
|
-
throw err;
|
|
455
486
|
}
|
|
487
|
+
});
|
|
488
|
+
for (const [index, outcome] of scored.entries()) {
|
|
489
|
+
if (outcome.stop === true) {
|
|
490
|
+
stopReason = "budgetExhausted";
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
const { score } = outcome;
|
|
456
494
|
if (score === void 0) continue;
|
|
495
|
+
const entry = scheduled[index];
|
|
457
496
|
programs.push(entry.candidate);
|
|
458
497
|
programScores.push([score]);
|
|
459
|
-
|
|
498
|
+
emit({
|
|
460
499
|
type: "candidate",
|
|
461
500
|
step,
|
|
462
501
|
strategy: entry.strategy,
|
|
@@ -479,38 +518,78 @@ async function run(args) {
|
|
|
479
518
|
}
|
|
480
519
|
}
|
|
481
520
|
if (signal?.aborted) stopReason = "aborted";
|
|
482
|
-
const
|
|
521
|
+
const contenders = [];
|
|
522
|
+
let owedForFinalists = 0;
|
|
483
523
|
for (const index of evenlySpacedIndices({
|
|
484
524
|
length: winners.length,
|
|
485
525
|
count: candidateCount + 1
|
|
486
526
|
})) {
|
|
487
527
|
const winner = winners[index];
|
|
488
|
-
|
|
528
|
+
const uncached = evaluator.countUncached({
|
|
489
529
|
candidate: winner.candidate,
|
|
490
530
|
ids: validationIds,
|
|
491
531
|
split: "val"
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
batch: validationSet,
|
|
498
|
-
ids: validationIds,
|
|
499
|
-
split: "val",
|
|
500
|
-
phase: index === 0 ? "seed" : "validation",
|
|
501
|
-
candidateId: index,
|
|
502
|
-
iteration: step
|
|
503
|
-
}));
|
|
504
|
-
} catch (err) {
|
|
505
|
-
if (err instanceof require_evaluation.BudgetExhausted || signal?.aborted) break;
|
|
506
|
-
throw err;
|
|
507
|
-
}
|
|
508
|
-
if (score !== void 0) finalists.push({
|
|
532
|
+
});
|
|
533
|
+
if (!budget.canAfford(owedForFinalists + uncached)) break;
|
|
534
|
+
owedForFinalists += uncached;
|
|
535
|
+
contenders.push({
|
|
536
|
+
index,
|
|
509
537
|
candidate: winner.candidate,
|
|
510
|
-
score,
|
|
511
538
|
step: winner.step
|
|
512
539
|
});
|
|
513
540
|
}
|
|
541
|
+
const sweeps = await mapDistinct({
|
|
542
|
+
items: contenders,
|
|
543
|
+
limit: concurrency,
|
|
544
|
+
key: (contender) => require_warnings.candidateHash(contender.candidate),
|
|
545
|
+
task: async (contender) => {
|
|
546
|
+
if (signal?.aborted) return { stop: true };
|
|
547
|
+
try {
|
|
548
|
+
const evaluation = await evaluator.evaluate({
|
|
549
|
+
candidate: contender.candidate,
|
|
550
|
+
batch: validationSet,
|
|
551
|
+
ids: validationIds,
|
|
552
|
+
split: "val",
|
|
553
|
+
phase: contender.index === 0 ? "seed" : "validation",
|
|
554
|
+
candidateId: contender.index,
|
|
555
|
+
iteration: step
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
score: require_warnings.measuredMean(evaluation),
|
|
559
|
+
evaluation
|
|
560
|
+
};
|
|
561
|
+
} catch (err) {
|
|
562
|
+
if (err instanceof require_warnings.BudgetExhausted || signal?.aborted) return { stop: true };
|
|
563
|
+
throw err;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
const finalists = [];
|
|
568
|
+
let acceptedCandidates = -1;
|
|
569
|
+
let acceptedScore = Number.NEGATIVE_INFINITY;
|
|
570
|
+
for (const [position, outcome] of sweeps.entries()) {
|
|
571
|
+
if (outcome.stop === true) break;
|
|
572
|
+
if (outcome.score === void 0) continue;
|
|
573
|
+
const contender = contenders[position];
|
|
574
|
+
finalists.push({
|
|
575
|
+
candidate: contender.candidate,
|
|
576
|
+
score: outcome.score,
|
|
577
|
+
step: contender.step
|
|
578
|
+
});
|
|
579
|
+
if (outcome.score > acceptedScore) {
|
|
580
|
+
acceptedScore = outcome.score;
|
|
581
|
+
acceptedCandidates += 1;
|
|
582
|
+
emit({
|
|
583
|
+
type: "candidateAccepted",
|
|
584
|
+
step: contender.step,
|
|
585
|
+
candidateId: acceptedCandidates,
|
|
586
|
+
candidate: contender.candidate,
|
|
587
|
+
aggregateScore: outcome.score,
|
|
588
|
+
instanceScores: require_warnings.instanceRow(outcome.evaluation),
|
|
589
|
+
...trackBestOutputs ? { outputs: outcome.evaluation.outputs } : {}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
514
593
|
const seedScore = finalists[0]?.score ?? 0;
|
|
515
594
|
finalists.sort((a, b) => b.score - a.score);
|
|
516
595
|
const best = finalists[0] ?? {
|
|
@@ -526,7 +605,7 @@ async function run(args) {
|
|
|
526
605
|
candidateId: null,
|
|
527
606
|
iteration: step
|
|
528
607
|
}))?.outputs;
|
|
529
|
-
const
|
|
608
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluator.evaluate({
|
|
530
609
|
candidate: best.candidate,
|
|
531
610
|
batch: testSet,
|
|
532
611
|
ids: testIds,
|
|
@@ -535,13 +614,18 @@ async function run(args) {
|
|
|
535
614
|
candidateId: null,
|
|
536
615
|
iteration: step,
|
|
537
616
|
charge: false
|
|
538
|
-
})
|
|
539
|
-
|
|
617
|
+
});
|
|
618
|
+
const testScore = heldOut === void 0 ? void 0 : require_warnings.measuredMean(heldOut);
|
|
619
|
+
emit({
|
|
540
620
|
type: "finish",
|
|
541
621
|
reason: stopReason,
|
|
622
|
+
warnings,
|
|
623
|
+
bestCandidateId: acceptedCandidates,
|
|
542
624
|
bestScore: best.score,
|
|
543
625
|
metricCalls: budget.spent(),
|
|
544
|
-
...testScore === void 0 ? {} : { testScore }
|
|
626
|
+
...testScore === void 0 ? {} : { testScore },
|
|
627
|
+
...heldOut === void 0 ? {} : { testInstanceScores: require_warnings.instanceRow(heldOut) },
|
|
628
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
545
629
|
});
|
|
546
630
|
return {
|
|
547
631
|
bestCandidate: best.candidate,
|
|
@@ -557,8 +641,10 @@ async function run(args) {
|
|
|
557
641
|
...bestOutputs === void 0 ? {} : { bestOutputs },
|
|
558
642
|
...testScore === void 0 ? {} : {
|
|
559
643
|
testScore,
|
|
560
|
-
testMetricCalls:
|
|
644
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
645
|
+
testUsage: evaluator.unchargedUsage()
|
|
561
646
|
},
|
|
647
|
+
warnings,
|
|
562
648
|
stopReason
|
|
563
649
|
};
|
|
564
650
|
/**
|
|
@@ -594,7 +680,11 @@ async function run(args) {
|
|
|
594
680
|
const drops = /* @__PURE__ */ new Set();
|
|
595
681
|
for (let draw = 0; draw < Math.min(wanted, held); draw += 1) drops.add(rng.nextInt(held));
|
|
596
682
|
const next = { ...candidate };
|
|
597
|
-
for (const [name, demos] of blocks) next[name] = require_demos.
|
|
683
|
+
for (const [name, demos] of blocks) next[name] = require_demos.replaceDemos({
|
|
684
|
+
text: next[name] ?? "",
|
|
685
|
+
demos: demos.filter((_, index) => !drops.has(index)),
|
|
686
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
687
|
+
});
|
|
598
688
|
return next;
|
|
599
689
|
}
|
|
600
690
|
/**
|
|
@@ -615,8 +705,12 @@ async function run(args) {
|
|
|
615
705
|
};
|
|
616
706
|
const next = { ...candidate };
|
|
617
707
|
for (const name of demoComponents) {
|
|
618
|
-
const
|
|
619
|
-
next[name] = require_demos.
|
|
708
|
+
const text = next[name] ?? "";
|
|
709
|
+
next[name] = require_demos.replaceDemos({
|
|
710
|
+
text,
|
|
711
|
+
demos: [...require_demos.parseDemos(text), demo],
|
|
712
|
+
...renderDemo === void 0 ? {} : { render: renderDemo }
|
|
713
|
+
});
|
|
620
714
|
}
|
|
621
715
|
return next;
|
|
622
716
|
}
|
|
@@ -644,6 +738,7 @@ async function run(args) {
|
|
|
644
738
|
const response = await reflect({
|
|
645
739
|
prompt: buildPrompt({
|
|
646
740
|
components: ruleComponents,
|
|
741
|
+
current: Object.fromEntries(ruleComponents.map((name) => [name, candidate[name] ?? ""])),
|
|
647
742
|
input: bucket.datum,
|
|
648
743
|
...better === void 0 ? {} : { better },
|
|
649
744
|
...worse === void 0 ? {} : { worse }
|
|
@@ -684,6 +779,7 @@ async function run(args) {
|
|
|
684
779
|
metricCalls: budget.spent(),
|
|
685
780
|
reflectionCalls,
|
|
686
781
|
cacheHits: evaluator.cacheHits(),
|
|
782
|
+
usage: evaluator.usage(),
|
|
687
783
|
rngState: rng.state(),
|
|
688
784
|
...sampler.state === void 0 ? {} : { sampler: sampler.state() },
|
|
689
785
|
...cached === void 0 ? {} : { cache: cached }
|
|
@@ -694,14 +790,51 @@ async function run(args) {
|
|
|
694
790
|
await onCheckpoint(takeSnapshot(completed));
|
|
695
791
|
}
|
|
696
792
|
}
|
|
697
|
-
|
|
698
|
-
|
|
793
|
+
/**
|
|
794
|
+
* Runs `items` concurrently, except that items sharing a key run one after the
|
|
795
|
+
* other.
|
|
796
|
+
*
|
|
797
|
+
* Two identical candidates cost one evaluation and one cache hit when they are
|
|
798
|
+
* scored in sequence, and two evaluations when they overlap: the cache is only
|
|
799
|
+
* written once a rollout returns. Serializing the duplicates is what keeps a
|
|
800
|
+
* fan-out from buying a second copy of a score the run has already paid for.
|
|
801
|
+
*/
|
|
802
|
+
async function mapDistinct(args) {
|
|
803
|
+
const { items, limit, key, task } = args;
|
|
804
|
+
const groups = /* @__PURE__ */ new Map();
|
|
805
|
+
items.forEach((item, index) => {
|
|
806
|
+
const group = groups.get(key(item));
|
|
807
|
+
if (group === void 0) {
|
|
808
|
+
groups.set(key(item), [index]);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
group.push(index);
|
|
812
|
+
});
|
|
813
|
+
const results = new Array(items.length);
|
|
814
|
+
await require_concurrency.mapWithConcurrency({
|
|
815
|
+
items: [...groups.values()],
|
|
816
|
+
limit,
|
|
817
|
+
task: async (indices) => {
|
|
818
|
+
for (const index of indices) results[index] = await task(items[index], index);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
return results;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Range checks on the search knobs, run at construction so a configuration
|
|
825
|
+
* that could never terminate is refused before a task is ever handed to it.
|
|
826
|
+
*/
|
|
827
|
+
function assertSimbaConfig(config) {
|
|
828
|
+
const { concurrency = 1 } = config;
|
|
829
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error(`concurrency must be a positive integer, received ${concurrency}`);
|
|
699
830
|
}
|
|
700
831
|
//#endregion
|
|
701
832
|
exports.SimbaOptimizer = SimbaOptimizer;
|
|
702
833
|
exports.buildAdvicePrompt = buildAdvicePrompt;
|
|
703
834
|
exports.buildBuckets = buildBuckets;
|
|
704
835
|
exports.evenlySpacedIndices = evenlySpacedIndices;
|
|
836
|
+
exports.isCandidateAccepted = require_warnings.isCandidateAccepted;
|
|
837
|
+
exports.isRunFinished = require_warnings.isRunFinished;
|
|
705
838
|
exports.parseAdvice = parseAdvice;
|
|
706
839
|
exports.percentile = percentile;
|
|
707
840
|
exports.samplePoisson = samplePoisson;
|
package/dist/simba/index.d.cts
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.cjs";
|
|
2
2
|
import { n as EvaluationCache, t as CachedScore } from "../cache-CuSo0NJ8.cjs";
|
|
3
3
|
import { t as Rng } from "../rng-BR5MOedA.cjs";
|
|
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-ByaLZy-Z.cjs";
|
|
5
|
+
import { n as OptimizerResult, r as OptimizerTask, s as EvaluationEvent, t as Optimizer } from "../optimizer-4Zv-Zt2t.cjs";
|
|
6
6
|
import { t as BatchSampler } from "../sampling-axOwfZf5.cjs";
|
|
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 };
|