textopt 0.1.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.
Files changed (56) hide show
  1. package/README.md +24 -6
  2. package/dist/bootstrap-search/index.cjs +29 -23
  3. package/dist/bootstrap-search/index.d.cts +3 -3
  4. package/dist/bootstrap-search/index.d.mts +3 -3
  5. package/dist/bootstrap-search/index.mjs +9 -3
  6. package/dist/{demos-D2o0qSSo.cjs → demos-9v5ts7F3.cjs} +4 -4
  7. package/dist/{demos-1b7JiUue.d.mts → demos-ASsSXYXA.d.mts} +1 -1
  8. package/dist/{demos-DE2oxNWX.mjs → demos-Brobjfuc.mjs} +1 -1
  9. package/dist/{demos-CU9dy2oT.d.cts → demos-ByaLZy-Z.d.cts} +1 -1
  10. package/dist/file-cache.cjs +16 -5
  11. package/dist/file-cache.d.cts +13 -0
  12. package/dist/file-cache.d.mts +13 -0
  13. package/dist/file-cache.mjs +16 -5
  14. package/dist/gepa/index.cjs +87 -44
  15. package/dist/gepa/index.d.cts +8 -6
  16. package/dist/gepa/index.d.mts +8 -6
  17. package/dist/gepa/index.mjs +55 -12
  18. package/dist/index.cjs +44 -19
  19. package/dist/index.d.cts +37 -5
  20. package/dist/index.d.mts +37 -5
  21. package/dist/index.mjs +29 -6
  22. package/dist/mipro/index.cjs +31 -25
  23. package/dist/mipro/index.d.cts +3 -3
  24. package/dist/mipro/index.d.mts +3 -3
  25. package/dist/mipro/index.mjs +9 -3
  26. package/dist/opro/index.cjs +31 -25
  27. package/dist/opro/index.d.cts +2 -2
  28. package/dist/opro/index.d.mts +2 -2
  29. package/dist/opro/index.mjs +8 -2
  30. package/dist/{optimizer-Bh5DPRMH.d.cts → optimizer-4Zv-Zt2t.d.cts} +41 -2
  31. package/dist/{optimizer-Ck6-e_8o.d.mts → optimizer-Ds5mzYjz.d.mts} +41 -2
  32. package/dist/random-search/index.cjs +27 -21
  33. package/dist/random-search/index.d.cts +2 -2
  34. package/dist/random-search/index.d.mts +2 -2
  35. package/dist/random-search/index.mjs +8 -2
  36. package/dist/{reflection-LRaAZP4e.d.mts → reflection-CMezGu6u.d.mts} +32 -4
  37. package/dist/{reflection-Dt3QrXhM.d.cts → reflection-D0A7eahD.d.cts} +32 -4
  38. package/dist/{reporting-DQbAohc9.d.cts → reporting-bq007_2z.d.cts} +55 -1
  39. package/dist/{reporting-DQbAohc9.d.mts → reporting-bq007_2z.d.mts} +55 -1
  40. package/dist/simba/index.cjs +31 -25
  41. package/dist/simba/index.d.cts +24 -5
  42. package/dist/simba/index.d.mts +24 -5
  43. package/dist/simba/index.mjs +9 -3
  44. package/dist/testing.d.cts +2 -2
  45. package/dist/testing.d.mts +2 -2
  46. package/dist/{reporting-CNHzbJC-.cjs → warnings-CWRJF-jA.cjs} +63 -0
  47. package/dist/{reporting-DY-DC4HG.mjs → warnings-OxvDi9kN.mjs} +52 -1
  48. package/docs/adapters.md +169 -0
  49. package/docs/benchmark.md +90 -0
  50. package/docs/data-prep.md +113 -0
  51. package/docs/distillation.md +128 -0
  52. package/docs/evaluation.md +87 -0
  53. package/docs/metric-preflight.md +132 -0
  54. package/docs/optimizers.md +293 -0
  55. package/docs/tuning.md +130 -0
  56. package/package.json +3 -2
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reporting = require("../reporting-CNHzbJC-.cjs");
2
+ const require_warnings = require("../warnings-CWRJF-jA.cjs");
3
3
  const require_concurrency = require("../concurrency-C-cFzWW2.cjs");
4
4
  const require_math = require("../math-BhlziRPc.cjs");
5
- const require_demos = require("../demos-D2o0qSSo.cjs");
5
+ const require_demos = require("../demos-9v5ts7F3.cjs");
6
6
  const require_rng = require("../rng-DbA_rPIo.cjs");
7
7
  const require_reflection = require("../reflection-DRfbk6hu.cjs");
8
8
  const require_sampling = require("../sampling-Dars7ctR.cjs");
@@ -334,7 +334,7 @@ function isEligibleAncestor(args) {
334
334
  */
335
335
  function hasComplementaryComponent(args) {
336
336
  const { ancestor, left, right } = args;
337
- return require_reporting.componentNames(ancestor).some((name) => {
337
+ return require_warnings.componentNames(ancestor).some((name) => {
338
338
  const base = ancestor[name];
339
339
  return (base === left[name] || base === right[name]) && left[name] !== right[name];
340
340
  });
@@ -347,7 +347,7 @@ function mergeComponents(args) {
347
347
  const rightRecord = records[rightId];
348
348
  const candidate = { ...ancestor };
349
349
  const sources = [];
350
- for (const name of require_reporting.componentNames(ancestor)) {
350
+ for (const name of require_warnings.componentNames(ancestor)) {
351
351
  const base = ancestor[name];
352
352
  const leftText = leftRecord.candidate[name];
353
353
  const rightText = rightRecord.candidate[name];
@@ -391,7 +391,7 @@ function buildAncestries(records) {
391
391
  return ancestries;
392
392
  }
393
393
  function fingerprint(candidate) {
394
- return JSON.stringify(require_reporting.componentNames(candidate).sort().map((name) => [name, candidate[name]]));
394
+ return JSON.stringify(require_warnings.componentNames(candidate).sort().map((name) => [name, candidate[name]]));
395
395
  }
396
396
  //#endregion
397
397
  //#region src/gepa/pareto.ts
@@ -513,7 +513,9 @@ function hasSurvivor(args) {
513
513
  * `frontier` chooses what the fronts are taken over. "instance" is GEPA as
514
514
  * published. "objective" tracks candidates leading each named objective the
515
515
  * adapter reports, and "hybrid" pools both — a candidate then earns selection
516
- * weight for every instance it wins *and* every objective it leads.
516
+ * weight for every instance it wins *and* every objective it leads. The
517
+ * objectives are whatever the adapter put in `objectiveScores`, which for a
518
+ * judge is every criterion it graded, including any at `weight: 0`.
517
519
  */
518
520
  function paretoSelector(args = {}) {
519
521
  const { epsilon = 0, frontier = "instance" } = args;
@@ -628,14 +630,14 @@ function subsampledEvaluationPolicy(args) {
628
630
  */
629
631
  function roundRobinComponentSelector() {
630
632
  return ({ candidate, cursor }) => {
631
- const names = require_reporting.componentNames(candidate);
633
+ const names = require_warnings.componentNames(candidate);
632
634
  if (names.length === 0) throw new Error("Candidate has no components to update");
633
635
  return [names[cursor % names.length]];
634
636
  };
635
637
  }
636
638
  /** Update every component in a single reflection call. */
637
639
  function allComponentsSelector() {
638
- return ({ candidate }) => require_reporting.componentNames(candidate);
640
+ return ({ candidate }) => require_warnings.componentNames(candidate);
639
641
  }
640
642
  /**
641
643
  * Accept a mutation only when it beats its parent on the same minibatch. Cheap
@@ -660,7 +662,8 @@ function improvementAcceptance(args = {}) {
660
662
  */
661
663
  function pairedPermutationAcceptance(args = {}) {
662
664
  const { alpha = .2, maxExact = 16 } = args;
663
- return ({ parentScores, childScores }) => {
665
+ if (!Number.isFinite(alpha) || alpha <= 0 || alpha > 1) throw new Error(`alpha must be greater than 0 and at most 1, received ${alpha}`);
666
+ const policy = ({ parentScores, childScores }) => {
664
667
  const differences = [];
665
668
  for (let index = 0; index < parentScores.length; index += 1) differences.push(childScores[index] - parentScores[index]);
666
669
  const observed = require_math.sum(differences);
@@ -671,6 +674,33 @@ function pairedPermutationAcceptance(args = {}) {
671
674
  maxExact
672
675
  }) <= alpha;
673
676
  };
677
+ policy.minimumPairs = smallestAcceptableBatch({
678
+ alpha,
679
+ maxExact
680
+ });
681
+ return policy;
682
+ }
683
+ /**
684
+ * Smallest paired batch on which a sign-flip test at `alpha` could return a
685
+ * verdict of "accept" at all.
686
+ *
687
+ * Enumerating n non-zero differences gives 2^n equally likely sign
688
+ * assignments, so the smallest attainable p-value is 2^-n and no batch below
689
+ * `log2(1/alpha)` pairs can clear the bar. Past `maxExact` the p-value comes
690
+ * from a normal approximation, which has no such floor — so when the exact
691
+ * requirement is out of that regime's reach, the first batch that leaves the
692
+ * regime is the honest answer rather than an exact size that never applies.
693
+ */
694
+ function smallestAcceptableBatch(args) {
695
+ const { alpha, maxExact } = args;
696
+ let pairs = 1;
697
+ let smallestPValue = .5;
698
+ while (smallestPValue > alpha) {
699
+ pairs += 1;
700
+ smallestPValue /= 2;
701
+ if (pairs > maxExact) return maxExact + 1;
702
+ }
703
+ return pairs;
674
704
  }
675
705
  /**
676
706
  * Highest mean over the instances it was scored on, with wider coverage
@@ -745,16 +775,20 @@ var GepaOptimizer = class {
745
775
  task
746
776
  });
747
777
  } finally {
748
- await require_reporting.flushReporters(task.reporters ?? []);
778
+ await require_warnings.flushReporters(task.reporters ?? []);
749
779
  }
750
780
  }
751
781
  };
752
782
  async function runGepa(args) {
753
783
  const { config, task } = args;
754
784
  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;
755
- const { seedCandidate, trainingSet, validationSet = trainingSet, testSet, adapter, reflect, maxMetricCalls, componentSelector = roundRobinComponentSelector(), batchSampler = require_sampling.createEpochShuffledSampler({ minibatchSize }), valEvaluationPolicy = fullEvaluationPolicy(), cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_reporting.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
756
- const deadline = require_reporting.createDeadline({ maxWallClockMs });
757
- const seedComponents = require_reporting.componentNames(seedCandidate);
785
+ const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, maxMetricCalls, componentSelector = roundRobinComponentSelector(), batchSampler = require_sampling.createEpochShuffledSampler({ minibatchSize }), valEvaluationPolicy = fullEvaluationPolicy(), cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = require_warnings.defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
786
+ const { validationSet, warnings } = require_warnings.resolveValidationSet({
787
+ validationSet: requestedValidationSet,
788
+ trainingSet
789
+ });
790
+ const deadline = require_warnings.createDeadline({ maxWallClockMs });
791
+ const seedComponents = require_warnings.componentNames(seedCandidate);
758
792
  const mergeConfig = {
759
793
  enabled: merge?.enabled ?? seedComponents.length > 1,
760
794
  maxInvocations: merge?.maxInvocations ?? DEFAULT_MAX_MERGES,
@@ -767,7 +801,7 @@ async function runGepa(args) {
767
801
  if (validationSet.length === 0) throw new Error("optimize requires a non-empty validationSet; the Pareto frontier is tracked over validation instances");
768
802
  if (seedComponents.length === 0) throw new Error("optimize requires a seed candidate with at least one component");
769
803
  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");
770
- const evaluationCache = cache === false ? void 0 : cache ?? require_reporting.createMemoryCache();
804
+ const evaluationCache = cache === false ? void 0 : cache ?? require_warnings.createMemoryCache();
771
805
  const propose = adapter.proposeNewTexts?.bind(adapter) ?? require_reflection.createDefaultProposer({
772
806
  ...reflection?.buildPrompt === void 0 ? {} : { buildPrompt: reflection.buildPrompt },
773
807
  ...reflection?.strategies === void 0 ? {} : { strategies: reflection.strategies },
@@ -788,19 +822,19 @@ async function runGepa(args) {
788
822
  datum,
789
823
  index
790
824
  })) ?? [];
791
- const fingerprint = require_reporting.runFingerprint({
825
+ const fingerprint = require_warnings.runFingerprint({
792
826
  seedCandidate,
793
827
  trainingIds,
794
828
  validationIds,
795
829
  seed,
796
830
  ...cacheNamespace === void 0 ? {} : { cacheNamespace }
797
831
  });
798
- require_reporting.assertResumable({
832
+ require_warnings.assertResumable({
799
833
  fingerprint,
800
834
  ...resumeFrom === void 0 ? {} : { snapshot: resumeFrom }
801
835
  });
802
836
  const rng = require_rng.createSeededRng(seed, resumeFrom?.rngState);
803
- const budget = require_reporting.createBudget({
837
+ const budget = require_warnings.createBudget({
804
838
  maxMetricCalls,
805
839
  spent: resumeFrom?.metricCalls ?? 0
806
840
  });
@@ -820,7 +854,7 @@ async function runGepa(args) {
820
854
  records: resumeFrom?.records ?? [],
821
855
  seedCandidate
822
856
  });
823
- const seenCandidates = new Set(records.map((record) => require_reporting.candidateFingerprint(record.candidate)));
857
+ const seenCandidates = new Set(records.map((record) => require_warnings.candidateFingerprint(record.candidate)));
824
858
  const outputsByCandidate = /* @__PURE__ */ new Map();
825
859
  const rejectedProposals = restoreRejections({
826
860
  rejections: resumeFrom?.rejectedProposals ?? {},
@@ -832,7 +866,7 @@ async function runGepa(args) {
832
866
  let mergesDue = resumeFrom?.merge.due ?? 0;
833
867
  let totalMergesTested = resumeFrom?.merge.tested ?? 0;
834
868
  let lastIterationAccepted = resumeFrom?.merge.lastIterationAccepted ?? false;
835
- const emit = require_reporting.createEmitter(reporters);
869
+ const emit = require_warnings.createEmitter(reporters);
836
870
  /**
837
871
  * Everything an acceptance means, in one event: the text, the aggregate, and
838
872
  * the row it put on the frontier. Emitted from one place because the merge
@@ -889,7 +923,7 @@ async function runGepa(args) {
889
923
  if (onCheckpoint === void 0) return;
890
924
  await onCheckpoint(takeSnapshot());
891
925
  }
892
- const evaluator = require_reporting.createEvaluator({
926
+ const evaluator = require_warnings.createEvaluator({
893
927
  adapter,
894
928
  budget,
895
929
  ...retry === void 0 ? {} : { retry },
@@ -977,7 +1011,7 @@ async function runGepa(args) {
977
1011
  componentCursor: inheritedCursor(args.parentIds)
978
1012
  };
979
1013
  records.push(record);
980
- seenCandidates.add(require_reporting.candidateFingerprint(args.candidate));
1014
+ seenCandidates.add(require_warnings.candidateFingerprint(args.candidate));
981
1015
  if (trackBestOutputs) outputsByCandidate.set(record.id, args.evaluation.outputs);
982
1016
  if (args.source !== "merge") {
983
1017
  lastIterationAccepted = true;
@@ -989,7 +1023,7 @@ async function runGepa(args) {
989
1023
  function rememberRejection(args) {
990
1024
  const { proposed, parentScore, childScore } = args;
991
1025
  if (rejectedProposalMemory <= 0) return;
992
- for (const component of require_reporting.componentNames(proposed)) {
1026
+ for (const component of require_warnings.componentNames(proposed)) {
993
1027
  const text = proposed[component];
994
1028
  if (text === void 0) continue;
995
1029
  const history = rejectedProposals[component] ?? [];
@@ -1017,18 +1051,23 @@ async function runGepa(args) {
1017
1051
  if (records.length === 0) {
1018
1052
  const seedInstances = selectValInstances(seedCandidate);
1019
1053
  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`);
1054
+ const seedEvaluation = await evaluateValidation({
1055
+ candidate: seedCandidate,
1056
+ instances: seedInstances,
1057
+ phase: "seed",
1058
+ candidateId: 0
1059
+ });
1020
1060
  emitAccepted(addCandidate({
1021
1061
  candidate: seedCandidate,
1022
1062
  parentIds: [],
1023
- evaluation: await evaluateValidation({
1024
- candidate: seedCandidate,
1025
- instances: seedInstances,
1026
- phase: "seed",
1027
- candidateId: 0
1028
- }),
1063
+ evaluation: seedEvaluation,
1029
1064
  source: "seed",
1030
1065
  updatedComponents: []
1031
1066
  }));
1067
+ warnings.push(...require_warnings.seedScoreWarnings({
1068
+ scores: seedEvaluation.scores,
1069
+ perfectScore
1070
+ }));
1032
1071
  lastIterationAccepted = false;
1033
1072
  mergesDue = 0;
1034
1073
  await checkpoint();
@@ -1115,7 +1154,7 @@ async function runGepa(args) {
1115
1154
  parentIds: [...proposal.parentIds],
1116
1155
  evaluation,
1117
1156
  source: "merge",
1118
- updatedComponents: require_reporting.componentNames(proposal.candidate).filter((name) => proposal.candidate[name] !== ancestor.candidate[name])
1157
+ updatedComponents: require_warnings.componentNames(proposal.candidate).filter((name) => proposal.candidate[name] !== ancestor.candidate[name])
1119
1158
  });
1120
1159
  mergesDue -= 1;
1121
1160
  totalMergesTested += 1;
@@ -1158,7 +1197,7 @@ async function runGepa(args) {
1158
1197
  candidate: parent.candidate,
1159
1198
  source: "componentSelector"
1160
1199
  });
1161
- parent.componentCursor = (parent.componentCursor + 1) % Math.max(1, require_reporting.componentNames(parent.candidate).length);
1200
+ parent.componentCursor = (parent.componentCursor + 1) % Math.max(1, require_warnings.componentNames(parent.candidate).length);
1162
1201
  plans.push({
1163
1202
  parent,
1164
1203
  batch: batchIndices.map((index) => trainingSet[index]),
@@ -1208,7 +1247,7 @@ async function runGepa(args) {
1208
1247
  throw err;
1209
1248
  }
1210
1249
  assertComponents({
1211
- names: require_reporting.componentNames(proposed),
1250
+ names: require_warnings.componentNames(proposed),
1212
1251
  candidate: parent.candidate,
1213
1252
  source: "proposeNewTexts"
1214
1253
  });
@@ -1216,7 +1255,7 @@ async function runGepa(args) {
1216
1255
  ...parent.candidate,
1217
1256
  ...proposed
1218
1257
  };
1219
- const changed = require_reporting.componentNames(proposed).length > 0 && !seenCandidates.has(require_reporting.candidateFingerprint(child));
1258
+ const changed = require_warnings.componentNames(proposed).length > 0 && !seenCandidates.has(require_warnings.candidateFingerprint(child));
1220
1259
  emit({
1221
1260
  type: "proposal",
1222
1261
  iteration,
@@ -1236,7 +1275,7 @@ async function runGepa(args) {
1236
1275
  candidateId: null
1237
1276
  });
1238
1277
  } catch (err) {
1239
- if (err instanceof require_reporting.BudgetExhausted) return { status: "budgetExhausted" };
1278
+ if (err instanceof require_warnings.BudgetExhausted) return { status: "budgetExhausted" };
1240
1279
  throw err;
1241
1280
  }
1242
1281
  const screened = pairMeasured({
@@ -1279,7 +1318,7 @@ async function runGepa(args) {
1279
1318
  stop ??= "reflectionBudgetExhausted";
1280
1319
  continue;
1281
1320
  }
1282
- const fingerprint = require_reporting.candidateFingerprint(outcome.child);
1321
+ const fingerprint = require_warnings.candidateFingerprint(outcome.child);
1283
1322
  if (claimed.has(fingerprint)) continue;
1284
1323
  claimed.add(fingerprint);
1285
1324
  if (!outcome.accepted) {
@@ -1351,7 +1390,7 @@ async function runGepa(args) {
1351
1390
  })
1352
1391
  };
1353
1392
  } catch (err) {
1354
- if (err instanceof require_reporting.BudgetExhausted) return {
1393
+ if (err instanceof require_warnings.BudgetExhausted) return {
1355
1394
  item,
1356
1395
  evaluation: void 0
1357
1396
  };
@@ -1369,7 +1408,7 @@ async function runGepa(args) {
1369
1408
  parentIds: [item.outcome.plan.parent.id],
1370
1409
  evaluation,
1371
1410
  source: "mutation",
1372
- updatedComponents: require_reporting.componentNames(item.outcome.proposed)
1411
+ updatedComponents: require_warnings.componentNames(item.outcome.proposed)
1373
1412
  }));
1374
1413
  }
1375
1414
  return stop;
@@ -1387,7 +1426,7 @@ async function runGepa(args) {
1387
1426
  stopReason = "aborted";
1388
1427
  break;
1389
1428
  }
1390
- if (require_reporting.costExhausted({
1429
+ if (require_warnings.costExhausted({
1391
1430
  usage: evaluator.usage(),
1392
1431
  maxCostUsd
1393
1432
  })) {
@@ -1434,7 +1473,7 @@ async function runGepa(args) {
1434
1473
  stopReason = "aborted";
1435
1474
  break;
1436
1475
  }
1437
- if (err instanceof require_reporting.BudgetExhausted) {
1476
+ if (err instanceof require_warnings.BudgetExhausted) {
1438
1477
  stopReason = "budgetExhausted";
1439
1478
  break;
1440
1479
  }
@@ -1463,15 +1502,16 @@ async function runGepa(args) {
1463
1502
  candidateId: bestCandidateId,
1464
1503
  charge: false
1465
1504
  });
1466
- const testScore = heldOut === void 0 ? void 0 : require_reporting.measuredMean(heldOut);
1505
+ const testScore = heldOut === void 0 ? void 0 : require_warnings.measuredMean(heldOut);
1467
1506
  emit({
1468
1507
  type: "finish",
1469
1508
  reason: stopReason,
1509
+ warnings,
1470
1510
  bestCandidateId,
1471
1511
  bestScore: best.aggregateScore,
1472
1512
  metricCalls: budget.spent(),
1473
1513
  ...testScore === void 0 ? {} : { testScore },
1474
- ...heldOut === void 0 ? {} : { testInstanceScores: require_reporting.instanceRow(heldOut) },
1514
+ ...heldOut === void 0 ? {} : { testInstanceScores: require_warnings.instanceRow(heldOut) },
1475
1515
  ...heldOut === void 0 || !trackBestOutputs ? {} : { testOutputs: heldOut.outputs }
1476
1516
  });
1477
1517
  const perObjectiveBest = collectPerObjectiveBest(records);
@@ -1495,6 +1535,7 @@ async function runGepa(args) {
1495
1535
  reflectionCalls,
1496
1536
  cacheHits: evaluator.cacheHits(),
1497
1537
  iterations: iteration,
1538
+ warnings,
1498
1539
  stopReason,
1499
1540
  snapshot: takeSnapshot()
1500
1541
  };
@@ -1507,13 +1548,15 @@ async function runGepa(args) {
1507
1548
  function assertGepaConfig(config) {
1508
1549
  if (config.reflection?.buildPrompt !== void 0 && config.reflection.strategies !== void 0) throw new Error("reflection takes buildPrompt or strategies, not both");
1509
1550
  if (config.reflection?.strategies?.length === 0) throw new Error("reflection.strategies must not be empty");
1510
- const { minibatchSize = DEFAULT_MINIBATCH_SIZE, maxIterations = Number.POSITIVE_INFINITY, perfectScore = 1, rejectedProposalMemory = DEFAULT_REJECTED_PROPOSAL_MEMORY, proposals } = config;
1551
+ const { minibatchSize = DEFAULT_MINIBATCH_SIZE, maxIterations = Number.POSITIVE_INFINITY, acceptance, perfectScore = 1, rejectedProposalMemory = DEFAULT_REJECTED_PROPOSAL_MEMORY, proposals } = config;
1511
1552
  const proposalsPerIteration = proposals?.perIteration ?? 1;
1512
1553
  const proposalConcurrency = proposals?.concurrency ?? 1;
1513
1554
  if (!Number.isInteger(proposalsPerIteration) || proposalsPerIteration < 1) throw new Error(`proposals.perIteration must be a positive integer, received ${proposalsPerIteration}`);
1514
1555
  if (!Number.isInteger(proposalConcurrency) || proposalConcurrency < 1) throw new Error(`proposals.concurrency must be a positive integer, received ${proposalConcurrency}`);
1515
1556
  keepCount(proposals?.selection ?? "all");
1516
1557
  if (!Number.isInteger(minibatchSize) || minibatchSize < 1) throw new Error(`minibatchSize must be a positive integer, received ${minibatchSize}`);
1558
+ const minimumPairs = acceptance?.minimumPairs;
1559
+ if (minimumPairs !== void 0 && minibatchSize < minimumPairs) throw new Error(`this acceptance policy cannot accept anything on fewer than ${minimumPairs} instances, but minibatchSize is ${minibatchSize}; raise minibatchSize or loosen the policy`);
1517
1560
  if (!Number.isFinite(perfectScore)) throw new Error(`perfectScore must be a finite number, received ${perfectScore}`);
1518
1561
  if (!Number.isInteger(rejectedProposalMemory) || rejectedProposalMemory < 0) throw new Error(`rejectedProposalMemory must be a non-negative integer, received ${rejectedProposalMemory}`);
1519
1562
  if (maxIterations !== Number.POSITIVE_INFINITY && (!Number.isInteger(maxIterations) || maxIterations < 0)) throw new Error(`maxIterations must be a non-negative integer or Infinity, received ${maxIterations}`);
@@ -1527,7 +1570,7 @@ function assertGepaConfig(config) {
1527
1570
  */
1528
1571
  function restoreRecords(args) {
1529
1572
  const { records, seedCandidate } = args;
1530
- const known = new Set(require_reporting.componentNames(seedCandidate));
1573
+ const known = new Set(require_warnings.componentNames(seedCandidate));
1531
1574
  for (const record of records) {
1532
1575
  const named = [...Object.keys(record.candidate), ...record.updatedComponents];
1533
1576
  for (const name of named) if (!known.has(name)) throw new Error(`checkpoint names the component "${name}", which the seed candidate does not have (${[...known].join(", ")})`);
@@ -1666,8 +1709,8 @@ exports.diverseReflectionStrategies = require_reflection.diverseReflectionStrate
1666
1709
  exports.epsilonGreedySelector = epsilonGreedySelector;
1667
1710
  exports.fullEvaluationPolicy = fullEvaluationPolicy;
1668
1711
  exports.improvementAcceptance = improvementAcceptance;
1669
- exports.isCandidateAccepted = require_reporting.isCandidateAccepted;
1670
- exports.isRunFinished = require_reporting.isRunFinished;
1712
+ exports.isCandidateAccepted = require_warnings.isCandidateAccepted;
1713
+ exports.isRunFinished = require_warnings.isRunFinished;
1671
1714
  exports.lowerBoundEvaluationPolicy = lowerBoundEvaluationPolicy;
1672
1715
  exports.pairedPermutationAcceptance = pairedPermutationAcceptance;
1673
1716
  exports.paretoSelector = paretoSelector;
@@ -1,8 +1,8 @@
1
- import { _ as TextModel, a as RunFinished, g as ScoreResult, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted } from "../reporting-DQbAohc9.cjs";
1
+ import { a as RunFinished, b as ScoreResult, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.cjs";
2
2
  import { n as EvaluationCache } from "../cache-CuSo0NJ8.cjs";
3
- import { r as DemoRenderer } from "../demos-CU9dy2oT.cjs";
4
- import { n as OptimizerResult, r as OptimizerTask, t as Optimizer } from "../optimizer-Bh5DPRMH.cjs";
5
- import { C as RejectedProposal, S as ReflectiveRecord, T as ValEvaluationPolicy, _ as GepaStopReason, a as buildRewritePrompt, b as ProposeArgs, c as AcceptancePolicy, d as CandidateSource, f as ComponentPatch, g as GepaSnapshot, h as GepaEvent, i as buildReflectionPrompt, l as CandidateRecord, m as GepaAdapter, n as ReflectionPromptBuilder, o as buildSimplifyPrompt, p as ComponentSelector, r as buildGeneralizePrompt, s as diverseReflectionStrategies, t as ReflectionPromptArgs, u as CandidateSelector, v as MakeReflectiveDatasetArgs, w as SelectionState, x as ReflectiveDataset, y as ParetoFrontier } from "../reflection-Dt3QrXhM.cjs";
3
+ import { r as DemoRenderer } from "../demos-ByaLZy-Z.cjs";
4
+ import { n as OptimizerResult, r as OptimizerTask, t as Optimizer } from "../optimizer-4Zv-Zt2t.cjs";
5
+ import { C as ReflectiveRecord, E as ValEvaluationPolicy, S as ReflectiveDataset, T as SelectionState, _ as GepaStopReason, a as buildRewritePrompt, b as ProposeArgs, c as AcceptancePolicy, d as CandidateSource, f as ComponentPatch, g as GepaSnapshot, h as GepaEvent, i as buildReflectionPrompt, l as CandidateRecord, m as GepaAdapter, n as ReflectionPromptBuilder, o as buildSimplifyPrompt, p as ComponentSelector, r as buildGeneralizePrompt, s as diverseReflectionStrategies, t as ReflectionPromptArgs, u as CandidateSelector, v as MakeReflectiveDatasetArgs, w as RejectedProposal, x as ReflectiveBatch, y as ParetoFrontier } from "../reflection-D0A7eahD.cjs";
6
6
  import { t as BatchSampler } from "../sampling-axOwfZf5.cjs";
7
7
  //#region src/gepa/demos.d.ts
8
8
  /**
@@ -305,7 +305,9 @@ declare class GepaOptimizer implements Optimizer<GepaStopReason> {
305
305
  * `frontier` chooses what the fronts are taken over. "instance" is GEPA as
306
306
  * published. "objective" tracks candidates leading each named objective the
307
307
  * adapter reports, and "hybrid" pools both — a candidate then earns selection
308
- * weight for every instance it wins *and* every objective it leads.
308
+ * weight for every instance it wins *and* every objective it leads. The
309
+ * objectives are whatever the adapter put in `objectiveScores`, which for a
310
+ * judge is every criterion it graded, including any at `weight: 0`.
309
311
  */
310
312
  declare function paretoSelector(args?: {
311
313
  epsilon?: number;
@@ -388,4 +390,4 @@ declare function pairedPermutationAcceptance(args?: {
388
390
  maxExact?: number;
389
391
  }): AcceptancePolicy;
390
392
  //#endregion
391
- export { type AcceptancePolicy, type CandidateAccepted, type CandidateRecord, type CandidateSelector, type CandidateSource, type ComponentPatch, type ComponentSelector, type GepaAdapter, type GepaConfig, type GepaEvent, GepaOptimizer, type GepaResult, type GepaSnapshot, type GepaStopReason, type GepaTask, type MakeReflectiveDatasetArgs, type OptimizerEvent, type PipelineModule, type PipelineStep, type PipelineTrace, type ProposeArgs, type ReflectionPromptArgs, type ReflectionPromptBuilder, type ReflectiveDataset, type ReflectiveRecord, type RejectedProposal, type ReportableEvent, type Reporter, type RunFinished, type SelectionState, type ValEvaluationPolicy, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, isCandidateAccepted, isRunFinished, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
393
+ export { type AcceptancePolicy, type CandidateAccepted, type CandidateRecord, type CandidateSelector, type CandidateSource, type ComponentPatch, type ComponentSelector, type GepaAdapter, type GepaConfig, type GepaEvent, GepaOptimizer, type GepaResult, type GepaSnapshot, type GepaStopReason, type GepaTask, type MakeReflectiveDatasetArgs, type OptimizerEvent, type PipelineModule, type PipelineStep, type PipelineTrace, type ProposeArgs, type ReflectionPromptArgs, type ReflectionPromptBuilder, type ReflectiveBatch, type ReflectiveDataset, type ReflectiveRecord, type RejectedProposal, type ReportableEvent, type Reporter, type RunFinished, type SelectionState, type ValEvaluationPolicy, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, isCandidateAccepted, isRunFinished, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
@@ -1,8 +1,8 @@
1
- import { _ as TextModel, a as RunFinished, g as ScoreResult, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted } from "../reporting-DQbAohc9.mjs";
1
+ import { a as RunFinished, b as ScoreResult, i as Reporter, n as OptimizerEvent, o as isCandidateAccepted, r as ReportableEvent, s as isRunFinished, t as CandidateAccepted, x as TextModel } from "../reporting-bq007_2z.mjs";
2
2
  import { n as EvaluationCache } from "../cache-CuSo0NJ8.mjs";
3
- import { r as DemoRenderer } from "../demos-1b7JiUue.mjs";
4
- import { n as OptimizerResult, r as OptimizerTask, t as Optimizer } from "../optimizer-Ck6-e_8o.mjs";
5
- import { C as RejectedProposal, S as ReflectiveRecord, T as ValEvaluationPolicy, _ as GepaStopReason, a as buildRewritePrompt, b as ProposeArgs, c as AcceptancePolicy, d as CandidateSource, f as ComponentPatch, g as GepaSnapshot, h as GepaEvent, i as buildReflectionPrompt, l as CandidateRecord, m as GepaAdapter, n as ReflectionPromptBuilder, o as buildSimplifyPrompt, p as ComponentSelector, r as buildGeneralizePrompt, s as diverseReflectionStrategies, t as ReflectionPromptArgs, u as CandidateSelector, v as MakeReflectiveDatasetArgs, w as SelectionState, x as ReflectiveDataset, y as ParetoFrontier } from "../reflection-LRaAZP4e.mjs";
3
+ import { r as DemoRenderer } from "../demos-ASsSXYXA.mjs";
4
+ import { n as OptimizerResult, r as OptimizerTask, t as Optimizer } from "../optimizer-Ds5mzYjz.mjs";
5
+ import { C as ReflectiveRecord, E as ValEvaluationPolicy, S as ReflectiveDataset, T as SelectionState, _ as GepaStopReason, a as buildRewritePrompt, b as ProposeArgs, c as AcceptancePolicy, d as CandidateSource, f as ComponentPatch, g as GepaSnapshot, h as GepaEvent, i as buildReflectionPrompt, l as CandidateRecord, m as GepaAdapter, n as ReflectionPromptBuilder, o as buildSimplifyPrompt, p as ComponentSelector, r as buildGeneralizePrompt, s as diverseReflectionStrategies, t as ReflectionPromptArgs, u as CandidateSelector, v as MakeReflectiveDatasetArgs, w as RejectedProposal, x as ReflectiveBatch, y as ParetoFrontier } from "../reflection-CMezGu6u.mjs";
6
6
  import { t as BatchSampler } from "../sampling-DFo_7RNJ.mjs";
7
7
  //#region src/gepa/demos.d.ts
8
8
  /**
@@ -305,7 +305,9 @@ declare class GepaOptimizer implements Optimizer<GepaStopReason> {
305
305
  * `frontier` chooses what the fronts are taken over. "instance" is GEPA as
306
306
  * published. "objective" tracks candidates leading each named objective the
307
307
  * adapter reports, and "hybrid" pools both — a candidate then earns selection
308
- * weight for every instance it wins *and* every objective it leads.
308
+ * weight for every instance it wins *and* every objective it leads. The
309
+ * objectives are whatever the adapter put in `objectiveScores`, which for a
310
+ * judge is every criterion it graded, including any at `weight: 0`.
309
311
  */
310
312
  declare function paretoSelector(args?: {
311
313
  epsilon?: number;
@@ -388,4 +390,4 @@ declare function pairedPermutationAcceptance(args?: {
388
390
  maxExact?: number;
389
391
  }): AcceptancePolicy;
390
392
  //#endregion
391
- export { type AcceptancePolicy, type CandidateAccepted, type CandidateRecord, type CandidateSelector, type CandidateSource, type ComponentPatch, type ComponentSelector, type GepaAdapter, type GepaConfig, type GepaEvent, GepaOptimizer, type GepaResult, type GepaSnapshot, type GepaStopReason, type GepaTask, type MakeReflectiveDatasetArgs, type OptimizerEvent, type PipelineModule, type PipelineStep, type PipelineTrace, type ProposeArgs, type ReflectionPromptArgs, type ReflectionPromptBuilder, type ReflectiveDataset, type ReflectiveRecord, type RejectedProposal, type ReportableEvent, type Reporter, type RunFinished, type SelectionState, type ValEvaluationPolicy, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, isCandidateAccepted, isRunFinished, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
393
+ export { type AcceptancePolicy, type CandidateAccepted, type CandidateRecord, type CandidateSelector, type CandidateSource, type ComponentPatch, type ComponentSelector, type GepaAdapter, type GepaConfig, type GepaEvent, GepaOptimizer, type GepaResult, type GepaSnapshot, type GepaStopReason, type GepaTask, type MakeReflectiveDatasetArgs, type OptimizerEvent, type PipelineModule, type PipelineStep, type PipelineTrace, type ProposeArgs, type ReflectionPromptArgs, type ReflectionPromptBuilder, type ReflectiveBatch, type ReflectiveDataset, type ReflectiveRecord, type RejectedProposal, type ReportableEvent, type Reporter, type RunFinished, type SelectionState, type ValEvaluationPolicy, allComponentsSelector, buildGeneralizePrompt, buildReflectionPrompt, buildRewritePrompt, buildSimplifyPrompt, createDemoProposer, createPipelineAdapter, currentBestSelector, diverseReflectionStrategies, epsilonGreedySelector, fullEvaluationPolicy, improvementAcceptance, isCandidateAccepted, isRunFinished, lowerBoundEvaluationPolicy, pairedPermutationAcceptance, paretoSelector, roundRobinComponentSelector, subsampledEvaluationPolicy, topKParetoSelector };
@@ -1,7 +1,7 @@
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";
1
+ import { S as createBudget, _ as componentNames, a as instanceRow, b as defaultInstanceId, c as BudgetExhausted, d as measuredMean, g as runFingerprint, h as candidateFingerprint, i as flushReporters, l as costExhausted, m as assertResumable, n as seedScoreWarnings, o as isCandidateAccepted, p as createDeadline, r as createEmitter, s as isRunFinished, t as resolveValidationSet, u as createEvaluator, y as createMemoryCache } from "../warnings-OxvDi9kN.mjs";
2
2
  import { t as mapWithConcurrency } from "../concurrency-D58PWeSk.mjs";
3
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";
4
+ import { r as parseDemos, t as formatDemos } from "../demos-Brobjfuc.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";
@@ -512,7 +512,9 @@ function hasSurvivor(args) {
512
512
  * `frontier` chooses what the fronts are taken over. "instance" is GEPA as
513
513
  * published. "objective" tracks candidates leading each named objective the
514
514
  * adapter reports, and "hybrid" pools both — a candidate then earns selection
515
- * weight for every instance it wins *and* every objective it leads.
515
+ * weight for every instance it wins *and* every objective it leads. The
516
+ * objectives are whatever the adapter put in `objectiveScores`, which for a
517
+ * judge is every criterion it graded, including any at `weight: 0`.
516
518
  */
517
519
  function paretoSelector(args = {}) {
518
520
  const { epsilon = 0, frontier = "instance" } = args;
@@ -659,7 +661,8 @@ function improvementAcceptance(args = {}) {
659
661
  */
660
662
  function pairedPermutationAcceptance(args = {}) {
661
663
  const { alpha = .2, maxExact = 16 } = args;
662
- return ({ parentScores, childScores }) => {
664
+ if (!Number.isFinite(alpha) || alpha <= 0 || alpha > 1) throw new Error(`alpha must be greater than 0 and at most 1, received ${alpha}`);
665
+ const policy = ({ parentScores, childScores }) => {
663
666
  const differences = [];
664
667
  for (let index = 0; index < parentScores.length; index += 1) differences.push(childScores[index] - parentScores[index]);
665
668
  const observed = sum(differences);
@@ -670,6 +673,33 @@ function pairedPermutationAcceptance(args = {}) {
670
673
  maxExact
671
674
  }) <= alpha;
672
675
  };
676
+ policy.minimumPairs = smallestAcceptableBatch({
677
+ alpha,
678
+ maxExact
679
+ });
680
+ return policy;
681
+ }
682
+ /**
683
+ * Smallest paired batch on which a sign-flip test at `alpha` could return a
684
+ * verdict of "accept" at all.
685
+ *
686
+ * Enumerating n non-zero differences gives 2^n equally likely sign
687
+ * assignments, so the smallest attainable p-value is 2^-n and no batch below
688
+ * `log2(1/alpha)` pairs can clear the bar. Past `maxExact` the p-value comes
689
+ * from a normal approximation, which has no such floor — so when the exact
690
+ * requirement is out of that regime's reach, the first batch that leaves the
691
+ * regime is the honest answer rather than an exact size that never applies.
692
+ */
693
+ function smallestAcceptableBatch(args) {
694
+ const { alpha, maxExact } = args;
695
+ let pairs = 1;
696
+ let smallestPValue = .5;
697
+ while (smallestPValue > alpha) {
698
+ pairs += 1;
699
+ smallestPValue /= 2;
700
+ if (pairs > maxExact) return maxExact + 1;
701
+ }
702
+ return pairs;
673
703
  }
674
704
  /**
675
705
  * Highest mean over the instances it was scored on, with wider coverage
@@ -751,7 +781,11 @@ var GepaOptimizer = class {
751
781
  async function runGepa(args) {
752
782
  const { config, task } = args;
753
783
  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;
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;
784
+ const { seedCandidate, trainingSet, validationSet: requestedValidationSet, testSet, adapter, reflect, maxMetricCalls, componentSelector = roundRobinComponentSelector(), batchSampler = createEpochShuffledSampler({ minibatchSize }), valEvaluationPolicy = fullEvaluationPolicy(), cache, cacheNamespace, retry, maxCostUsd, maxWallClockMs, instanceId = defaultInstanceId, reporters = [], onCheckpoint, resumeFrom, signal } = task;
785
+ const { validationSet, warnings } = resolveValidationSet({
786
+ validationSet: requestedValidationSet,
787
+ trainingSet
788
+ });
755
789
  const deadline = createDeadline({ maxWallClockMs });
756
790
  const seedComponents = componentNames(seedCandidate);
757
791
  const mergeConfig = {
@@ -1016,18 +1050,23 @@ async function runGepa(args) {
1016
1050
  if (records.length === 0) {
1017
1051
  const seedInstances = selectValInstances(seedCandidate);
1018
1052
  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`);
1053
+ const seedEvaluation = await evaluateValidation({
1054
+ candidate: seedCandidate,
1055
+ instances: seedInstances,
1056
+ phase: "seed",
1057
+ candidateId: 0
1058
+ });
1019
1059
  emitAccepted(addCandidate({
1020
1060
  candidate: seedCandidate,
1021
1061
  parentIds: [],
1022
- evaluation: await evaluateValidation({
1023
- candidate: seedCandidate,
1024
- instances: seedInstances,
1025
- phase: "seed",
1026
- candidateId: 0
1027
- }),
1062
+ evaluation: seedEvaluation,
1028
1063
  source: "seed",
1029
1064
  updatedComponents: []
1030
1065
  }));
1066
+ warnings.push(...seedScoreWarnings({
1067
+ scores: seedEvaluation.scores,
1068
+ perfectScore
1069
+ }));
1031
1070
  lastIterationAccepted = false;
1032
1071
  mergesDue = 0;
1033
1072
  await checkpoint();
@@ -1466,6 +1505,7 @@ async function runGepa(args) {
1466
1505
  emit({
1467
1506
  type: "finish",
1468
1507
  reason: stopReason,
1508
+ warnings,
1469
1509
  bestCandidateId,
1470
1510
  bestScore: best.aggregateScore,
1471
1511
  metricCalls: budget.spent(),
@@ -1494,6 +1534,7 @@ async function runGepa(args) {
1494
1534
  reflectionCalls,
1495
1535
  cacheHits: evaluator.cacheHits(),
1496
1536
  iterations: iteration,
1537
+ warnings,
1497
1538
  stopReason,
1498
1539
  snapshot: takeSnapshot()
1499
1540
  };
@@ -1506,13 +1547,15 @@ async function runGepa(args) {
1506
1547
  function assertGepaConfig(config) {
1507
1548
  if (config.reflection?.buildPrompt !== void 0 && config.reflection.strategies !== void 0) throw new Error("reflection takes buildPrompt or strategies, not both");
1508
1549
  if (config.reflection?.strategies?.length === 0) throw new Error("reflection.strategies must not be empty");
1509
- const { minibatchSize = DEFAULT_MINIBATCH_SIZE, maxIterations = Number.POSITIVE_INFINITY, perfectScore = 1, rejectedProposalMemory = DEFAULT_REJECTED_PROPOSAL_MEMORY, proposals } = config;
1550
+ const { minibatchSize = DEFAULT_MINIBATCH_SIZE, maxIterations = Number.POSITIVE_INFINITY, acceptance, perfectScore = 1, rejectedProposalMemory = DEFAULT_REJECTED_PROPOSAL_MEMORY, proposals } = config;
1510
1551
  const proposalsPerIteration = proposals?.perIteration ?? 1;
1511
1552
  const proposalConcurrency = proposals?.concurrency ?? 1;
1512
1553
  if (!Number.isInteger(proposalsPerIteration) || proposalsPerIteration < 1) throw new Error(`proposals.perIteration must be a positive integer, received ${proposalsPerIteration}`);
1513
1554
  if (!Number.isInteger(proposalConcurrency) || proposalConcurrency < 1) throw new Error(`proposals.concurrency must be a positive integer, received ${proposalConcurrency}`);
1514
1555
  keepCount(proposals?.selection ?? "all");
1515
1556
  if (!Number.isInteger(minibatchSize) || minibatchSize < 1) throw new Error(`minibatchSize must be a positive integer, received ${minibatchSize}`);
1557
+ const minimumPairs = acceptance?.minimumPairs;
1558
+ if (minimumPairs !== void 0 && minibatchSize < minimumPairs) throw new Error(`this acceptance policy cannot accept anything on fewer than ${minimumPairs} instances, but minibatchSize is ${minibatchSize}; raise minibatchSize or loosen the policy`);
1516
1559
  if (!Number.isFinite(perfectScore)) throw new Error(`perfectScore must be a finite number, received ${perfectScore}`);
1517
1560
  if (!Number.isInteger(rejectedProposalMemory) || rejectedProposalMemory < 0) throw new Error(`rejectedProposalMemory must be a non-negative integer, received ${rejectedProposalMemory}`);
1518
1561
  if (maxIterations !== Number.POSITIVE_INFINITY && (!Number.isInteger(maxIterations) || maxIterations < 0)) throw new Error(`maxIterations must be a non-negative integer or Infinity, received ${maxIterations}`);