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,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Matrix aggregation job — Phase 4 WS6 (plan §4). Pure fold: telemetry
|
|
4
|
+
* rows → priors cells keyed (fingerprint_cell × model), each carrying
|
|
5
|
+
* outcome stats. Writes go to priors_cells ONLY (deny-all RLS, no UI, no
|
|
6
|
+
* cross-account use — standing decision 7; the gate stays off).
|
|
7
|
+
*
|
|
8
|
+
* Contribution discipline (spec WS6): the flag decides whether a cell may
|
|
9
|
+
* ever aggregate beyond its own account — respected here in code even
|
|
10
|
+
* though today's deployment is single-tenant. Non-contributing sources
|
|
11
|
+
* produce "account"-scoped cells, full stop.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.aggregationScope = aggregationScope;
|
|
15
|
+
exports.aggregatePairwiseFromTelemetry = aggregatePairwiseFromTelemetry;
|
|
16
|
+
exports.aggregatePriors = aggregatePriors;
|
|
17
|
+
exports.writePriorsCells = writePriorsCells;
|
|
18
|
+
/**
|
|
19
|
+
* Whether these rows may become community knowledge.
|
|
20
|
+
*
|
|
21
|
+
* One function so the two-veto rule has a single home and a name, rather than
|
|
22
|
+
* living as a `&&` inside a ternary inside a map.
|
|
23
|
+
*/
|
|
24
|
+
function aggregationScope(opts) {
|
|
25
|
+
const posture = opts.telemetryPosture ?? "pure_router";
|
|
26
|
+
return opts.contribution && posture === "optimized" ? "community" : "account";
|
|
27
|
+
}
|
|
28
|
+
/** A2: fold paired replay outcomes from telemetry. A pair exists where the
|
|
29
|
+
* SAME captureId was replayed through two different models within a cell.
|
|
30
|
+
* Sample outcome = did ANY attempt of the latest replay run for that
|
|
31
|
+
* (capture, model) conform — sample-level, retry-inclusive, matching the
|
|
32
|
+
* bake-off's own conformance notion. Single-source deployment folds
|
|
33
|
+
* sources: 1; cross-account distinct counts arrive with the community job. */
|
|
34
|
+
function aggregatePairwiseFromTelemetry(rows) {
|
|
35
|
+
const samples = new Map();
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
if (row.fingerprintCell === null || row.provider === "search")
|
|
38
|
+
continue;
|
|
39
|
+
let tags;
|
|
40
|
+
try {
|
|
41
|
+
tags = JSON.parse(row.tagsJson);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const sampleTag = tags.replay_capture;
|
|
47
|
+
const replayId = tags.replay;
|
|
48
|
+
if (typeof sampleTag !== "string" || typeof replayId !== "string")
|
|
49
|
+
continue;
|
|
50
|
+
const captureId = sampleTag.slice(replayId.length + 1);
|
|
51
|
+
if (captureId === "")
|
|
52
|
+
continue;
|
|
53
|
+
const modelKey = `${row.provider}/${row.model}`;
|
|
54
|
+
const key = `${row.fingerprintCell}|${captureId}|${modelKey}`;
|
|
55
|
+
const entry = samples.get(key) ?? {
|
|
56
|
+
key: { cell: row.fingerprintCell, captureId, modelKey },
|
|
57
|
+
runs: new Map(),
|
|
58
|
+
};
|
|
59
|
+
const run = entry.runs.get(replayId) ?? { replayId, latestTs: row.ts, conform: false };
|
|
60
|
+
if (row.ts > run.latestTs)
|
|
61
|
+
run.latestTs = row.ts;
|
|
62
|
+
if (row.failureClass === null)
|
|
63
|
+
run.conform = true;
|
|
64
|
+
entry.runs.set(replayId, run);
|
|
65
|
+
samples.set(key, entry);
|
|
66
|
+
}
|
|
67
|
+
// Latest run per sample → (cell, capture) → model outcomes.
|
|
68
|
+
const byCapture = new Map(); // cell|capture → model → conform
|
|
69
|
+
for (const { key, runs } of samples.values()) {
|
|
70
|
+
const latest = [...runs.values()].sort((a, b) => (a.latestTs < b.latestTs ? 1 : -1))[0];
|
|
71
|
+
const captureKey = `${key.cell}|${key.captureId}`;
|
|
72
|
+
const models = byCapture.get(captureKey) ?? new Map();
|
|
73
|
+
models.set(key.modelKey, latest.conform);
|
|
74
|
+
byCapture.set(captureKey, models);
|
|
75
|
+
}
|
|
76
|
+
const out = new Map();
|
|
77
|
+
function record(cell, a, b, outcome) {
|
|
78
|
+
const byModel = out.get(cell) ?? new Map();
|
|
79
|
+
const opponents = byModel.get(a) ?? new Map();
|
|
80
|
+
const prev = opponents.get(b) ?? { wins: 0, losses: 0, ties: 0, samples: 0, sources: 1 };
|
|
81
|
+
opponents.set(b, {
|
|
82
|
+
wins: prev.wins + (outcome === "win" ? 1 : 0),
|
|
83
|
+
losses: prev.losses + (outcome === "loss" ? 1 : 0),
|
|
84
|
+
ties: prev.ties + (outcome === "tie" ? 1 : 0),
|
|
85
|
+
samples: prev.samples + 1,
|
|
86
|
+
sources: 1,
|
|
87
|
+
});
|
|
88
|
+
byModel.set(a, opponents);
|
|
89
|
+
out.set(cell, byModel);
|
|
90
|
+
}
|
|
91
|
+
for (const [captureKey, models] of byCapture) {
|
|
92
|
+
if (models.size < 2)
|
|
93
|
+
continue;
|
|
94
|
+
const cell = captureKey.slice(0, captureKey.indexOf("|"));
|
|
95
|
+
const entries = [...models.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
96
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
97
|
+
for (let j = i + 1; j < entries.length; j += 1) {
|
|
98
|
+
const [modelA, conformA] = entries[i];
|
|
99
|
+
const [modelB, conformB] = entries[j];
|
|
100
|
+
if (conformA === conformB) {
|
|
101
|
+
record(cell, modelA, modelB, "tie");
|
|
102
|
+
record(cell, modelB, modelA, "tie");
|
|
103
|
+
}
|
|
104
|
+
else if (conformA) {
|
|
105
|
+
record(cell, modelA, modelB, "win");
|
|
106
|
+
record(cell, modelB, modelA, "loss");
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
record(cell, modelA, modelB, "loss");
|
|
110
|
+
record(cell, modelB, modelA, "win");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
function aggregatePriors(rows, opts) {
|
|
118
|
+
const byCell = new Map();
|
|
119
|
+
for (const row of rows) {
|
|
120
|
+
if (row.fingerprintCell === null)
|
|
121
|
+
continue;
|
|
122
|
+
const key = `${row.fingerprintCell} ${row.provider}/${row.model}`;
|
|
123
|
+
const agg = byCell.get(key) ?? { conform: 0, repaired: 0, samples: 0, pricedCost: 0, priced: 0 };
|
|
124
|
+
agg.samples += 1;
|
|
125
|
+
if (row.failureClass === null)
|
|
126
|
+
agg.conform += 1;
|
|
127
|
+
if (row.repairedBy !== null)
|
|
128
|
+
agg.repaired += 1;
|
|
129
|
+
if (!row.pricingMissing) {
|
|
130
|
+
agg.pricedCost += row.costEstimateUsd;
|
|
131
|
+
agg.priced += 1;
|
|
132
|
+
}
|
|
133
|
+
byCell.set(key, agg);
|
|
134
|
+
}
|
|
135
|
+
// A2: the pairwise fold rides the same pass — records attach to their
|
|
136
|
+
// (cell, model) row's stats; scope/RLS/contribution treatment identical.
|
|
137
|
+
const pairwiseByModel = aggregatePairwiseFromTelemetry(rows);
|
|
138
|
+
return [...byCell.entries()]
|
|
139
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
140
|
+
.map(([key, agg]) => {
|
|
141
|
+
const [cellKey, modelKey] = key.split(" ");
|
|
142
|
+
const opponents = pairwiseByModel.get(cellKey)?.get(modelKey);
|
|
143
|
+
return {
|
|
144
|
+
cellKey,
|
|
145
|
+
modelKey,
|
|
146
|
+
stats: {
|
|
147
|
+
samples: agg.samples,
|
|
148
|
+
conform_rate: agg.samples === 0 ? 0 : agg.conform / agg.samples,
|
|
149
|
+
repair_rate: agg.samples === 0 ? 0 : agg.repaired / agg.samples,
|
|
150
|
+
mean_cost_usd: agg.priced === 0 ? null : agg.pricedCost / agg.priced,
|
|
151
|
+
scope: aggregationScope(opts),
|
|
152
|
+
...(opponents !== undefined && opponents.size > 0
|
|
153
|
+
? {
|
|
154
|
+
pairwise: Object.fromEntries([...opponents.entries()].sort(([a], [b]) => a.localeCompare(b))),
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/** Upsert cells into priors_cells (service role — the ONLY principal that
|
|
162
|
+
* can touch the table; RLS denies everyone else including anon). */
|
|
163
|
+
async function writePriorsCells(cells, config) {
|
|
164
|
+
if (cells.length === 0)
|
|
165
|
+
return;
|
|
166
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
167
|
+
const base = config.supabaseUrl.replace(/\/$/, "");
|
|
168
|
+
const response = await fetchFn(`${base}/rest/v1/priors_cells?on_conflict=cell_key,model_key`, {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
"content-type": "application/json",
|
|
172
|
+
apikey: config.serviceRoleKey,
|
|
173
|
+
authorization: `Bearer ${config.serviceRoleKey}`,
|
|
174
|
+
prefer: "resolution=merge-duplicates,return=minimal",
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify(cells.map((c) => ({
|
|
177
|
+
cell_key: c.cellKey,
|
|
178
|
+
model_key: c.modelKey,
|
|
179
|
+
stats: c.stats,
|
|
180
|
+
updated_at: new Date().toISOString(),
|
|
181
|
+
}))),
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
throw new Error(`priors_cells upsert → ${response.status} ${await response.text().catch(() => "")}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The telemetry query grammar — Phase 4 WS5 ("your exhaust is your data").
|
|
3
|
+
* A hand-rolled recursive-descent parser over a DELIBERATELY tiny SQL-ish
|
|
4
|
+
* language:
|
|
5
|
+
*
|
|
6
|
+
* SELECT item (, item)* FROM inferences
|
|
7
|
+
* [WHERE cond (AND cond)*] [GROUP BY col (, col)*] [LIMIT n]
|
|
8
|
+
* item := col | count(*) | sum(m)|avg(m)|min(m)|max(m)
|
|
9
|
+
* col := route|provider|model|ts|failure_class|served_variant|tag.<name>
|
|
10
|
+
* m := cost|tokens_in|tokens_out|tokens_cached|latency_ms|ttfb_ms
|
|
11
|
+
* cond := col op literal op := = != > < >= <=
|
|
12
|
+
*
|
|
13
|
+
* Security model = the allowlist: unknown tokens throw, string literals
|
|
14
|
+
* only ever travel as bind parameters, the output is a single read-only
|
|
15
|
+
* SELECT. There is no raw-SQL path — by construction, not by filtering.
|
|
16
|
+
* Row scoping: a token's scope is ANDed into WHERE as one more bind
|
|
17
|
+
* parameter; the caller cannot opt out of it.
|
|
18
|
+
*/
|
|
19
|
+
export declare class QueryGrammarError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
/** What a query token is allowed to see. */
|
|
23
|
+
export type QueryScope = {
|
|
24
|
+
readonly kind: "all";
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: "tag";
|
|
27
|
+
readonly tag: string;
|
|
28
|
+
readonly value: string;
|
|
29
|
+
};
|
|
30
|
+
export interface CompiledQuery {
|
|
31
|
+
readonly sql: string;
|
|
32
|
+
readonly params: ReadonlyArray<string | number>;
|
|
33
|
+
}
|
|
34
|
+
export declare function compileQuery(input: string, scope: QueryScope): CompiledQuery;
|
|
35
|
+
//# sourceMappingURL=grammar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../src/query/grammar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CACjD;AAwHD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CAkJ5E"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The telemetry query grammar — Phase 4 WS5 ("your exhaust is your data").
|
|
4
|
+
* A hand-rolled recursive-descent parser over a DELIBERATELY tiny SQL-ish
|
|
5
|
+
* language:
|
|
6
|
+
*
|
|
7
|
+
* SELECT item (, item)* FROM inferences
|
|
8
|
+
* [WHERE cond (AND cond)*] [GROUP BY col (, col)*] [LIMIT n]
|
|
9
|
+
* item := col | count(*) | sum(m)|avg(m)|min(m)|max(m)
|
|
10
|
+
* col := route|provider|model|ts|failure_class|served_variant|tag.<name>
|
|
11
|
+
* m := cost|tokens_in|tokens_out|tokens_cached|latency_ms|ttfb_ms
|
|
12
|
+
* cond := col op literal op := = != > < >= <=
|
|
13
|
+
*
|
|
14
|
+
* Security model = the allowlist: unknown tokens throw, string literals
|
|
15
|
+
* only ever travel as bind parameters, the output is a single read-only
|
|
16
|
+
* SELECT. There is no raw-SQL path — by construction, not by filtering.
|
|
17
|
+
* Row scoping: a token's scope is ANDed into WHERE as one more bind
|
|
18
|
+
* parameter; the caller cannot opt out of it.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.QueryGrammarError = void 0;
|
|
22
|
+
exports.compileQuery = compileQuery;
|
|
23
|
+
class QueryGrammarError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "QueryGrammarError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.QueryGrammarError = QueryGrammarError;
|
|
30
|
+
const PLAIN_COLUMNS = {
|
|
31
|
+
route: "route",
|
|
32
|
+
provider: "provider",
|
|
33
|
+
model: "model",
|
|
34
|
+
ts: "ts",
|
|
35
|
+
failure_class: "failure_class",
|
|
36
|
+
served_variant: "served_variant",
|
|
37
|
+
};
|
|
38
|
+
const METRICS = {
|
|
39
|
+
cost: "cost_estimate_usd",
|
|
40
|
+
tokens_in: "tokens_in",
|
|
41
|
+
tokens_out: "tokens_out",
|
|
42
|
+
tokens_cached: "tokens_cached",
|
|
43
|
+
latency_ms: "latency_ms",
|
|
44
|
+
ttfb_ms: "ttfb_ms",
|
|
45
|
+
};
|
|
46
|
+
const FUNCTIONS = new Set(["sum", "avg", "min", "max", "count"]);
|
|
47
|
+
const OPERATORS = new Set(["=", "!=", ">", "<", ">=", "<="]);
|
|
48
|
+
const TAG_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
49
|
+
const DEFAULT_LIMIT = 1000;
|
|
50
|
+
const MAX_LIMIT = 10000;
|
|
51
|
+
function tokenize(input) {
|
|
52
|
+
const tokens = [];
|
|
53
|
+
let i = 0;
|
|
54
|
+
while (i < input.length) {
|
|
55
|
+
const ch = input[i];
|
|
56
|
+
if (/\s/.test(ch)) {
|
|
57
|
+
i += 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (ch === "'") {
|
|
61
|
+
// Single-quoted string; '' escapes a quote. Anything else inside is data.
|
|
62
|
+
let value = "";
|
|
63
|
+
i += 1;
|
|
64
|
+
for (;;) {
|
|
65
|
+
if (i >= input.length)
|
|
66
|
+
throw new QueryGrammarError("unterminated string literal");
|
|
67
|
+
if (input[i] === "'") {
|
|
68
|
+
if (input[i + 1] === "'") {
|
|
69
|
+
value += "'";
|
|
70
|
+
i += 2;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
i += 1;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
value += input[i];
|
|
77
|
+
i += 1;
|
|
78
|
+
}
|
|
79
|
+
tokens.push({ kind: "string", text: value });
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (/[0-9]/.test(ch) || (ch === "." && /[0-9]/.test(input[i + 1] ?? ""))) {
|
|
83
|
+
let num = "";
|
|
84
|
+
while (i < input.length && /[0-9.]/.test(input[i])) {
|
|
85
|
+
num += input[i];
|
|
86
|
+
i += 1;
|
|
87
|
+
}
|
|
88
|
+
if (!/^\d+(\.\d+)?$/.test(num))
|
|
89
|
+
throw new QueryGrammarError(`bad number: ${num}`);
|
|
90
|
+
tokens.push({ kind: "number", text: num });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (/[a-zA-Z_]/.test(ch)) {
|
|
94
|
+
let word = "";
|
|
95
|
+
while (i < input.length && /[a-zA-Z0-9_.]/.test(input[i])) {
|
|
96
|
+
word += input[i];
|
|
97
|
+
i += 1;
|
|
98
|
+
}
|
|
99
|
+
tokens.push({ kind: "word", text: word });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (ch === ">" || ch === "<" || ch === "!") {
|
|
103
|
+
const two = input.slice(i, i + 2);
|
|
104
|
+
if (two === ">=" || two === "<=" || two === "!=") {
|
|
105
|
+
tokens.push({ kind: "punct", text: two });
|
|
106
|
+
i += 2;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === "!")
|
|
110
|
+
throw new QueryGrammarError("unexpected '!'");
|
|
111
|
+
tokens.push({ kind: "punct", text: ch });
|
|
112
|
+
i += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (ch === "=" || ch === "," || ch === "(" || ch === ")" || ch === "*") {
|
|
116
|
+
tokens.push({ kind: "punct", text: ch });
|
|
117
|
+
i += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
throw new QueryGrammarError(`unexpected character: ${JSON.stringify(ch)}`);
|
|
121
|
+
}
|
|
122
|
+
return tokens;
|
|
123
|
+
}
|
|
124
|
+
/** Column reference → SQL expression. tag.<name> compiles to a
|
|
125
|
+
* json_extract over tags_json with a VALIDATED identifier (never
|
|
126
|
+
* interpolated user text beyond the identifier alphabet). */
|
|
127
|
+
function columnSql(word) {
|
|
128
|
+
if (word.startsWith("tag.")) {
|
|
129
|
+
const tag = word.slice(4);
|
|
130
|
+
if (!TAG_NAME.test(tag))
|
|
131
|
+
throw new QueryGrammarError(`bad tag name: ${tag}`);
|
|
132
|
+
return `json_extract(tags_json, '$.${tag}')`;
|
|
133
|
+
}
|
|
134
|
+
const column = PLAIN_COLUMNS[word];
|
|
135
|
+
if (column === undefined) {
|
|
136
|
+
throw new QueryGrammarError(`unknown column "${word}" — allowed: ${Object.keys(PLAIN_COLUMNS).join(", ")}, tag.<name>`);
|
|
137
|
+
}
|
|
138
|
+
return column;
|
|
139
|
+
}
|
|
140
|
+
function compileQuery(input, scope) {
|
|
141
|
+
const tokens = tokenize(input);
|
|
142
|
+
let pos = 0;
|
|
143
|
+
const peek = () => tokens[pos];
|
|
144
|
+
function next() {
|
|
145
|
+
const token = tokens[pos];
|
|
146
|
+
if (token === undefined)
|
|
147
|
+
throw new QueryGrammarError("unexpected end of query");
|
|
148
|
+
pos += 1;
|
|
149
|
+
return token;
|
|
150
|
+
}
|
|
151
|
+
function expectWord(word) {
|
|
152
|
+
const token = next();
|
|
153
|
+
if (token.kind !== "word" || token.text.toLowerCase() !== word) {
|
|
154
|
+
throw new QueryGrammarError(`expected ${word.toUpperCase()}, got "${token.text}"`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function expectPunct(text) {
|
|
158
|
+
const token = next();
|
|
159
|
+
if (token.kind !== "punct" || token.text !== text) {
|
|
160
|
+
throw new QueryGrammarError(`expected "${text}", got "${token.text}"`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
expectWord("select");
|
|
164
|
+
const selectSql = [];
|
|
165
|
+
for (;;) {
|
|
166
|
+
const token = next();
|
|
167
|
+
if (token.kind !== "word")
|
|
168
|
+
throw new QueryGrammarError(`bad select item: "${token.text}"`);
|
|
169
|
+
const lower = token.text.toLowerCase();
|
|
170
|
+
if (FUNCTIONS.has(lower) && peek()?.kind === "punct" && peek()?.text === "(") {
|
|
171
|
+
expectPunct("(");
|
|
172
|
+
const arg = next();
|
|
173
|
+
expectPunct(")");
|
|
174
|
+
if (lower === "count") {
|
|
175
|
+
if (arg.kind !== "punct" || arg.text !== "*") {
|
|
176
|
+
throw new QueryGrammarError("count takes exactly count(*)");
|
|
177
|
+
}
|
|
178
|
+
selectSql.push(`COUNT(*) AS "count"`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
if (arg.kind !== "word")
|
|
182
|
+
throw new QueryGrammarError(`bad metric in ${lower}()`);
|
|
183
|
+
const metric = METRICS[arg.text.toLowerCase()];
|
|
184
|
+
if (metric === undefined) {
|
|
185
|
+
throw new QueryGrammarError(`unknown metric "${arg.text}" — allowed: ${Object.keys(METRICS).join(", ")}`);
|
|
186
|
+
}
|
|
187
|
+
selectSql.push(`${lower.toUpperCase()}(${metric}) AS "${lower}_${arg.text.toLowerCase()}"`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// tag.client aliases to "client" — result keys read naturally.
|
|
192
|
+
const alias = token.text.startsWith("tag.") ? token.text.slice(4) : token.text;
|
|
193
|
+
selectSql.push(`${columnSql(token.text)} AS "${alias}"`);
|
|
194
|
+
}
|
|
195
|
+
if (peek()?.kind === "punct" && peek()?.text === ",") {
|
|
196
|
+
pos += 1;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
expectWord("from");
|
|
202
|
+
const table = next();
|
|
203
|
+
if (table.kind !== "word" || table.text.toLowerCase() !== "inferences") {
|
|
204
|
+
throw new QueryGrammarError(`only "inferences" is queryable, got "${table.text}"`);
|
|
205
|
+
}
|
|
206
|
+
const whereSql = [];
|
|
207
|
+
const params = [];
|
|
208
|
+
if (peek()?.kind === "word" && peek()?.text.toLowerCase() === "where") {
|
|
209
|
+
pos += 1;
|
|
210
|
+
for (;;) {
|
|
211
|
+
const column = next();
|
|
212
|
+
if (column.kind !== "word")
|
|
213
|
+
throw new QueryGrammarError(`bad condition column`);
|
|
214
|
+
const columnExpr = METRICS[column.text.toLowerCase()] !== undefined
|
|
215
|
+
? METRICS[column.text.toLowerCase()]
|
|
216
|
+
: columnSql(column.text);
|
|
217
|
+
const op = next();
|
|
218
|
+
if (op.kind !== "punct" || !OPERATORS.has(op.text)) {
|
|
219
|
+
throw new QueryGrammarError(`bad operator "${op.text}" — allowed: ${[...OPERATORS].join(" ")}`);
|
|
220
|
+
}
|
|
221
|
+
const value = next();
|
|
222
|
+
if (value.kind === "string") {
|
|
223
|
+
params.push(value.text);
|
|
224
|
+
}
|
|
225
|
+
else if (value.kind === "number") {
|
|
226
|
+
params.push(Number(value.text));
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
throw new QueryGrammarError(`condition value must be a 'string' or number, got "${value.text}"`);
|
|
230
|
+
}
|
|
231
|
+
whereSql.push(`${columnExpr} ${op.text} ?`);
|
|
232
|
+
if (peek()?.kind === "word" && peek()?.text.toLowerCase() === "and") {
|
|
233
|
+
pos += 1;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Row scoping — ANDed in unconditionally; the caller cannot remove it.
|
|
240
|
+
if (scope.kind === "tag") {
|
|
241
|
+
if (!TAG_NAME.test(scope.tag))
|
|
242
|
+
throw new QueryGrammarError(`bad scope tag: ${scope.tag}`);
|
|
243
|
+
whereSql.push(`json_extract(tags_json, '$.${scope.tag}') = ?`);
|
|
244
|
+
params.push(scope.value);
|
|
245
|
+
}
|
|
246
|
+
const groupSql = [];
|
|
247
|
+
if (peek()?.kind === "word" && peek()?.text.toLowerCase() === "group") {
|
|
248
|
+
pos += 1;
|
|
249
|
+
expectWord("by");
|
|
250
|
+
for (;;) {
|
|
251
|
+
const column = next();
|
|
252
|
+
if (column.kind !== "word")
|
|
253
|
+
throw new QueryGrammarError("bad GROUP BY column");
|
|
254
|
+
groupSql.push(columnSql(column.text));
|
|
255
|
+
if (peek()?.kind === "punct" && peek()?.text === ",") {
|
|
256
|
+
pos += 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
let limit = DEFAULT_LIMIT;
|
|
263
|
+
if (peek()?.kind === "word" && peek()?.text.toLowerCase() === "limit") {
|
|
264
|
+
pos += 1;
|
|
265
|
+
const n = next();
|
|
266
|
+
if (n.kind !== "number" || !/^\d+$/.test(n.text)) {
|
|
267
|
+
throw new QueryGrammarError("LIMIT takes a bare integer");
|
|
268
|
+
}
|
|
269
|
+
limit = Math.min(Number(n.text), MAX_LIMIT);
|
|
270
|
+
}
|
|
271
|
+
if (pos !== tokens.length) {
|
|
272
|
+
throw new QueryGrammarError(`unexpected trailing input: "${tokens[pos].text}"`);
|
|
273
|
+
}
|
|
274
|
+
const sql = `SELECT ${selectSql.join(", ")} FROM inferences` +
|
|
275
|
+
(whereSql.length > 0 ? ` WHERE ${whereSql.join(" AND ")}` : "") +
|
|
276
|
+
(groupSql.length > 0 ? ` GROUP BY ${groupSql.join(", ")}` : "") +
|
|
277
|
+
` LIMIT ${limit}`;
|
|
278
|
+
return { sql, params };
|
|
279
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query execution — Phase 4 WS5. Opens the telemetry file READONLY and runs
|
|
3
|
+
* one compiled (grammar-validated, parameterized) SELECT. The readonly
|
|
4
|
+
* handle is defense in depth under the grammar allowlist.
|
|
5
|
+
*/
|
|
6
|
+
import type { CompiledQuery } from "./grammar";
|
|
7
|
+
export declare function runTelemetryQuery(dbPath: string, compiled: CompiledQuery): Array<Record<string, unknown>>;
|
|
8
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/query/run.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,aAAa,GACtB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOhC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Query execution — Phase 4 WS5. Opens the telemetry file READONLY and runs
|
|
4
|
+
* one compiled (grammar-validated, parameterized) SELECT. The readonly
|
|
5
|
+
* handle is defense in depth under the grammar allowlist.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.runTelemetryQuery = runTelemetryQuery;
|
|
12
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
13
|
+
function runTelemetryQuery(dbPath, compiled) {
|
|
14
|
+
const db = new better_sqlite3_1.default(dbPath, { readonly: true, fileMustExist: true });
|
|
15
|
+
try {
|
|
16
|
+
return db.prepare(compiled.sql).all(...compiled.params);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
db.close();
|
|
20
|
+
}
|
|
21
|
+
}
|