modelrig 0.0.1 → 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/LICENSE +201 -0
- package/README.md +145 -8
- 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 +90 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +296 -0
- 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 +47 -0
- package/dist/adapters/deepseek.d.ts.map +1 -0
- package/dist/adapters/deepseek.js +216 -0
- package/dist/adapters/gemini.d.ts +44 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +262 -0
- package/dist/adapters/grok.d.ts +53 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +232 -0
- package/dist/adapters/openai-compat.d.ts +80 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -0
- package/dist/adapters/openai-compat.js +244 -0
- 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 +55 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +281 -0
- 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/engine.d.ts +52 -0
- package/dist/bakeoff/engine.d.ts.map +1 -0
- package/dist/bakeoff/engine.js +163 -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/replay.d.ts +52 -0
- package/dist/bakeoff/replay.d.ts.map +1 -0
- package/dist/bakeoff/replay.js +94 -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 +77 -0
- package/dist/bakeoff/samples.d.ts.map +1 -0
- package/dist/bakeoff/samples.js +148 -0
- package/dist/bakeoff/stats.d.ts +13 -0
- package/dist/bakeoff/stats.d.ts.map +1 -0
- package/dist/bakeoff/stats.js +48 -0
- package/dist/bakeoff/types.d.ts +37 -0
- package/dist/bakeoff/types.d.ts.map +1 -0
- package/dist/bakeoff/types.js +10 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +590 -0
- package/dist/config.d.ts +204 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +252 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -0
- package/dist/feedback/coverage.d.ts +32 -0
- package/dist/feedback/coverage.d.ts.map +1 -0
- package/dist/feedback/coverage.js +95 -0
- package/dist/feedback/feedback.d.ts +35 -0
- package/dist/feedback/feedback.d.ts.map +1 -0
- package/dist/feedback/feedback.js +39 -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 +242 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +576 -0
- package/dist/init/finder.d.ts +94 -0
- package/dist/init/finder.d.ts.map +1 -0
- package/dist/init/finder.js +343 -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/init/run.d.ts +99 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +281 -0
- package/dist/init/signatures.d.ts +39 -0
- package/dist/init/signatures.d.ts.map +1 -0
- package/dist/init/signatures.js +116 -0
- package/dist/matrix/aggregate.d.ts +89 -0
- package/dist/matrix/aggregate.d.ts.map +1 -0
- package/dist/matrix/aggregate.js +186 -0
- package/dist/query/grammar.d.ts +35 -0
- package/dist/query/grammar.d.ts.map +1 -0
- package/dist/query/grammar.js +279 -0
- package/dist/query/run.d.ts +8 -0
- package/dist/query/run.d.ts.map +1 -0
- package/dist/query/run.js +21 -0
- package/dist/registry/build.d.ts +219 -0
- package/dist/registry/build.d.ts.map +1 -0
- package/dist/registry/build.js +313 -0
- 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 +11 -0
- package/dist/registry/call-notes.d.ts.map +1 -0
- package/dist/registry/call-notes.js +110 -0
- package/dist/registry/claims.d.ts +63 -0
- package/dist/registry/claims.d.ts.map +1 -0
- package/dist/registry/claims.js +107 -0
- 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 +32 -0
- package/dist/registry/facts.d.ts.map +1 -0
- package/dist/registry/facts.js +136 -0
- package/dist/registry/fingerprint.d.ts +20 -0
- package/dist/registry/fingerprint.d.ts.map +1 -0
- package/dist/registry/fingerprint.js +150 -0
- package/dist/registry/parity.d.ts +52 -0
- package/dist/registry/parity.d.ts.map +1 -0
- package/dist/registry/parity.js +82 -0
- 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 +43 -0
- package/dist/registry/pricing.d.ts.map +1 -0
- package/dist/registry/pricing.js +127 -0
- package/dist/registry/rows.d.ts +16 -0
- package/dist/registry/rows.d.ts.map +1 -0
- package/dist/registry/rows.js +57 -0
- package/dist/registry/seed.d.ts +43 -0
- package/dist/registry/seed.d.ts.map +1 -0
- package/dist/registry/seed.js +97 -0
- package/dist/registry/select.d.ts +68 -0
- package/dist/registry/select.d.ts.map +1 -0
- package/dist/registry/select.js +141 -0
- package/dist/registry/staleness.d.ts +18 -0
- package/dist/registry/staleness.d.ts.map +1 -0
- package/dist/registry/staleness.js +39 -0
- 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/draft.d.ts +83 -0
- package/dist/routes/draft.d.ts.map +1 -0
- package/dist/routes/draft.js +135 -0
- package/dist/routes/loader.d.ts +63 -0
- package/dist/routes/loader.d.ts.map +1 -0
- package/dist/routes/loader.js +574 -0
- package/dist/routes/manifest.d.ts +24 -0
- package/dist/routes/manifest.d.ts.map +1 -0
- package/dist/routes/manifest.js +118 -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/routes/serveability.d.ts +52 -0
- package/dist/routes/serveability.d.ts.map +1 -0
- package/dist/routes/serveability.js +73 -0
- package/dist/routes/template.d.ts +18 -0
- package/dist/routes/template.d.ts.map +1 -0
- package/dist/routes/template.js +94 -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 +26 -0
- package/dist/run/cost.d.ts.map +1 -0
- package/dist/run/cost.js +41 -0
- 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/envelope.d.ts +21 -0
- package/dist/run/envelope.d.ts.map +1 -0
- package/dist/run/envelope.js +45 -0
- package/dist/run/grounding.d.ts +56 -0
- package/dist/run/grounding.d.ts.map +1 -0
- package/dist/run/grounding.js +89 -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/repair.d.ts +36 -0
- package/dist/run/repair.d.ts.map +1 -0
- package/dist/run/repair.js +61 -0
- package/dist/run/resolve.d.ts +99 -0
- package/dist/run/resolve.d.ts.map +1 -0
- package/dist/run/resolve.js +203 -0
- package/dist/run/retry.d.ts +19 -0
- package/dist/run/retry.d.ts.map +1 -0
- package/dist/run/retry.js +50 -0
- package/dist/run/run.d.ts +55 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +540 -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 +15 -0
- package/dist/run/validate.d.ts.map +1 -0
- package/dist/run/validate.js +55 -0
- 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 +38 -0
- package/dist/savings/emit.d.ts.map +1 -0
- package/dist/savings/emit.js +100 -0
- package/dist/savings/odometer.d.ts +32 -0
- package/dist/savings/odometer.d.ts.map +1 -0
- package/dist/savings/odometer.js +70 -0
- package/dist/savings/swap-execute.d.ts +58 -0
- package/dist/savings/swap-execute.d.ts.map +1 -0
- package/dist/savings/swap-execute.js +130 -0
- package/dist/savings/swaps-client.d.ts +55 -0
- package/dist/savings/swaps-client.d.ts.map +1 -0
- package/dist/savings/swaps-client.js +95 -0
- 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 +31 -0
- package/dist/telemetry/capture.d.ts.map +1 -0
- package/dist/telemetry/capture.js +81 -0
- 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 +137 -0
- package/dist/telemetry/exporter.d.ts.map +1 -0
- package/dist/telemetry/exporter.js +510 -0
- 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 +310 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
- package/dist/telemetry/ingest-protocol.js +363 -0
- 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 +90 -0
- package/dist/telemetry/sinks.d.ts.map +1 -0
- package/dist/telemetry/sinks.js +180 -0
- package/dist/telemetry/store.d.ts +65 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +547 -0
- package/dist/tenancy/api-key-format.d.ts +77 -0
- package/dist/tenancy/api-key-format.d.ts.map +1 -0
- package/dist/tenancy/api-key-format.js +110 -0
- package/dist/tenancy/constants.d.ts +23 -0
- package/dist/tenancy/constants.d.ts.map +1 -0
- package/dist/tenancy/constants.js +25 -0
- package/dist/tenancy/scrub.d.ts +47 -0
- package/dist/tenancy/scrub.d.ts.map +1 -0
- package/dist/tenancy/scrub.js +85 -0
- 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/failures-teaching.d.ts +56 -0
- package/dist/types/failures-teaching.d.ts.map +1 -0
- package/dist/types/failures-teaching.js +177 -0
- package/dist/types/failures.d.ts +27 -0
- package/dist/types/failures.d.ts.map +1 -0
- package/dist/types/failures.js +41 -0
- package/dist/types/provider.d.ts +200 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +40 -0
- package/dist/types/route.d.ts +205 -0
- package/dist/types/route.d.ts.map +1 -0
- package/dist/types/route.js +6 -0
- package/dist/types/telemetry.d.ts +206 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/telemetry.js +6 -0
- 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/budget.d.ts +19 -0
- package/dist/watch/budget.d.ts.map +1 -0
- package/dist/watch/budget.js +35 -0
- package/dist/watch/evaluate.d.ts +225 -0
- package/dist/watch/evaluate.d.ts.map +1 -0
- package/dist/watch/evaluate.js +155 -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 +89 -0
- package/dist/watch/proposals.d.ts.map +1 -0
- package/dist/watch/proposals.js +197 -0
- package/dist/watch/run-record.d.ts +64 -0
- package/dist/watch/run-record.d.ts.map +1 -0
- package/dist/watch/run-record.js +86 -0
- package/dist/watch/run-reported.d.ts +20 -0
- package/dist/watch/run-reported.d.ts.map +1 -0
- package/dist/watch/run-reported.js +53 -0
- package/dist/watch/run.d.ts +73 -0
- package/dist/watch/run.d.ts.map +1 -0
- package/dist/watch/run.js +83 -0
- package/dist/watch/state-store.d.ts +48 -0
- package/dist/watch/state-store.d.ts.map +1 -0
- package/dist/watch/state-store.js +86 -0
- package/dist/watch/state.d.ts +21 -0
- package/dist/watch/state.d.ts.map +1 -0
- package/dist/watch/state.js +93 -0
- package/package.json +56 -14
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/index.js +0 -3
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Watch diff-evaluators — Phase 4 WS1 (plan §4). Pure: no store, no
|
|
4
|
+
* network, no clock beyond the expiry stamp. Two event classes:
|
|
5
|
+
*
|
|
6
|
+
* price_change — a candidate's pricing moved; when the blended cost shift
|
|
7
|
+
* clears the threshold (default ±10%), every route declaring that
|
|
8
|
+
* candidate gets a suggested bake-off proposal.
|
|
9
|
+
* new_model — a new pricing-map entry on a provider a route already
|
|
10
|
+
* uses → suggested bake-off (the re-probe/bake-off run IS the evidence
|
|
11
|
+
* step; the proposal never claims conformance it hasn't measured).
|
|
12
|
+
*
|
|
13
|
+
* Proposals NEVER execute anything. Idempotency: the trigger hash is a
|
|
14
|
+
* stable digest of (route, trigger content) — re-running a watch cycle over
|
|
15
|
+
* the same facts produces the same hashes, and the control-plane unique
|
|
16
|
+
* index (route, trigger_hash) makes duplicates a no-op.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.PROPOSAL_TTL_DAYS = void 0;
|
|
20
|
+
exports.triggerHash = triggerHash;
|
|
21
|
+
exports.evaluateEvents = evaluateEvents;
|
|
22
|
+
exports.buildCacheContext = buildCacheContext;
|
|
23
|
+
exports.explainCacheContext = explainCacheContext;
|
|
24
|
+
const node_crypto_1 = require("node:crypto");
|
|
25
|
+
const effective_rate_1 = require("../registry/effective-rate");
|
|
26
|
+
exports.PROPOSAL_TTL_DAYS = 30;
|
|
27
|
+
/** Reference token mix for blending input/output rates into one comparable
|
|
28
|
+
* number: 3 input tokens per output token — a conservative, route-agnostic
|
|
29
|
+
* default (schema-bound analysis routes run input-heavy). */
|
|
30
|
+
const BLEND_IN = 3;
|
|
31
|
+
const BLEND_OUT = 1;
|
|
32
|
+
function blended(rates) {
|
|
33
|
+
return rates.inputUsdPerTok * BLEND_IN + rates.outputUsdPerTok * BLEND_OUT;
|
|
34
|
+
}
|
|
35
|
+
function triggerHash(route, trigger) {
|
|
36
|
+
return (0, node_crypto_1.createHash)("sha256").update(`${route}\n${JSON.stringify(trigger)}`).digest("hex").slice(0, 32);
|
|
37
|
+
}
|
|
38
|
+
function expiryStamp() {
|
|
39
|
+
return new Date(Date.now() + exports.PROPOSAL_TTL_DAYS * 24 * 3600 * 1000).toISOString();
|
|
40
|
+
}
|
|
41
|
+
function evaluateEvents(routes, events, config) {
|
|
42
|
+
const drafts = [];
|
|
43
|
+
for (const event of events) {
|
|
44
|
+
if (event.type === "price_change") {
|
|
45
|
+
const before = blended(event.old);
|
|
46
|
+
if (before <= 0)
|
|
47
|
+
continue; // nothing sane to compare against
|
|
48
|
+
const deltaPct = ((blended(event.new) - before) / before) * 100;
|
|
49
|
+
if (Math.abs(deltaPct) < config.thresholdPct)
|
|
50
|
+
continue;
|
|
51
|
+
const trigger = {
|
|
52
|
+
type: "price_change",
|
|
53
|
+
provider: event.provider,
|
|
54
|
+
model: event.model,
|
|
55
|
+
deltaPct,
|
|
56
|
+
oldRates: event.old,
|
|
57
|
+
newRates: event.new,
|
|
58
|
+
};
|
|
59
|
+
for (const route of routes) {
|
|
60
|
+
if (!route.candidates.some((c) => c.provider === event.provider && c.model === event.model)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
drafts.push({
|
|
64
|
+
route: route.route,
|
|
65
|
+
kind: "bakeoff",
|
|
66
|
+
trigger,
|
|
67
|
+
triggerHash: triggerHash(route.route, trigger),
|
|
68
|
+
expiresAt: expiryStamp(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const trigger = {
|
|
74
|
+
type: "new_model",
|
|
75
|
+
provider: event.provider,
|
|
76
|
+
model: event.model,
|
|
77
|
+
pricing: event.pricing,
|
|
78
|
+
};
|
|
79
|
+
for (const route of routes) {
|
|
80
|
+
if (!route.candidates.some((c) => c.provider === event.provider))
|
|
81
|
+
continue;
|
|
82
|
+
// A model the route already declares is not "new" for it.
|
|
83
|
+
if (route.candidates.some((c) => c.provider === event.provider && c.model === event.model)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
drafts.push({
|
|
87
|
+
route: route.route,
|
|
88
|
+
kind: "bakeoff",
|
|
89
|
+
trigger,
|
|
90
|
+
triggerHash: triggerHash(route.route, trigger),
|
|
91
|
+
expiresAt: expiryStamp(),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return drafts;
|
|
97
|
+
}
|
|
98
|
+
const PER_MTOK = 1e6;
|
|
99
|
+
/**
|
|
100
|
+
* Assemble the `cache_context` evidence block + verdict for a proposal (spec
|
|
101
|
+
* §5.5), pure. The verdict compares the incumbent's cache-ADJUSTED effective
|
|
102
|
+
* input rate against the challenger's COLD list rate, amortized over the switch
|
|
103
|
+
* cost, and gates on the hysteresis config. The advantage is measured PER
|
|
104
|
+
* CACHED PREFIX (the tokens the decision actually turns on), so a per-call
|
|
105
|
+
* dollar figure is directly comparable to the floor.
|
|
106
|
+
*/
|
|
107
|
+
function buildCacheContext(input, config = effective_rate_1.DEFAULT_CACHE_HYSTERESIS) {
|
|
108
|
+
const incumbentEff = (0, effective_rate_1.effectiveInputRate)(input.observed, input.incumbentRates);
|
|
109
|
+
const challengerCold = input.challengerRates.input;
|
|
110
|
+
// Switch cost of leaving the warm incumbent for the cold challenger, and the
|
|
111
|
+
// per-call saving the incumbent's warmth is worth (spec §4.2–4.3).
|
|
112
|
+
const switch_cost_usd = (0, effective_rate_1.switchCostUsd)(input.incumbentPrefixTokens, input.challengerRates, input.incumbentRates);
|
|
113
|
+
const perCallSaving = (0, effective_rate_1.perCallCacheSavingsUsd)(input.incumbentPrefixTokens, input.observed.h, input.incumbentRates);
|
|
114
|
+
const cache_option_value_usd = (0, effective_rate_1.cacheOptionValueUsd)(input.remainingCalls, perCallSaving);
|
|
115
|
+
const breakeven_conformant_calls = (0, effective_rate_1.breakevenCalls)(switch_cost_usd, perCallSaving);
|
|
116
|
+
// The cache-adjusted per-call advantage of the challenger over the incumbent,
|
|
117
|
+
// on the cached prefix: (incumbent effective − challenger cold) per prefix
|
|
118
|
+
// token, times the prefix. Positive ⇒ the cold challenger is still cheaper.
|
|
119
|
+
const perCallAdvantageUsd = (incumbentEff - challengerCold) * input.incumbentPrefixTokens;
|
|
120
|
+
const totalAdvantageUsd = perCallAdvantageUsd * Math.max(input.remainingCalls, 0);
|
|
121
|
+
const verdict = (0, effective_rate_1.classifyCacheVerdict)({
|
|
122
|
+
hasCacheSignal: input.hasCacheSignal,
|
|
123
|
+
perCallAdvantageUsd,
|
|
124
|
+
totalAdvantageUsd,
|
|
125
|
+
windowSpendUsd: input.windowSpendUsd,
|
|
126
|
+
}, config);
|
|
127
|
+
return {
|
|
128
|
+
observed_hit_rate: input.observed.h,
|
|
129
|
+
observed_write_rate: input.observed.w,
|
|
130
|
+
window_days: input.windowDays,
|
|
131
|
+
incumbent_effective_in_per_mtok: incumbentEff * PER_MTOK,
|
|
132
|
+
challenger_cold_in_per_mtok: challengerCold * PER_MTOK,
|
|
133
|
+
switch_cost_usd,
|
|
134
|
+
cache_option_value_usd,
|
|
135
|
+
breakeven_conformant_calls,
|
|
136
|
+
verdict,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The [Briefing §30] rendered explanation — one prose line a human reads on the
|
|
141
|
+
* console and the `swap execute` approval prompt. Single-sourced here so the
|
|
142
|
+
* console, the CLI, and the OTLP span never phrase the same decision three ways.
|
|
143
|
+
*/
|
|
144
|
+
function explainCacheContext(ctx, incumbentLabel, challengerLabel) {
|
|
145
|
+
const warmMtok = ctx.incumbent_effective_in_per_mtok.toFixed(2);
|
|
146
|
+
const coldMtok = ctx.challenger_cold_in_per_mtok.toFixed(2);
|
|
147
|
+
const hit = `${(ctx.observed_hit_rate * 100).toFixed(0)}%`;
|
|
148
|
+
const breakeven = Number.isFinite(ctx.breakeven_conformant_calls)
|
|
149
|
+
? `breaks even after ${Math.ceil(ctx.breakeven_conformant_calls)} calls`
|
|
150
|
+
: "never breaks even";
|
|
151
|
+
return (`${incumbentLabel} is warm (${hit} hit rate, effective $${warmMtok}/Mtok in); ` +
|
|
152
|
+
`${challengerLabel} is nominally cheaper but cold ($${coldMtok}/Mtok in). ` +
|
|
153
|
+
`Switching costs $${ctx.switch_cost_usd.toFixed(4)} and ${breakeven}. ` +
|
|
154
|
+
`Verdict: ${ctx.verdict}.`);
|
|
155
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monitor v1 — conform-rate degradation → bake-off proposal (Expansion E4 chunk B).
|
|
3
|
+
*
|
|
4
|
+
* The watch loop reacts to PRICE moves; the Monitor reacts to QUALITY moves. It
|
|
5
|
+
* rolls up the deterministic-eval conform rate per (org, task) into a daily
|
|
6
|
+
* series, and when a recent window falls below the trailing baseline by a ruled
|
|
7
|
+
* margin — with enough evaluations on BOTH sides to trust the number — it emits
|
|
8
|
+
* ONE `proposals` row (kind='bakeoff') asking for a re-evaluation. It reuses the
|
|
9
|
+
* ENTIRE existing proposals machinery: same table, same kind, same
|
|
10
|
+
* (route, trigger_hash) idempotency, same human-approval gate. No new proposal
|
|
11
|
+
* kind, no auto-actuation — the swap gate is untouched (spec §3.2).
|
|
12
|
+
*
|
|
13
|
+
* ── MEASURED-ONLY DOCTRINE ────────────────────────────────────────────────
|
|
14
|
+
* A degradation is proposed only from DETERMINISTIC evaluations (evaluator_kind
|
|
15
|
+
* = 'deterministic' — no model-judge softness), and only when BOTH windows clear
|
|
16
|
+
* `MONITOR_MIN_N`. Below that the rate is too noisy to act on: two failures can
|
|
17
|
+
* swing a small sample past the margin, and the house rule is to refuse rather
|
|
18
|
+
* than propose on thin data.
|
|
19
|
+
*/
|
|
20
|
+
import type { ProposalDraft } from "./evaluate";
|
|
21
|
+
/** The recent window whose conformance we judge. A week smooths daily noise
|
|
22
|
+
* while staying responsive to a regression that just landed. */
|
|
23
|
+
export declare const MONITOR_WINDOW_DAYS = 7;
|
|
24
|
+
/** The trailing baseline the window is compared against — the 28 days
|
|
25
|
+
* immediately BEFORE the window (non-overlapping). Four weeks is a stable
|
|
26
|
+
* reference that predates a week-old regression; it matches the proposal TTL
|
|
27
|
+
* cadence, so a baseline never silently includes an unresolved degradation. */
|
|
28
|
+
export declare const MONITOR_BASELINE_DAYS = 28;
|
|
29
|
+
/** The drop, in percentage POINTS of conform rate, that flags a degradation.
|
|
30
|
+
* Mirrors the watch loop's ±10% material-shift threshold — one house number for
|
|
31
|
+
* "materially worse" — so the two loops speak the same language. */
|
|
32
|
+
export declare const MONITOR_MARGIN_PTS = 10;
|
|
33
|
+
/** Minimum deterministic evaluations required in EACH window before the rate is
|
|
34
|
+
* trustworthy enough to propose on (measured-only). */
|
|
35
|
+
export declare const MONITOR_MIN_N = 20;
|
|
36
|
+
export interface MonitorConfig {
|
|
37
|
+
readonly windowDays: number;
|
|
38
|
+
readonly baselineDays: number;
|
|
39
|
+
readonly marginPts: number;
|
|
40
|
+
readonly minN: number;
|
|
41
|
+
}
|
|
42
|
+
export declare const DEFAULT_MONITOR_CONFIG: MonitorConfig;
|
|
43
|
+
/** One day's deterministic-eval counts for a task. `conformN` ≤ `n`. */
|
|
44
|
+
export interface ConformDay {
|
|
45
|
+
readonly date: string;
|
|
46
|
+
readonly n: number;
|
|
47
|
+
readonly conformN: number;
|
|
48
|
+
}
|
|
49
|
+
export interface TaskConformSeries {
|
|
50
|
+
readonly task: string;
|
|
51
|
+
readonly days: readonly ConformDay[];
|
|
52
|
+
}
|
|
53
|
+
/** A single deterministic evaluation row for the rollup (server read shape). */
|
|
54
|
+
export interface EvalRollupRow {
|
|
55
|
+
readonly artifactId: string;
|
|
56
|
+
readonly passed: boolean | null;
|
|
57
|
+
readonly createdAt: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Roll deterministic-eval rows up into per-(task) daily conform counts. The
|
|
61
|
+
* task comes from `taskByArtifact` (the caller joins artifacts→task locally,
|
|
62
|
+
* since artifact_evaluations carries only artifact_id). Rows whose artifact has
|
|
63
|
+
* no known task, or whose `passed` is null (not a pass/fail eval), are dropped.
|
|
64
|
+
*/
|
|
65
|
+
export declare function rollupConformRate(rows: readonly EvalRollupRow[], taskByArtifact: ReadonlyMap<string, string>): TaskConformSeries[];
|
|
66
|
+
/**
|
|
67
|
+
* The degradation rule (measured-only). For each task, compare the recent
|
|
68
|
+
* window to the trailing baseline; emit a bake-off draft when the drop clears
|
|
69
|
+
* the margin AND both windows clear min-n. Deterministic over `now` (passed in,
|
|
70
|
+
* never read from the clock) so the fixture test is reproducible. The
|
|
71
|
+
* trigger_hash keys on the task + both window bounds, so re-running the SAME
|
|
72
|
+
* day is a no-op (the (route, trigger_hash) unique index dedupes it).
|
|
73
|
+
*/
|
|
74
|
+
export declare function evaluateConformDegradation(series: readonly TaskConformSeries[], now: Date, config?: MonitorConfig): ProposalDraft[];
|
|
75
|
+
/**
|
|
76
|
+
* The reads a Monitor cycle needs, injected so the orchestrator is testable
|
|
77
|
+
* without a database. BOTH must be scoped to ONE org (the rollup groups by task
|
|
78
|
+
* within that scope — cross-org tasks sharing a name must never merge): a
|
|
79
|
+
* hosted cron iterates orgs, a self-host has exactly one.
|
|
80
|
+
*/
|
|
81
|
+
export interface MonitorReads {
|
|
82
|
+
/** Deterministic artifact evaluations created at/after `sinceIso`. */
|
|
83
|
+
readonly recentDeterministicEvals: (sinceIso: string) => Promise<readonly EvalRollupRow[]>;
|
|
84
|
+
/** task for each of the given artifact ids (the rollup join — artifact_evaluations
|
|
85
|
+
* carries only artifact_id). Only the ids that actually have evals are asked
|
|
86
|
+
* for, so the read stays bounded regardless of total artifact count. */
|
|
87
|
+
readonly taskFor: (artifactIds: readonly string[]) => Promise<ReadonlyMap<string, string>>;
|
|
88
|
+
}
|
|
89
|
+
export interface MonitorCycleResult {
|
|
90
|
+
readonly series: TaskConformSeries[];
|
|
91
|
+
readonly drafts: ProposalDraft[];
|
|
92
|
+
/** How many proposals the control plane actually created (0 when duplicates). */
|
|
93
|
+
readonly inserted: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* One Monitor cycle: read the recent deterministic evals + task map, roll up,
|
|
97
|
+
* apply the degradation rule, and submit any drafts through the EXISTING
|
|
98
|
+
* proposals client (idempotent on (route, trigger_hash)). Returns the series +
|
|
99
|
+
* drafts + inserted count so a caller/CLI can report without a second query.
|
|
100
|
+
* Never actuates — submitting a proposal is the whole of its authority (§3.2).
|
|
101
|
+
*/
|
|
102
|
+
export declare function runMonitorCycle(reads: MonitorReads, submitDrafts: (drafts: readonly ProposalDraft[]) => Promise<number>, now: Date, config?: MonitorConfig): Promise<MonitorCycleResult>;
|
|
103
|
+
export interface MonitorReadsConfig {
|
|
104
|
+
readonly supabaseUrl: string;
|
|
105
|
+
readonly serviceRoleKey: string;
|
|
106
|
+
/** The org this cycle runs for — reads are org-scoped so tasks never merge
|
|
107
|
+
* across tenants. A hosted cron iterates orgs; a self-host has exactly one. */
|
|
108
|
+
readonly orgId: string;
|
|
109
|
+
readonly fetchFn?: typeof fetch;
|
|
110
|
+
}
|
|
111
|
+
/** PostgREST reads for a Monitor cycle (service role; mirrors the proposals
|
|
112
|
+
* client). Two org-scoped queries, joined in-SDK by `runMonitorCycle` — no DDL,
|
|
113
|
+
* no view (spec §3.1). `taskFor` batches ids ≤100 (T9 — PostgREST URLs fail
|
|
114
|
+
* silently around 8KB). */
|
|
115
|
+
export declare function createMonitorReads(config: MonitorReadsConfig): MonitorReads;
|
|
116
|
+
//# sourceMappingURL=monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/watch/monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAA4C,aAAa,EAAE,MAAM,YAAY,CAAC;AAG1F;gEACgE;AAChE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC;;;+EAG+E;AAC/E,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC;;oEAEoE;AACpE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC;uDACuD;AACvD,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAKpC,CAAC;AAEF,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAC;CACtC;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,aAAa,EAAE,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,iBAAiB,EAAE,CA0BrB;AAeD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,aAAsC,GAC7C,aAAa,EAAE,CAgCjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IAC3F;;4EAEwE;IACxE,QAAQ,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IACjC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,EACnE,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,aAAsC,GAC7C,OAAO,CAAC,kBAAkB,CAAC,CAU7B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;mFAC+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAYD;;;2BAG2B;AAC3B,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CA6B3E"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Monitor v1 — conform-rate degradation → bake-off proposal (Expansion E4 chunk B).
|
|
4
|
+
*
|
|
5
|
+
* The watch loop reacts to PRICE moves; the Monitor reacts to QUALITY moves. It
|
|
6
|
+
* rolls up the deterministic-eval conform rate per (org, task) into a daily
|
|
7
|
+
* series, and when a recent window falls below the trailing baseline by a ruled
|
|
8
|
+
* margin — with enough evaluations on BOTH sides to trust the number — it emits
|
|
9
|
+
* ONE `proposals` row (kind='bakeoff') asking for a re-evaluation. It reuses the
|
|
10
|
+
* ENTIRE existing proposals machinery: same table, same kind, same
|
|
11
|
+
* (route, trigger_hash) idempotency, same human-approval gate. No new proposal
|
|
12
|
+
* kind, no auto-actuation — the swap gate is untouched (spec §3.2).
|
|
13
|
+
*
|
|
14
|
+
* ── MEASURED-ONLY DOCTRINE ────────────────────────────────────────────────
|
|
15
|
+
* A degradation is proposed only from DETERMINISTIC evaluations (evaluator_kind
|
|
16
|
+
* = 'deterministic' — no model-judge softness), and only when BOTH windows clear
|
|
17
|
+
* `MONITOR_MIN_N`. Below that the rate is too noisy to act on: two failures can
|
|
18
|
+
* swing a small sample past the margin, and the house rule is to refuse rather
|
|
19
|
+
* than propose on thin data.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.DEFAULT_MONITOR_CONFIG = exports.MONITOR_MIN_N = exports.MONITOR_MARGIN_PTS = exports.MONITOR_BASELINE_DAYS = exports.MONITOR_WINDOW_DAYS = void 0;
|
|
23
|
+
exports.rollupConformRate = rollupConformRate;
|
|
24
|
+
exports.evaluateConformDegradation = evaluateConformDegradation;
|
|
25
|
+
exports.runMonitorCycle = runMonitorCycle;
|
|
26
|
+
exports.createMonitorReads = createMonitorReads;
|
|
27
|
+
const evaluate_1 = require("./evaluate");
|
|
28
|
+
/** The recent window whose conformance we judge. A week smooths daily noise
|
|
29
|
+
* while staying responsive to a regression that just landed. */
|
|
30
|
+
exports.MONITOR_WINDOW_DAYS = 7;
|
|
31
|
+
/** The trailing baseline the window is compared against — the 28 days
|
|
32
|
+
* immediately BEFORE the window (non-overlapping). Four weeks is a stable
|
|
33
|
+
* reference that predates a week-old regression; it matches the proposal TTL
|
|
34
|
+
* cadence, so a baseline never silently includes an unresolved degradation. */
|
|
35
|
+
exports.MONITOR_BASELINE_DAYS = 28;
|
|
36
|
+
/** The drop, in percentage POINTS of conform rate, that flags a degradation.
|
|
37
|
+
* Mirrors the watch loop's ±10% material-shift threshold — one house number for
|
|
38
|
+
* "materially worse" — so the two loops speak the same language. */
|
|
39
|
+
exports.MONITOR_MARGIN_PTS = 10;
|
|
40
|
+
/** Minimum deterministic evaluations required in EACH window before the rate is
|
|
41
|
+
* trustworthy enough to propose on (measured-only). */
|
|
42
|
+
exports.MONITOR_MIN_N = 20;
|
|
43
|
+
exports.DEFAULT_MONITOR_CONFIG = {
|
|
44
|
+
windowDays: exports.MONITOR_WINDOW_DAYS,
|
|
45
|
+
baselineDays: exports.MONITOR_BASELINE_DAYS,
|
|
46
|
+
marginPts: exports.MONITOR_MARGIN_PTS,
|
|
47
|
+
minN: exports.MONITOR_MIN_N,
|
|
48
|
+
};
|
|
49
|
+
/** ISO day (YYYY-MM-DD) of a timestamp. */
|
|
50
|
+
function dayOf(iso) {
|
|
51
|
+
return iso.slice(0, 10);
|
|
52
|
+
}
|
|
53
|
+
/** N days before `d`, as a YYYY-MM-DD day string. */
|
|
54
|
+
function dayMinus(d, days) {
|
|
55
|
+
return new Date(d.getTime() - days * 86400000).toISOString().slice(0, 10);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Roll deterministic-eval rows up into per-(task) daily conform counts. The
|
|
59
|
+
* task comes from `taskByArtifact` (the caller joins artifacts→task locally,
|
|
60
|
+
* since artifact_evaluations carries only artifact_id). Rows whose artifact has
|
|
61
|
+
* no known task, or whose `passed` is null (not a pass/fail eval), are dropped.
|
|
62
|
+
*/
|
|
63
|
+
function rollupConformRate(rows, taskByArtifact) {
|
|
64
|
+
// task → day → {n, conformN}
|
|
65
|
+
const byTask = new Map();
|
|
66
|
+
for (const r of rows) {
|
|
67
|
+
if (r.passed === null)
|
|
68
|
+
continue;
|
|
69
|
+
const task = taskByArtifact.get(r.artifactId);
|
|
70
|
+
if (task === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
const day = dayOf(r.createdAt);
|
|
73
|
+
let days = byTask.get(task);
|
|
74
|
+
if (days === undefined) {
|
|
75
|
+
days = new Map();
|
|
76
|
+
byTask.set(task, days);
|
|
77
|
+
}
|
|
78
|
+
const cell = days.get(day) ?? { n: 0, conformN: 0 };
|
|
79
|
+
cell.n += 1;
|
|
80
|
+
if (r.passed)
|
|
81
|
+
cell.conformN += 1;
|
|
82
|
+
days.set(day, cell);
|
|
83
|
+
}
|
|
84
|
+
return [...byTask.entries()]
|
|
85
|
+
.map(([task, days]) => ({
|
|
86
|
+
task,
|
|
87
|
+
days: [...days.entries()]
|
|
88
|
+
.map(([date, c]) => ({ date, n: c.n, conformN: c.conformN }))
|
|
89
|
+
.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0)),
|
|
90
|
+
}))
|
|
91
|
+
.sort((a, b) => (a.task < b.task ? -1 : a.task > b.task ? 1 : 0));
|
|
92
|
+
}
|
|
93
|
+
/** Aggregate the days whose date is in [from, to) into one window. */
|
|
94
|
+
function windowOf(days, from, to) {
|
|
95
|
+
let n = 0;
|
|
96
|
+
let conformN = 0;
|
|
97
|
+
for (const d of days) {
|
|
98
|
+
if (d.date >= from && d.date < to) {
|
|
99
|
+
n += d.n;
|
|
100
|
+
conformN += d.conformN;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { from, to, n, conformRate: n > 0 ? conformN / n : 0 };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The degradation rule (measured-only). For each task, compare the recent
|
|
107
|
+
* window to the trailing baseline; emit a bake-off draft when the drop clears
|
|
108
|
+
* the margin AND both windows clear min-n. Deterministic over `now` (passed in,
|
|
109
|
+
* never read from the clock) so the fixture test is reproducible. The
|
|
110
|
+
* trigger_hash keys on the task + both window bounds, so re-running the SAME
|
|
111
|
+
* day is a no-op (the (route, trigger_hash) unique index dedupes it).
|
|
112
|
+
*/
|
|
113
|
+
function evaluateConformDegradation(series, now, config = exports.DEFAULT_MONITOR_CONFIG) {
|
|
114
|
+
const windowFrom = dayMinus(now, config.windowDays);
|
|
115
|
+
const windowTo = dayMinus(now, 0);
|
|
116
|
+
const baselineFrom = dayMinus(now, config.windowDays + config.baselineDays);
|
|
117
|
+
const baselineTo = windowFrom;
|
|
118
|
+
const expiresAt = new Date(now.getTime() + evaluate_1.PROPOSAL_TTL_DAYS * 86400000).toISOString();
|
|
119
|
+
const drafts = [];
|
|
120
|
+
for (const s of series) {
|
|
121
|
+
const window = windowOf(s.days, windowFrom, windowTo);
|
|
122
|
+
const baseline = windowOf(s.days, baselineFrom, baselineTo);
|
|
123
|
+
if (window.n < config.minN || baseline.n < config.minN)
|
|
124
|
+
continue; // thin data → refuse
|
|
125
|
+
const dropPts = (baseline.conformRate - window.conformRate) * 100;
|
|
126
|
+
if (dropPts < config.marginPts)
|
|
127
|
+
continue; // not materially worse
|
|
128
|
+
const trigger = {
|
|
129
|
+
type: "conform_degradation",
|
|
130
|
+
task: s.task,
|
|
131
|
+
window,
|
|
132
|
+
baseline,
|
|
133
|
+
dropPts,
|
|
134
|
+
marginPts: config.marginPts,
|
|
135
|
+
minN: config.minN,
|
|
136
|
+
};
|
|
137
|
+
drafts.push({
|
|
138
|
+
route: s.task,
|
|
139
|
+
kind: "bakeoff",
|
|
140
|
+
trigger,
|
|
141
|
+
triggerHash: (0, evaluate_1.triggerHash)(s.task, trigger),
|
|
142
|
+
expiresAt,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return drafts;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* One Monitor cycle: read the recent deterministic evals + task map, roll up,
|
|
149
|
+
* apply the degradation rule, and submit any drafts through the EXISTING
|
|
150
|
+
* proposals client (idempotent on (route, trigger_hash)). Returns the series +
|
|
151
|
+
* drafts + inserted count so a caller/CLI can report without a second query.
|
|
152
|
+
* Never actuates — submitting a proposal is the whole of its authority (§3.2).
|
|
153
|
+
*/
|
|
154
|
+
async function runMonitorCycle(reads, submitDrafts, now, config = exports.DEFAULT_MONITOR_CONFIG) {
|
|
155
|
+
const sinceMs = now.getTime() - (config.windowDays + config.baselineDays) * 86400000;
|
|
156
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
157
|
+
const rows = await reads.recentDeterministicEvals(sinceIso);
|
|
158
|
+
const ids = [...new Set(rows.map((r) => r.artifactId))];
|
|
159
|
+
const tasks = ids.length > 0 ? await reads.taskFor(ids) : new Map();
|
|
160
|
+
const series = rollupConformRate(rows, tasks);
|
|
161
|
+
const drafts = evaluateConformDegradation(series, now, config);
|
|
162
|
+
const inserted = drafts.length > 0 ? await submitDrafts(drafts) : 0;
|
|
163
|
+
return { series, drafts, inserted };
|
|
164
|
+
}
|
|
165
|
+
/** PostgREST reads for a Monitor cycle (service role; mirrors the proposals
|
|
166
|
+
* client). Two org-scoped queries, joined in-SDK by `runMonitorCycle` — no DDL,
|
|
167
|
+
* no view (spec §3.1). `taskFor` batches ids ≤100 (T9 — PostgREST URLs fail
|
|
168
|
+
* silently around 8KB). */
|
|
169
|
+
function createMonitorReads(config) {
|
|
170
|
+
const base = config.supabaseUrl.replace(/\/$/, "");
|
|
171
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
172
|
+
const org = encodeURIComponent(config.orgId);
|
|
173
|
+
async function get(path) {
|
|
174
|
+
const res = await fetchFn(`${base}/rest/v1/${path}`, {
|
|
175
|
+
headers: { apikey: config.serviceRoleKey, authorization: `Bearer ${config.serviceRoleKey}` },
|
|
176
|
+
});
|
|
177
|
+
if (!res.ok)
|
|
178
|
+
throw new Error(`monitor read ${path} → ${res.status} ${await res.text().catch(() => "")}`);
|
|
179
|
+
return (await res.json().catch(() => []));
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
async recentDeterministicEvals(sinceIso) {
|
|
183
|
+
const rows = await get(`artifact_evaluations?select=artifact_id,passed,created_at&org_id=eq.${org}` +
|
|
184
|
+
`&evaluator_kind=eq.deterministic&created_at=gte.${encodeURIComponent(sinceIso)}`);
|
|
185
|
+
return rows.map((r) => ({ artifactId: r.artifact_id, passed: r.passed, createdAt: r.created_at }));
|
|
186
|
+
},
|
|
187
|
+
async taskFor(artifactIds) {
|
|
188
|
+
const out = new Map();
|
|
189
|
+
for (let i = 0; i < artifactIds.length; i += 100) {
|
|
190
|
+
const batch = artifactIds.slice(i, i + 100).map(encodeURIComponent).join(",");
|
|
191
|
+
const rows = await get(`artifacts?select=id,task&org_id=eq.${org}&id=in.(${batch})`);
|
|
192
|
+
for (const r of rows)
|
|
193
|
+
if (r.task !== null)
|
|
194
|
+
out.set(r.id, r.task);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proposal lifecycle + control-plane client — Phase 4 WS1 (plan §4).
|
|
3
|
+
*
|
|
4
|
+
* suggested → evidenced (bake-off attached)
|
|
5
|
+
* → approved (human, recorded) → executed (swap machinery)
|
|
6
|
+
* suggested|evidenced → rejected | expired
|
|
7
|
+
*
|
|
8
|
+
* Proposals NEVER auto-execute: "executed" is only reachable through
|
|
9
|
+
* "approved", and approval is a human CLI action (`swap execute --approve`).
|
|
10
|
+
* Transport is PostgREST with the service-role key — proposals are
|
|
11
|
+
* control-plane-only state (cross-run truth, not per-inference telemetry).
|
|
12
|
+
*/
|
|
13
|
+
import type { ProposalDraft, ProposalKind, ProposalTrigger } from "./evaluate";
|
|
14
|
+
export type ProposalStatus = "suggested" | "evidenced" | "approved" | "rejected" | "executed" | "expired";
|
|
15
|
+
export declare function assertTransition(from: ProposalStatus, to: ProposalStatus): void;
|
|
16
|
+
export interface ProposalRecord {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly route: string;
|
|
19
|
+
readonly kind: ProposalKind;
|
|
20
|
+
readonly trigger: ProposalTrigger;
|
|
21
|
+
readonly triggerHash: string;
|
|
22
|
+
readonly evidence: Record<string, unknown> | null;
|
|
23
|
+
readonly status: ProposalStatus;
|
|
24
|
+
readonly createdAt: string;
|
|
25
|
+
readonly updatedAt: string;
|
|
26
|
+
readonly expiresAt: string;
|
|
27
|
+
/** Who approved or rejected (A1.2). Null while undecided. */
|
|
28
|
+
readonly approvedBy: string | null;
|
|
29
|
+
/** When the decision was recorded — NOT when a swap executed. */
|
|
30
|
+
readonly decidedAt: string | null;
|
|
31
|
+
}
|
|
32
|
+
export interface ProposalsClientConfig {
|
|
33
|
+
readonly supabaseUrl: string;
|
|
34
|
+
readonly serviceRoleKey: string;
|
|
35
|
+
/**
|
|
36
|
+
* The tenant this client writes for. When set (the multi-tenant hosted cron —
|
|
37
|
+
* managed-loop §2), `submitDrafts` STAMPS every row with this org and dedupes
|
|
38
|
+
* against the real `(org_id, route, trigger_hash)` unique index, and
|
|
39
|
+
* `expireOverdue` sweeps only THIS org's rows. When omitted (the CLI /
|
|
40
|
+
* self-host lane) behaviour is unchanged: `org_id` falls to its house-org
|
|
41
|
+
* column default and one deployment sweeps its own single tenant. Omitting it
|
|
42
|
+
* on a hosted cron would file every org's proposals under the house org — the
|
|
43
|
+
* whole reason this field exists. Reads keyed by id (`get`/`orgOf`/`decide`)
|
|
44
|
+
* are org-agnostic and unaffected; `list` already takes its org as a param.
|
|
45
|
+
*/
|
|
46
|
+
readonly orgId?: string;
|
|
47
|
+
readonly fetchFn?: typeof fetch;
|
|
48
|
+
}
|
|
49
|
+
export interface ProposalsClient {
|
|
50
|
+
/** Insert drafts; duplicates on (route, trigger_hash) are silent no-ops.
|
|
51
|
+
* Returns how many rows the control plane actually created. */
|
|
52
|
+
submitDrafts(drafts: readonly ProposalDraft[]): Promise<number>;
|
|
53
|
+
/**
|
|
54
|
+
* List proposals for ONE org, or across all of them.
|
|
55
|
+
*
|
|
56
|
+
* `orgId` is required and `null` is the explicit "every org" case, used by
|
|
57
|
+
* the CLI and by self-host deployments where there is exactly one. An
|
|
58
|
+
* OPTIONAL parameter would have been the same code with a worse failure
|
|
59
|
+
* mode: the unscoped read this replaces was unscoped because nobody passed
|
|
60
|
+
* anything, and an omitted argument reads as an oversight while an explicit
|
|
61
|
+
* null reads as a decision (C6 review ruling §7.2).
|
|
62
|
+
*/
|
|
63
|
+
list(orgId: string | null, status?: ProposalStatus): Promise<ProposalRecord[]>;
|
|
64
|
+
get(id: string): Promise<ProposalRecord | null>;
|
|
65
|
+
/** Bake-off evidence + the suggested→evidenced flip, one status-scoped PATCH.
|
|
66
|
+
* Only a proposal still OPEN to evidence (suggested, or evidenced being
|
|
67
|
+
* re-run) matches — a terminal proposal (approved/executed/rejected/expired)
|
|
68
|
+
* matches zero rows, so evidence can never resurrect it. Returns false when
|
|
69
|
+
* nothing matched (already moved on), like `decide`. */
|
|
70
|
+
attachEvidence(id: string, evidence: Record<string, unknown>): Promise<boolean>;
|
|
71
|
+
/** Validated status change (assertTransition is the caller's guard for
|
|
72
|
+
* reads-before-writes; the PATCH is still status-scoped server-side). */
|
|
73
|
+
setStatus(id: string, from: ProposalStatus, to: ProposalStatus): Promise<void>;
|
|
74
|
+
/** A HUMAN decision on a proposal — addendum A1.2. Records who decided and
|
|
75
|
+
* when alongside the status flip, in one status-scoped PATCH.
|
|
76
|
+
*
|
|
77
|
+
* Approving is NOT executing: this writes a decision, never a swap_event.
|
|
78
|
+
* The swap still happens at a CLI (`modelrig swap execute --approve`) and
|
|
79
|
+
* the YAML it emits is still applied by git. Returns false when the row had
|
|
80
|
+
* already moved on (a concurrent decision), so the caller can say so
|
|
81
|
+
* instead of reporting a success that did not happen. */
|
|
82
|
+
/** The org that owns this proposal — the tenant a write must be authorized against. */
|
|
83
|
+
orgOf(id: string): Promise<string | null>;
|
|
84
|
+
decide(id: string, from: ProposalStatus, to: "approved" | "rejected", decidedBy: string, decidedByUserId?: string | null, orgId?: string): Promise<boolean>;
|
|
85
|
+
/** One sweep: overdue suggested/evidenced → expired. */
|
|
86
|
+
expireOverdue(now: Date): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
export declare function createProposalsClient(config: ProposalsClientConfig): ProposalsClient;
|
|
89
|
+
//# sourceMappingURL=proposals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../../src/watch/proposals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,GACV,UAAU,GACV,SAAS,CAAC;AAWd,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAO/E;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAkCD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B;mEAC+D;IAC/D,YAAY,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE;;;;;;;;;OASG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC/E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD;;;;4DAIwD;IACxD,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF;6EACyE;IACzE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;;;;6DAOyD;IACzD,uFAAuF;IACvF,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,CACJ,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,UAAU,GAAG,UAAU,EAC3B,SAAS,EAAE,MAAM,EACjB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,wDAAwD;IACxD,aAAa,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAuLpF"}
|