modelrig 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.
- package/CHANGELOG.md +94 -0
- package/README.md +41 -11
- package/data/PROVENANCE.md +19 -0
- package/data/pricing-manual.json +22 -0
- package/data/pricing-map.json +46362 -0
- package/data/pricing-overlay.json +419 -0
- package/dist/adapters/anthropic.d.ts +50 -6
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +123 -29
- package/dist/adapters/build-adapters.d.ts +11 -0
- package/dist/adapters/build-adapters.d.ts.map +1 -0
- package/dist/adapters/build-adapters.js +45 -0
- package/dist/adapters/deepseek.d.ts +6 -0
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +54 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +46 -6
- package/dist/adapters/grok.d.ts +9 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +67 -3
- package/dist/adapters/openai-compat.d.ts +6 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +58 -1
- package/dist/adapters/openai-stream.d.ts +58 -0
- package/dist/adapters/openai-stream.d.ts.map +1 -0
- package/dist/adapters/openai-stream.js +120 -0
- package/dist/adapters/openai.d.ts +6 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +60 -1
- package/dist/adapters/sampling.d.ts +28 -0
- package/dist/adapters/sampling.d.ts.map +1 -0
- package/dist/adapters/sampling.js +34 -0
- package/dist/adapters/tool-mapping.d.ts +92 -0
- package/dist/adapters/tool-mapping.d.ts.map +1 -0
- package/dist/adapters/tool-mapping.js +143 -0
- package/dist/artifacts/artifact.d.ts +137 -0
- package/dist/artifacts/artifact.d.ts.map +1 -0
- package/dist/artifacts/artifact.js +377 -0
- package/dist/artifacts/content-uploader.d.ts +87 -0
- package/dist/artifacts/content-uploader.d.ts.map +1 -0
- package/dist/artifacts/content-uploader.js +124 -0
- package/dist/artifacts/index.d.ts +63 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +80 -0
- package/dist/artifacts/run-context.d.ts +129 -0
- package/dist/artifacts/run-context.d.ts.map +1 -0
- package/dist/artifacts/run-context.js +208 -0
- package/dist/artifacts/scrub.d.ts +60 -0
- package/dist/artifacts/scrub.d.ts.map +1 -0
- package/dist/artifacts/scrub.js +139 -0
- package/dist/artifacts/step.d.ts +26 -0
- package/dist/artifacts/step.d.ts.map +1 -0
- package/dist/artifacts/step.js +37 -0
- package/dist/artifacts/taint.d.ts +63 -0
- package/dist/artifacts/taint.d.ts.map +1 -0
- package/dist/artifacts/taint.js +76 -0
- package/dist/bakeoff/cache-sim.d.ts +99 -0
- package/dist/bakeoff/cache-sim.d.ts.map +1 -0
- package/dist/bakeoff/cache-sim.js +126 -0
- package/dist/bakeoff/eval-cases.d.ts +96 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases.js +105 -0
- package/dist/bakeoff/report.d.ts +41 -0
- package/dist/bakeoff/report.d.ts.map +1 -0
- package/dist/bakeoff/report.js +142 -0
- package/dist/bakeoff/samples.d.ts +15 -0
- package/dist/bakeoff/samples.d.ts.map +1 -1
- package/dist/bakeoff/samples.js +20 -0
- package/dist/cli.d.ts +1 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +134 -116
- package/dist/config.d.ts +100 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -0
- package/dist/gateway/http-protocol.d.ts +175 -0
- package/dist/gateway/http-protocol.d.ts.map +1 -0
- package/dist/gateway/http-protocol.js +105 -0
- package/dist/index.d.ts +105 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -138
- package/dist/init/finder.d.ts +15 -0
- package/dist/init/finder.d.ts.map +1 -1
- package/dist/init/finder.js +46 -0
- package/dist/init/init-cli.d.ts +10 -0
- package/dist/init/init-cli.d.ts.map +1 -0
- package/dist/init/init-cli.js +86 -0
- package/dist/init/observe-cli.d.ts +19 -0
- package/dist/init/observe-cli.d.ts.map +1 -0
- package/dist/init/observe-cli.js +73 -0
- package/dist/init/observe.d.ts +81 -0
- package/dist/init/observe.d.ts.map +1 -0
- package/dist/init/observe.js +245 -0
- package/dist/registry/build.d.ts +94 -1
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/build.js +125 -4
- package/dist/registry/cache-policy.d.ts +94 -0
- package/dist/registry/cache-policy.d.ts.map +1 -0
- package/dist/registry/cache-policy.js +238 -0
- package/dist/registry/call-notes.d.ts.map +1 -1
- package/dist/registry/call-notes.js +40 -2
- package/dist/registry/effective-rate.d.ts +151 -0
- package/dist/registry/effective-rate.d.ts.map +1 -0
- package/dist/registry/effective-rate.js +156 -0
- package/dist/registry/facts.d.ts.map +1 -1
- package/dist/registry/facts.js +16 -0
- package/dist/registry/parity.d.ts +26 -0
- package/dist/registry/parity.d.ts.map +1 -1
- package/dist/registry/parity.js +43 -2
- package/dist/registry/pricing-shape.d.ts +113 -0
- package/dist/registry/pricing-shape.d.ts.map +1 -0
- package/dist/registry/pricing-shape.js +123 -0
- package/dist/registry/pricing.d.ts +25 -1
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +64 -6
- package/dist/rigindex/bootstrap.d.ts +36 -0
- package/dist/rigindex/bootstrap.d.ts.map +1 -0
- package/dist/rigindex/bootstrap.js +97 -0
- package/dist/rigindex/fit.d.ts +24 -0
- package/dist/rigindex/fit.d.ts.map +1 -0
- package/dist/rigindex/fit.js +76 -0
- package/dist/rigindex/gates.d.ts +52 -0
- package/dist/rigindex/gates.d.ts.map +1 -0
- package/dist/rigindex/gates.js +109 -0
- package/dist/rigindex/graph.d.ts +31 -0
- package/dist/rigindex/graph.d.ts.map +1 -0
- package/dist/rigindex/graph.js +120 -0
- package/dist/rigindex/index.d.ts +23 -0
- package/dist/rigindex/index.d.ts.map +1 -0
- package/dist/rigindex/index.js +112 -0
- package/dist/rigindex/types.d.ts +76 -0
- package/dist/rigindex/types.d.ts.map +1 -0
- package/dist/rigindex/types.js +8 -0
- package/dist/routes/cache-lint.d.ts +41 -0
- package/dist/routes/cache-lint.d.ts.map +1 -0
- package/dist/routes/cache-lint.js +81 -0
- package/dist/routes/loader.d.ts +37 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +138 -0
- package/dist/routes/resolved-bundle.d.ts +79 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -0
- package/dist/routes/resolved-bundle.js +148 -0
- package/dist/routes/sampling-config.d.ts +32 -0
- package/dist/routes/sampling-config.d.ts.map +1 -0
- package/dist/routes/sampling-config.js +91 -0
- package/dist/run/ajv.d.ts +22 -0
- package/dist/run/ajv.d.ts.map +1 -0
- package/dist/run/ajv.js +32 -0
- package/dist/run/cache-handle.d.ts +58 -0
- package/dist/run/cache-handle.d.ts.map +1 -0
- package/dist/run/cache-handle.js +54 -0
- package/dist/run/cache-stamp.d.ts +65 -0
- package/dist/run/cache-stamp.d.ts.map +1 -0
- package/dist/run/cache-stamp.js +0 -0
- package/dist/run/capture-write.d.ts +33 -0
- package/dist/run/capture-write.d.ts.map +1 -0
- package/dist/run/capture-write.js +55 -0
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +1 -1
- package/dist/run/dispatch.d.ts +99 -0
- package/dist/run/dispatch.d.ts.map +1 -0
- package/dist/run/dispatch.js +191 -0
- package/dist/run/raw.d.ts +88 -0
- package/dist/run/raw.d.ts.map +1 -0
- package/dist/run/raw.js +286 -0
- package/dist/run/record-attempt.d.ts +40 -0
- package/dist/run/record-attempt.d.ts.map +1 -0
- package/dist/run/record-attempt.js +99 -0
- package/dist/run/resolve.d.ts +75 -2
- package/dist/run/resolve.d.ts.map +1 -1
- package/dist/run/resolve.js +126 -4
- package/dist/run/run.d.ts +14 -0
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +0 -0
- package/dist/run/stream.d.ts +49 -0
- package/dist/run/stream.d.ts.map +1 -0
- package/dist/run/stream.js +87 -0
- package/dist/run/validate.d.ts.map +1 -1
- package/dist/run/validate.js +6 -7
- package/dist/savings/cache-ledger.d.ts +108 -0
- package/dist/savings/cache-ledger.d.ts.map +1 -0
- package/dist/savings/cache-ledger.js +174 -0
- package/dist/savings/cache-reconcile.d.ts +64 -0
- package/dist/savings/cache-reconcile.d.ts.map +1 -0
- package/dist/savings/cache-reconcile.js +59 -0
- package/dist/savings/cache-stats.d.ts +63 -0
- package/dist/savings/cache-stats.d.ts.map +1 -0
- package/dist/savings/cache-stats.js +108 -0
- package/dist/savings/emit.d.ts +13 -0
- package/dist/savings/emit.d.ts.map +1 -1
- package/dist/savings/emit.js +25 -0
- package/dist/savings/swap-execute.d.ts +14 -0
- package/dist/savings/swap-execute.d.ts.map +1 -1
- package/dist/savings/swap-execute.js +44 -1
- package/dist/savings/swaps-client.d.ts +14 -0
- package/dist/savings/swaps-client.d.ts.map +1 -1
- package/dist/savings/swaps-client.js +16 -2
- package/dist/settings/api-fetcher.d.ts +39 -0
- package/dist/settings/api-fetcher.d.ts.map +1 -0
- package/dist/settings/api-fetcher.js +92 -0
- package/dist/settings/index.d.ts +10 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +15 -0
- package/dist/settings/task-settings.d.ts +91 -0
- package/dist/settings/task-settings.d.ts.map +1 -0
- package/dist/settings/task-settings.js +116 -0
- package/dist/status.d.ts +77 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +113 -0
- package/dist/telemetry/artifact-store.d.ts +48 -0
- package/dist/telemetry/artifact-store.d.ts.map +1 -0
- package/dist/telemetry/artifact-store.js +189 -0
- package/dist/telemetry/cache-miss-streak.d.ts +79 -0
- package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
- package/dist/telemetry/cache-miss-streak.js +99 -0
- package/dist/telemetry/capture.d.ts +3 -0
- package/dist/telemetry/capture.d.ts.map +1 -1
- package/dist/telemetry/capture.js +15 -8
- package/dist/telemetry/exporter-setup.d.ts +28 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -0
- package/dist/telemetry/exporter-setup.js +34 -0
- package/dist/telemetry/exporter.d.ts +75 -0
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +324 -47
- package/dist/telemetry/health.d.ts +76 -0
- package/dist/telemetry/health.d.ts.map +1 -0
- package/dist/telemetry/health.js +92 -0
- package/dist/telemetry/ingest-protocol.d.ts +152 -3
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +204 -4
- package/dist/telemetry/load-error-report.d.ts +24 -0
- package/dist/telemetry/load-error-report.d.ts.map +1 -0
- package/dist/telemetry/load-error-report.js +50 -0
- package/dist/telemetry/otel-map.d.ts +121 -0
- package/dist/telemetry/otel-map.d.ts.map +1 -0
- package/dist/telemetry/otel-map.js +224 -0
- package/dist/telemetry/otel-normalize.d.ts +104 -0
- package/dist/telemetry/otel-normalize.d.ts.map +1 -0
- package/dist/telemetry/otel-normalize.js +530 -0
- package/dist/telemetry/otlp-emitter.d.ts +52 -0
- package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
- package/dist/telemetry/otlp-emitter.js +144 -0
- package/dist/telemetry/otlp-proto.d.ts +29 -0
- package/dist/telemetry/otlp-proto.d.ts.map +1 -0
- package/dist/telemetry/otlp-proto.js +266 -0
- package/dist/telemetry/sinks.d.ts +37 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +121 -30
- package/dist/telemetry/store.d.ts +28 -1
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +218 -3
- package/dist/tenancy/api-key-format.d.ts +15 -5
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +24 -5
- package/dist/types/config-teaching.d.ts +62 -0
- package/dist/types/config-teaching.d.ts.map +1 -0
- package/dist/types/config-teaching.js +183 -0
- package/dist/types/provider.d.ts +122 -1
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +21 -1
- package/dist/types/route.d.ts +79 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +117 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts +12 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate-cli.js +57 -0
- package/dist/verify/capture.d.ts +66 -0
- package/dist/verify/capture.d.ts.map +1 -0
- package/dist/verify/capture.js +180 -0
- package/dist/verify/cli-run.d.ts +23 -0
- package/dist/verify/cli-run.d.ts.map +1 -0
- package/dist/verify/cli-run.js +57 -0
- package/dist/verify/diff.d.ts +48 -0
- package/dist/verify/diff.d.ts.map +1 -0
- package/dist/verify/diff.js +157 -0
- package/dist/verify/index.d.ts +17 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +24 -0
- package/dist/verify/report.d.ts +14 -0
- package/dist/verify/report.d.ts.map +1 -0
- package/dist/verify/report.js +88 -0
- package/dist/verify/types.d.ts +101 -0
- package/dist/verify/types.d.ts.map +1 -0
- package/dist/verify/types.js +17 -0
- package/dist/verify/verify.d.ts +29 -0
- package/dist/verify/verify.d.ts.map +1 -0
- package/dist/verify/verify.js +148 -0
- package/dist/watch/evaluate.d.ts +162 -3
- package/dist/watch/evaluate.d.ts.map +1 -1
- package/dist/watch/evaluate.js +61 -0
- package/dist/watch/monitor.d.ts +116 -0
- package/dist/watch/monitor.d.ts.map +1 -0
- package/dist/watch/monitor.js +199 -0
- package/dist/watch/proposals.d.ts +20 -4
- package/dist/watch/proposals.d.ts.map +1 -1
- package/dist/watch/proposals.js +31 -3
- package/dist/watch/run.d.ts +33 -0
- package/dist/watch/run.d.ts.map +1 -1
- package/dist/watch/run.js +39 -0
- package/package.json +7 -1
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/dist/telemetry/supabase-exporter.d.ts +0 -64
- package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
- package/dist/telemetry/supabase-exporter.js +0 -211
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `@step` / `step()` — execution scope for NON-LLM stages (spec §1.4, §9).
|
|
4
|
+
*
|
|
5
|
+
* A step is an execution operation ("what happened"). For LLM calls `rig.run()`
|
|
6
|
+
* auto-creates a ground-truth step (its inference row already exists); this
|
|
7
|
+
* wrapper is for the OTHER stages — retrieval, transforms, tool calls, external
|
|
8
|
+
* APIs — which have no inference row.
|
|
9
|
+
*
|
|
10
|
+
* ── WHY THIS WRITES NO STEP ROW THIS MISSION ──────────────────────────────
|
|
11
|
+
* The `steps` CHECK requires EXACTLY ONE reference: ground-truth (an inference)
|
|
12
|
+
* XOR attested (a foreign OTLP span). A pure non-LLM stage has neither — its
|
|
13
|
+
* span reference arrives with OTLP INGEST (E3), and nothing writes `attested`
|
|
14
|
+
* rows this mission. So `step()` establishes a NAMED execution scope inside the
|
|
15
|
+
* active run context (chunk B attributes artifact save/load lineage to it) and
|
|
16
|
+
* runs the body; it does not persist a row until it has a valid reference.
|
|
17
|
+
*
|
|
18
|
+
* Instrumentation never changes the wrapped function's return value, and never
|
|
19
|
+
* throws into it — a disabled or absent run context just runs the body.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.step = step;
|
|
23
|
+
const run_context_1 = require("./run-context");
|
|
24
|
+
/**
|
|
25
|
+
* Run `fn` as a named execution step within the active run context. Returns the
|
|
26
|
+
* body's value unchanged (a promise if the body is async). Outside a run context
|
|
27
|
+
* (namespace disabled, or no run.start on this path) it simply runs the body.
|
|
28
|
+
*/
|
|
29
|
+
function step(key, fn) {
|
|
30
|
+
const ctx = (0, run_context_1.getActiveRunContext)();
|
|
31
|
+
if (ctx === null)
|
|
32
|
+
return fn();
|
|
33
|
+
// Reserve a unique step key so chunk B can attribute this scope's artifact
|
|
34
|
+
// saves/loads. No row is written (see header).
|
|
35
|
+
ctx.nextStepKey(key);
|
|
36
|
+
return fn();
|
|
37
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taint propagation — downstream closure over the artifact graph (Expansion E4
|
|
3
|
+
* chunk C, spec §Chunk C). "If THIS artifact is bad, what derived from it?" —
|
|
4
|
+
* the condemn-or-clear question. Follows lineage edges forward (an edge means
|
|
5
|
+
* `from` was produced from / consumed `to`, so `from` is DOWNSTREAM of `to`) and
|
|
6
|
+
* `supersedes` chains forward (a version that supersedes a tainted one inherits
|
|
7
|
+
* the taint). Served as a bounded DTO the artifact page renders and the replay
|
|
8
|
+
* affordance (chunk A) links into.
|
|
9
|
+
*
|
|
10
|
+
* ── BOUNDED + HONEST ──────────────────────────────────────────────────────
|
|
11
|
+
* Real graphs can be deep, wide, or cyclic (a redaction supersedes an artifact
|
|
12
|
+
* that a later step also consumed). So the closure is:
|
|
13
|
+
* - depth-bounded (default 20) — `depthLimited` says more may exist deeper;
|
|
14
|
+
* - cycle-guarded (a visited set — a node is expanded once);
|
|
15
|
+
* - count-capped (default 500) — `truncated` says "≥ cap", never a silent cut.
|
|
16
|
+
* The production path is a recursive CTE with the SAME bounds (server); this pure
|
|
17
|
+
* function is the algorithm both the CTE golden-tests against and a caller can
|
|
18
|
+
* run over fetched edges without a database.
|
|
19
|
+
*/
|
|
20
|
+
/** A stored lineage edge: `from` depends on `to` (kind = how). */
|
|
21
|
+
export interface TaintEdge {
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly to: string;
|
|
24
|
+
readonly kind: "consumed" | "derived_from";
|
|
25
|
+
}
|
|
26
|
+
/** A supersession: `artifactId` supersedes `supersedesArtifactId` (a newer
|
|
27
|
+
* version). The superseder is downstream of what it supersedes. */
|
|
28
|
+
export interface Supersession {
|
|
29
|
+
readonly artifactId: string;
|
|
30
|
+
readonly supersedesArtifactId: string;
|
|
31
|
+
}
|
|
32
|
+
export interface TaintNode {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** Hops from the root (1 = a direct descendant). */
|
|
35
|
+
readonly depth: number;
|
|
36
|
+
}
|
|
37
|
+
export interface TaintClosure {
|
|
38
|
+
readonly root: string;
|
|
39
|
+
/** Downstream artifacts, breadth-first (nearest first), deduped, capped. */
|
|
40
|
+
readonly downstream: readonly TaintNode[];
|
|
41
|
+
/** `downstream.length` — the count actually returned. */
|
|
42
|
+
readonly count: number;
|
|
43
|
+
/** True when the closure hit the count cap — the honest "≥ count" signal. */
|
|
44
|
+
readonly truncated: boolean;
|
|
45
|
+
/** True when the frontier reached `maxDepth` — more may exist deeper. */
|
|
46
|
+
readonly depthLimited: boolean;
|
|
47
|
+
readonly maxDepth: number;
|
|
48
|
+
readonly cap: number;
|
|
49
|
+
}
|
|
50
|
+
export interface TaintOptions {
|
|
51
|
+
/** Max hops from the root (default 20). */
|
|
52
|
+
readonly maxDepth?: number;
|
|
53
|
+
/** Max downstream nodes returned before truncation (default 500). */
|
|
54
|
+
readonly cap?: number;
|
|
55
|
+
}
|
|
56
|
+
export declare const TAINT_DEFAULT_MAX_DEPTH = 20;
|
|
57
|
+
export declare const TAINT_DEFAULT_CAP = 500;
|
|
58
|
+
/**
|
|
59
|
+
* Downstream closure from `root`. Pure and deterministic. Cycle-guarded via a
|
|
60
|
+
* visited set; depth-bounded; count-capped with an honest `truncated` flag.
|
|
61
|
+
*/
|
|
62
|
+
export declare function computeDownstreamClosure(root: string, edges: readonly TaintEdge[], supersessions: readonly Supersession[], options?: TaintOptions): TaintClosure;
|
|
63
|
+
//# sourceMappingURL=taint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taint.d.ts","sourceRoot":"","sources":["../../src/artifacts/taint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC;CAC5C;AAED;mEACmE;AACnE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,aAAa,EAAE,SAAS,YAAY,EAAE,EACtC,OAAO,GAAE,YAAiB,GACzB,YAAY,CA2Cd"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Taint propagation — downstream closure over the artifact graph (Expansion E4
|
|
4
|
+
* chunk C, spec §Chunk C). "If THIS artifact is bad, what derived from it?" —
|
|
5
|
+
* the condemn-or-clear question. Follows lineage edges forward (an edge means
|
|
6
|
+
* `from` was produced from / consumed `to`, so `from` is DOWNSTREAM of `to`) and
|
|
7
|
+
* `supersedes` chains forward (a version that supersedes a tainted one inherits
|
|
8
|
+
* the taint). Served as a bounded DTO the artifact page renders and the replay
|
|
9
|
+
* affordance (chunk A) links into.
|
|
10
|
+
*
|
|
11
|
+
* ── BOUNDED + HONEST ──────────────────────────────────────────────────────
|
|
12
|
+
* Real graphs can be deep, wide, or cyclic (a redaction supersedes an artifact
|
|
13
|
+
* that a later step also consumed). So the closure is:
|
|
14
|
+
* - depth-bounded (default 20) — `depthLimited` says more may exist deeper;
|
|
15
|
+
* - cycle-guarded (a visited set — a node is expanded once);
|
|
16
|
+
* - count-capped (default 500) — `truncated` says "≥ cap", never a silent cut.
|
|
17
|
+
* The production path is a recursive CTE with the SAME bounds (server); this pure
|
|
18
|
+
* function is the algorithm both the CTE golden-tests against and a caller can
|
|
19
|
+
* run over fetched edges without a database.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.TAINT_DEFAULT_CAP = exports.TAINT_DEFAULT_MAX_DEPTH = void 0;
|
|
23
|
+
exports.computeDownstreamClosure = computeDownstreamClosure;
|
|
24
|
+
exports.TAINT_DEFAULT_MAX_DEPTH = 20;
|
|
25
|
+
exports.TAINT_DEFAULT_CAP = 500;
|
|
26
|
+
/**
|
|
27
|
+
* Downstream closure from `root`. Pure and deterministic. Cycle-guarded via a
|
|
28
|
+
* visited set; depth-bounded; count-capped with an honest `truncated` flag.
|
|
29
|
+
*/
|
|
30
|
+
function computeDownstreamClosure(root, edges, supersessions, options = {}) {
|
|
31
|
+
const maxDepth = options.maxDepth ?? exports.TAINT_DEFAULT_MAX_DEPTH;
|
|
32
|
+
const cap = options.cap ?? exports.TAINT_DEFAULT_CAP;
|
|
33
|
+
// Adjacency: node → its direct downstream nodes. An edge (from → to) makes
|
|
34
|
+
// `from` downstream of `to`; a supersession makes the superseder downstream of
|
|
35
|
+
// what it supersedes. Both collapse into the same "who depends on X" map.
|
|
36
|
+
const downstreamOf = new Map();
|
|
37
|
+
const add = (parent, child) => {
|
|
38
|
+
const cur = downstreamOf.get(parent);
|
|
39
|
+
if (cur === undefined)
|
|
40
|
+
downstreamOf.set(parent, [child]);
|
|
41
|
+
else
|
|
42
|
+
cur.push(child);
|
|
43
|
+
};
|
|
44
|
+
for (const e of edges)
|
|
45
|
+
add(e.to, e.from);
|
|
46
|
+
for (const s of supersessions)
|
|
47
|
+
add(s.supersedesArtifactId, s.artifactId);
|
|
48
|
+
const visited = new Set([root]); // root is never listed as its own downstream
|
|
49
|
+
const downstream = [];
|
|
50
|
+
let truncated = false;
|
|
51
|
+
let depthLimited = false;
|
|
52
|
+
// BFS by depth so `downstream` is nearest-first and depth is well-defined.
|
|
53
|
+
let frontier = [root];
|
|
54
|
+
for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth++) {
|
|
55
|
+
const next = [];
|
|
56
|
+
for (const node of frontier) {
|
|
57
|
+
for (const child of downstreamOf.get(node) ?? []) {
|
|
58
|
+
if (visited.has(child))
|
|
59
|
+
continue; // cycle / diamond guard: expand once
|
|
60
|
+
visited.add(child);
|
|
61
|
+
if (downstream.length >= cap) {
|
|
62
|
+
truncated = true;
|
|
63
|
+
continue; // keep draining `visited` so we don't re-count, but add no more
|
|
64
|
+
}
|
|
65
|
+
downstream.push({ id: child, depth });
|
|
66
|
+
next.push(child);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
frontier = next;
|
|
70
|
+
}
|
|
71
|
+
// If we stopped because we hit maxDepth but there was still a frontier, more
|
|
72
|
+
// may exist deeper than we looked.
|
|
73
|
+
if (frontier.length > 0)
|
|
74
|
+
depthLimited = true;
|
|
75
|
+
return { root, downstream, count: downstream.length, truncated, depthLimited, maxDepth, cap };
|
|
76
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Virtual cache simulator — cache-aware-routing spec §5.4, [Briefing §26]. PURE:
|
|
3
|
+
* no store/clock/network. THE fix for the independent-turn evaluation fallacy:
|
|
4
|
+
* replay currently scores every sample COLD, so a bake-off report can never see
|
|
5
|
+
* that a candidate would run warm at production cadence. This walks a candidate's
|
|
6
|
+
* call sequence in timestamp order, maintaining a per-candidate VIRTUAL cache
|
|
7
|
+
* from the POLICY REGISTRY (TTL + min-cacheable + refresh-on-read), and emits a
|
|
8
|
+
* per-call {read, write, uncached} token split.
|
|
9
|
+
*
|
|
10
|
+
* ── SIMULATOR HONESTY (spec §5.4, §10 credibility risk) ───────────────────
|
|
11
|
+
* • State comes ONLY from the policy registry — never from a guessed model of
|
|
12
|
+
* how a provider caches. An undocumented TTL uses a CONSERVATIVE fallback
|
|
13
|
+
* (short), so the sim UNDER-credits rather than over-credits warmth.
|
|
14
|
+
* • The output is always LABELLED simulation. The odometer stays measured-only
|
|
15
|
+
* (this module never feeds it) — simulator output is never "verified savings".
|
|
16
|
+
* • Cache identity is the caller-supplied `prefixFingerprint`. A template
|
|
17
|
+
* mutation or a tool-order change is, by construction, a DIFFERENT
|
|
18
|
+
* fingerprint — so a mutation forces a miss/rewrite with no special-casing
|
|
19
|
+
* here (the synthetic tests exercise exactly that).
|
|
20
|
+
*
|
|
21
|
+
* A "switch-and-return" is modelled by a time GAP in this candidate's sequence:
|
|
22
|
+
* if the gap is within TTL the return is a read (still warm); if it exceeds TTL
|
|
23
|
+
* the cache has expired and the return rewrites. Nothing about the OTHER
|
|
24
|
+
* candidate is needed — each candidate is simulated over its own calls.
|
|
25
|
+
*/
|
|
26
|
+
import type { CachePolicy } from "../registry/cache-policy";
|
|
27
|
+
import type { EffectiveRates } from "../registry/effective-rate";
|
|
28
|
+
import type { ProviderId } from "../types/provider";
|
|
29
|
+
/** One call in a candidate's sequence. `prefixTokens` is the cacheable prefix
|
|
30
|
+
* length; `promptTokens` the whole input (prefix + volatile tail). */
|
|
31
|
+
export interface SimCall {
|
|
32
|
+
readonly tsMs: number;
|
|
33
|
+
readonly prefixFingerprint: string;
|
|
34
|
+
readonly prefixTokens: number;
|
|
35
|
+
readonly promptTokens: number;
|
|
36
|
+
}
|
|
37
|
+
/** Per-call token split — a disjoint partition of `promptTokens`:
|
|
38
|
+
* `simulated_read + simulated_write + simulated_uncached === promptTokens`. */
|
|
39
|
+
export interface SimCallResult {
|
|
40
|
+
readonly simulated_read: number;
|
|
41
|
+
readonly simulated_write: number;
|
|
42
|
+
readonly simulated_uncached: number;
|
|
43
|
+
/** True when the cacheable prefix was served from a warm virtual cache. */
|
|
44
|
+
readonly hit: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** The policy assumptions the sim ran under — PRINTED with every report so the
|
|
47
|
+
* simulation's basis is never hidden (spec §5.4 "assumptions printed"). */
|
|
48
|
+
export interface CacheSimAssumptions {
|
|
49
|
+
readonly provider: string;
|
|
50
|
+
readonly model: string;
|
|
51
|
+
readonly cacheSupported: boolean;
|
|
52
|
+
/** TTL used to expire virtual entries (seconds). */
|
|
53
|
+
readonly ttlSeconds: number;
|
|
54
|
+
/** false ⇒ `ttlSeconds` is the conservative fallback, not a published value. */
|
|
55
|
+
readonly ttlSourced: boolean;
|
|
56
|
+
readonly minCacheableTokens: number;
|
|
57
|
+
readonly refreshOnRead: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface CacheSimResult {
|
|
60
|
+
readonly perCall: readonly SimCallResult[];
|
|
61
|
+
readonly totalRead: number;
|
|
62
|
+
readonly totalWrite: number;
|
|
63
|
+
readonly totalUncached: number;
|
|
64
|
+
readonly hitCount: number;
|
|
65
|
+
readonly assumptions: CacheSimAssumptions;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Conservative TTL (seconds) for a provider that publishes none
|
|
69
|
+
* (`default_ttl_seconds: null`). Deliberately SHORT — the shortest real registry
|
|
70
|
+
* TTL (Anthropic 5m) — so the simulator never over-credits warmth for a provider
|
|
71
|
+
* whose true window is longer but undocumented (spec §5.4 honesty).
|
|
72
|
+
*/
|
|
73
|
+
export declare const FALLBACK_SIM_TTL_SECONDS = 300;
|
|
74
|
+
export interface CacheSimOptions {
|
|
75
|
+
/** Policy override — injected for tests; defaults to the live registry. */
|
|
76
|
+
readonly policy?: CachePolicy;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Simulate one candidate's cache trajectory over `calls`. Calls are processed in
|
|
80
|
+
* ascending timestamp order (a copy is sorted; the input is not mutated). The
|
|
81
|
+
* per-call results are returned in that same timestamp order.
|
|
82
|
+
*/
|
|
83
|
+
export declare function simulateCache(provider: ProviderId, model: string, calls: readonly SimCall[], opts?: CacheSimOptions): CacheSimResult;
|
|
84
|
+
/**
|
|
85
|
+
* Cache-ADJUSTED input cost of a simulated trajectory (spec §5.4): the read /
|
|
86
|
+
* write / uncached token buckets each priced at their own rate, with the
|
|
87
|
+
* fail-closed fallback to the input rate (dev-rule L5) when a cache rate is
|
|
88
|
+
* unverified. This is what the candidate WOULD pay at production cadence — the
|
|
89
|
+
* right-hand column of the bake-off's side-by-side.
|
|
90
|
+
*/
|
|
91
|
+
export declare function simulatedInputCostUsd(sim: CacheSimResult, rates: EffectiveRates): number;
|
|
92
|
+
/**
|
|
93
|
+
* Measured-COLD input cost (spec §5.4): every input token at the list input
|
|
94
|
+
* rate — what independent-turn replay actually pays. The left-hand column. This
|
|
95
|
+
* equals `simulatedInputCostUsd` exactly when the sim produced zero reads and
|
|
96
|
+
* zero writes (the regression pin — no cache activity, no divergence).
|
|
97
|
+
*/
|
|
98
|
+
export declare function coldInputCostUsd(sim: CacheSimResult, rates: EffectiveRates): number;
|
|
99
|
+
//# sourceMappingURL=cache-sim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-sim.d.ts","sourceRoot":"","sources":["../../src/bakeoff/cache-sim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;sEACsE;AACtE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;+EAC+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB;AAED;2EAC2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AASD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,IAAI,GAAE,eAAoB,GACzB,cAAc,CAkEhB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAIxF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnF"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Virtual cache simulator — cache-aware-routing spec §5.4, [Briefing §26]. PURE:
|
|
4
|
+
* no store/clock/network. THE fix for the independent-turn evaluation fallacy:
|
|
5
|
+
* replay currently scores every sample COLD, so a bake-off report can never see
|
|
6
|
+
* that a candidate would run warm at production cadence. This walks a candidate's
|
|
7
|
+
* call sequence in timestamp order, maintaining a per-candidate VIRTUAL cache
|
|
8
|
+
* from the POLICY REGISTRY (TTL + min-cacheable + refresh-on-read), and emits a
|
|
9
|
+
* per-call {read, write, uncached} token split.
|
|
10
|
+
*
|
|
11
|
+
* ── SIMULATOR HONESTY (spec §5.4, §10 credibility risk) ───────────────────
|
|
12
|
+
* • State comes ONLY from the policy registry — never from a guessed model of
|
|
13
|
+
* how a provider caches. An undocumented TTL uses a CONSERVATIVE fallback
|
|
14
|
+
* (short), so the sim UNDER-credits rather than over-credits warmth.
|
|
15
|
+
* • The output is always LABELLED simulation. The odometer stays measured-only
|
|
16
|
+
* (this module never feeds it) — simulator output is never "verified savings".
|
|
17
|
+
* • Cache identity is the caller-supplied `prefixFingerprint`. A template
|
|
18
|
+
* mutation or a tool-order change is, by construction, a DIFFERENT
|
|
19
|
+
* fingerprint — so a mutation forces a miss/rewrite with no special-casing
|
|
20
|
+
* here (the synthetic tests exercise exactly that).
|
|
21
|
+
*
|
|
22
|
+
* A "switch-and-return" is modelled by a time GAP in this candidate's sequence:
|
|
23
|
+
* if the gap is within TTL the return is a read (still warm); if it exceeds TTL
|
|
24
|
+
* the cache has expired and the return rewrites. Nothing about the OTHER
|
|
25
|
+
* candidate is needed — each candidate is simulated over its own calls.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.FALLBACK_SIM_TTL_SECONDS = void 0;
|
|
29
|
+
exports.simulateCache = simulateCache;
|
|
30
|
+
exports.simulatedInputCostUsd = simulatedInputCostUsd;
|
|
31
|
+
exports.coldInputCostUsd = coldInputCostUsd;
|
|
32
|
+
const cache_policy_1 = require("../registry/cache-policy");
|
|
33
|
+
/**
|
|
34
|
+
* Conservative TTL (seconds) for a provider that publishes none
|
|
35
|
+
* (`default_ttl_seconds: null`). Deliberately SHORT — the shortest real registry
|
|
36
|
+
* TTL (Anthropic 5m) — so the simulator never over-credits warmth for a provider
|
|
37
|
+
* whose true window is longer but undocumented (spec §5.4 honesty).
|
|
38
|
+
*/
|
|
39
|
+
exports.FALLBACK_SIM_TTL_SECONDS = 300;
|
|
40
|
+
/**
|
|
41
|
+
* Simulate one candidate's cache trajectory over `calls`. Calls are processed in
|
|
42
|
+
* ascending timestamp order (a copy is sorted; the input is not mutated). The
|
|
43
|
+
* per-call results are returned in that same timestamp order.
|
|
44
|
+
*/
|
|
45
|
+
function simulateCache(provider, model, calls, opts = {}) {
|
|
46
|
+
const policy = opts.policy ?? (0, cache_policy_1.getCachePolicy)(provider);
|
|
47
|
+
const ttlSourced = policy.cache_supported && policy.default_ttl_seconds !== null;
|
|
48
|
+
const ttlSeconds = policy.cache_supported && policy.default_ttl_seconds !== null
|
|
49
|
+
? policy.default_ttl_seconds
|
|
50
|
+
: exports.FALLBACK_SIM_TTL_SECONDS;
|
|
51
|
+
const ttlMs = ttlSeconds * 1000;
|
|
52
|
+
const minCacheable = policy.min_cacheable_tokens ?? 0;
|
|
53
|
+
const assumptions = {
|
|
54
|
+
provider,
|
|
55
|
+
model,
|
|
56
|
+
cacheSupported: policy.cache_supported,
|
|
57
|
+
ttlSeconds,
|
|
58
|
+
ttlSourced,
|
|
59
|
+
minCacheableTokens: minCacheable,
|
|
60
|
+
refreshOnRead: policy.ttl_refresh_on_read,
|
|
61
|
+
};
|
|
62
|
+
const ordered = [...calls].sort((a, b) => a.tsMs - b.tsMs);
|
|
63
|
+
const cache = new Map();
|
|
64
|
+
const perCall = [];
|
|
65
|
+
let totalRead = 0;
|
|
66
|
+
let totalWrite = 0;
|
|
67
|
+
let totalUncached = 0;
|
|
68
|
+
let hitCount = 0;
|
|
69
|
+
for (const call of ordered) {
|
|
70
|
+
const prompt = Math.max(call.promptTokens, 0);
|
|
71
|
+
const prefix = Math.min(Math.max(call.prefixTokens, 0), prompt);
|
|
72
|
+
// Provider does not cache on our path, or the prefix is below the minimum
|
|
73
|
+
// cacheable size: caching does not engage — the whole prompt is uncached and
|
|
74
|
+
// NO virtual state is written (spec §5.4: min-cacheable gates the write).
|
|
75
|
+
if (!policy.cache_supported || prefix < minCacheable || prefix === 0) {
|
|
76
|
+
perCall.push({ simulated_read: 0, simulated_write: 0, simulated_uncached: prompt, hit: false });
|
|
77
|
+
totalUncached += prompt;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const entry = cache.get(call.prefixFingerprint);
|
|
81
|
+
const warm = entry !== undefined && call.tsMs < entry.expiresAtMs;
|
|
82
|
+
if (warm) {
|
|
83
|
+
// HIT: read the cached prefix; the volatile tail is uncached.
|
|
84
|
+
const read = Math.min(prefix, entry.tokens);
|
|
85
|
+
const uncached = prompt - read;
|
|
86
|
+
perCall.push({ simulated_read: read, simulated_write: 0, simulated_uncached: uncached, hit: true });
|
|
87
|
+
totalRead += read;
|
|
88
|
+
totalUncached += uncached;
|
|
89
|
+
hitCount += 1;
|
|
90
|
+
// TTL base: a read refreshes the window only when the policy says so;
|
|
91
|
+
// otherwise the entry keeps expiring from its WRITE time.
|
|
92
|
+
if (policy.ttl_refresh_on_read)
|
|
93
|
+
entry.expiresAtMs = call.tsMs + ttlMs;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// MISS (cold or expired): WRITE the prefix into cache; the tail is uncached.
|
|
97
|
+
const uncached = prompt - prefix;
|
|
98
|
+
perCall.push({ simulated_read: 0, simulated_write: prefix, simulated_uncached: uncached, hit: false });
|
|
99
|
+
totalWrite += prefix;
|
|
100
|
+
totalUncached += uncached;
|
|
101
|
+
cache.set(call.prefixFingerprint, { tokens: prefix, expiresAtMs: call.tsMs + ttlMs });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { perCall, totalRead, totalWrite, totalUncached, hitCount, assumptions };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cache-ADJUSTED input cost of a simulated trajectory (spec §5.4): the read /
|
|
108
|
+
* write / uncached token buckets each priced at their own rate, with the
|
|
109
|
+
* fail-closed fallback to the input rate (dev-rule L5) when a cache rate is
|
|
110
|
+
* unverified. This is what the candidate WOULD pay at production cadence — the
|
|
111
|
+
* right-hand column of the bake-off's side-by-side.
|
|
112
|
+
*/
|
|
113
|
+
function simulatedInputCostUsd(sim, rates) {
|
|
114
|
+
const readRate = rates.cacheRead ?? rates.input;
|
|
115
|
+
const writeRate = rates.cacheWrite ?? rates.input;
|
|
116
|
+
return sim.totalRead * readRate + sim.totalWrite * writeRate + sim.totalUncached * rates.input;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Measured-COLD input cost (spec §5.4): every input token at the list input
|
|
120
|
+
* rate — what independent-turn replay actually pays. The left-hand column. This
|
|
121
|
+
* equals `simulatedInputCostUsd` exactly when the sim produced zero reads and
|
|
122
|
+
* zero writes (the regression pin — no cache activity, no divergence).
|
|
123
|
+
*/
|
|
124
|
+
function coldInputCostUsd(sim, rates) {
|
|
125
|
+
return (sim.totalRead + sim.totalWrite + sim.totalUncached) * rates.input;
|
|
126
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bake-off input selection from eval cases — Expansion E4 chunk A (spec §3).
|
|
3
|
+
*
|
|
4
|
+
* `bakeoff --from-eval-cases <task>` replays the promoted regression/golden cases
|
|
5
|
+
* for a task instead of the last-N captures. A case only holds METADATA (a source
|
|
6
|
+
* artifact id); the INPUT to replay lives in a LOCAL capture, reachable through
|
|
7
|
+
* the chain:
|
|
8
|
+
*
|
|
9
|
+
* eval_case → source artifact (run_id, step_key) → step's inference_id → capture
|
|
10
|
+
*
|
|
11
|
+
* Any link may be missing — the source artifact predates local retention, the
|
|
12
|
+
* route never had `capture: true`, the capture was evicted. When it is, the case
|
|
13
|
+
* is UNREHYDRATABLE. The load-bearing doctrine (spec §3.3): the bake-off runs on
|
|
14
|
+
* the rehydratable subset and NAMES every refused case with a reason — NEVER a
|
|
15
|
+
* silent subset. `selectEvalCaseCaptures` returns both halves so the CLI can
|
|
16
|
+
* print the honest-refusal report before it spends a cent.
|
|
17
|
+
*/
|
|
18
|
+
import type { CaptureRow, CaptureStore } from "../telemetry/capture";
|
|
19
|
+
import type { ArtifactStore } from "../telemetry/artifact-store";
|
|
20
|
+
import type { ExportableTelemetryStore } from "../telemetry/store";
|
|
21
|
+
import type { EvalCaseRow } from "../types/telemetry";
|
|
22
|
+
import type { RunDeps } from "../run/run";
|
|
23
|
+
import { type BakeoffSamplesSink } from "./engine";
|
|
24
|
+
import type { KeepOutputsMode } from "./samples";
|
|
25
|
+
import type { BakeoffArtifact } from "./types";
|
|
26
|
+
/** The narrow store surface the selection reads — kept minimal so this module
|
|
27
|
+
* depends on four reads, not three whole stores. */
|
|
28
|
+
export interface EvalCaseSelectionDeps {
|
|
29
|
+
readonly readEvalCasesByTask: (task: string) => EvalCaseRow[];
|
|
30
|
+
readonly getArtifactById: (id: string) => {
|
|
31
|
+
runId: string | null;
|
|
32
|
+
stepKey: string | null;
|
|
33
|
+
} | null;
|
|
34
|
+
readonly getStepInferenceId: (runId: string, stepKey: string) => string | null;
|
|
35
|
+
readonly getCaptureByInferenceId: (inferenceId: string) => CaptureRow | null;
|
|
36
|
+
}
|
|
37
|
+
export interface RehydratedCase {
|
|
38
|
+
readonly caseId: string;
|
|
39
|
+
readonly capture: CaptureRow;
|
|
40
|
+
}
|
|
41
|
+
export interface EvalCaseRefusal {
|
|
42
|
+
readonly caseId: string;
|
|
43
|
+
readonly sourceArtifactId: string;
|
|
44
|
+
/** Which link of the chain broke — the honest, actionable reason. */
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
}
|
|
47
|
+
export interface EvalCaseSelection {
|
|
48
|
+
readonly task: string;
|
|
49
|
+
/** Total eval cases found for the task (rehydratable + refused). */
|
|
50
|
+
readonly total: number;
|
|
51
|
+
readonly rehydratable: readonly RehydratedCase[];
|
|
52
|
+
readonly refusals: readonly EvalCaseRefusal[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Partition a task's eval cases into rehydratable (with their captured input) and
|
|
56
|
+
* refused (named, with a reason). Pure over the injected reads — no network, no
|
|
57
|
+
* spend; the CLI runs this first and reports before replaying.
|
|
58
|
+
*/
|
|
59
|
+
export declare function selectEvalCaseCaptures(deps: EvalCaseSelectionDeps, task: string): EvalCaseSelection;
|
|
60
|
+
/**
|
|
61
|
+
* A read-only in-memory CaptureStore over a fixed set of rehydrated captures, so
|
|
62
|
+
* the EXISTING bake-off engine (which reads `captures.readLast(route, n)`) runs
|
|
63
|
+
* over the eval-case selection with NO second actuation path (spec §3.3). Only
|
|
64
|
+
* `readLast` is meaningful; `getByInferenceId` is supported for symmetry; writes
|
|
65
|
+
* throw (this store is a replay lens, never a sink).
|
|
66
|
+
*/
|
|
67
|
+
export declare function evalCaseCaptureStore(rows: readonly RehydratedCase[]): CaptureStore;
|
|
68
|
+
export interface EvalCaseBakeoffResult {
|
|
69
|
+
readonly selection: EvalCaseSelection;
|
|
70
|
+
/** The bake-off artifact over the rehydratable subset, or null when NOTHING
|
|
71
|
+
* was rehydratable (the CLI then reports the refusals and exits without spend). */
|
|
72
|
+
readonly artifact: BakeoffArtifact | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `bakeoff --from-eval-cases <task>` end to end: select rehydratable cases, then
|
|
76
|
+
* run the EXISTING bake-off engine over exactly those captured inputs (no second
|
|
77
|
+
* actuation path — spec §3.3). Replays every rehydratable case (replayLastN =
|
|
78
|
+
* the rehydratable count). Returns the selection (so the caller reports refusals)
|
|
79
|
+
* alongside the artifact.
|
|
80
|
+
*/
|
|
81
|
+
/** The rig-side store bundle the orchestrator adapts into `EvalCaseSelectionDeps`
|
|
82
|
+
* — passed straight from `createRig` so `index.ts` stays a one-line delegate.
|
|
83
|
+
* `artifacts` is null when the namespace is off (nothing to rehydrate); `captures`
|
|
84
|
+
* is a thunk because the rig opens that handle lazily. */
|
|
85
|
+
export interface EvalCaseStores {
|
|
86
|
+
readonly artifacts: Pick<ArtifactStore, "readEvalCasesByTask" | "getArtifactById"> | null;
|
|
87
|
+
readonly telemetry: Pick<ExportableTelemetryStore, "getStepInferenceId">;
|
|
88
|
+
readonly captures: () => Pick<CaptureStore, "getByInferenceId">;
|
|
89
|
+
}
|
|
90
|
+
export declare function runBakeoffFromEvalCases(deps: RunDeps, stores: EvalCaseStores, req: {
|
|
91
|
+
readonly task: string;
|
|
92
|
+
readonly variants: readonly string[];
|
|
93
|
+
readonly envelopeUsdPerVariant: number;
|
|
94
|
+
readonly keepOutputs?: KeepOutputsMode;
|
|
95
|
+
}, samplesSink?: BakeoffSamplesSink): Promise<EvalCaseBakeoffResult>;
|
|
96
|
+
//# sourceMappingURL=eval-cases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval-cases.d.ts","sourceRoot":"","sources":["../../src/bakeoff/eval-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;oDACoD;AACpD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;IAC9D,QAAQ,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAClG,QAAQ,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/E,QAAQ,CAAC,uBAAuB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;CAC9E;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAgCnG;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,YAAY,CAclF;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC;uFACmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;GAMG;AACH;;;0DAG0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,qBAAqB,GAAG,iBAAiB,CAAC,GAAG,IAAI,CAAC;IAC1F,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,CAAC;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;CACjE;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,eAAe,CAAC;CACxC,EACD,WAAW,CAAC,EAAE,kBAAkB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CA6BhC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bake-off input selection from eval cases — Expansion E4 chunk A (spec §3).
|
|
4
|
+
*
|
|
5
|
+
* `bakeoff --from-eval-cases <task>` replays the promoted regression/golden cases
|
|
6
|
+
* for a task instead of the last-N captures. A case only holds METADATA (a source
|
|
7
|
+
* artifact id); the INPUT to replay lives in a LOCAL capture, reachable through
|
|
8
|
+
* the chain:
|
|
9
|
+
*
|
|
10
|
+
* eval_case → source artifact (run_id, step_key) → step's inference_id → capture
|
|
11
|
+
*
|
|
12
|
+
* Any link may be missing — the source artifact predates local retention, the
|
|
13
|
+
* route never had `capture: true`, the capture was evicted. When it is, the case
|
|
14
|
+
* is UNREHYDRATABLE. The load-bearing doctrine (spec §3.3): the bake-off runs on
|
|
15
|
+
* the rehydratable subset and NAMES every refused case with a reason — NEVER a
|
|
16
|
+
* silent subset. `selectEvalCaseCaptures` returns both halves so the CLI can
|
|
17
|
+
* print the honest-refusal report before it spends a cent.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.selectEvalCaseCaptures = selectEvalCaseCaptures;
|
|
21
|
+
exports.evalCaseCaptureStore = evalCaseCaptureStore;
|
|
22
|
+
exports.runBakeoffFromEvalCases = runBakeoffFromEvalCases;
|
|
23
|
+
const engine_1 = require("./engine");
|
|
24
|
+
/**
|
|
25
|
+
* Partition a task's eval cases into rehydratable (with their captured input) and
|
|
26
|
+
* refused (named, with a reason). Pure over the injected reads — no network, no
|
|
27
|
+
* spend; the CLI runs this first and reports before replaying.
|
|
28
|
+
*/
|
|
29
|
+
function selectEvalCaseCaptures(deps, task) {
|
|
30
|
+
const cases = deps.readEvalCasesByTask(task);
|
|
31
|
+
const rehydratable = [];
|
|
32
|
+
const refusals = [];
|
|
33
|
+
for (const c of cases) {
|
|
34
|
+
const refuse = (reason) => {
|
|
35
|
+
refusals.push({ caseId: c.id, sourceArtifactId: c.sourceArtifactId, reason });
|
|
36
|
+
};
|
|
37
|
+
const art = deps.getArtifactById(c.sourceArtifactId);
|
|
38
|
+
if (art === null) {
|
|
39
|
+
refuse("source artifact is not in the local store (promoted on another deployment, or evicted)");
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (art.runId === null || art.stepKey === null) {
|
|
43
|
+
refuse("source artifact was saved outside a step — no inference to key its input");
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const inferenceId = deps.getStepInferenceId(art.runId, art.stepKey);
|
|
47
|
+
if (inferenceId === null) {
|
|
48
|
+
refuse("no ground-truth inference recorded for the source step");
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const capture = deps.getCaptureByInferenceId(inferenceId);
|
|
52
|
+
if (capture === null) {
|
|
53
|
+
refuse("input not captured locally (route `capture` was off, or the capture was evicted)");
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
rehydratable.push({ caseId: c.id, capture });
|
|
57
|
+
}
|
|
58
|
+
return { task, total: cases.length, rehydratable, refusals };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A read-only in-memory CaptureStore over a fixed set of rehydrated captures, so
|
|
62
|
+
* the EXISTING bake-off engine (which reads `captures.readLast(route, n)`) runs
|
|
63
|
+
* over the eval-case selection with NO second actuation path (spec §3.3). Only
|
|
64
|
+
* `readLast` is meaningful; `getByInferenceId` is supported for symmetry; writes
|
|
65
|
+
* throw (this store is a replay lens, never a sink).
|
|
66
|
+
*/
|
|
67
|
+
function evalCaseCaptureStore(rows) {
|
|
68
|
+
const captures = rows.map((r) => r.capture);
|
|
69
|
+
return {
|
|
70
|
+
record() {
|
|
71
|
+
throw new Error("evalCaseCaptureStore is read-only (a replay lens, not a sink)");
|
|
72
|
+
},
|
|
73
|
+
readLast(route, n) {
|
|
74
|
+
return captures.filter((c) => c.route === route).slice(0, n);
|
|
75
|
+
},
|
|
76
|
+
getByInferenceId(inferenceId) {
|
|
77
|
+
return captures.find((c) => c.inferenceId === inferenceId) ?? null;
|
|
78
|
+
},
|
|
79
|
+
close() { },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function runBakeoffFromEvalCases(deps, stores, req, samplesSink) {
|
|
83
|
+
const empty = { task: req.task, total: 0, rehydratable: [], refusals: [] };
|
|
84
|
+
// No artifact namespace ⇒ nothing to rehydrate; honest empty selection.
|
|
85
|
+
if (stores.artifacts === null)
|
|
86
|
+
return { selection: empty, artifact: null };
|
|
87
|
+
const arts = stores.artifacts;
|
|
88
|
+
const caps = stores.captures();
|
|
89
|
+
const selection = selectEvalCaseCaptures({
|
|
90
|
+
readEvalCasesByTask: (t) => arts.readEvalCasesByTask(t),
|
|
91
|
+
getArtifactById: (id) => arts.getArtifactById(id),
|
|
92
|
+
getStepInferenceId: (r, s) => stores.telemetry.getStepInferenceId(r, s),
|
|
93
|
+
getCaptureByInferenceId: (i) => caps.getByInferenceId(i),
|
|
94
|
+
}, req.task);
|
|
95
|
+
if (selection.rehydratable.length === 0)
|
|
96
|
+
return { selection, artifact: null };
|
|
97
|
+
const artifact = await (0, engine_1.runBakeoff)(deps, evalCaseCaptureStore(selection.rehydratable), {
|
|
98
|
+
route: req.task,
|
|
99
|
+
variants: req.variants,
|
|
100
|
+
replayLastN: selection.rehydratable.length, // replay every rehydratable case
|
|
101
|
+
envelopeUsdPerVariant: req.envelopeUsdPerVariant,
|
|
102
|
+
...(req.keepOutputs !== undefined ? { keepOutputs: req.keepOutputs } : {}),
|
|
103
|
+
}, samplesSink);
|
|
104
|
+
return { selection, artifact };
|
|
105
|
+
}
|