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/gepa/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { _ as createMemoryCache, a as isRunFinished, b as createBudget, c as createEvaluator, d as createDeadline, f as assertResumable, h as componentNames, i as isCandidateAccepted, l as measuredMean, m as runFingerprint, n as flushReporters, o as BudgetExhausted, p as candidateFingerprint, r as instanceRow, s as costExhausted, t as createEmitter, v as defaultInstanceId } from "../reporting-DY-DC4HG.mjs";
|
|
2
2
|
import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { a as sum, i as signFlipPValue, r as mean, t as argmax } from "../math-Dqme4rYz.mjs";
|
|
4
|
+
import { r as parseDemos, t as formatDemos } from "../demos-DE2oxNWX.mjs";
|
|
5
5
|
import { t as createSeededRng } from "../rng-Dtc5eZ_W.mjs";
|
|
6
6
|
import { a as createDefaultProposer, i as buildSimplifyPrompt, n as buildReflectionPrompt, o as diverseReflectionStrategies, r as buildRewritePrompt, t as buildGeneralizePrompt } from "../reflection-mwMhrjs_.mjs";
|
|
7
7
|
import { t as createEpochShuffledSampler } from "../sampling-CfHt7Gue.mjs";
|
|
@@ -738,16 +738,20 @@ var GepaOptimizer = class {
|
|
|
738
738
|
this.#config = config;
|
|
739
739
|
}
|
|
740
740
|
async optimize(task) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
741
|
+
try {
|
|
742
|
+
return await runGepa({
|
|
743
|
+
config: this.#config,
|
|
744
|
+
task
|
|
745
|
+
});
|
|
746
|
+
} finally {
|
|
747
|
+
await flushReporters(task.reporters ?? []);
|
|
748
|
+
}
|
|
745
749
|
}
|
|
746
750
|
};
|
|
747
751
|
async function runGepa(args) {
|
|
748
752
|
const { config, task } = args;
|
|
749
753
|
const { minibatchSize = DEFAULT_MINIBATCH_SIZE, maxIterations = Number.POSITIVE_INFINITY, seed = 0, candidateSelector = paretoSelector(), acceptance = improvementAcceptance(), merge, skipPerfectScore = true, perfectScore = 1, rejectedProposalMemory = DEFAULT_REJECTED_PROPOSAL_MEMORY, proposals, reflection, checkpointCache = true, trackBestOutputs = false, raiseOnError = true } = config;
|
|
750
|
-
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, componentSelector = roundRobinComponentSelector(), batchSampler = createEpochShuffledSampler({ minibatchSize }), valEvaluationPolicy = fullEvaluationPolicy(), cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId,
|
|
754
|
+
const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, componentSelector = roundRobinComponentSelector(), batchSampler = createEpochShuffledSampler({ minibatchSize }), valEvaluationPolicy = fullEvaluationPolicy(), cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
|
|
751
755
|
const deadline = createDeadline({ maxWallClockMs });
|
|
752
756
|
const seedComponents = componentNames(seedCandidate);
|
|
753
757
|
const mergeConfig = {
|
|
@@ -827,8 +831,26 @@ async function runGepa(args) {
|
|
|
827
831
|
let mergesDue = resumeFrom?.merge.due ?? 0;
|
|
828
832
|
let totalMergesTested = resumeFrom?.merge.tested ?? 0;
|
|
829
833
|
let lastIterationAccepted = resumeFrom?.merge.lastIterationAccepted ?? false;
|
|
830
|
-
|
|
831
|
-
|
|
834
|
+
const emit = createEmitter(reporters);
|
|
835
|
+
/**
|
|
836
|
+
* Everything an acceptance means, in one event: the text, the aggregate, and
|
|
837
|
+
* the row it put on the frontier. Emitted from one place because the merge
|
|
838
|
+
* path and the mutation path accept candidates separately, and a payload
|
|
839
|
+
* assembled twice is a payload that drifts.
|
|
840
|
+
*/
|
|
841
|
+
function emitAccepted(record) {
|
|
842
|
+
const outputs = outputsByCandidate.get(record.id);
|
|
843
|
+
emit({
|
|
844
|
+
type: "candidateAccepted",
|
|
845
|
+
iteration,
|
|
846
|
+
candidateId: record.id,
|
|
847
|
+
parentIds: record.parentIds,
|
|
848
|
+
aggregateScore: record.aggregateScore,
|
|
849
|
+
source: record.source,
|
|
850
|
+
candidate: record.candidate,
|
|
851
|
+
instanceScores: record.instanceScores,
|
|
852
|
+
...outputs === void 0 ? {} : { outputs }
|
|
853
|
+
});
|
|
832
854
|
}
|
|
833
855
|
/**
|
|
834
856
|
* Copies everything mutable: a snapshot handed to `onCheckpoint` is a record
|
|
@@ -845,6 +867,7 @@ async function runGepa(args) {
|
|
|
845
867
|
metricCalls: budget.spent(),
|
|
846
868
|
reflectionCalls,
|
|
847
869
|
cacheHits: evaluator.cacheHits(),
|
|
870
|
+
usage: evaluator.usage(),
|
|
848
871
|
...samplerState === void 0 ? {} : { sampler: samplerState },
|
|
849
872
|
rejectedProposals: snapshotRejections({
|
|
850
873
|
rejections: rejectedProposals,
|
|
@@ -873,6 +896,7 @@ async function runGepa(args) {
|
|
|
873
896
|
...evaluationCache === void 0 ? {} : { cache: evaluationCache },
|
|
874
897
|
trackOutputs: trackBestOutputs,
|
|
875
898
|
cacheHits: resumeFrom?.cacheHits ?? 0,
|
|
899
|
+
...resumeFrom?.usage === void 0 ? {} : { usage: resumeFrom.usage },
|
|
876
900
|
...signal === void 0 ? {} : { signal },
|
|
877
901
|
onEvaluation: (event) => emit({
|
|
878
902
|
type: "evaluation",
|
|
@@ -992,7 +1016,7 @@ async function runGepa(args) {
|
|
|
992
1016
|
if (records.length === 0) {
|
|
993
1017
|
const seedInstances = selectValInstances(seedCandidate);
|
|
994
1018
|
if (!budget.canAfford(seedInstances.length)) throw new Error(`maxMetricCalls (${maxMetricCalls}) is smaller than the ${seedInstances.length} validation instances selected for scoring; the seed candidate cannot be scored`);
|
|
995
|
-
addCandidate({
|
|
1019
|
+
emitAccepted(addCandidate({
|
|
996
1020
|
candidate: seedCandidate,
|
|
997
1021
|
parentIds: [],
|
|
998
1022
|
evaluation: await evaluateValidation({
|
|
@@ -1003,7 +1027,7 @@ async function runGepa(args) {
|
|
|
1003
1027
|
}),
|
|
1004
1028
|
source: "seed",
|
|
1005
1029
|
updatedComponents: []
|
|
1006
|
-
});
|
|
1030
|
+
}));
|
|
1007
1031
|
lastIterationAccepted = false;
|
|
1008
1032
|
mergesDue = 0;
|
|
1009
1033
|
await checkpoint();
|
|
@@ -1094,14 +1118,7 @@ async function runGepa(args) {
|
|
|
1094
1118
|
});
|
|
1095
1119
|
mergesDue -= 1;
|
|
1096
1120
|
totalMergesTested += 1;
|
|
1097
|
-
|
|
1098
|
-
type: "candidateAccepted",
|
|
1099
|
-
iteration,
|
|
1100
|
-
candidateId: record.id,
|
|
1101
|
-
parentIds: record.parentIds,
|
|
1102
|
-
aggregateScore: record.aggregateScore,
|
|
1103
|
-
source: "merge"
|
|
1104
|
-
});
|
|
1121
|
+
emitAccepted(record);
|
|
1105
1122
|
return "attempted";
|
|
1106
1123
|
}
|
|
1107
1124
|
/**
|
|
@@ -1346,21 +1363,13 @@ async function runGepa(args) {
|
|
|
1346
1363
|
stop ??= "budgetExhausted";
|
|
1347
1364
|
break;
|
|
1348
1365
|
}
|
|
1349
|
-
|
|
1366
|
+
emitAccepted(addCandidate({
|
|
1350
1367
|
candidate: item.outcome.child,
|
|
1351
1368
|
parentIds: [item.outcome.plan.parent.id],
|
|
1352
1369
|
evaluation,
|
|
1353
1370
|
source: "mutation",
|
|
1354
1371
|
updatedComponents: componentNames(item.outcome.proposed)
|
|
1355
|
-
});
|
|
1356
|
-
emit({
|
|
1357
|
-
type: "candidateAccepted",
|
|
1358
|
-
iteration,
|
|
1359
|
-
candidateId: record.id,
|
|
1360
|
-
parentIds: record.parentIds,
|
|
1361
|
-
aggregateScore: record.aggregateScore,
|
|
1362
|
-
source: "mutation"
|
|
1363
|
-
});
|
|
1372
|
+
}));
|
|
1364
1373
|
}
|
|
1365
1374
|
return stop;
|
|
1366
1375
|
}
|
|
@@ -1444,7 +1453,7 @@ async function runGepa(args) {
|
|
|
1444
1453
|
}
|
|
1445
1454
|
const bestCandidateId = valEvaluationPolicy.bestCandidate(records);
|
|
1446
1455
|
const best = records[bestCandidateId];
|
|
1447
|
-
const
|
|
1456
|
+
const heldOut = testSet === void 0 ? void 0 : await evaluateCached({
|
|
1448
1457
|
candidate: best.candidate,
|
|
1449
1458
|
batch: testSet,
|
|
1450
1459
|
ids: testIds,
|
|
@@ -1452,13 +1461,17 @@ async function runGepa(args) {
|
|
|
1452
1461
|
phase: "test",
|
|
1453
1462
|
candidateId: bestCandidateId,
|
|
1454
1463
|
charge: false
|
|
1455
|
-
})
|
|
1464
|
+
});
|
|
1465
|
+
const testScore = heldOut === void 0 ? void 0 : measuredMean(heldOut);
|
|
1456
1466
|
emit({
|
|
1457
1467
|
type: "finish",
|
|
1458
1468
|
reason: stopReason,
|
|
1459
1469
|
bestCandidateId,
|
|
1470
|
+
bestScore: best.aggregateScore,
|
|
1460
1471
|
metricCalls: budget.spent(),
|
|
1461
|
-
...testScore === void 0 ? {} : { testScore }
|
|
1472
|
+
...testScore === void 0 ? {} : { testScore },
|
|
1473
|
+
...heldOut === void 0 ? {} : { testInstanceScores: instanceRow(heldOut) },
|
|
1474
|
+
...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
|
|
1462
1475
|
});
|
|
1463
1476
|
const perObjectiveBest = collectPerObjectiveBest(records);
|
|
1464
1477
|
const bestOutputs = outputsByCandidate.get(bestCandidateId);
|
|
@@ -1469,7 +1482,8 @@ async function runGepa(args) {
|
|
|
1469
1482
|
bestCandidateId,
|
|
1470
1483
|
...testScore === void 0 ? {} : {
|
|
1471
1484
|
testScore,
|
|
1472
|
-
testMetricCalls: evaluator.unchargedCalls()
|
|
1485
|
+
testMetricCalls: evaluator.unchargedCalls(),
|
|
1486
|
+
testUsage: evaluator.unchargedUsage()
|
|
1473
1487
|
},
|
|
1474
1488
|
...bestOutputs === void 0 ? {} : { bestOutputs },
|
|
1475
1489
|
candidates: records,
|
|
@@ -1617,17 +1631,6 @@ function collectDominatorIds(records) {
|
|
|
1617
1631
|
return [...ids].sort((a, b) => a - b);
|
|
1618
1632
|
}
|
|
1619
1633
|
/**
|
|
1620
|
-
* Names an instance by a hash of its content rather than by the content
|
|
1621
|
-
* itself: the id ends up inside every cache key and inside the checkpoint
|
|
1622
|
-
* fingerprint, and embedding whole examples there costs memory proportional to
|
|
1623
|
-
* the dataset for no benefit. Data that will not serialize falls back to its
|
|
1624
|
-
* position, which is stable for as long as the dataset order is.
|
|
1625
|
-
*/
|
|
1626
|
-
function defaultInstanceId(args) {
|
|
1627
|
-
const hash = stableHash(args.datum);
|
|
1628
|
-
return hash === "" ? String(args.index) : hash;
|
|
1629
|
-
}
|
|
1630
|
-
/**
|
|
1631
1634
|
* The two rollout sets restricted to the instances both of them measured.
|
|
1632
1635
|
*
|
|
1633
1636
|
* Screening is a paired comparison over one minibatch: a transient row is a
|
|
@@ -1649,4 +1652,4 @@ function pairMeasured(args) {
|
|
|
1649
1652
|
};
|
|
1650
1653
|
}
|
|
1651
1654
|
//#endregion
|
|
1652
|
-
export { GepaOptimizer, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
|
|
1655
|
+
export { GepaOptimizer, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, isCandidateAccepted, isRunFinished, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
|
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_reporting = require("./reporting-CNHzbJC-.cjs");
|
|
3
3
|
const require_concurrency = require("./concurrency-C-cFzWW2.cjs");
|
|
4
|
-
const require_math = require("./math-
|
|
5
|
-
const require_demos = require("./demos-
|
|
4
|
+
const require_math = require("./math-BhlziRPc.cjs");
|
|
5
|
+
const require_demos = require("./demos-D2o0qSSo.cjs");
|
|
6
6
|
const require_text = require("./text-CK_HB3su.cjs");
|
|
7
7
|
//#region src/compare.ts
|
|
8
|
-
const EXACT_LIMIT =
|
|
8
|
+
const EXACT_LIMIT = 20;
|
|
9
|
+
/** Well above float subtraction noise (~1e-16), well below a real margin. */
|
|
10
|
+
const DEGENERACY_TOLERANCE = 1e-9;
|
|
9
11
|
/**
|
|
10
12
|
* Run several optimizers over the same seeds and report which one actually won.
|
|
11
13
|
*
|
|
@@ -20,7 +22,7 @@ const EXACT_LIMIT = 16;
|
|
|
20
22
|
* seed is constructor config and every optimizer here is deterministic given
|
|
21
23
|
* one — comparing two entrants at a single seed compares two anecdotes. It also
|
|
22
24
|
* leaves the task where the caller builds it, which is the only place the
|
|
23
|
-
* optimizer-specific parts of it (`reflect`, `cache`, `
|
|
25
|
+
* optimizer-specific parts of it (`reflect`, `cache`, `reporters`) are known.
|
|
24
26
|
*/
|
|
25
27
|
async function compare(args) {
|
|
26
28
|
const { entrants, seeds, concurrency = 1 } = args;
|
|
@@ -43,6 +45,8 @@ async function compare(args) {
|
|
|
43
45
|
bestScore: result.bestScore,
|
|
44
46
|
...result.testScore === void 0 ? {} : { testScore: result.testScore },
|
|
45
47
|
metricCalls: result.metricCalls,
|
|
48
|
+
cacheHits: result.cacheHits,
|
|
49
|
+
reflectionCalls: result.reflectionCalls ?? 0,
|
|
46
50
|
costUsd: result.usage.costUsd,
|
|
47
51
|
stopReason: result.stopReason
|
|
48
52
|
};
|
|
@@ -53,17 +57,25 @@ async function compare(args) {
|
|
|
53
57
|
runs: runs.filter((run) => run.entrant === entrant)
|
|
54
58
|
}));
|
|
55
59
|
const winner = summaries.reduce((best, summary) => summary.meanScore > best.meanScore ? summary : best);
|
|
60
|
+
const rawPValues = summaries.map((summary) => summary.entrant === winner.entrant ? void 0 : margin({
|
|
61
|
+
winner: winner.entrant,
|
|
62
|
+
entrant: summary.entrant,
|
|
63
|
+
runs,
|
|
64
|
+
seeds
|
|
65
|
+
}));
|
|
66
|
+
const holmAdjusted = holmAdjustSparse({
|
|
67
|
+
pValues: rawPValues,
|
|
68
|
+
familySize: names.length - 1
|
|
69
|
+
});
|
|
56
70
|
return {
|
|
57
71
|
winner: winner.entrant,
|
|
58
|
-
summaries: summaries.map((summary) =>
|
|
72
|
+
summaries: summaries.map((summary, index) => ({
|
|
59
73
|
...summary,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
66
|
-
}),
|
|
74
|
+
...rawPValues[index] === void 0 ? {} : {
|
|
75
|
+
pValueVsWinner: rawPValues[index],
|
|
76
|
+
pValueVsWinnerHolm: holmAdjusted[index]
|
|
77
|
+
}
|
|
78
|
+
})),
|
|
67
79
|
runs
|
|
68
80
|
};
|
|
69
81
|
}
|
|
@@ -78,13 +90,28 @@ function summarize(args) {
|
|
|
78
90
|
minScore: Math.min(...scores),
|
|
79
91
|
maxScore: Math.max(...scores),
|
|
80
92
|
meanMetricCalls: mean(runs.map((run) => run.metricCalls)),
|
|
81
|
-
meanCostUsd: mean(runs.map((run) => run.costUsd))
|
|
93
|
+
meanCostUsd: mean(runs.map((run) => run.costUsd)),
|
|
94
|
+
meanCacheHits: mean(runs.map((run) => run.cacheHits)),
|
|
95
|
+
meanReflectionCalls: mean(runs.map((run) => run.reflectionCalls)),
|
|
96
|
+
distinctScores: new Set(scores).size
|
|
82
97
|
};
|
|
83
98
|
}
|
|
84
99
|
/**
|
|
85
100
|
* Paired across seeds rather than pooled: the same seed puts both entrants on
|
|
86
101
|
* the same sampling order, so the difference at a seed is a comparison and the
|
|
87
102
|
* spread between seeds is not.
|
|
103
|
+
*
|
|
104
|
+
* Returns `undefined` when every seed produced the exact same nonzero margin.
|
|
105
|
+
* That is not evidence of an n-seed-strong result — it is one realization the
|
|
106
|
+
* seed never varied, and a sign-flip p-value would report a precision from n
|
|
107
|
+
* independent trials that never happened. A margin of exactly zero every seed
|
|
108
|
+
* is not this case: `signFlipPValue` already reports that honestly as 1, no
|
|
109
|
+
* significance claimed either way, which is not a fabricated number.
|
|
110
|
+
*
|
|
111
|
+
* "Exact same" is judged within `DEGENERACY_TOLERANCE`, not `===`: subtracting
|
|
112
|
+
* two scores that are equal in substance can still land a few ULPs apart
|
|
113
|
+
* (0.95 - 0.55 and 0.9 - 0.5 differ at the 16th digit), and treating that as
|
|
114
|
+
* n real trials would be the same fabrication this check exists to prevent.
|
|
88
115
|
*/
|
|
89
116
|
function margin(args) {
|
|
90
117
|
const { winner, entrant, runs, seeds } = args;
|
|
@@ -99,6 +126,10 @@ function margin(args) {
|
|
|
99
126
|
seed
|
|
100
127
|
});
|
|
101
128
|
});
|
|
129
|
+
const [first] = differences;
|
|
130
|
+
if (first !== void 0) {
|
|
131
|
+
if (differences.reduce((widest, difference) => Math.max(widest, Math.abs(difference - first)), 0) < DEGENERACY_TOLERANCE && Math.abs(first) > DEGENERACY_TOLERANCE) return;
|
|
132
|
+
}
|
|
102
133
|
return require_math.signFlipPValue({
|
|
103
134
|
differences,
|
|
104
135
|
observed: differences.reduce((total, value) => total + value, 0),
|
|
@@ -109,6 +140,27 @@ function scoreOf(args) {
|
|
|
109
140
|
const { runs, entrant, seed } = args;
|
|
110
141
|
return runs.find((candidate) => candidate.entrant === entrant && candidate.seed === seed)?.score ?? 0;
|
|
111
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* `holmAdjust` over the raw p-values that exist, skipping the slots a
|
|
145
|
+
* withheld comparison left `undefined` — those still count toward
|
|
146
|
+
* `familySize`, they just have nothing of their own to adjust.
|
|
147
|
+
*/
|
|
148
|
+
function holmAdjustSparse(args) {
|
|
149
|
+
const { pValues, familySize } = args;
|
|
150
|
+
const present = pValues.map((pValue, index) => ({
|
|
151
|
+
pValue,
|
|
152
|
+
index
|
|
153
|
+
})).filter((entry) => entry.pValue !== void 0);
|
|
154
|
+
const adjusted = require_math.holmAdjust({
|
|
155
|
+
pValues: present.map((entry) => entry.pValue),
|
|
156
|
+
familySize
|
|
157
|
+
});
|
|
158
|
+
const result = new Array(pValues.length).fill(void 0);
|
|
159
|
+
present.forEach((entry, rank) => {
|
|
160
|
+
result[entry.index] = adjusted[rank];
|
|
161
|
+
});
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
112
164
|
function mean(values) {
|
|
113
165
|
return values.reduce((total, value) => total + value, 0) / values.length;
|
|
114
166
|
}
|
|
@@ -119,6 +171,51 @@ function standardDeviation(values) {
|
|
|
119
171
|
return Math.sqrt(variance);
|
|
120
172
|
}
|
|
121
173
|
//#endregion
|
|
174
|
+
//#region src/distillation.ts
|
|
175
|
+
const ROLES = /* @__PURE__ */ new Set([
|
|
176
|
+
"system",
|
|
177
|
+
"user",
|
|
178
|
+
"assistant"
|
|
179
|
+
]);
|
|
180
|
+
/**
|
|
181
|
+
* Serialize harvested rollouts as JSONL, one training example per line.
|
|
182
|
+
*
|
|
183
|
+
* The chat-messages shape rather than any one vendor's: it is what Axolotl,
|
|
184
|
+
* Together, Fireworks and the Hugging Face trainers all ingest, and the
|
|
185
|
+
* providers that read it outlive the ones that do not.
|
|
186
|
+
*
|
|
187
|
+
* Returns the text rather than writing it. Only `file-cache` touches the
|
|
188
|
+
* filesystem, and a caller uploading this straight to a provider should not
|
|
189
|
+
* have to round-trip it through a file to do so.
|
|
190
|
+
*/
|
|
191
|
+
function toTrainingJsonl(args) {
|
|
192
|
+
const { rollouts, render } = args;
|
|
193
|
+
const lines = [];
|
|
194
|
+
for (const [index, rollout] of rollouts.entries()) {
|
|
195
|
+
const example = render({
|
|
196
|
+
rollout,
|
|
197
|
+
index
|
|
198
|
+
});
|
|
199
|
+
if (example === null) continue;
|
|
200
|
+
assertUsable(example, index);
|
|
201
|
+
lines.push(JSON.stringify(example));
|
|
202
|
+
}
|
|
203
|
+
return lines.join("\n");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Checked here rather than left to the provider. A malformed line surfaces as
|
|
207
|
+
* a rejected upload hours later, naming a line number in a file the caller
|
|
208
|
+
* never wrote by hand; naming the rollout at the point it was rendered is the
|
|
209
|
+
* same error while it is still fixable.
|
|
210
|
+
*/
|
|
211
|
+
function assertUsable(example, index) {
|
|
212
|
+
if (!Array.isArray(example.messages) || example.messages.length === 0) throw new Error(`rollout ${index} rendered no messages`);
|
|
213
|
+
for (const message of example.messages) {
|
|
214
|
+
if (!ROLES.has(message.role)) throw new Error(`rollout ${index} rendered the unknown role ${message.role}`);
|
|
215
|
+
if (typeof message.content !== "string") throw new Error(`rollout ${index} rendered non-text content`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
122
219
|
//#region src/judge.ts
|
|
123
220
|
const DEFAULT_SCALE = 5;
|
|
124
221
|
const SCORE = /<score\s+name="([^"]+)"\s*>\s*([\d.]+)\s*<\/score>/g;
|
|
@@ -141,6 +238,7 @@ const FEEDBACK = /<feedback>([\s\S]*?)<\/feedback>/;
|
|
|
141
238
|
function createJudge(args) {
|
|
142
239
|
const { model, criteria, scale = DEFAULT_SCALE, renderInput = stringify, renderOutput = stringify, buildPrompt = buildJudgePrompt } = args;
|
|
143
240
|
if (criteria.length === 0) throw new Error("createJudge requires at least one criterion");
|
|
241
|
+
if (!Number.isFinite(scale) || scale <= 0) throw new Error(`scale must be a positive number, received ${scale}`);
|
|
144
242
|
return async ({ input, output, expected, signal }) => {
|
|
145
243
|
return readVerdict({
|
|
146
244
|
response: await model({
|
|
@@ -243,24 +341,28 @@ function priceUsage(args) {
|
|
|
243
341
|
};
|
|
244
342
|
}
|
|
245
343
|
//#endregion
|
|
246
|
-
exports.BudgetExhausted =
|
|
247
|
-
exports.assertResumable =
|
|
248
|
-
exports.bootstrapDemos = require_demos.bootstrapDemos;
|
|
344
|
+
exports.BudgetExhausted = require_reporting.BudgetExhausted;
|
|
345
|
+
exports.assertResumable = require_reporting.assertResumable;
|
|
249
346
|
exports.buildJudgePrompt = buildJudgePrompt;
|
|
250
|
-
exports.candidateFingerprint =
|
|
347
|
+
exports.candidateFingerprint = require_reporting.candidateFingerprint;
|
|
251
348
|
exports.compare = compare;
|
|
252
|
-
exports.componentNames =
|
|
253
|
-
exports.costExhausted =
|
|
254
|
-
exports.createBudget =
|
|
255
|
-
exports.createDeadline =
|
|
256
|
-
exports.createEvaluator =
|
|
349
|
+
exports.componentNames = require_reporting.componentNames;
|
|
350
|
+
exports.costExhausted = require_reporting.costExhausted;
|
|
351
|
+
exports.createBudget = require_reporting.createBudget;
|
|
352
|
+
exports.createDeadline = require_reporting.createDeadline;
|
|
353
|
+
exports.createEvaluator = require_reporting.createEvaluator;
|
|
257
354
|
exports.createJudge = createJudge;
|
|
258
|
-
exports.createMemoryCache =
|
|
355
|
+
exports.createMemoryCache = require_reporting.createMemoryCache;
|
|
259
356
|
exports.formatDemos = require_demos.formatDemos;
|
|
357
|
+
exports.harvestFewShotExamples = require_demos.harvestFewShotExamples;
|
|
358
|
+
exports.harvestRollouts = require_demos.harvestRollouts;
|
|
359
|
+
exports.isCandidateAccepted = require_reporting.isCandidateAccepted;
|
|
360
|
+
exports.isRunFinished = require_reporting.isRunFinished;
|
|
260
361
|
exports.mapWithConcurrency = require_concurrency.mapWithConcurrency;
|
|
261
|
-
exports.measuredMean =
|
|
362
|
+
exports.measuredMean = require_reporting.measuredMean;
|
|
262
363
|
exports.parseDemos = require_demos.parseDemos;
|
|
263
364
|
exports.parseProposedText = require_text.parseProposedText;
|
|
264
365
|
exports.priceUsage = priceUsage;
|
|
265
|
-
exports.requireMeasuredMean =
|
|
266
|
-
exports.runFingerprint =
|
|
366
|
+
exports.requireMeasuredMean = require_reporting.requireMeasuredMean;
|
|
367
|
+
exports.runFingerprint = require_reporting.runFingerprint;
|
|
368
|
+
exports.toTrainingJsonl = toTrainingJsonl;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { _ as TextModel, a as RunFinished, c as Adapter, d as EvaluationBatch, f as EvaluationContext, g as ScoreResult, h as RolloutUsage, i as Reporter, l as Candidate, m as EvaluationSplit, n as OptimizerEvent, o as isCandidateAccepted, p as EvaluationPhase, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, u as EvaluateArgs, v as UsageTotals, y as componentNames } from "./reporting-DQbAohc9.cjs";
|
|
2
2
|
import { n as EvaluationCache, r as createMemoryCache, t as CachedScore } from "./cache-CuSo0NJ8.cjs";
|
|
3
3
|
import { t as Rng } from "./rng-BR5MOedA.cjs";
|
|
4
|
-
import { a as
|
|
5
|
-
import { a as EvaluateBatchArgs, c as Evaluator, d as costExhausted, f as createEvaluator, g as createBudget, h as Budget, i as BudgetExhausted, l as RetryPolicy, m as requireMeasuredMean, n as OptimizerResult, o as EvaluateTracedArgs, p as measuredMean, r as OptimizerTask, s as EvaluationEvent, t as Optimizer, u as ScoredBatch } from "./optimizer-
|
|
4
|
+
import { a as harvestFewShotExamples, i as formatDemos, n as Demo, o as parseDemos, r as DemoRenderer, t as BootstrapResult } from "./demos-CU9dy2oT.cjs";
|
|
5
|
+
import { a as EvaluateBatchArgs, c as Evaluator, d as costExhausted, f as createEvaluator, g as createBudget, h as Budget, i as BudgetExhausted, l as RetryPolicy, m as requireMeasuredMean, n as OptimizerResult, o as EvaluateTracedArgs, p as measuredMean, r as OptimizerTask, s as EvaluationEvent, t as Optimizer, u as ScoredBatch } from "./optimizer-Bh5DPRMH.cjs";
|
|
6
6
|
import { t as BatchSampler } from "./sampling-axOwfZf5.cjs";
|
|
7
7
|
//#region src/checkpoint.d.ts
|
|
8
8
|
/**
|
|
@@ -46,6 +46,10 @@ interface ComparisonRun {
|
|
|
46
46
|
bestScore: number;
|
|
47
47
|
testScore?: number;
|
|
48
48
|
metricCalls: number;
|
|
49
|
+
/** Rollouts this run got from the cache instead of paying for. */
|
|
50
|
+
cacheHits: number;
|
|
51
|
+
/** Calls to a proposal or reflection model, outside the metric budget. */
|
|
52
|
+
reflectionCalls: number;
|
|
49
53
|
costUsd: number;
|
|
50
54
|
stopReason: string;
|
|
51
55
|
}
|
|
@@ -58,12 +62,33 @@ interface ComparisonSummary {
|
|
|
58
62
|
maxScore: number;
|
|
59
63
|
meanMetricCalls: number;
|
|
60
64
|
meanCostUsd: number;
|
|
65
|
+
meanCacheHits: number;
|
|
66
|
+
meanReflectionCalls: number;
|
|
67
|
+
/**
|
|
68
|
+
* How many distinct values this entrant's score took across its seeds.
|
|
69
|
+
* 1 means every seed landed on the same number — the seed changed nothing
|
|
70
|
+
* about the outcome, whatever the search did internally with it.
|
|
71
|
+
*/
|
|
72
|
+
distinctScores: number;
|
|
61
73
|
/**
|
|
62
74
|
* How often the winner's margin over this entrant would arise if the two
|
|
63
|
-
* were equally good and each seed's outcome were a coin flip. Absent for
|
|
64
|
-
* winner itself
|
|
75
|
+
* were equally good and each seed's outcome were a coin flip. Absent for
|
|
76
|
+
* the winner itself, and also absent when every seed produced the exact
|
|
77
|
+
* same margin: a sign-flip test over n seeds is answering a question about
|
|
78
|
+
* n independent trials, and identical margins mean the seed never actually
|
|
79
|
+
* put that to the test — there was one realization, repeated. Reporting a
|
|
80
|
+
* p-value there would state a precision (as fine as 2^-n) that the run
|
|
81
|
+
* never earned, so it is withheld rather than printed misleadingly small.
|
|
65
82
|
*/
|
|
66
83
|
pValueVsWinner?: number;
|
|
84
|
+
/**
|
|
85
|
+
* `pValueVsWinner` after Holm-Bonferroni step-down across the other
|
|
86
|
+
* entrants in this same `compare()` call — the family the raw p-value
|
|
87
|
+
* would otherwise be read against in isolation. Absent wherever the raw
|
|
88
|
+
* p-value is: a withheld comparison has nothing to adjust, but it still
|
|
89
|
+
* occupies a slot in the family the other comparisons are corrected for.
|
|
90
|
+
*/
|
|
91
|
+
pValueVsWinnerHolm?: number;
|
|
67
92
|
}
|
|
68
93
|
interface Comparison {
|
|
69
94
|
/** Highest mean score. Read `pValueVsWinner` before believing it. */
|
|
@@ -85,7 +110,7 @@ interface Comparison {
|
|
|
85
110
|
* seed is constructor config and every optimizer here is deterministic given
|
|
86
111
|
* one — comparing two entrants at a single seed compares two anecdotes. It also
|
|
87
112
|
* leaves the task where the caller builds it, which is the only place the
|
|
88
|
-
* optimizer-specific parts of it (`reflect`, `cache`, `
|
|
113
|
+
* optimizer-specific parts of it (`reflect`, `cache`, `reporters`) are known.
|
|
89
114
|
*/
|
|
90
115
|
declare function compare<K extends string, Output = unknown>(args: {
|
|
91
116
|
entrants: Record<string, (args: {
|
|
@@ -136,6 +161,119 @@ declare function createDeadline(args: {
|
|
|
136
161
|
now?: () => number;
|
|
137
162
|
}): Deadline;
|
|
138
163
|
//#endregion
|
|
164
|
+
//#region src/harvest.d.ts
|
|
165
|
+
/**
|
|
166
|
+
* One rollout worth keeping: what went in, what the system produced, and how
|
|
167
|
+
* that output scored. Harvested, never written — the value of the pair is that
|
|
168
|
+
* the system actually produced the output and the metric actually rewarded it.
|
|
169
|
+
*/
|
|
170
|
+
interface Rollout<Datum = unknown, Output = unknown> {
|
|
171
|
+
input: Datum;
|
|
172
|
+
output: Output;
|
|
173
|
+
score: number;
|
|
174
|
+
}
|
|
175
|
+
interface HarvestResult<Datum, Output> {
|
|
176
|
+
rollouts: Rollout<Datum, Output>[];
|
|
177
|
+
/** Rollouts this cost. Harvesting is cheap, not free. */
|
|
178
|
+
metricCalls: number;
|
|
179
|
+
/**
|
|
180
|
+
* Tokens and dollars this cost. Harvesting runs on its own evaluator, so a
|
|
181
|
+
* caller that bounds spend has to fold these into its own totals.
|
|
182
|
+
*/
|
|
183
|
+
usage: UsageTotals;
|
|
184
|
+
/** Instances run, including the ones the metric did not reward. */
|
|
185
|
+
attempted: number;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Run a candidate over data and keep the rollouts the metric rewarded.
|
|
189
|
+
*
|
|
190
|
+
* The library's one paid collection primitive, with two consumers: a few-shot
|
|
191
|
+
* block wants four of these, and a distillation set wants thousands. Both are
|
|
192
|
+
* the same pass — run the candidate, score it, keep what cleared the bar — so
|
|
193
|
+
* both share the budget, retry and transient-failure handling that pass needs.
|
|
194
|
+
*
|
|
195
|
+
* Which data to sweep is the caller's decision and the consequential one. A
|
|
196
|
+
* validation set is the wrong choice: it is the set that selected the candidate,
|
|
197
|
+
* so the rollouts it yields are enriched for the candidate's fit to those
|
|
198
|
+
* instances rather than to the task. Prefer the training set, or a pool held
|
|
199
|
+
* out of the run entirely.
|
|
200
|
+
*/
|
|
201
|
+
declare function harvestRollouts<Datum, Trajectory, Output, K extends string = string>(args: {
|
|
202
|
+
adapter: Adapter<Datum, Trajectory, Output, K>;
|
|
203
|
+
/** The candidate to run. Usually a run's winner, sometimes the seed. */
|
|
204
|
+
candidate: Candidate<K>;
|
|
205
|
+
data: readonly Datum[];
|
|
206
|
+
/**
|
|
207
|
+
* Score a rollout must reach to be kept. Unset keeps every rollout the
|
|
208
|
+
* metric rewarded at all, which is what MIPROv2's bootstrapper does without
|
|
209
|
+
* a `metric_threshold`: it keeps a trace on any truthy score and only
|
|
210
|
+
* compares against a number once one is configured.
|
|
211
|
+
*
|
|
212
|
+
* Demanding a perfect score instead is the right call for a boolean metric
|
|
213
|
+
* and the wrong one for a graded metric, where it throws away every rollout
|
|
214
|
+
* that was most of the way there — which on a hard task is all of them.
|
|
215
|
+
*/
|
|
216
|
+
minScore?: number;
|
|
217
|
+
/** Rollouts to collect before stopping. Unset sweeps the whole pool. */
|
|
218
|
+
maxRollouts?: number;
|
|
219
|
+
/**
|
|
220
|
+
* Instances per rollout batch. Smaller batches stop closer to the moment
|
|
221
|
+
* enough rollouts exist, at the cost of less concurrency inside the adapter.
|
|
222
|
+
*/
|
|
223
|
+
batchSize?: number;
|
|
224
|
+
/** Ceiling on rollouts run. Defaults to one pass over `data`. */
|
|
225
|
+
maxMetricCalls?: number;
|
|
226
|
+
/**
|
|
227
|
+
* Ceiling on dollars this pass may spend, checked between batches. Rollout
|
|
228
|
+
* counts are a poor proxy for spend, and a caller bounding dollars cannot
|
|
229
|
+
* bound this pass from outside: it runs on its own evaluator.
|
|
230
|
+
*/
|
|
231
|
+
maxCostUsd?: number;
|
|
232
|
+
/** Shuffles `data` first, so rollouts are not all drawn from its head. */
|
|
233
|
+
rng?: Rng;
|
|
234
|
+
signal?: AbortSignal;
|
|
235
|
+
}): Promise<HarvestResult<Datum, Output>>;
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/distillation.d.ts
|
|
238
|
+
/** A turn in a training example, in the shape trainers read. */
|
|
239
|
+
interface ChatMessage {
|
|
240
|
+
role: "system" | "user" | "assistant";
|
|
241
|
+
content: string;
|
|
242
|
+
}
|
|
243
|
+
interface TrainingExample {
|
|
244
|
+
messages: ChatMessage[];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Turns one harvested rollout into the example to train on, or `null` to skip
|
|
248
|
+
* it. The callback exists because only the caller knows how to render a
|
|
249
|
+
* `Datum` — the library never sees inside one.
|
|
250
|
+
*
|
|
251
|
+
* It is also where the consequential decision lives: how much of the optimized
|
|
252
|
+
* candidate to leave in the input. Dropping it entirely moves the whole prompt
|
|
253
|
+
* into weights and gives up the ability to steer the model with text
|
|
254
|
+
* afterwards; keeping a short task statement distills away only the tokens the
|
|
255
|
+
* search accreted. See `docs/distillation.md`.
|
|
256
|
+
*/
|
|
257
|
+
type TrainingExampleRenderer<Datum, Output> = (args: {
|
|
258
|
+
rollout: Rollout<Datum, Output>;
|
|
259
|
+
index: number;
|
|
260
|
+
}) => TrainingExample | null;
|
|
261
|
+
/**
|
|
262
|
+
* Serialize harvested rollouts as JSONL, one training example per line.
|
|
263
|
+
*
|
|
264
|
+
* The chat-messages shape rather than any one vendor's: it is what Axolotl,
|
|
265
|
+
* Together, Fireworks and the Hugging Face trainers all ingest, and the
|
|
266
|
+
* providers that read it outlive the ones that do not.
|
|
267
|
+
*
|
|
268
|
+
* Returns the text rather than writing it. Only `file-cache` touches the
|
|
269
|
+
* filesystem, and a caller uploading this straight to a provider should not
|
|
270
|
+
* have to round-trip it through a file to do so.
|
|
271
|
+
*/
|
|
272
|
+
declare function toTrainingJsonl<Datum, Output>(args: {
|
|
273
|
+
rollouts: readonly Rollout<Datum, Output>[];
|
|
274
|
+
render: TrainingExampleRenderer<Datum, Output>;
|
|
275
|
+
}): string;
|
|
276
|
+
//#endregion
|
|
139
277
|
//#region src/judge.d.ts
|
|
140
278
|
/** One thing the judge grades, and what a perfect answer looks like for it. */
|
|
141
279
|
interface JudgeCriterion {
|
|
@@ -218,4 +356,4 @@ declare function priceUsage(args: {
|
|
|
218
356
|
pricing?: TokenPricing;
|
|
219
357
|
}): RolloutUsage;
|
|
220
358
|
//#endregion
|
|
221
|
-
export { type Adapter, type BatchSampler, type BootstrapResult, type Budget, BudgetExhausted, type CachedScore, type Candidate, type Comparison, type ComparisonRun, type ComparisonSummary, type Deadline, type Demo, type DemoRenderer, type EvaluateArgs, type EvaluateBatchArgs, type EvaluateTracedArgs, type EvaluationBatch, type EvaluationCache, type EvaluationContext, type EvaluationEvent, type EvaluationPhase, type EvaluationSplit, type Evaluator, type Judge, type JudgeCriterion, type JudgePromptBuilder, type Optimizer, type OptimizerResult, type OptimizerTask, type RetryPolicy, type Rng, type RolloutUsage, type ScoreResult, type ScoredBatch, type TextModel, type TokenPricing, type UsageTotals, assertResumable,
|
|
359
|
+
export { type Adapter, type BatchSampler, type BootstrapResult, type Budget, BudgetExhausted, type CachedScore, type Candidate, type CandidateAccepted, type ChatMessage, type Comparison, type ComparisonRun, type ComparisonSummary, type Deadline, type Demo, type DemoRenderer, type EvaluateArgs, type EvaluateBatchArgs, type EvaluateTracedArgs, type EvaluationBatch, type EvaluationCache, type EvaluationContext, type EvaluationEvent, type EvaluationPhase, type EvaluationSplit, type Evaluator, type HarvestResult, type Judge, type JudgeCriterion, type JudgePromptBuilder, type Optimizer, type OptimizerEvent, type OptimizerResult, type OptimizerTask, type ReportableEvent, type Reporter, type RetryPolicy, type Rng, type Rollout, type RolloutUsage, type RunFinished, type ScoreResult, type ScoredBatch, type TextModel, type TokenPricing, type TrainingExample, type TrainingExampleRenderer, type UsageTotals, assertResumable, buildJudgePrompt, candidateFingerprint, compare, componentNames, costExhausted, createBudget, createDeadline, createEvaluator, createJudge, createMemoryCache, formatDemos, harvestFewShotExamples, harvestRollouts, isCandidateAccepted, isRunFinished, mapWithConcurrency, measuredMean, parseDemos, parseProposedText, priceUsage, requireMeasuredMean, runFingerprint, toTrainingJsonl };
|