@tangle-network/agent-eval 0.195.1 → 0.196.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 (29) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/adapters/http.d.ts +1 -1
  3. package/dist/analyst/index.d.ts +1 -1
  4. package/dist/analyst/index.js +1 -1
  5. package/dist/{benchmark-command-D6ZXuRFF.js → benchmark-command-Bw1he2JM.js} +2 -2
  6. package/dist/{benchmark-command-D6ZXuRFF.js.map → benchmark-command-Bw1he2JM.js.map} +1 -1
  7. package/dist/benchmarks/index.d.ts +1 -1
  8. package/dist/benchmarks/index.js +1 -1
  9. package/dist/campaign/index.d.ts +2 -2
  10. package/dist/campaign/index.js +3 -3
  11. package/dist/{campaign-Df5qmzXW.js → campaign-qe1WC3Sz.js} +2 -2
  12. package/dist/{campaign-Df5qmzXW.js.map → campaign-qe1WC3Sz.js.map} +1 -1
  13. package/dist/cli.js +1 -1
  14. package/dist/contract/index.d.ts +3 -3
  15. package/dist/contract/index.js +1 -1
  16. package/dist/{define-agent-eval-C_oqRN9O.d.ts → define-agent-eval-DwAaPvvH.d.ts} +2 -2
  17. package/dist/{define-agent-eval-C_oqRN9O.d.ts.map → define-agent-eval-DwAaPvvH.d.ts.map} +1 -1
  18. package/dist/{index-D7o8xhFO.d.ts → index-CigojbNH.d.ts} +46 -13
  19. package/dist/index-CigojbNH.d.ts.map +1 -0
  20. package/dist/{index-B7omS8L-.d.ts → index-CpCIXObe.d.ts} +3 -3
  21. package/dist/{index-B7omS8L-.d.ts.map → index-CpCIXObe.d.ts.map} +1 -1
  22. package/dist/index.d.ts +2 -2
  23. package/dist/openapi.json +1 -1
  24. package/dist/{skillopt-optimization-method-B3Oq4iim.js → skillopt-optimization-method-IbEhlA1V.js} +112 -29
  25. package/dist/skillopt-optimization-method-IbEhlA1V.js.map +1 -0
  26. package/docs/search-ledger.md +2 -1
  27. package/package.json +1 -1
  28. package/dist/index-D7o8xhFO.d.ts.map +0 -1
  29. package/dist/skillopt-optimization-method-B3Oq4iim.js.map +0 -1
@@ -1413,18 +1413,22 @@ function isGepaCandidate(value, maxChars) {
1413
1413
  //#endregion
1414
1414
  //#region src/campaign/gepa-search-import.ts
1415
1415
  /**
1416
- * Import an optimizer's own record into a search ledger.
1416
+ * Import an external optimizer's own record into a search ledger.
1417
1417
  *
1418
- * GEPA searches inside its own process. Its candidate population names each
1419
- * candidate's parents by index, and the evaluation callback's observation log
1420
- * holds every candidate it evaluated. The importer turns both into ledger
1421
- * events: one node per distinct candidate, one `correlated` edge per population
1422
- * entry (joined by content digest from GEPA's own parent record), and one cell
1423
- * per evaluation. Nothing is inferred from timing or order: a candidate GEPA
1424
- * evaluated but kept out of its population has no parent record, so its edge
1425
- * is `unknown` and has no parents.
1418
+ * GEPA and SkillOpt search inside their own processes. The evaluation
1419
+ * callback's observation log holds every candidate either one evaluated, and
1420
+ * GEPA's candidate population also names each candidate's parents by index.
1421
+ * The importer turns both into ledger events: one node per distinct candidate,
1422
+ * one `correlated` edge per population entry (joined by content digest from
1423
+ * GEPA's own parent record), and one cell per evaluation. Nothing is inferred
1424
+ * from timing or order: a candidate with no parent record (every SkillOpt
1425
+ * candidate, and a candidate GEPA kept out of its population) has an `unknown`
1426
+ * edge and no parents.
1426
1427
  */
1427
- const NO_GEPA_RATIONALE = { unknown: "GEPA records no rationale per candidate; its reflection attaches to the proposer operation" };
1428
+ function noRationale(optimizer) {
1429
+ return { unknown: `${optimizer} records no rationale per candidate; its reflection attaches to the proposer operation` };
1430
+ }
1431
+ const NO_GEPA_RATIONALE = noRationale("GEPA");
1428
1432
  /**
1429
1433
  * Register GEPA's population as nodes and its parent indices as `correlated`
1430
1434
  * edges. A parentless entry (GEPA's seed) gets a seed edge unless its node is
@@ -1501,10 +1505,12 @@ async function importGepaPopulation(input) {
1501
1505
  /**
1502
1506
  * Record every evaluation in the callback's observation log as an `external`
1503
1507
  * cell attempt. A candidate that is not yet a node (GEPA evaluated it on a
1504
- * minibatch and discarded it) is registered with an `unknown` edge.
1508
+ * minibatch and discarded it, or SkillOpt, which reports no parents, proposed
1509
+ * it) is registered with an `unknown` edge.
1505
1510
  */
1506
1511
  async function importExternalEvaluations(input) {
1507
1512
  const { recorder } = input;
1513
+ const optimizer = input.optimizer ?? "GEPA";
1508
1514
  const reps = /* @__PURE__ */ new Map();
1509
1515
  const unrecorded = [];
1510
1516
  let cells = 0;
@@ -1520,10 +1526,10 @@ async function importExternalEvaluations(input) {
1520
1526
  operator: "improve",
1521
1527
  attribution: "unknown",
1522
1528
  proposer: input.proposer,
1523
- proposalKey: `gepa-evaluated:${candidateHash}`,
1524
- rationale: NO_GEPA_RATIONALE,
1529
+ proposalKey: `${optimizer.toLowerCase()}-evaluated:${candidateHash}`,
1530
+ rationale: noRationale(optimizer),
1525
1531
  diffs: [],
1526
- label: "GEPA proposal outside its population"
1532
+ label: optimizer === "GEPA" ? "GEPA proposal outside its population" : `${optimizer} proposal without a parent record`
1527
1533
  });
1528
1534
  unrecorded.push(registered.nodeId);
1529
1535
  }
@@ -1605,7 +1611,31 @@ function externalSurface(candidate) {
1605
1611
  * outside this search.
1606
1612
  */
1607
1613
  async function recordGepaSearch(input) {
1608
- const { identity, population } = input;
1614
+ const best = input.population.candidates.find((candidate) => candidate.index === input.population.bestIndex);
1615
+ if (!best) throw new Error(`GEPA candidate population has no bestIndex entry ${input.population.bestIndex}`);
1616
+ return recordExternalSearch({
1617
+ ...input,
1618
+ optimizer: "GEPA",
1619
+ selected: externalSurface(best.candidate)
1620
+ });
1621
+ }
1622
+ /**
1623
+ * Write SkillOpt's finished search into its ledger: the baseline as the seeded
1624
+ * root, every candidate SkillOpt evaluated as a node with an `unknown` edge
1625
+ * (SkillOpt reports no parents), every callback evaluation as an `external`
1626
+ * cell, SkillOpt's optimizer spend as one operation, SkillOpt's choice as the
1627
+ * selected node, and the close. Like GEPA's, the ledger carries no claim.
1628
+ */
1629
+ async function recordSkillOptSearch(input) {
1630
+ return recordExternalSearch({
1631
+ ...input,
1632
+ optimizer: "SkillOpt",
1633
+ population: null
1634
+ });
1635
+ }
1636
+ async function recordExternalSearch(input) {
1637
+ const { identity, population, optimizer } = input;
1638
+ const policyName = optimizer.toLowerCase();
1609
1639
  const tasks = (scenarios) => scenarios.map((scenario) => ({
1610
1640
  taskId: scenario.id,
1611
1641
  unitId: scenario.id,
@@ -1646,8 +1676,8 @@ async function recordGepaSearch(input) {
1646
1676
  heldOutUnits: true
1647
1677
  },
1648
1678
  policy: {
1649
- expansion: "gepa",
1650
- allocation: "gepa",
1679
+ expansion: policyName,
1680
+ allocation: policyName,
1651
1681
  seed: input.seed
1652
1682
  },
1653
1683
  budget: {
@@ -1681,7 +1711,7 @@ async function recordGepaSearch(input) {
1681
1711
  diffs: [],
1682
1712
  label: "baseline"
1683
1713
  });
1684
- const operationId = "gepa-proposals";
1714
+ const operationId = `${policyName}-proposals`;
1685
1715
  await recorder.startOperation({
1686
1716
  operationId,
1687
1717
  operationKind: "candidate-generation"
@@ -1692,7 +1722,7 @@ async function recordGepaSearch(input) {
1692
1722
  operationId,
1693
1723
  source: identity.proposer.source
1694
1724
  };
1695
- const imported = await importGepaPopulation({
1725
+ if (population) await importGepaPopulation({
1696
1726
  recorder,
1697
1727
  population,
1698
1728
  proposer
@@ -1710,7 +1740,20 @@ async function recordGepaSearch(input) {
1710
1740
  revision: taskSet
1711
1741
  }
1712
1742
  },
1713
- splitOf: (exampleId) => trainIds.has(exampleId) ? "train" : "selection"
1743
+ splitOf: (exampleId) => trainIds.has(exampleId) ? "train" : "selection",
1744
+ optimizer
1745
+ });
1746
+ const chosen = await recorder.registerNode(surfaceNode(recorder, input.selected));
1747
+ if (((await recorder.state()).node(chosen.nodeId)?.edgeIds.length ?? 0) === 0) await recorder.recordEdge({
1748
+ childNodeId: chosen.nodeId,
1749
+ parents: [],
1750
+ operator: "improve",
1751
+ attribution: "unknown",
1752
+ proposer,
1753
+ proposalKey: `${policyName}-selected`,
1754
+ rationale: noRationale(optimizer),
1755
+ diffs: [],
1756
+ label: `${optimizer} choice without an evaluation`
1714
1757
  });
1715
1758
  await recorder.recordOperation({
1716
1759
  operationId,
@@ -1719,17 +1762,19 @@ async function recordGepaSearch(input) {
1719
1762
  outcome: { status: "completed" },
1720
1763
  accounting: input.generationAccounting
1721
1764
  });
1722
- const best = imported.nodeIds.get(population.bestIndex);
1723
1765
  const state = await recorder.state();
1724
1766
  const nodes = state.nodes();
1725
- const bestScored = best !== void 0 && state.cells({ nodeId: best }).some((cell) => cell.score !== null);
1767
+ const best = chosen.nodeId;
1768
+ const bestScored = state.cells({ nodeId: best }).some((cell) => cell.score !== null);
1769
+ const rule = population ? "gepa-best-aggregate" : `${policyName}-choice`;
1770
+ const basis = population ? "its aggregate selection score" : "its own validation";
1726
1771
  for (const node of nodes) if (node.nodeId === best && bestScored) await recorder.decideNode({
1727
1772
  nodeId: node.nodeId,
1728
1773
  decision: { status: "selected" },
1729
- rule: "gepa-best-aggregate",
1730
- reason: "GEPA chose this candidate by its aggregate selection score"
1774
+ rule,
1775
+ reason: `${optimizer} chose this candidate by ${basis}`
1731
1776
  });
1732
- else if (evaluations.unrecordedParentNodeIds.includes(node.nodeId)) await recorder.decideNode({
1777
+ else if (population && evaluations.unrecordedParentNodeIds.includes(node.nodeId)) await recorder.decideNode({
1733
1778
  nodeId: node.nodeId,
1734
1779
  decision: { status: "pruned" },
1735
1780
  rule: "gepa-population",
@@ -1738,8 +1783,8 @@ async function recordGepaSearch(input) {
1738
1783
  else await recorder.decideNode({
1739
1784
  nodeId: node.nodeId,
1740
1785
  decision: node.nodeId === best ? { status: "invalid" } : { status: "rejected" },
1741
- rule: "gepa-best-aggregate",
1742
- reason: node.nodeId === best ? "GEPA chose this candidate, but no evaluation of it was scored" : "GEPA chose another candidate"
1786
+ rule,
1787
+ reason: node.nodeId === best ? `${optimizer} chose this candidate, but no evaluation of it was scored` : `${optimizer} chose another candidate`
1743
1788
  });
1744
1789
  await recorder.close({
1745
1790
  reason: "budget",
@@ -2463,8 +2508,46 @@ function skillOptOptimizationMethod(config) {
2463
2508
  label: "optimizer model",
2464
2509
  cost: optimizerCost
2465
2510
  }]);
2511
+ const searchHistory = config.searchLedger ? await recordSkillOptSearch({
2512
+ name,
2513
+ path: config.searchLedger.path ?? `${runDir}/search-ledger.jsonl`,
2514
+ searchId: runId,
2515
+ identity: config.searchLedger.identity,
2516
+ storage,
2517
+ seed: input.seed,
2518
+ baselineSurface: input.baselineSurface,
2519
+ trainScenarios: input.trainScenarios,
2520
+ selectionScenarios: input.selectionScenarios,
2521
+ evaluationLimit: config.maxEvaluations,
2522
+ observations: readExternalOptimizerObservationArtifact({
2523
+ summary: observationLog.summary(),
2524
+ storage
2525
+ }),
2526
+ selected: result.bestCandidate,
2527
+ generationAccounting: {
2528
+ tokens: optimizerUsage.usageComplete ? {
2529
+ status: "known",
2530
+ inputTokens: optimizerUsage.inputTokens,
2531
+ outputTokens: optimizerUsage.outputTokens,
2532
+ cachedTokens: optimizerUsage.cachedTokens
2533
+ } : {
2534
+ status: "unknown",
2535
+ reason: "an optimizer call reported no token usage"
2536
+ },
2537
+ cost: optimizerCost.accountingComplete ? {
2538
+ status: "known",
2539
+ usd: optimizerCost.totalCostUsd,
2540
+ source: "provider"
2541
+ } : {
2542
+ status: "unknown",
2543
+ knownLowerBoundUsd: optimizerCost.totalCostUsd,
2544
+ reason: optimizerCost.incompleteReasons.join("; ") || "optimizer spend incomplete"
2545
+ }
2546
+ }
2547
+ }) : void 0;
2466
2548
  return {
2467
2549
  winnerSurface: result.bestCandidate,
2550
+ ...searchHistory ? { searchHistory } : {},
2468
2551
  cost: combinedCost,
2469
2552
  durationMs: Date.now() - started,
2470
2553
  provenance: {
@@ -2485,6 +2568,6 @@ function skillOptOptimizationMethod(config) {
2485
2568
  };
2486
2569
  }
2487
2570
  //#endregion
2488
- export { importGepaPopulation as a, composeGate as c, readExternalOptimizerObservationArtifact as d, REFERENCE_EQUIVALENCE_INPUT_LIMITS as f, runReferenceEquivalenceJudge as h, importExternalEvaluations as i, externalTextOptimizationMethod as l, createReferenceEquivalenceJudge as m, gepaOptimizationMethod as n, recordGepaSearch as o, REFERENCE_EQUIVALENCE_JUDGE_VERSION as p, externalSurface as r, heldOutGate as s, skillOptOptimizationMethod as t, decodeExternalTextCandidate as u };
2571
+ export { importGepaPopulation as a, heldOutGate as c, decodeExternalTextCandidate as d, readExternalOptimizerObservationArtifact as f, runReferenceEquivalenceJudge as g, createReferenceEquivalenceJudge as h, importExternalEvaluations as i, composeGate as l, REFERENCE_EQUIVALENCE_JUDGE_VERSION as m, gepaOptimizationMethod as n, recordGepaSearch as o, REFERENCE_EQUIVALENCE_INPUT_LIMITS as p, externalSurface as r, recordSkillOptSearch as s, skillOptOptimizationMethod as t, externalTextOptimizationMethod as u };
2489
2572
 
2490
- //# sourceMappingURL=skillopt-optimization-method-B3Oq4iim.js.map
2573
+ //# sourceMappingURL=skillopt-optimization-method-IbEhlA1V.js.map