@tangle-network/agent-eval 0.142.2 → 0.144.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/CHANGELOG.md +40 -0
- package/README.md +42 -8
- package/dist/analyst/index.d.ts +6 -6
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +3 -3
- package/dist/{benchmark-command-bTf4ibj-.js → benchmark-command-4c7N_rlw.js} +7 -6
- package/dist/benchmark-command-4c7N_rlw.js.map +1 -0
- package/dist/benchmarks/index.d.ts +1 -1
- package/dist/benchmarks/index.js +1 -1
- package/dist/{benchmarks-Ce8GdJbc.js → benchmarks-05UGZ8sZ.js} +4 -4
- package/dist/{benchmarks-Ce8GdJbc.js.map → benchmarks-05UGZ8sZ.js.map} +1 -1
- package/dist/campaign/index.d.ts +5 -4
- package/dist/campaign/index.js +4 -4
- package/dist/{campaign-COFTVy3Q.js → campaign-BKOtvRAB.js} +15 -11
- package/dist/campaign-BKOtvRAB.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{client-BoqGxEqx.d.ts → client-Cgl6KasJ.d.ts} +2 -2
- package/dist/{client-BoqGxEqx.d.ts.map → client-Cgl6KasJ.d.ts.map} +1 -1
- package/dist/contract/index.d.ts +5 -5
- package/dist/contract/index.js +4 -4
- package/dist/{default-registry-Ci7wAAR8.d.ts → default-registry-D3uqKbo6.d.ts} +2 -2
- package/dist/{default-registry-Ci7wAAR8.d.ts.map → default-registry-D3uqKbo6.d.ts.map} +1 -1
- package/dist/{default-registry-BupFSXSN.js → default-registry-DfHJEwYh.js} +2 -2
- package/dist/{default-registry-BupFSXSN.js.map → default-registry-DfHJEwYh.js.map} +1 -1
- package/dist/{dspy-rlm-engine-Bw8Zz9CX.js → dspy-rlm-engine-CBFwlyaY.js} +89 -7
- package/dist/dspy-rlm-engine-CBFwlyaY.js.map +1 -0
- package/dist/external-optimizer-contracts-iK0yu4AR.d.ts +128 -0
- package/dist/external-optimizer-contracts-iK0yu4AR.d.ts.map +1 -0
- package/dist/hosted/index.d.ts +2 -2
- package/dist/{index-BKjcUH13.d.ts → index-BZUe-ODI.d.ts} +41 -5
- package/dist/index-BZUe-ODI.d.ts.map +1 -0
- package/dist/{index-DCP4I2Qx.d.ts → index-DgbFl4cv.d.ts} +14 -11
- package/dist/{index-DCP4I2Qx.d.ts.map → index-DgbFl4cv.d.ts.map} +1 -1
- package/dist/{index-CKI1CXTL.d.ts → index-DtMpBKVF.d.ts} +4 -4
- package/dist/{index-CKI1CXTL.d.ts.map → index-DtMpBKVF.d.ts.map} +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/{integrity-hruKe6oe.js → integrity-DY6tIbl0.js} +48 -18
- package/dist/integrity-DY6tIbl0.js.map +1 -0
- package/dist/multishot/index.d.ts +1 -1
- package/dist/openapi.json +1 -1
- package/dist/{replay-BqTgoioO.d.ts → replay-BuJM6kLh.d.ts} +2 -2
- package/dist/{replay-BqTgoioO.d.ts.map → replay-BuJM6kLh.d.ts.map} +1 -1
- package/dist/{reward-hacking-GyN0kMd8.js → reward-hacking-DjTi9HLb.js} +34 -14
- package/dist/reward-hacking-DjTi9HLb.js.map +1 -0
- package/dist/rl.d.ts +263 -2
- package/dist/rl.d.ts.map +1 -1
- package/dist/rl.js +332 -3
- package/dist/rl.js.map +1 -1
- package/dist/{single-run-lock-BcvPQY57.js → single-run-lock-t1si1ob7.js} +371 -153
- package/dist/single-run-lock-t1si1ob7.js.map +1 -0
- package/dist/{skill-usage-CNZTk_LT.d.ts → skill-usage-BiVEU0QY.d.ts} +5 -5
- package/dist/{skill-usage-CNZTk_LT.d.ts.map → skill-usage-BiVEU0QY.d.ts.map} +1 -1
- package/dist/{skillopt-optimization-method-D1dqGzzH.d.ts → skillopt-optimization-method-B7o01OdX.d.ts} +63 -13
- package/dist/skillopt-optimization-method-B7o01OdX.d.ts.map +1 -0
- package/dist/{skillopt-optimization-method-DV6c4ooy.js → skillopt-optimization-method-Ds8J1_K8.js} +440 -270
- package/dist/skillopt-optimization-method-Ds8J1_K8.js.map +1 -0
- package/dist/supervisor-run/index.d.ts +3 -3
- package/dist/supervisor-run/index.js +3 -3
- package/dist/{supervisor-run-BEns49Nx.js → supervisor-run-DiyQVczd.js} +301 -25
- package/dist/supervisor-run-DiyQVczd.js.map +1 -0
- package/dist/{tool-groups-DVQTy9lq.d.ts → tool-groups-Bqy4A3QB.d.ts} +2 -2
- package/dist/{tool-groups-DVQTy9lq.d.ts.map → tool-groups-Bqy4A3QB.d.ts.map} +1 -1
- package/dist/traces.d.ts +2 -2
- package/dist/{types-Dk7PB7vh.d.ts → types-KEqL1pZc.d.ts} +6 -4
- package/dist/{types-Dk7PB7vh.d.ts.map → types-KEqL1pZc.d.ts.map} +1 -1
- package/dist/{types-Dea6tiVI.d.ts → types-yLK8gXE9.d.ts} +15 -2
- package/dist/types-yLK8gXE9.d.ts.map +1 -0
- package/docs/campaign-proposers.md +11 -7
- package/docs/verified-labels-flywheel.md +46 -0
- package/package.json +1 -1
- package/dist/benchmark-command-bTf4ibj-.js.map +0 -1
- package/dist/campaign-COFTVy3Q.js.map +0 -1
- package/dist/dspy-rlm-engine-Bw8Zz9CX.js.map +0 -1
- package/dist/external-optimizer-contracts-nb7c_WAR.d.ts +0 -42
- package/dist/external-optimizer-contracts-nb7c_WAR.d.ts.map +0 -1
- package/dist/index-BKjcUH13.d.ts.map +0 -1
- package/dist/integrity-hruKe6oe.js.map +0 -1
- package/dist/reward-hacking-GyN0kMd8.js.map +0 -1
- package/dist/single-run-lock-BcvPQY57.js.map +0 -1
- package/dist/skillopt-optimization-method-D1dqGzzH.d.ts.map +0 -1
- package/dist/skillopt-optimization-method-DV6c4ooy.js.map +0 -1
- package/dist/supervisor-run-BEns49Nx.js.map +0 -1
- package/dist/types-Dea6tiVI.d.ts.map +0 -1
package/dist/{skillopt-optimization-method-DV6c4ooy.js → skillopt-optimization-method-Ds8J1_K8.js}
RENAMED
|
@@ -2,13 +2,13 @@ import { c as ValidationError, i as JudgeError, t as AgentEvalError } from "./er
|
|
|
2
2
|
import { i as CostLedger, t as CostAccountingIncompleteError } from "./cost-ledger-DMFxsLKr.js";
|
|
3
3
|
import { f as maximumChargeForLlmRequest, l as costReceiptFromLlm, m as stripFencedJson, u as costReceiptFromLlmError } from "./llm-client-B3WXSH5Y.js";
|
|
4
4
|
import { a as clamp01, o as combineAbortSignals, t as assertProposalFindings } from "./proposal-findings-2GIUo1et.js";
|
|
5
|
-
import { C as
|
|
5
|
+
import { C as assertNoCredentialValues, D as removeCredentialEnvironment, E as isRecord, O as safePathComponent, S as assertJsonValue, T as isExternalTextCandidate, _ as canonicalJson, d as runWithCleanup, f as startExternalOptimizerModelProxy, h as sendJson, l as runExternalOptimizerProcess, m as listenLocal, n as createRunCostLedger, p as closeServer, r as fsCampaignStorage, t as acquireSingleRunLock, u as closeExternalOptimizerResources, v as contentHash, w as isCandidateText, x as assertExternalOptimizerModelBudget } from "./single-run-lock-t1si1ob7.js";
|
|
6
6
|
import { p as mapConcurrent } from "./ledger-core-Dxz0Rkwa.js";
|
|
7
7
|
import { E as pairedBootstrap, H as weightedComposite, M as pairedRiskDifferenceScore, N as pairedSignTest, O as pairedDeltaTieFraction, T as pairedBinaryScale, d as confidenceInterval, j as pairedRiskDifferenceExact } from "./statistics-ByxzSiOM.js";
|
|
8
8
|
import { t as DEFAULT_REDACTION_RULES } from "./redact-7Aq1ukl-.js";
|
|
9
|
-
import { a as
|
|
9
|
+
import { a as campaignCellCostProvenance, o as campaignCellExecutionEvidence, t as detectRewardHacking, u as projectCampaignCellQuality } from "./reward-hacking-DjTi9HLb.js";
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
import {
|
|
11
|
+
import { writeFileSync } from "node:fs";
|
|
12
12
|
import { basename, isAbsolute, join } from "node:path";
|
|
13
13
|
import { createHash, randomBytes } from "node:crypto";
|
|
14
14
|
import { createServer } from "node:http";
|
|
@@ -656,160 +656,6 @@ function paretoFrontierWithCrowding(candidates, objectives) {
|
|
|
656
656
|
return crowdingDistance(frontier, objectives).sort((a, b) => b.distance - a.distance);
|
|
657
657
|
}
|
|
658
658
|
//#endregion
|
|
659
|
-
//#region src/verdict-cache.ts
|
|
660
|
-
/**
|
|
661
|
-
* Content-addressed judge-verdict caching.
|
|
662
|
-
*
|
|
663
|
-
* LAW: cache JUDGE VERDICTS only — judging the same artifact with the same
|
|
664
|
-
* judge+rubric is pure. NEVER cache agent rollouts. (A router that cached
|
|
665
|
-
* identical fanout prompts silently destroyed best-of-N diversity; rollout
|
|
666
|
-
* caching reintroduces that failure class. Judging has no diversity to
|
|
667
|
-
* destroy — same artifact + same rubric ⇒ same verdict is the desired
|
|
668
|
-
* property, not a bug.)
|
|
669
|
-
*
|
|
670
|
-
* The cache key is a sha-256 over the canonical JSON of everything that can
|
|
671
|
-
* change a verdict: the artifact content, the scenario id, the judge name,
|
|
672
|
-
* the full dimension list (key + description — the description IS the rubric
|
|
673
|
-
* text shown to the judge), and a caller-supplied `judgeVersion`.
|
|
674
|
-
* `judgeVersion` is REQUIRED: a judge whose prompt/model/ensemble changes
|
|
675
|
-
* without a version bump would otherwise silently serve stale verdicts.
|
|
676
|
-
*
|
|
677
|
-
* Strict canonicalization (`canonicalJson`) throws on undefined / function /
|
|
678
|
-
* symbol / non-finite numbers — an artifact that cannot be unambiguously
|
|
679
|
-
* serialized cannot be content-addressed, and coercing it would let two
|
|
680
|
-
* different artifacts collide on one key.
|
|
681
|
-
*/
|
|
682
|
-
function canonicalizeAt(value, path) {
|
|
683
|
-
if (value === null) return "null";
|
|
684
|
-
switch (typeof value) {
|
|
685
|
-
case "boolean": return value ? "true" : "false";
|
|
686
|
-
case "number":
|
|
687
|
-
if (!Number.isFinite(value)) throw new Error(`canonicalJson: non-finite number (${value}) at ${path} — ambiguity is an error, not a coercion`);
|
|
688
|
-
return JSON.stringify(value);
|
|
689
|
-
case "string": return JSON.stringify(value);
|
|
690
|
-
case "undefined":
|
|
691
|
-
case "function":
|
|
692
|
-
case "symbol": throw new Error(`canonicalJson: ${typeof value} at ${path} — ambiguity is an error, not a coercion`);
|
|
693
|
-
case "bigint": throw new Error(`canonicalJson: bigint at ${path} — not representable in JSON`);
|
|
694
|
-
case "object": break;
|
|
695
|
-
}
|
|
696
|
-
const obj = value;
|
|
697
|
-
if (typeof obj.toJSON === "function") return canonicalizeAt(obj.toJSON(), path);
|
|
698
|
-
if (Array.isArray(obj)) return `[${obj.map((item, i) => canonicalizeAt(item, `${path}[${i}]`)).join(",")}]`;
|
|
699
|
-
if (obj instanceof Map || obj instanceof Set) throw new Error(`canonicalJson: ${obj instanceof Map ? "Map" : "Set"} at ${path} — would serialize as '{}'; convert to a plain object/array first`);
|
|
700
|
-
return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${canonicalizeAt(obj[k], `${path}.${k}`)}`).join(",")}}`;
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Stable JSON stringify: object keys sorted recursively, so two semantically
|
|
704
|
-
* equal values produce byte-identical output regardless of key insertion
|
|
705
|
-
* order. Throws on undefined / function / symbol / NaN / ±Infinity / bigint /
|
|
706
|
-
* Map / Set — anything JSON.stringify would coerce or drop silently.
|
|
707
|
-
*
|
|
708
|
-
* Distinct from `pre-registration.ts`'s `canonicalize`/`hashJson`, which are
|
|
709
|
-
* permissive (coercion allowed) and async (web-crypto). Use THIS pair when a
|
|
710
|
-
* hash collision or silent coercion would corrupt a cache key or attestation.
|
|
711
|
-
*/
|
|
712
|
-
function canonicalJson(value) {
|
|
713
|
-
return canonicalizeAt(value, "$");
|
|
714
|
-
}
|
|
715
|
-
/** Hex sha-256 over `canonicalJson(value)`. The content address used by the
|
|
716
|
-
* verdict cache and report attestation. */
|
|
717
|
-
function contentHash(value) {
|
|
718
|
-
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
719
|
-
}
|
|
720
|
-
/** Process-local Map-backed store. */
|
|
721
|
-
function inMemoryVerdictCache() {
|
|
722
|
-
const entries = /* @__PURE__ */ new Map();
|
|
723
|
-
return {
|
|
724
|
-
get: (key) => entries.get(key),
|
|
725
|
-
set: (key, score) => {
|
|
726
|
-
entries.set(key, score);
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
function parseCacheLine(line, path, lineNo) {
|
|
731
|
-
let parsed;
|
|
732
|
-
try {
|
|
733
|
-
parsed = JSON.parse(line);
|
|
734
|
-
} catch (err) {
|
|
735
|
-
throw new Error(`fileVerdictCache: corrupt JSONL at ${path}:${lineNo} — ${err instanceof Error ? err.message : String(err)}`);
|
|
736
|
-
}
|
|
737
|
-
const rec = parsed;
|
|
738
|
-
if (typeof rec !== "object" || rec === null || typeof rec.key !== "string" || typeof rec.score !== "object" || rec.score === null || typeof rec.score.composite !== "number" || typeof rec.score.dimensions !== "object") throw new Error(`fileVerdictCache: invalid record shape at ${path}:${lineNo} — expected {key, score:{dimensions, composite, notes}}`);
|
|
739
|
-
return rec;
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* JSONL-file-backed store: the full file is loaded into an in-memory index at
|
|
743
|
-
* construction; every `set` appends one line synchronously (durable before
|
|
744
|
-
* the verdict is returned). A corrupt or malformed line throws at load with
|
|
745
|
-
* file:line — a skipped line would silently re-judge (cost) or, worse, mask
|
|
746
|
-
* a half-written file that needs operator attention.
|
|
747
|
-
*/
|
|
748
|
-
function fileVerdictCache(path) {
|
|
749
|
-
const entries = /* @__PURE__ */ new Map();
|
|
750
|
-
if (existsSync(path)) {
|
|
751
|
-
const lines = readFileSync(path, "utf8").split("\n");
|
|
752
|
-
for (let i = 0; i < lines.length; i++) {
|
|
753
|
-
const line = lines[i];
|
|
754
|
-
if (line === void 0 || line.trim() === "") continue;
|
|
755
|
-
const rec = parseCacheLine(line, path, i + 1);
|
|
756
|
-
entries.set(rec.key, rec.score);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
return {
|
|
760
|
-
get: (key) => entries.get(key),
|
|
761
|
-
set: (key, score) => {
|
|
762
|
-
appendFileSync(path, `${JSON.stringify({
|
|
763
|
-
key,
|
|
764
|
-
score
|
|
765
|
-
})}\n`, "utf8");
|
|
766
|
-
entries.set(key, score);
|
|
767
|
-
}
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* Wrap a `JudgeConfig` so repeat judgments of the same artifact are served
|
|
772
|
-
* from the store instead of re-invoking `score()`. The wrapper is generic
|
|
773
|
-
* over the judge's own type parameters and preserves `appliesTo` — it is a
|
|
774
|
-
* drop-in replacement anywhere a `JudgeConfig` is accepted.
|
|
775
|
-
*
|
|
776
|
-
* A judge that throws is NOT cached: the error propagates and the next
|
|
777
|
-
* attempt re-judges (caching a failure would pin a transient outage forever).
|
|
778
|
-
*/
|
|
779
|
-
function cachedJudge(judge, store, options) {
|
|
780
|
-
if (typeof options.judgeVersion !== "string" || options.judgeVersion.trim() === "") throw new Error("cachedJudge: judgeVersion is required and must be a non-empty string");
|
|
781
|
-
const stats = {
|
|
782
|
-
hits: 0,
|
|
783
|
-
misses: 0
|
|
784
|
-
};
|
|
785
|
-
const wrapped = {
|
|
786
|
-
name: judge.name,
|
|
787
|
-
dimensions: judge.dimensions,
|
|
788
|
-
judgeVersion: options.judgeVersion,
|
|
789
|
-
async score(input) {
|
|
790
|
-
const key = contentHash({
|
|
791
|
-
artifact: canonicalJson(input.artifact),
|
|
792
|
-
scenarioId: input.scenario.id,
|
|
793
|
-
judgeName: judge.name,
|
|
794
|
-
dimensions: judge.dimensions,
|
|
795
|
-
judgeVersion: options.judgeVersion
|
|
796
|
-
});
|
|
797
|
-
const cached = await store.get(key);
|
|
798
|
-
if (cached !== void 0) {
|
|
799
|
-
stats.hits += 1;
|
|
800
|
-
return cached;
|
|
801
|
-
}
|
|
802
|
-
const score = await judge.score(input);
|
|
803
|
-
await store.set(key, score);
|
|
804
|
-
stats.misses += 1;
|
|
805
|
-
return score;
|
|
806
|
-
},
|
|
807
|
-
stats: () => ({ ...stats })
|
|
808
|
-
};
|
|
809
|
-
if (judge.appliesTo) wrapped.appliesTo = judge.appliesTo;
|
|
810
|
-
return wrapped;
|
|
811
|
-
}
|
|
812
|
-
//#endregion
|
|
813
659
|
//#region src/llm-judge.ts
|
|
814
660
|
/**
|
|
815
661
|
* `llmJudge` — the single-LLM-call bridge that turns a rubric prompt into a
|
|
@@ -1813,6 +1659,8 @@ async function startExternalOptimizerCallback(args) {
|
|
|
1813
1659
|
let closePromise;
|
|
1814
1660
|
const activeControllers = /* @__PURE__ */ new Set();
|
|
1815
1661
|
const activeHandlers = /* @__PURE__ */ new Set();
|
|
1662
|
+
const proposedCandidateHashes = /* @__PURE__ */ new Set();
|
|
1663
|
+
let observationSequence = 0;
|
|
1816
1664
|
const server = createServer((request, response) => {
|
|
1817
1665
|
if (!accepting) {
|
|
1818
1666
|
sendJsonIfOpen(response, 503, { error: "external optimizer callback is closing" });
|
|
@@ -1833,7 +1681,13 @@ async function startExternalOptimizerCallback(args) {
|
|
|
1833
1681
|
if (accepted > args.maxEvaluations) return void 0;
|
|
1834
1682
|
evaluations += 1;
|
|
1835
1683
|
return accepted;
|
|
1836
|
-
}
|
|
1684
|
+
}, (observation) => {
|
|
1685
|
+
if (!args.observe) return;
|
|
1686
|
+
args.observe({
|
|
1687
|
+
...observation,
|
|
1688
|
+
sequence: ++observationSequence
|
|
1689
|
+
});
|
|
1690
|
+
}, proposedCandidateHashes).finally(() => {
|
|
1837
1691
|
controller.signal.removeEventListener("abort", abortRequest);
|
|
1838
1692
|
activeControllers.delete(controller);
|
|
1839
1693
|
activeHandlers.delete(handler);
|
|
@@ -1868,7 +1722,7 @@ async function startExternalOptimizerCallback(args) {
|
|
|
1868
1722
|
if (serverResult?.status === "rejected") throw serverResult.reason;
|
|
1869
1723
|
}
|
|
1870
1724
|
}
|
|
1871
|
-
async function handleCallback(request, response, signal, args, nextEvaluation) {
|
|
1725
|
+
async function handleCallback(request, response, signal, args, nextEvaluation, observe, proposedCandidateHashes) {
|
|
1872
1726
|
try {
|
|
1873
1727
|
if (request.method !== "POST" || request.url !== "/evaluate") {
|
|
1874
1728
|
sendJsonIfOpen(response, 404, { error: "not found" });
|
|
@@ -1878,19 +1732,76 @@ async function handleCallback(request, response, signal, args, nextEvaluation) {
|
|
|
1878
1732
|
sendJsonIfOpen(response, 401, { error: "unauthorized" });
|
|
1879
1733
|
return;
|
|
1880
1734
|
}
|
|
1881
|
-
|
|
1735
|
+
let body;
|
|
1736
|
+
try {
|
|
1737
|
+
body = await readJson(request);
|
|
1738
|
+
} catch {
|
|
1739
|
+
observe({
|
|
1740
|
+
kind: "refusal",
|
|
1741
|
+
reason: "invalid-request"
|
|
1742
|
+
});
|
|
1743
|
+
sendJsonIfOpen(response, 400, { error: "request body must be bounded valid JSON" });
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1882
1746
|
if (!isRecord(body) || !isExternalTextCandidate(body.candidate) || typeof body.exampleId !== "string") {
|
|
1747
|
+
observe({
|
|
1748
|
+
kind: "refusal",
|
|
1749
|
+
reason: "invalid-request"
|
|
1750
|
+
});
|
|
1883
1751
|
sendJsonIfOpen(response, 400, { error: "candidate and exampleId are required strings" });
|
|
1884
1752
|
return;
|
|
1885
1753
|
}
|
|
1886
|
-
|
|
1754
|
+
const candidate = cloneCandidate(body.candidate);
|
|
1755
|
+
const candidateHash = contentHash({
|
|
1756
|
+
kind: "external-text-candidate",
|
|
1757
|
+
candidate
|
|
1758
|
+
});
|
|
1759
|
+
if (!proposedCandidateHashes.has(candidateHash)) {
|
|
1760
|
+
observe({
|
|
1761
|
+
kind: "proposal",
|
|
1762
|
+
candidate,
|
|
1763
|
+
candidateHash
|
|
1764
|
+
});
|
|
1765
|
+
proposedCandidateHashes.add(candidateHash);
|
|
1766
|
+
}
|
|
1767
|
+
const count = nextEvaluation();
|
|
1768
|
+
if (count === void 0) {
|
|
1769
|
+
observe({
|
|
1770
|
+
kind: "refusal",
|
|
1771
|
+
reason: "evaluation-limit",
|
|
1772
|
+
candidate,
|
|
1773
|
+
candidateHash,
|
|
1774
|
+
exampleId: body.exampleId
|
|
1775
|
+
});
|
|
1887
1776
|
sendJsonIfOpen(response, 429, { error: "evaluation limit reached" });
|
|
1888
1777
|
return;
|
|
1889
1778
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1779
|
+
let result;
|
|
1780
|
+
try {
|
|
1781
|
+
result = await args.evaluate({
|
|
1782
|
+
candidate,
|
|
1783
|
+
exampleId: body.exampleId
|
|
1784
|
+
}, signal);
|
|
1785
|
+
} catch {
|
|
1786
|
+
observe({
|
|
1787
|
+
kind: "refusal",
|
|
1788
|
+
reason: "evaluation-failed",
|
|
1789
|
+
candidate,
|
|
1790
|
+
candidateHash,
|
|
1791
|
+
exampleId: body.exampleId
|
|
1792
|
+
});
|
|
1793
|
+
sendJsonIfOpen(response, 500, { error: "evaluation failed" });
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
observe({
|
|
1797
|
+
kind: "evaluation",
|
|
1798
|
+
candidate,
|
|
1799
|
+
candidateHash,
|
|
1800
|
+
exampleId: body.exampleId,
|
|
1801
|
+
evaluationNumber: count,
|
|
1802
|
+
response: structuredClone(result)
|
|
1803
|
+
});
|
|
1804
|
+
sendJsonIfOpen(response, 200, result);
|
|
1894
1805
|
} catch {
|
|
1895
1806
|
sendJsonIfOpen(response, 500, { error: "evaluation failed" });
|
|
1896
1807
|
}
|
|
@@ -1930,6 +1841,10 @@ function assertCallbackConfig(args) {
|
|
|
1930
1841
|
if (!Number.isSafeInteger(args.maxEvaluations) || args.maxEvaluations <= 0) throw new Error("external optimizer callback: maxEvaluations must be a positive safe integer");
|
|
1931
1842
|
if (args.acceptEvaluation !== void 0 && typeof args.acceptEvaluation !== "function") throw new Error("external optimizer callback: acceptEvaluation must be a function");
|
|
1932
1843
|
if (typeof args.evaluate !== "function") throw new Error("external optimizer callback: evaluate must be a function");
|
|
1844
|
+
if (args.observe !== void 0 && typeof args.observe !== "function") throw new Error("external optimizer callback: observe must be a function");
|
|
1845
|
+
}
|
|
1846
|
+
function cloneCandidate(candidate) {
|
|
1847
|
+
return typeof candidate === "string" ? candidate : { ...candidate };
|
|
1933
1848
|
}
|
|
1934
1849
|
//#endregion
|
|
1935
1850
|
//#region src/campaign/external-optimizer-run-budget.ts
|
|
@@ -2069,6 +1984,15 @@ async function runCampaign(opts) {
|
|
|
2069
1984
|
const cells = [];
|
|
2070
1985
|
const artifactsByPath = {};
|
|
2071
1986
|
const schedule = buildCellSchedule(opts.scenarios, seed, reps);
|
|
1987
|
+
if (resumable) assertScheduleCachesReusable({
|
|
1988
|
+
schedule,
|
|
1989
|
+
runDir: opts.runDir,
|
|
1990
|
+
manifestHash,
|
|
1991
|
+
storage,
|
|
1992
|
+
costLedger,
|
|
1993
|
+
costTags: opts.costTags,
|
|
1994
|
+
rerunInvalidCachedCells: opts.rerunInvalidCachedCells ?? false
|
|
1995
|
+
});
|
|
2072
1996
|
const campaignAbort = new AbortController();
|
|
2073
1997
|
const onOwnerAbort = () => campaignAbort.abort(opts.signal?.reason);
|
|
2074
1998
|
if (opts.signal?.aborted) campaignAbort.abort(opts.signal.reason);
|
|
@@ -2153,15 +2077,9 @@ async function runCampaign(opts) {
|
|
|
2153
2077
|
}
|
|
2154
2078
|
async function executeCell(args) {
|
|
2155
2079
|
const storage = args.storage;
|
|
2156
|
-
const cellDir =
|
|
2080
|
+
const cellDir = cellDirectory(args.opts.runDir, args.slot.cellId);
|
|
2157
2081
|
storage.ensureDir(cellDir);
|
|
2158
|
-
const stableCostTags =
|
|
2159
|
-
...args.opts.costTags ?? {},
|
|
2160
|
-
runDir: args.opts.runDir,
|
|
2161
|
-
cellId: args.slot.cellId,
|
|
2162
|
-
scenarioId: args.slot.scenario.id,
|
|
2163
|
-
rep: String(args.slot.rep)
|
|
2164
|
-
};
|
|
2082
|
+
const stableCostTags = stableCostTagsFor(args.opts, args.slot);
|
|
2165
2083
|
const costTags = {
|
|
2166
2084
|
...stableCostTags,
|
|
2167
2085
|
runAttemptId: args.runAttemptId
|
|
@@ -2174,24 +2092,27 @@ async function executeCell(args) {
|
|
|
2174
2092
|
cellId: args.slot.cellId,
|
|
2175
2093
|
manifestHash: args.manifestHash
|
|
2176
2094
|
});
|
|
2095
|
+
if (cached.status === "miss" && cacheIssueRequiresExplicitRerun(cached.reason) && !args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
|
|
2096
|
+
cellId: args.slot.cellId,
|
|
2097
|
+
reason: cached.reason
|
|
2098
|
+
}]);
|
|
2177
2099
|
if (cached.status === "hit") {
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2100
|
+
const receiptProblem = cachedCellReceiptProblem(cached.cell, args.costLedger, stableCostTags);
|
|
2101
|
+
if (receiptProblem === void 0) {
|
|
2102
|
+
enforceDispatchUsage(cached.cell, args.opts.expectUsage ?? "warn");
|
|
2103
|
+
return {
|
|
2104
|
+
cell: {
|
|
2105
|
+
...cached.cell,
|
|
2106
|
+
cached: true
|
|
2107
|
+
},
|
|
2108
|
+
artifactsByPath: {}
|
|
2109
|
+
};
|
|
2187
2110
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
artifactsByPath: {}
|
|
2194
|
-
};
|
|
2111
|
+
if (!args.opts.rerunInvalidCachedCells) throw invalidCachedCellsError([{
|
|
2112
|
+
cellId: args.slot.cellId,
|
|
2113
|
+
reason: "invalid-cost-receipts",
|
|
2114
|
+
detail: receiptProblem
|
|
2115
|
+
}]);
|
|
2195
2116
|
}
|
|
2196
2117
|
}
|
|
2197
2118
|
const startMs = Date.now();
|
|
@@ -2321,7 +2242,9 @@ async function executeCell(args) {
|
|
|
2321
2242
|
const tokenUsage = {
|
|
2322
2243
|
input: agentCost.inputTokens,
|
|
2323
2244
|
output: agentCost.outputTokens,
|
|
2324
|
-
...agentCost.
|
|
2245
|
+
...agentCost.reasoningTokens !== void 0 && agentCost.reasoningTokens > 0 ? { reasoning: agentCost.reasoningTokens } : {},
|
|
2246
|
+
...agentCost.cachedTokens > 0 ? { cached: agentCost.cachedTokens } : {},
|
|
2247
|
+
...agentCost.cacheWriteTokens !== void 0 && agentCost.cacheWriteTokens > 0 ? { cacheWrite: agentCost.cacheWriteTokens } : {}
|
|
2325
2248
|
};
|
|
2326
2249
|
const resolvedModel = agentReceipts.at(-1)?.model;
|
|
2327
2250
|
const dispatchResult = {
|
|
@@ -2377,7 +2300,7 @@ async function executeCell(args) {
|
|
|
2377
2300
|
artifact: artifact ?? null,
|
|
2378
2301
|
judgeScores,
|
|
2379
2302
|
costUsd: agentCost.totalCostUsd,
|
|
2380
|
-
|
|
2303
|
+
costProvenance: agentCost.costProvenance,
|
|
2381
2304
|
costCallIds,
|
|
2382
2305
|
tokenUsage,
|
|
2383
2306
|
...resolvedModel ? { resolvedModel } : {},
|
|
@@ -2467,6 +2390,11 @@ function planCampaignRun(opts) {
|
|
|
2467
2390
|
assertCampaignDesign(opts.scenarios, reps);
|
|
2468
2391
|
if (typeof opts.runDir !== "string" || opts.runDir.trim().length === 0) throw new Error("planCampaignRun: runDir is required and must be a non-empty string");
|
|
2469
2392
|
opts.runDir = resolveRunDir(opts.runDir, opts.repo);
|
|
2393
|
+
const costLedger = opts.costLedger ?? createRunCostLedger({
|
|
2394
|
+
storage,
|
|
2395
|
+
runDir: opts.runDir,
|
|
2396
|
+
ensureRunDir: false
|
|
2397
|
+
});
|
|
2470
2398
|
const manifestHash = computeManifestHash({
|
|
2471
2399
|
scenarios: opts.scenarios,
|
|
2472
2400
|
judges: opts.judges ?? [],
|
|
@@ -2476,7 +2404,7 @@ function planCampaignRun(opts) {
|
|
|
2476
2404
|
});
|
|
2477
2405
|
const splitDigest = campaignSplitDigest(opts.scenarios, reps);
|
|
2478
2406
|
const cells = buildCellSchedule(opts.scenarios, seed, reps).map((slot) => {
|
|
2479
|
-
const cachePath =
|
|
2407
|
+
const cachePath = cellCachePath(opts.runDir, slot.cellId);
|
|
2480
2408
|
if (!resumable) return {
|
|
2481
2409
|
cellId: slot.cellId,
|
|
2482
2410
|
scenarioId: slot.scenario.id,
|
|
@@ -2492,31 +2420,48 @@ function planCampaignRun(opts) {
|
|
|
2492
2420
|
cellId: slot.cellId,
|
|
2493
2421
|
manifestHash
|
|
2494
2422
|
});
|
|
2495
|
-
if (cached.status === "hit")
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2423
|
+
if (cached.status === "hit") {
|
|
2424
|
+
if (cachedCellReceiptProblem(cached.cell, costLedger, stableCostTagsFor({
|
|
2425
|
+
runDir: opts.runDir,
|
|
2426
|
+
costTags: opts.costTags
|
|
2427
|
+
}, slot)) !== void 0) return {
|
|
2428
|
+
cellId: slot.cellId,
|
|
2429
|
+
scenarioId: slot.scenario.id,
|
|
2430
|
+
rep: slot.rep,
|
|
2431
|
+
seed: slot.cellSeed,
|
|
2432
|
+
cachePath,
|
|
2433
|
+
status: opts.rerunInvalidCachedCells ? "run" : "blocked",
|
|
2434
|
+
reason: "invalid-cost-receipts"
|
|
2435
|
+
};
|
|
2436
|
+
return {
|
|
2437
|
+
cellId: slot.cellId,
|
|
2438
|
+
scenarioId: slot.scenario.id,
|
|
2439
|
+
rep: slot.rep,
|
|
2440
|
+
seed: slot.cellSeed,
|
|
2441
|
+
cachePath,
|
|
2442
|
+
status: "cached"
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
const blocked = cacheIssueRequiresExplicitRerun(cached.reason) && !opts.rerunInvalidCachedCells;
|
|
2503
2446
|
return {
|
|
2504
2447
|
cellId: slot.cellId,
|
|
2505
2448
|
scenarioId: slot.scenario.id,
|
|
2506
2449
|
rep: slot.rep,
|
|
2507
2450
|
seed: slot.cellSeed,
|
|
2508
2451
|
cachePath,
|
|
2509
|
-
status: "run",
|
|
2452
|
+
status: blocked ? "blocked" : "run",
|
|
2510
2453
|
reason: cached.reason
|
|
2511
2454
|
};
|
|
2512
2455
|
});
|
|
2513
2456
|
const cellsCached = cells.filter((cell) => cell.status === "cached").length;
|
|
2457
|
+
const cellsBlocked = cells.filter((cell) => cell.status === "blocked").length;
|
|
2514
2458
|
return {
|
|
2515
2459
|
manifestHash,
|
|
2516
2460
|
splitDigest,
|
|
2517
2461
|
totalCells: cells.length,
|
|
2518
2462
|
cellsCached,
|
|
2519
|
-
|
|
2463
|
+
cellsBlocked,
|
|
2464
|
+
cellsToRun: cells.filter((cell) => cell.status === "run").length,
|
|
2520
2465
|
cells
|
|
2521
2466
|
};
|
|
2522
2467
|
}
|
|
@@ -2633,6 +2578,59 @@ function buildCellSchedule(scenarios, seed, reps) {
|
|
|
2633
2578
|
}
|
|
2634
2579
|
return schedule;
|
|
2635
2580
|
}
|
|
2581
|
+
function cellDirectory(runDir, cellId) {
|
|
2582
|
+
return join(runDir, cellId.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
2583
|
+
}
|
|
2584
|
+
function cellCachePath(runDir, cellId) {
|
|
2585
|
+
return join(cellDirectory(runDir, cellId), "cached-result.json");
|
|
2586
|
+
}
|
|
2587
|
+
function stableCostTagsFor(opts, slot) {
|
|
2588
|
+
return {
|
|
2589
|
+
...opts.costTags ?? {},
|
|
2590
|
+
runDir: opts.runDir,
|
|
2591
|
+
cellId: slot.cellId,
|
|
2592
|
+
scenarioId: slot.scenario.id,
|
|
2593
|
+
rep: String(slot.rep)
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
function assertScheduleCachesReusable(args) {
|
|
2597
|
+
const blocked = [];
|
|
2598
|
+
for (const slot of args.schedule) {
|
|
2599
|
+
const cached = readCachedCell({
|
|
2600
|
+
storage: args.storage,
|
|
2601
|
+
cachePath: cellCachePath(args.runDir, slot.cellId),
|
|
2602
|
+
cellId: slot.cellId,
|
|
2603
|
+
manifestHash: args.manifestHash
|
|
2604
|
+
});
|
|
2605
|
+
if (cached.status === "hit") {
|
|
2606
|
+
const receiptProblem = cachedCellReceiptProblem(cached.cell, args.costLedger, stableCostTagsFor({
|
|
2607
|
+
runDir: args.runDir,
|
|
2608
|
+
costTags: args.costTags
|
|
2609
|
+
}, slot));
|
|
2610
|
+
if (receiptProblem !== void 0 && !args.rerunInvalidCachedCells) blocked.push({
|
|
2611
|
+
cellId: slot.cellId,
|
|
2612
|
+
reason: "invalid-cost-receipts",
|
|
2613
|
+
detail: receiptProblem
|
|
2614
|
+
});
|
|
2615
|
+
} else if (cacheIssueRequiresExplicitRerun(cached.reason) && !args.rerunInvalidCachedCells) blocked.push({
|
|
2616
|
+
cellId: slot.cellId,
|
|
2617
|
+
reason: cached.reason
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
if (blocked.length > 0) throw invalidCachedCellsError(blocked);
|
|
2621
|
+
}
|
|
2622
|
+
function cachedCellReceiptProblem(cached, costLedger, stableCostTags) {
|
|
2623
|
+
const reportsPaidActivity = cached.costProvenance.kind !== "observed" || cached.costUsd > 0 || cached.tokenUsage.input > 0 || cached.tokenUsage.output > 0;
|
|
2624
|
+
if (cached.costCallIds === void 0) {
|
|
2625
|
+
if (reportsPaidActivity || Object.keys(cached.judgeScores).length > 0) return "does not identify its ledger receipts";
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
if (!Array.isArray(cached.costCallIds) || cached.costCallIds.some((callId) => typeof callId !== "string" || callId.trim().length === 0) || new Set(cached.costCallIds).size !== cached.costCallIds.length) return "has invalid ledger receipt IDs";
|
|
2629
|
+
if (cached.costCallIds.length === 0 && reportsPaidActivity) return "does not identify its ledger receipts";
|
|
2630
|
+
const restoredCallIds = new Set(costLedger.list({ tags: stableCostTags }).map((receipt) => receipt.callId));
|
|
2631
|
+
const missingCallIds = cached.costCallIds.filter((callId) => !restoredCallIds.has(callId));
|
|
2632
|
+
if (missingCallIds.length > 0) return `is missing ledger receipt(s): ${missingCallIds.join(", ")}`;
|
|
2633
|
+
}
|
|
2636
2634
|
function dispatchRefFor(dispatch, override) {
|
|
2637
2635
|
const ref = override ?? dispatch?.name ?? "anonymous";
|
|
2638
2636
|
if (typeof ref !== "string" || ref.trim().length === 0) throw new Error("runCampaign: dispatchRef must be a non-empty string when provided");
|
|
@@ -2642,18 +2640,32 @@ function readCachedCell(args) {
|
|
|
2642
2640
|
const raw = args.storage.read(args.cachePath);
|
|
2643
2641
|
if (raw === void 0) return {
|
|
2644
2642
|
status: "miss",
|
|
2645
|
-
reason: "missing"
|
|
2643
|
+
reason: args.storage.exists(args.cachePath) ? "corrupt" : "missing"
|
|
2646
2644
|
};
|
|
2645
|
+
let parsed;
|
|
2647
2646
|
try {
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
reason: "cell-mismatch"
|
|
2652
|
-
};
|
|
2653
|
-
if (cached.manifestHash !== args.manifestHash) return {
|
|
2647
|
+
parsed = JSON.parse(raw);
|
|
2648
|
+
} catch {
|
|
2649
|
+
return {
|
|
2654
2650
|
status: "miss",
|
|
2655
|
-
reason: "
|
|
2651
|
+
reason: "corrupt"
|
|
2656
2652
|
};
|
|
2653
|
+
}
|
|
2654
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {
|
|
2655
|
+
status: "miss",
|
|
2656
|
+
reason: "corrupt"
|
|
2657
|
+
};
|
|
2658
|
+
const cached = parsed;
|
|
2659
|
+
if (cached.cellId !== args.cellId) return {
|
|
2660
|
+
status: "miss",
|
|
2661
|
+
reason: "cell-mismatch"
|
|
2662
|
+
};
|
|
2663
|
+
if (cached.manifestHash !== args.manifestHash) return {
|
|
2664
|
+
status: "miss",
|
|
2665
|
+
reason: "manifest-mismatch"
|
|
2666
|
+
};
|
|
2667
|
+
try {
|
|
2668
|
+
campaignCellCostProvenance(cached);
|
|
2657
2669
|
return {
|
|
2658
2670
|
status: "hit",
|
|
2659
2671
|
cell: cached
|
|
@@ -2661,10 +2673,16 @@ function readCachedCell(args) {
|
|
|
2661
2673
|
} catch {
|
|
2662
2674
|
return {
|
|
2663
2675
|
status: "miss",
|
|
2664
|
-
reason: "
|
|
2676
|
+
reason: cached.costProvenance === void 0 ? "missing-cost-provenance" : "invalid-cost-provenance"
|
|
2665
2677
|
};
|
|
2666
2678
|
}
|
|
2667
2679
|
}
|
|
2680
|
+
function cacheIssueRequiresExplicitRerun(reason) {
|
|
2681
|
+
return reason === "cell-mismatch" || reason === "missing-cost-provenance" || reason === "invalid-cost-provenance" || reason === "invalid-cost-receipts" || reason === "corrupt";
|
|
2682
|
+
}
|
|
2683
|
+
function invalidCachedCellsError(cells) {
|
|
2684
|
+
return new CostAccountingIncompleteError(`runCampaign: cached cell(s) require explicit paid re-dispatch: ${cells.map((cell) => `${cell.cellId} (${cell.reason}${cell.detail ? `: ${cell.detail}` : ""})`).join(", ")}; refusing to begin campaign. Inspect planCampaignRun, then set rerunInvalidCachedCells: true to rerun only these cells while retaining valid caches, or resumable: false when a full rerun is intended.`);
|
|
2685
|
+
}
|
|
2668
2686
|
async function captureToStore(args) {
|
|
2669
2687
|
await args.store.observe({
|
|
2670
2688
|
scenario: args.scenario,
|
|
@@ -3389,7 +3407,7 @@ async function compareOptimizationMethods(opts) {
|
|
|
3389
3407
|
throw error;
|
|
3390
3408
|
}
|
|
3391
3409
|
});
|
|
3392
|
-
assertReportedCostWithinCeiling(
|
|
3410
|
+
assertReportedCostWithinCeiling(combineComparisonCosts(optimized.map((result) => ({
|
|
3393
3411
|
label: `method '${result.name}'`,
|
|
3394
3412
|
cost: result.cost
|
|
3395
3413
|
}))).totalCostUsd, opts.costCeiling, "optimization");
|
|
@@ -3474,12 +3492,12 @@ async function compareOptimizationMethods(opts) {
|
|
|
3474
3492
|
favored
|
|
3475
3493
|
};
|
|
3476
3494
|
});
|
|
3477
|
-
const optimizationCost =
|
|
3495
|
+
const optimizationCost = combineComparisonCosts(scores.map((score) => ({
|
|
3478
3496
|
label: `method '${score.name}'`,
|
|
3479
3497
|
cost: score.optimizationCost
|
|
3480
3498
|
})));
|
|
3481
3499
|
const testCost = costFromLedgerSummary(costLedger.summary({ phase: testCostPhase }));
|
|
3482
|
-
const totalCost =
|
|
3500
|
+
const totalCost = combineComparisonCosts([{
|
|
3483
3501
|
label: "optimization",
|
|
3484
3502
|
cost: optimizationCost
|
|
3485
3503
|
}, {
|
|
@@ -3542,6 +3560,7 @@ function assertOptimizationProvenance(methodName, value) {
|
|
|
3542
3560
|
for (const [field, entry] of [["sourceUrl", value.source.sourceUrl], ["revision", value.source.revision]]) if (entry !== void 0 && (typeof entry !== "string" || !entry.trim())) fail(`source.${field}`);
|
|
3543
3561
|
if (typeof value.runId !== "string" || !value.runId.trim()) fail("runId");
|
|
3544
3562
|
if (value.optimizerModel !== void 0 && (typeof value.optimizerModel !== "string" || !value.optimizerModel.trim() || value.optimizerModel.trim() !== value.optimizerModel)) fail("optimizerModel");
|
|
3563
|
+
if (value.optimizerCallRef !== void 0 && (typeof value.optimizerCallRef !== "string" || !value.optimizerCallRef.trim() || value.optimizerCallRef.trim() !== value.optimizerCallRef)) fail("optimizerCallRef");
|
|
3545
3564
|
if (typeof value.resumed !== "boolean") fail("resumed");
|
|
3546
3565
|
if (!Number.isSafeInteger(value.evaluationCount) || value.evaluationCount < 0) fail("evaluationCount");
|
|
3547
3566
|
if (typeof value.artifactDir !== "string" || !value.artifactDir.trim()) fail("artifactDir");
|
|
@@ -3564,6 +3583,24 @@ function assertOptimizationProvenance(methodName, value) {
|
|
|
3564
3583
|
if (value.tokenUsage.reasoningTokens !== void 0 && value.tokenUsage.reasoningTokens > value.tokenUsage.outputTokens) fail("tokenUsage.reasoningTokens");
|
|
3565
3584
|
if (value.tokenUsage.totalTokens !== value.tokenUsage.inputTokens + value.tokenUsage.outputTokens) fail("tokenUsage.totalTokens");
|
|
3566
3585
|
}
|
|
3586
|
+
if (value.observations !== void 0) {
|
|
3587
|
+
if (value.observations.scope !== "callback-submitted-candidates" || typeof value.observations.path !== "string" || !value.observations.path.trim() || typeof value.observations.sha256 !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.observations.sha256)) fail("observations");
|
|
3588
|
+
for (const field of [
|
|
3589
|
+
"submittedCandidates",
|
|
3590
|
+
"evaluations",
|
|
3591
|
+
"refusals"
|
|
3592
|
+
]) if (!Number.isSafeInteger(value.observations[field]) || value.observations[field] < 0) fail(`observations.${field}`);
|
|
3593
|
+
}
|
|
3594
|
+
if (value.modelExecutions !== void 0) {
|
|
3595
|
+
if (value.modelExecutions.scope !== "runtime-model-calls" || typeof value.modelExecutions.path !== "string" || !value.modelExecutions.path.trim() || typeof value.modelExecutions.sha256 !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.modelExecutions.sha256)) fail("modelExecutions");
|
|
3596
|
+
for (const field of [
|
|
3597
|
+
"calls",
|
|
3598
|
+
"succeeded",
|
|
3599
|
+
"failed"
|
|
3600
|
+
]) if (!Number.isSafeInteger(value.modelExecutions[field]) || value.modelExecutions[field] < 0) fail(`modelExecutions.${field}`);
|
|
3601
|
+
if (value.modelExecutions.calls !== value.modelExecutions.succeeded + value.modelExecutions.failed) fail("modelExecutions.calls");
|
|
3602
|
+
}
|
|
3603
|
+
if (value.optimizerModel === void 0 !== (value.optimizerCallRef === void 0) || value.optimizerModel === void 0 !== (value.modelExecutions === void 0)) fail("optimizerModel execution provenance");
|
|
3567
3604
|
}
|
|
3568
3605
|
function assertComparisonControls(opts, seed, resamples, confidence) {
|
|
3569
3606
|
if (opts.optimizationRunOptions && "costCeiling" in opts.optimizationRunOptions) throw new Error("compareOptimizationMethods: optimizationRunOptions.costCeiling is not supported; costCeiling covers optimization and final scoring");
|
|
@@ -3707,6 +3744,7 @@ function callerDispatchRef(opts) {
|
|
|
3707
3744
|
function costFromLedgerSummary(summary) {
|
|
3708
3745
|
const cost = {
|
|
3709
3746
|
totalCostUsd: summary.totalCostUsd,
|
|
3747
|
+
costProvenance: structuredClone(summary.costProvenance),
|
|
3710
3748
|
accountingComplete: summary.accountingComplete,
|
|
3711
3749
|
incompleteReasons: [...summary.incompleteReasons]
|
|
3712
3750
|
};
|
|
@@ -3733,19 +3771,37 @@ function optimizationTokenUsageFromSummary(summary, receipts) {
|
|
|
3733
3771
|
calls: summary.totalCalls
|
|
3734
3772
|
};
|
|
3735
3773
|
}
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3774
|
+
/** Combine method costs without turning one unknown bill into a known total. */
|
|
3775
|
+
function combineComparisonCosts(entries) {
|
|
3776
|
+
const totalCostUsd = entries.reduce((total, entry) => total + entry.cost.totalCostUsd, 0);
|
|
3777
|
+
const cost = {
|
|
3778
|
+
totalCostUsd,
|
|
3779
|
+
costProvenance: entries.some((entry) => entry.cost.costProvenance.kind === "uncaptured") ? {
|
|
3780
|
+
kind: "uncaptured",
|
|
3781
|
+
usd: null
|
|
3782
|
+
} : entries.every((entry) => entry.cost.costProvenance.kind === "observed") ? {
|
|
3783
|
+
kind: "observed",
|
|
3784
|
+
usd: totalCostUsd
|
|
3785
|
+
} : {
|
|
3786
|
+
kind: "estimated",
|
|
3787
|
+
usd: totalCostUsd
|
|
3788
|
+
},
|
|
3739
3789
|
accountingComplete: entries.every((entry) => entry.cost.accountingComplete),
|
|
3740
3790
|
incompleteReasons: entries.flatMap((entry) => entry.cost.incompleteReasons.map((reason) => `${entry.label}: ${reason}`))
|
|
3741
3791
|
};
|
|
3792
|
+
assertComparisonCost(cost, "combined cost");
|
|
3793
|
+
return cost;
|
|
3742
3794
|
}
|
|
3743
3795
|
function assertComparisonCost(cost, label) {
|
|
3744
3796
|
if (!cost || typeof cost !== "object") throw new Error(`compareOptimizationMethods: ${label} returned no cost`);
|
|
3745
3797
|
if (!Number.isFinite(cost.totalCostUsd) || cost.totalCostUsd < 0) throw new Error(`compareOptimizationMethods: ${label} returned an invalid totalCostUsd`);
|
|
3798
|
+
const provenance = cost.costProvenance;
|
|
3799
|
+
if (!provenance || typeof provenance !== "object" || provenance.kind !== "observed" && provenance.kind !== "estimated" && provenance.kind !== "uncaptured" || (provenance.kind === "uncaptured" ? provenance.usd !== null : !Number.isFinite(provenance.usd) || provenance.usd < 0)) throw new Error(`compareOptimizationMethods: ${label} returned invalid costProvenance`);
|
|
3800
|
+
if (provenance.kind !== "uncaptured" && provenance.usd !== cost.totalCostUsd) throw new Error(`compareOptimizationMethods: ${label} returned costProvenance inconsistent with totalCostUsd`);
|
|
3746
3801
|
if (typeof cost.accountingComplete !== "boolean") throw new Error(`compareOptimizationMethods: ${label} returned invalid accountingComplete`);
|
|
3747
3802
|
if (!Array.isArray(cost.incompleteReasons) || cost.incompleteReasons.some((reason) => typeof reason !== "string" || reason.trim().length === 0)) throw new Error(`compareOptimizationMethods: ${label} returned invalid incompleteReasons`);
|
|
3748
3803
|
if (cost.accountingComplete !== (cost.incompleteReasons.length === 0)) throw new Error(`compareOptimizationMethods: ${label} returned inconsistent cost completeness and reasons`);
|
|
3804
|
+
if (cost.accountingComplete && provenance.kind === "uncaptured") throw new Error(`compareOptimizationMethods: ${label} cannot mark uncaptured cost as complete`);
|
|
3749
3805
|
}
|
|
3750
3806
|
//#endregion
|
|
3751
3807
|
//#region src/campaign/external-text-optimization.ts
|
|
@@ -3994,16 +4050,23 @@ function externalTextOptimizationMethod(config) {
|
|
|
3994
4050
|
status: "completed"
|
|
3995
4051
|
}
|
|
3996
4052
|
});
|
|
4053
|
+
const measuredCost = combineComparisonCosts([{
|
|
4054
|
+
label: "evaluation",
|
|
4055
|
+
cost: evaluationCost
|
|
4056
|
+
}, {
|
|
4057
|
+
label: "optimizer",
|
|
4058
|
+
cost: optimizerCost
|
|
4059
|
+
}]);
|
|
3997
4060
|
return {
|
|
3998
4061
|
winnerSurface: decodeExternalTextCandidate(result.bestCandidate),
|
|
3999
4062
|
cost: {
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
]
|
|
4063
|
+
...measuredCost,
|
|
4064
|
+
...externalCostReason ? { costProvenance: {
|
|
4065
|
+
kind: "uncaptured",
|
|
4066
|
+
usd: null
|
|
4067
|
+
} } : {},
|
|
4068
|
+
accountingComplete: measuredCost.accountingComplete && externalCostReason === void 0,
|
|
4069
|
+
incompleteReasons: [...measuredCost.incompleteReasons, ...externalCostReason ? [`optimizer: external spend is not observed: ${externalCostReason}`] : []]
|
|
4007
4070
|
},
|
|
4008
4071
|
durationMs: Date.now() - started,
|
|
4009
4072
|
provenance: {
|
|
@@ -5236,8 +5299,8 @@ const TOKEN_USAGE_FIELDS = [
|
|
|
5236
5299
|
"calls"
|
|
5237
5300
|
];
|
|
5238
5301
|
function assertPriorExternalOptimizerUsage(summary, budget, name) {
|
|
5239
|
-
if (!summary.
|
|
5240
|
-
if (summary.totalCalls > budget.maxRequests || summary.totalCostUsd > budget.maxCostUsd + Number.EPSILON) throw new Error(`${name}: prior optimizer-model usage exceeds the configured budget`);
|
|
5302
|
+
if (!summary.usageComplete || budget.maxCostUsd !== void 0 && !summary.accountingComplete) throw new Error(`${name}: cannot resume optimizer-model work with incomplete bounded usage`);
|
|
5303
|
+
if (summary.totalCalls > budget.maxRequests || budget.maxCostUsd !== void 0 && summary.totalCostUsd > budget.maxCostUsd + Number.EPSILON) throw new Error(`${name}: prior optimizer-model usage exceeds the configured budget`);
|
|
5241
5304
|
}
|
|
5242
5305
|
function assertExternalOptimizerTokenUsage(usage, name, optimizer) {
|
|
5243
5306
|
if (usage === void 0) return;
|
|
@@ -5252,6 +5315,78 @@ function assertExternalOptimizerCompletionCount(upstream, requestAttempts, succe
|
|
|
5252
5315
|
if (upstream.requestAttempts !== void 0 && upstream.requestAttempts !== requestAttempts) throw new Error(`${name}: ${optimizer} reported ${upstream.requestAttempts} model attempts but the proxy received ${requestAttempts}`);
|
|
5253
5316
|
}
|
|
5254
5317
|
//#endregion
|
|
5318
|
+
//#region src/campaign/external-optimizer-observations.ts
|
|
5319
|
+
/** Append-only observation log for one external-optimizer attempt. */
|
|
5320
|
+
function openExternalOptimizerObservationLog(input) {
|
|
5321
|
+
if (input.storage.read(input.path) !== void 0 || input.storage.exists(input.path)) throw new Error(`external optimizer observation log already exists at '${input.path}'`);
|
|
5322
|
+
input.storage.write(input.path, "");
|
|
5323
|
+
let revision = 0;
|
|
5324
|
+
const counts = {
|
|
5325
|
+
submittedCandidates: 0,
|
|
5326
|
+
evaluations: 0,
|
|
5327
|
+
refusals: 0
|
|
5328
|
+
};
|
|
5329
|
+
return {
|
|
5330
|
+
observe(observation) {
|
|
5331
|
+
const expectedSequence = counts.submittedCandidates + counts.evaluations + counts.refusals + 1;
|
|
5332
|
+
if (observation.sequence !== expectedSequence) throw new Error(`external optimizer observation log expected sequence ${expectedSequence}, got ${observation.sequence}`);
|
|
5333
|
+
const line = `${canonicalJson(observation)}\n`;
|
|
5334
|
+
const next = input.storage.append(input.path, line, revision);
|
|
5335
|
+
if (next === void 0) throw new Error(`external optimizer observation log changed concurrently at '${input.path}'`);
|
|
5336
|
+
revision = next;
|
|
5337
|
+
if (observation.kind === "proposal") counts.submittedCandidates += 1;
|
|
5338
|
+
else if (observation.kind === "evaluation") counts.evaluations += 1;
|
|
5339
|
+
else counts.refusals += 1;
|
|
5340
|
+
},
|
|
5341
|
+
summary() {
|
|
5342
|
+
const contents = input.storage.read(input.path) ?? "";
|
|
5343
|
+
const bytes = new TextEncoder().encode(contents);
|
|
5344
|
+
if (bytes.byteLength !== revision) throw new Error(`external optimizer observation log revision changed at '${input.path}'`);
|
|
5345
|
+
return {
|
|
5346
|
+
scope: "callback-submitted-candidates",
|
|
5347
|
+
path: input.path,
|
|
5348
|
+
sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
|
|
5349
|
+
...counts
|
|
5350
|
+
};
|
|
5351
|
+
}
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
/** Append-only opaque Runtime execution records for one optimizer attempt. */
|
|
5355
|
+
function openExternalOptimizerExecutionLog(input) {
|
|
5356
|
+
if (input.storage.read(input.path) !== void 0 || input.storage.exists(input.path)) throw new Error(`external optimizer execution log already exists at '${input.path}'`);
|
|
5357
|
+
input.storage.write(input.path, "");
|
|
5358
|
+
let revision = 0;
|
|
5359
|
+
const counts = {
|
|
5360
|
+
calls: 0,
|
|
5361
|
+
succeeded: 0,
|
|
5362
|
+
failed: 0
|
|
5363
|
+
};
|
|
5364
|
+
return {
|
|
5365
|
+
observe(observation) {
|
|
5366
|
+
if (observation.sequence !== counts.calls + 1) throw new Error(`external optimizer execution log expected sequence ${counts.calls + 1}, got ${observation.sequence}`);
|
|
5367
|
+
const line = `${canonicalJson(observation)}\n`;
|
|
5368
|
+
const next = input.storage.append(input.path, line, revision);
|
|
5369
|
+
if (next === void 0) throw new Error(`external optimizer execution log changed concurrently at '${input.path}'`);
|
|
5370
|
+
revision = next;
|
|
5371
|
+
counts.calls += 1;
|
|
5372
|
+
if (observation.succeeded) counts.succeeded += 1;
|
|
5373
|
+
else counts.failed += 1;
|
|
5374
|
+
},
|
|
5375
|
+
summary() {
|
|
5376
|
+
const contents = input.storage.read(input.path) ?? "";
|
|
5377
|
+
const bytes = new TextEncoder().encode(contents);
|
|
5378
|
+
if (bytes.byteLength !== revision) throw new Error(`external optimizer execution log revision changed at '${input.path}'`);
|
|
5379
|
+
if (counts.calls !== counts.succeeded + counts.failed) throw new Error(`external optimizer execution log counts disagree at '${input.path}'`);
|
|
5380
|
+
return {
|
|
5381
|
+
scope: "runtime-model-calls",
|
|
5382
|
+
path: input.path,
|
|
5383
|
+
sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
|
|
5384
|
+
...counts
|
|
5385
|
+
};
|
|
5386
|
+
}
|
|
5387
|
+
};
|
|
5388
|
+
}
|
|
5389
|
+
//#endregion
|
|
5255
5390
|
//#region src/campaign/external-optimizer-run-config.ts
|
|
5256
5391
|
function externalOptimizerRunnerIdentity(runner, module) {
|
|
5257
5392
|
return {
|
|
@@ -5343,27 +5478,16 @@ function assertExternalOptimizerRuntimeIdentity(value, expectedPackage, label) {
|
|
|
5343
5478
|
//#region src/campaign/optimizer-model.ts
|
|
5344
5479
|
function assertOptimizerModel(value, label) {
|
|
5345
5480
|
if (!value || typeof value !== "object") throw new Error(`${label} is required`);
|
|
5346
|
-
for (const field of [
|
|
5347
|
-
|
|
5348
|
-
"baseUrl",
|
|
5349
|
-
"apiKey"
|
|
5350
|
-
]) {
|
|
5351
|
-
const item = value[field];
|
|
5352
|
-
if (typeof item !== "string" || !item.trim() || item.trim() !== item) throw new Error(`${label}.${field} must be trimmed and non-empty`);
|
|
5353
|
-
}
|
|
5354
|
-
let url;
|
|
5355
|
-
try {
|
|
5356
|
-
url = new URL(value.baseUrl);
|
|
5357
|
-
} catch {
|
|
5358
|
-
throw new Error(`${label}.baseUrl must be an absolute HTTP URL`);
|
|
5359
|
-
}
|
|
5360
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw new Error(`${label}.baseUrl must use HTTP or HTTPS without credentials, query, or fragment`);
|
|
5481
|
+
for (const [field, item] of [["model", value.model], ["callRef", value.callRef]]) if (typeof item !== "string" || !item.trim() || item.trim() !== item) throw new Error(`${label}.${field} must be trimmed and non-empty`);
|
|
5482
|
+
if (typeof value.call !== "function") throw new Error(`${label}.call must be a function`);
|
|
5361
5483
|
assertExternalOptimizerModelBudget(value.budget, `${label}.budget`);
|
|
5362
5484
|
}
|
|
5363
5485
|
function snapshotOptimizerModel(value) {
|
|
5364
5486
|
return {
|
|
5365
|
-
|
|
5366
|
-
budget: structuredClone(value.budget)
|
|
5487
|
+
model: value.model,
|
|
5488
|
+
budget: structuredClone(value.budget),
|
|
5489
|
+
call: value.call,
|
|
5490
|
+
callRef: value.callRef
|
|
5367
5491
|
};
|
|
5368
5492
|
}
|
|
5369
5493
|
const MAX_TIMER_DELAY_MS$1 = 2147483647;
|
|
@@ -5478,7 +5602,7 @@ function assertEngineRun(run, label) {
|
|
|
5478
5602
|
function assertEngineOptions(run, label) {
|
|
5479
5603
|
if (!run || typeof run !== "object") throw new Error(`gepaOptimizationMethod: ${label} is required`);
|
|
5480
5604
|
if (typeof run.engine !== "string" || !run.engine.trim() || run.engine.trim() !== run.engine) throw new Error(`gepaOptimizationMethod: ${label}.engine must be a trimmed non-empty string`);
|
|
5481
|
-
if (!Number.isFinite(run.maxProposerCostUsd) || run.maxProposerCostUsd <= 0) throw new Error(`gepaOptimizationMethod: ${label}.maxProposerCostUsd must be a positive finite number`);
|
|
5605
|
+
if (run.maxProposerCostUsd !== void 0 && (!Number.isFinite(run.maxProposerCostUsd) || run.maxProposerCostUsd <= 0)) throw new Error(`gepaOptimizationMethod: ${label}.maxProposerCostUsd must be a positive finite number when supplied`);
|
|
5482
5606
|
if (run.maxConcurrency !== void 0) assertPositiveSafeInteger$1(run.maxConcurrency, `${label}.maxConcurrency`);
|
|
5483
5607
|
if (run.stopAtScore !== void 0 && !Number.isFinite(run.stopAtScore)) throw new Error(`gepaOptimizationMethod: ${label}.stopAtScore must be a finite number`);
|
|
5484
5608
|
if (run.sandbox !== void 0 && typeof run.sandbox !== "boolean") throw new Error(`gepaOptimizationMethod: ${label}.sandbox must be a boolean`);
|
|
@@ -5605,7 +5729,7 @@ function gepaOptimizationMethod(config) {
|
|
|
5605
5729
|
maxEvidenceChars,
|
|
5606
5730
|
optimizerModel: config.optimizer ? {
|
|
5607
5731
|
model: config.optimizer.model,
|
|
5608
|
-
|
|
5732
|
+
callRef: config.optimizer.callRef,
|
|
5609
5733
|
budget: config.optimizer.budget
|
|
5610
5734
|
} : null,
|
|
5611
5735
|
runner: externalOptimizerRunnerIdentity(bridgeRunner, "agent_eval_rpc.gepa_bridge"),
|
|
@@ -5624,6 +5748,14 @@ function gepaOptimizationMethod(config) {
|
|
|
5624
5748
|
attemptId,
|
|
5625
5749
|
maxEvaluations: evaluationLimit
|
|
5626
5750
|
});
|
|
5751
|
+
const observationLog = openExternalOptimizerObservationLog({
|
|
5752
|
+
storage,
|
|
5753
|
+
path: `${runDir}/observations-${attemptId}.jsonl`
|
|
5754
|
+
});
|
|
5755
|
+
const executionLog = config.optimizer ? openExternalOptimizerExecutionLog({
|
|
5756
|
+
storage,
|
|
5757
|
+
path: `${runDir}/model-executions-${attemptId}.jsonl`
|
|
5758
|
+
}) : void 0;
|
|
5627
5759
|
const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "GEPA bridge");
|
|
5628
5760
|
const evaluate = createExternalTextEvaluator({
|
|
5629
5761
|
input,
|
|
@@ -5643,6 +5775,7 @@ function gepaOptimizationMethod(config) {
|
|
|
5643
5775
|
maxEvaluations: evaluationLimit,
|
|
5644
5776
|
acceptEvaluation: () => runBudget.acceptEvaluation(),
|
|
5645
5777
|
evaluate,
|
|
5778
|
+
observe: observationLog.observe,
|
|
5646
5779
|
...signal ? { signal } : {}
|
|
5647
5780
|
});
|
|
5648
5781
|
const runnerEnv = bridgeRunner?.env ?? {};
|
|
@@ -5662,8 +5795,9 @@ function gepaOptimizationMethod(config) {
|
|
|
5662
5795
|
});
|
|
5663
5796
|
assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
|
|
5664
5797
|
modelProxy = await startExternalOptimizerModelProxy({
|
|
5665
|
-
|
|
5666
|
-
|
|
5798
|
+
call: config.optimizer.call,
|
|
5799
|
+
callRef: config.optimizer.callRef,
|
|
5800
|
+
recordExecution: executionLog.observe,
|
|
5667
5801
|
model: config.optimizer.model,
|
|
5668
5802
|
budget: config.optimizer.budget,
|
|
5669
5803
|
costLedger,
|
|
@@ -5672,7 +5806,7 @@ function gepaOptimizationMethod(config) {
|
|
|
5672
5806
|
tags: { ...runBudget.attemptTags },
|
|
5673
5807
|
initialUsage: {
|
|
5674
5808
|
requests: priorOptimizerUsage.totalCalls,
|
|
5675
|
-
costUsd: priorOptimizerUsage.totalCostUsd
|
|
5809
|
+
...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
|
|
5676
5810
|
},
|
|
5677
5811
|
...signal ? { signal } : {}
|
|
5678
5812
|
});
|
|
@@ -5751,17 +5885,31 @@ function gepaOptimizationMethod(config) {
|
|
|
5751
5885
|
});
|
|
5752
5886
|
const optimizerCost = costFromLedgerSummary(optimizerSummary);
|
|
5753
5887
|
const reportedProposerCost = result.proposerCostUsd ?? 0;
|
|
5754
|
-
if (modelProxy)
|
|
5888
|
+
if (modelProxy) {
|
|
5889
|
+
modelProxy.assertExecutionComplete();
|
|
5890
|
+
assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "GEPA");
|
|
5891
|
+
}
|
|
5755
5892
|
const tokenUsage = modelProxy ? optimizationTokenUsageFromSummary(optimizerSummary, optimizerReceipts) : void 0;
|
|
5756
5893
|
const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
|
|
5894
|
+
const meteredCost = modelProxy ? combineComparisonCosts([{
|
|
5895
|
+
label: "evaluation",
|
|
5896
|
+
cost: evaluationCost
|
|
5897
|
+
}, {
|
|
5898
|
+
label: "optimizer model",
|
|
5899
|
+
cost: optimizerCost
|
|
5900
|
+
}]) : void 0;
|
|
5901
|
+
const externalTotalCostUsd = evaluationCost.totalCostUsd + reportedProposerCost;
|
|
5757
5902
|
return {
|
|
5758
5903
|
winnerSurface: decodeExternalTextCandidate(result.bestCandidate),
|
|
5759
|
-
cost: modelProxy ? {
|
|
5760
|
-
totalCostUsd:
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5904
|
+
cost: modelProxy ? meteredCost : {
|
|
5905
|
+
totalCostUsd: externalTotalCostUsd,
|
|
5906
|
+
costProvenance: result.proposerCostAccounting === "reported" && evaluationCost.costProvenance.kind !== "uncaptured" ? {
|
|
5907
|
+
kind: "estimated",
|
|
5908
|
+
usd: externalTotalCostUsd
|
|
5909
|
+
} : {
|
|
5910
|
+
kind: "uncaptured",
|
|
5911
|
+
usd: null
|
|
5912
|
+
},
|
|
5765
5913
|
accountingComplete: false,
|
|
5766
5914
|
incompleteReasons: [
|
|
5767
5915
|
...evaluationCost.incompleteReasons,
|
|
@@ -5772,13 +5920,18 @@ function gepaOptimizationMethod(config) {
|
|
|
5772
5920
|
durationMs: Date.now() - started,
|
|
5773
5921
|
provenance: {
|
|
5774
5922
|
...runtime,
|
|
5775
|
-
...config.optimizer ? {
|
|
5923
|
+
...config.optimizer ? {
|
|
5924
|
+
optimizerModel: config.optimizer.model,
|
|
5925
|
+
optimizerCallRef: config.optimizer.callRef
|
|
5926
|
+
} : {},
|
|
5776
5927
|
compatibleRunId,
|
|
5777
5928
|
runId,
|
|
5778
5929
|
resumed: result.resumed,
|
|
5779
5930
|
evaluationCount: runBudget.acceptedEvaluations(),
|
|
5780
5931
|
artifactDir: outputDir,
|
|
5781
|
-
...tokenUsage ? { tokenUsage } : {}
|
|
5932
|
+
...tokenUsage ? { tokenUsage } : {},
|
|
5933
|
+
observations: observationLog.summary(),
|
|
5934
|
+
...executionLog ? { modelExecutions: executionLog.summary() } : {}
|
|
5782
5935
|
}
|
|
5783
5936
|
};
|
|
5784
5937
|
}
|
|
@@ -6524,7 +6677,7 @@ function campaignMeasurementDigest(campaign) {
|
|
|
6524
6677
|
generation: cell.generation ?? null,
|
|
6525
6678
|
judgeScores: cell.judgeScores,
|
|
6526
6679
|
costUsd: cell.costUsd,
|
|
6527
|
-
|
|
6680
|
+
costProvenance: cell.costProvenance,
|
|
6528
6681
|
costCallIds: [...cell.costCallIds ?? []].sort(),
|
|
6529
6682
|
tokenUsage: cell.tokenUsage,
|
|
6530
6683
|
resolvedModel: cell.resolvedModel ?? null,
|
|
@@ -6973,7 +7126,7 @@ function skillOptOptimizationMethod(config) {
|
|
|
6973
7126
|
background: config.background ?? "",
|
|
6974
7127
|
optimizerModel: {
|
|
6975
7128
|
model: config.optimizer.model,
|
|
6976
|
-
|
|
7129
|
+
callRef: config.optimizer.callRef,
|
|
6977
7130
|
budget: config.optimizer.budget
|
|
6978
7131
|
},
|
|
6979
7132
|
seedCandidate: input.baselineSurface,
|
|
@@ -6998,6 +7151,14 @@ function skillOptOptimizationMethod(config) {
|
|
|
6998
7151
|
attemptId,
|
|
6999
7152
|
maxEvaluations: config.maxEvaluations
|
|
7000
7153
|
});
|
|
7154
|
+
const observationLog = openExternalOptimizerObservationLog({
|
|
7155
|
+
storage,
|
|
7156
|
+
path: `${runDir}/observations-${attemptId}.jsonl`
|
|
7157
|
+
});
|
|
7158
|
+
const executionLog = openExternalOptimizerExecutionLog({
|
|
7159
|
+
storage,
|
|
7160
|
+
path: `${runDir}/model-executions-${attemptId}.jsonl`
|
|
7161
|
+
});
|
|
7001
7162
|
const scenarioById = mapExternalScenarios(input.trainScenarios, input.selectionScenarios, "SkillOpt bridge");
|
|
7002
7163
|
const evaluate = createExternalTextEvaluator({
|
|
7003
7164
|
input,
|
|
@@ -7017,6 +7178,7 @@ function skillOptOptimizationMethod(config) {
|
|
|
7017
7178
|
maxEvaluations: config.maxEvaluations,
|
|
7018
7179
|
acceptEvaluation: () => runBudget.acceptEvaluation(),
|
|
7019
7180
|
evaluate,
|
|
7181
|
+
observe: observationLog.observe,
|
|
7020
7182
|
...signal ? { signal } : {}
|
|
7021
7183
|
});
|
|
7022
7184
|
const runnerEnv = bridgeRunner?.env ?? {};
|
|
@@ -7035,8 +7197,9 @@ function skillOptOptimizationMethod(config) {
|
|
|
7035
7197
|
});
|
|
7036
7198
|
assertPriorExternalOptimizerUsage(priorOptimizerUsage, config.optimizer.budget, name);
|
|
7037
7199
|
const modelProxy = await startExternalOptimizerModelProxy({
|
|
7038
|
-
|
|
7039
|
-
|
|
7200
|
+
call: config.optimizer.call,
|
|
7201
|
+
callRef: config.optimizer.callRef,
|
|
7202
|
+
recordExecution: executionLog.observe,
|
|
7040
7203
|
model: config.optimizer.model,
|
|
7041
7204
|
budget: config.optimizer.budget,
|
|
7042
7205
|
costLedger,
|
|
@@ -7045,7 +7208,7 @@ function skillOptOptimizationMethod(config) {
|
|
|
7045
7208
|
tags: { ...runBudget.attemptTags },
|
|
7046
7209
|
initialUsage: {
|
|
7047
7210
|
requests: priorOptimizerUsage.totalCalls,
|
|
7048
|
-
costUsd: priorOptimizerUsage.totalCostUsd
|
|
7211
|
+
...priorOptimizerUsage.costProvenance.kind === "uncaptured" ? {} : { costUsd: priorOptimizerUsage.totalCostUsd }
|
|
7049
7212
|
},
|
|
7050
7213
|
...signal ? { signal } : {}
|
|
7051
7214
|
});
|
|
@@ -7134,32 +7297,39 @@ function skillOptOptimizationMethod(config) {
|
|
|
7134
7297
|
tags: runBudget.runTags
|
|
7135
7298
|
});
|
|
7136
7299
|
const optimizerCost = costFromLedgerSummary(optimizerUsage);
|
|
7300
|
+
modelProxy.assertExecutionComplete();
|
|
7137
7301
|
assertExternalOptimizerCompletionCount(result.tokenUsage, modelProxy.requestAttempts(), modelProxy.successfulCompletions(), name, "SkillOpt");
|
|
7138
7302
|
const tokenUsage = optimizationTokenUsageFromSummary(optimizerUsage, optimizerReceipts);
|
|
7139
7303
|
const runtime = observedExternalOptimizerRuntime(runtimeIdentity);
|
|
7304
|
+
const combinedCost = combineComparisonCosts([{
|
|
7305
|
+
label: "evaluation",
|
|
7306
|
+
cost: evaluationCost
|
|
7307
|
+
}, {
|
|
7308
|
+
label: "optimizer model",
|
|
7309
|
+
cost: optimizerCost
|
|
7310
|
+
}]);
|
|
7140
7311
|
return {
|
|
7141
7312
|
winnerSurface: result.bestCandidate,
|
|
7142
|
-
cost:
|
|
7143
|
-
totalCostUsd: evaluationCost.totalCostUsd + optimizerCost.totalCostUsd,
|
|
7144
|
-
accountingComplete: evaluationCost.accountingComplete && optimizerCost.accountingComplete,
|
|
7145
|
-
incompleteReasons: [...evaluationCost.incompleteReasons.map((reason) => `evaluation: ${reason}`), ...optimizerCost.incompleteReasons.map((reason) => `optimizer model: ${reason}`)]
|
|
7146
|
-
},
|
|
7313
|
+
cost: combinedCost,
|
|
7147
7314
|
durationMs: Date.now() - started,
|
|
7148
7315
|
provenance: {
|
|
7149
7316
|
...runtime,
|
|
7150
7317
|
optimizerModel: config.optimizer.model,
|
|
7318
|
+
optimizerCallRef: config.optimizer.callRef,
|
|
7151
7319
|
compatibleRunId,
|
|
7152
7320
|
runId,
|
|
7153
7321
|
resumed: result.resumed,
|
|
7154
7322
|
evaluationCount: runBudget.acceptedEvaluations(),
|
|
7155
7323
|
artifactDir: outputDir,
|
|
7156
|
-
...tokenUsage ? { tokenUsage } : {}
|
|
7324
|
+
...tokenUsage ? { tokenUsage } : {},
|
|
7325
|
+
observations: observationLog.summary(),
|
|
7326
|
+
modelExecutions: executionLog.summary()
|
|
7157
7327
|
}
|
|
7158
7328
|
};
|
|
7159
7329
|
}
|
|
7160
7330
|
};
|
|
7161
7331
|
}
|
|
7162
7332
|
//#endregion
|
|
7163
|
-
export {
|
|
7333
|
+
export { assertCampaignSplitIdentity as $, assertOptimizationResult as A, summarizeAgentReceiptIntegrity as At, surfaceContentHash as B, detectScale as C, decidePairedPromotion as Ct, runCanaries as D, BackendIntegrityError as Dt, pairHoldout as E, pairedDeltaTest as Et, assertCodeSurfaceIdentity as F, DEFAULT_MUTATION_PRIMITIVES as G, campaignBreakdown as H, assertComponentSurface as I, planCampaignRun as J, buildReflectionPrompt as K, codeSurfaceIdentityMaterial as L, compareOptimizationMethods as M, JudgeParseError as Mt, costFromLedgerSummary as N, composeGate as O, assertRealAgentReceipts as Ot, optimizationTokenUsageFromSummary as P, assertCampaignDesign as Q, componentSurfaceIdentityMaterial as R, defaultProductionGate as S, recoverTruncatedJson as St, heldoutSignificance as T, minimumPairsForPairedDeltaTest as Tt, campaignMeanComposite as U, surfaceHash as V, compareRankKeys as W, resolveRunDir as X, runCampaign as Y, tangleTracesRoot as Z, buildEvidenceVector as _, crowdingDistance as _t, emitLoopProvenance as a, REFERENCE_EQUIVALENCE_JUDGE_VERSION as at, powerPreflight as b, paretoFrontierWithCrowding as bt, provenanceRecordPath as c, DEFAULT_RED_TEAM_CORPUS as ct, runImprovementLoop as d, scoreRedTeamOutput as dt, campaignScenarioIdentity as et, runOptimization as f, toolNamesForRun as ft, gepaOptimizationMethod as g, llmJudge as gt, labelTrustRank as h, hashScenarios as ht, canonicalDigest as i, REFERENCE_EQUIVALENCE_INPUT_LIMITS as it, combineComparisonCosts as j, summarizeBackendIntegrity as jt, externalTextOptimizationMethod as k, assertRealBackend as kt, provenanceSpansPath as l, redTeamDataset as lt, isProposedCandidate as m, HoldoutLockedError as mt, buildLoopProvenanceRecord as n, campaignSplitDigestFromIdentities as nt, loopProvenanceArgsFromResult as o, createReferenceEquivalenceJudge as ot, runEval as p, Dataset as pt, parseReflectionResponse as q, campaignMeasurementDigest as r, openAutoPr as rt, loopProvenanceSpans as s, runReferenceEquivalenceJudge as st, skillOptOptimizationMethod as t, campaignSplitDigest as tt, verifyLoopProvenanceRecord as u, redTeamReport as ut, paretoPolicy as v, dominates as vt, dimensionRegressions as w, pairedDecisionShape as wt, heldOutGate as x, scalarScore as xt, paretoSignificanceGate as y, paretoFrontier as yt, renderSurfaceDiff as z };
|
|
7164
7334
|
|
|
7165
|
-
//# sourceMappingURL=skillopt-optimization-method-
|
|
7335
|
+
//# sourceMappingURL=skillopt-optimization-method-Ds8J1_K8.js.map
|