modelrig 0.8.1 → 0.10.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 +324 -0
- package/README.md +55 -29
- package/data/PROVENANCE.md +14 -0
- package/data/pricing-map.json +26 -0
- package/dist/adapters/attempt-meta-map.d.ts +8 -1
- package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
- package/dist/adapters/attempt-meta-map.js +4 -0
- package/dist/adapters/build-adapters.js +2 -2
- package/dist/adapters/gemini.d.ts +28 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +38 -9
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +53 -4
- package/dist/adapters/openai-compat.d.ts +8 -2
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +8 -1
- package/dist/adapters/openai-stream.d.ts +10 -2
- package/dist/adapters/openai-stream.d.ts.map +1 -1
- package/dist/adapters/openai-stream.js +5 -1
- package/dist/adapters/openai.d.ts +10 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +57 -7
- package/dist/adapters/responses-api.d.ts +61 -0
- package/dist/adapters/responses-api.d.ts.map +1 -0
- package/dist/adapters/responses-api.js +124 -0
- package/dist/artifacts/artifact.d.ts +31 -12
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +218 -188
- package/dist/artifacts/index.d.ts +15 -5
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/index.js +19 -5
- package/dist/artifacts/run-context.d.ts +3 -24
- package/dist/artifacts/run-context.d.ts.map +1 -1
- package/dist/artifacts/run-context.js +3 -39
- package/dist/bakeoff/engine.d.ts +29 -0
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +47 -0
- package/dist/bakeoff/eval-cases-rig.d.ts +36 -0
- package/dist/bakeoff/eval-cases-rig.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases-rig.js +27 -0
- package/dist/bakeoff/eval-cases.d.ts +19 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -1
- package/dist/bakeoff/eval-cases.js +18 -1
- package/dist/bakeoff/golden-judge.d.ts +47 -0
- package/dist/bakeoff/golden-judge.d.ts.map +1 -0
- package/dist/bakeoff/golden-judge.js +49 -0
- package/dist/bakeoff/groundedness.d.ts +58 -0
- package/dist/bakeoff/groundedness.d.ts.map +1 -0
- package/dist/bakeoff/groundedness.js +165 -0
- package/dist/bakeoff/quality.d.ts +43 -0
- package/dist/bakeoff/quality.d.ts.map +1 -1
- package/dist/bakeoff/quality.js +31 -0
- package/dist/bakeoff/replay.d.ts +11 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +12 -0
- package/dist/bakeoff/report.d.ts +7 -0
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +26 -0
- package/dist/cli-judge.d.ts +12 -0
- package/dist/cli-judge.d.ts.map +1 -0
- package/dist/cli-judge.js +48 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +22 -42
- package/dist/config.d.ts +85 -29
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +109 -47
- package/dist/detect/detector.d.ts +4 -2
- package/dist/detect/detector.d.ts.map +1 -1
- package/dist/detect/safe-harbor.d.ts +57 -0
- package/dist/detect/safe-harbor.d.ts.map +1 -0
- package/dist/detect/safe-harbor.js +278 -0
- package/dist/doctor-cli.d.ts +7 -0
- package/dist/doctor-cli.d.ts.map +1 -0
- package/dist/doctor-cli.js +67 -0
- package/dist/doctor-preflight.d.ts +52 -0
- package/dist/doctor-preflight.d.ts.map +1 -0
- package/dist/doctor-preflight.js +112 -0
- package/dist/feedback/coverage.d.ts +11 -8
- package/dist/feedback/coverage.d.ts.map +1 -1
- package/dist/feedback/coverage.js +29 -16
- package/dist/feedback/grade.d.ts +58 -0
- package/dist/feedback/grade.d.ts.map +1 -0
- package/dist/feedback/grade.js +67 -0
- package/dist/gateway/http-protocol.d.ts +20 -1
- package/dist/gateway/http-protocol.d.ts.map +1 -1
- package/dist/gateway/http-protocol.js +1 -1
- package/dist/gateway/serve-client.d.ts +99 -0
- package/dist/gateway/serve-client.d.ts.map +1 -0
- package/dist/gateway/serve-client.js +360 -0
- package/dist/index.d.ts +68 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +167 -46
- package/dist/init/observe-cli.js +1 -1
- package/dist/init/observe.d.ts +2 -2
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +11 -7
- package/dist/mask/arm.d.ts +112 -0
- package/dist/mask/arm.d.ts.map +1 -0
- package/dist/mask/arm.js +179 -0
- package/dist/mask/index.d.ts +2 -0
- package/dist/mask/index.d.ts.map +1 -1
- package/dist/mask/index.js +7 -1
- package/dist/mask/mask-policy-cli.d.ts +12 -4
- package/dist/mask/mask-policy-cli.d.ts.map +1 -1
- package/dist/mask/mask-policy-cli.js +34 -9
- package/dist/prompts/manifest.d.ts +128 -0
- package/dist/prompts/manifest.d.ts.map +1 -0
- package/dist/prompts/manifest.js +214 -0
- package/dist/prompts/prompt-fetch.d.ts +84 -0
- package/dist/prompts/prompt-fetch.d.ts.map +1 -0
- package/dist/prompts/prompt-fetch.js +120 -0
- package/dist/prompts/prompt-resolve.d.ts +76 -0
- package/dist/prompts/prompt-resolve.d.ts.map +1 -0
- package/dist/prompts/prompt-resolve.js +93 -0
- package/dist/prompts/slot-resolver.d.ts +91 -0
- package/dist/prompts/slot-resolver.d.ts.map +1 -0
- package/dist/prompts/slot-resolver.js +135 -0
- package/dist/prompts/tools-register.d.ts +60 -0
- package/dist/prompts/tools-register.d.ts.map +1 -0
- package/dist/prompts/tools-register.js +48 -0
- package/dist/prompts/version-hash.d.ts +29 -0
- package/dist/prompts/version-hash.d.ts.map +1 -0
- package/dist/prompts/version-hash.js +54 -0
- package/dist/prompts-export-cli.d.ts +21 -0
- package/dist/prompts-export-cli.d.ts.map +1 -0
- package/dist/prompts-export-cli.js +130 -0
- package/dist/query/grammar.d.ts +2 -2
- package/dist/query/grammar.d.ts.map +1 -1
- package/dist/query/grammar.js +3 -1
- package/dist/query/saved.d.ts +13 -0
- package/dist/query/saved.d.ts.map +1 -1
- package/dist/query/saved.js +67 -1
- package/dist/receipts/types.d.ts +1 -1
- package/dist/receipts/types.d.ts.map +1 -1
- package/dist/receipts/types.js +1 -0
- package/dist/registry/build.d.ts +43 -1
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/build.js +52 -0
- package/dist/registry/cache-policy.d.ts.map +1 -1
- package/dist/registry/cache-policy.js +22 -0
- package/dist/registry/pricing-shape.d.ts +67 -8
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +50 -13
- package/dist/registry/pricing.d.ts +6 -5
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +9 -5
- package/dist/registry/rows.d.ts.map +1 -1
- package/dist/registry/rows.js +1 -0
- package/dist/registry/select.d.ts +6 -1
- package/dist/registry/select.d.ts.map +1 -1
- package/dist/registry/select.js +11 -0
- package/dist/routes/call-config.d.ts +199 -0
- package/dist/routes/call-config.d.ts.map +1 -0
- package/dist/routes/call-config.js +343 -0
- package/dist/routes/destination-loader.d.ts +16 -0
- package/dist/routes/destination-loader.d.ts.map +1 -0
- package/dist/routes/destination-loader.js +29 -0
- package/dist/routes/draft.d.ts +7 -0
- package/dist/routes/draft.d.ts.map +1 -1
- package/dist/routes/draft.js +12 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +31 -56
- package/dist/routes/objective-loader.d.ts +12 -0
- package/dist/routes/objective-loader.d.ts.map +1 -1
- package/dist/routes/objective-loader.js +82 -0
- package/dist/routes/output-loader.d.ts +42 -0
- package/dist/routes/output-loader.d.ts.map +1 -0
- package/dist/routes/output-loader.js +112 -0
- package/dist/routes/prompt-loader.d.ts +49 -0
- package/dist/routes/prompt-loader.d.ts.map +1 -0
- package/dist/routes/prompt-loader.js +113 -0
- package/dist/routes/resolved-bundle.d.ts +69 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +76 -0
- package/dist/routes/screen-loader.d.ts +29 -1
- package/dist/routes/screen-loader.d.ts.map +1 -1
- package/dist/routes/screen-loader.js +59 -1
- package/dist/run/accept.d.ts +5 -0
- package/dist/run/accept.d.ts.map +1 -1
- package/dist/run/accept.js +2 -0
- package/dist/run/cost.d.ts +31 -2
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +57 -2
- package/dist/run/destination.d.ts +39 -0
- package/dist/run/destination.d.ts.map +1 -0
- package/dist/run/destination.js +45 -0
- package/dist/run/dispatch.d.ts +67 -16
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +133 -29
- package/dist/run/grounding.d.ts +28 -2
- package/dist/run/grounding.d.ts.map +1 -1
- package/dist/run/grounding.js +54 -10
- package/dist/run/raw-defaults.d.ts.map +1 -1
- package/dist/run/raw-defaults.js +6 -1
- package/dist/run/raw-finalize.d.ts +50 -0
- package/dist/run/raw-finalize.d.ts.map +1 -0
- package/dist/run/raw-finalize.js +266 -0
- package/dist/run/raw-knobs.d.ts +36 -0
- package/dist/run/raw-knobs.d.ts.map +1 -0
- package/dist/run/raw-knobs.js +99 -0
- package/dist/run/raw-request.d.ts +24 -0
- package/dist/run/raw-request.d.ts.map +1 -0
- package/dist/run/raw-request.js +69 -0
- package/dist/run/raw-schema.d.ts +76 -0
- package/dist/run/raw-schema.d.ts.map +1 -0
- package/dist/run/raw-schema.js +166 -0
- package/dist/run/raw-search-cost.d.ts +48 -0
- package/dist/run/raw-search-cost.d.ts.map +1 -0
- package/dist/run/raw-search-cost.js +78 -0
- package/dist/run/raw-support.d.ts +64 -6
- package/dist/run/raw-support.d.ts.map +1 -1
- package/dist/run/raw-support.js +84 -11
- package/dist/run/raw.d.ts +25 -26
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +30 -351
- package/dist/run/record-attempt.d.ts +12 -0
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +30 -12
- package/dist/run/repair-attempt.d.ts +68 -0
- package/dist/run/repair-attempt.d.ts.map +1 -0
- package/dist/run/repair-attempt.js +180 -0
- package/dist/run/resolve-destination.d.ts +49 -0
- package/dist/run/resolve-destination.d.ts.map +1 -0
- package/dist/run/resolve-destination.js +76 -0
- package/dist/run/routing-docs.d.ts.map +1 -1
- package/dist/run/routing-docs.js +10 -5
- package/dist/run/run.d.ts +30 -1
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +94 -172
- package/dist/run/telemetry-row.d.ts +92 -0
- package/dist/run/telemetry-row.d.ts.map +1 -0
- package/dist/run/telemetry-row.js +81 -0
- package/dist/run/validate.d.ts +12 -1
- package/dist/run/validate.d.ts.map +1 -1
- package/dist/run/validate.js +31 -0
- package/dist/run/web-search-pricing.d.ts +43 -0
- package/dist/run/web-search-pricing.d.ts.map +1 -0
- package/dist/run/web-search-pricing.js +56 -0
- package/dist/savings/odometer.d.ts +4 -0
- package/dist/savings/odometer.d.ts.map +1 -1
- package/dist/savings/odometer.js +20 -1
- package/dist/savings/swap-execute.d.ts +7 -0
- package/dist/savings/swap-execute.d.ts.map +1 -1
- package/dist/savings/swap-execute.js +30 -0
- package/dist/savings/swaps-client.d.ts +10 -0
- package/dist/savings/swaps-client.d.ts.map +1 -1
- package/dist/screen/fixtures/probe-corpora.d.ts +23 -0
- package/dist/screen/fixtures/probe-corpora.d.ts.map +1 -0
- package/dist/screen/fixtures/probe-corpora.js +69 -0
- package/dist/screen/index.d.ts +21 -0
- package/dist/screen/index.d.ts.map +1 -0
- package/dist/screen/index.js +64 -0
- package/dist/screen/model-screen.d.ts +111 -0
- package/dist/screen/model-screen.d.ts.map +1 -0
- package/dist/screen/model-screen.js +178 -0
- package/dist/screen/patterns.d.ts +51 -0
- package/dist/screen/patterns.d.ts.map +1 -0
- package/dist/screen/patterns.js +151 -0
- package/dist/screen/phi-gate.d.ts +52 -0
- package/dist/screen/phi-gate.d.ts.map +1 -0
- package/dist/screen/phi-gate.js +86 -0
- package/dist/screen/policy.d.ts +71 -0
- package/dist/screen/policy.d.ts.map +1 -0
- package/dist/screen/policy.js +334 -0
- package/dist/screen/probe.d.ts +118 -0
- package/dist/screen/probe.d.ts.map +1 -0
- package/dist/screen/probe.js +132 -0
- package/dist/screen/screen-cli.d.ts +16 -0
- package/dist/screen/screen-cli.d.ts.map +1 -0
- package/dist/screen/screen-cli.js +178 -0
- package/dist/screen/screen.d.ts +99 -0
- package/dist/screen/screen.d.ts.map +1 -0
- package/dist/screen/screen.js +0 -0
- package/dist/screen/sidecar.d.ts +69 -0
- package/dist/screen/sidecar.d.ts.map +1 -0
- package/dist/screen/sidecar.js +145 -0
- package/dist/screen/types.d.ts +43 -2
- package/dist/screen/types.d.ts.map +1 -1
- package/dist/settings/api-fetcher.d.ts +14 -3
- package/dist/settings/api-fetcher.d.ts.map +1 -1
- package/dist/settings/api-fetcher.js +61 -10
- package/dist/settings/grading-guide.d.ts +102 -0
- package/dist/settings/grading-guide.d.ts.map +1 -0
- package/dist/settings/grading-guide.js +23 -0
- package/dist/settings/index.d.ts +3 -2
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js +2 -1
- package/dist/settings/task-settings.d.ts +44 -7
- package/dist/settings/task-settings.d.ts.map +1 -1
- package/dist/settings/task-settings.js +53 -12
- package/dist/status.d.ts +32 -20
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +51 -30
- package/dist/telemetry/artifact-store.d.ts +5 -6
- package/dist/telemetry/artifact-store.d.ts.map +1 -1
- package/dist/telemetry/artifact-store.js +21 -24
- package/dist/telemetry/attempt-meta.d.ts +76 -1
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +33 -1
- package/dist/telemetry/exporter-setup.d.ts +2 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -1
- package/dist/telemetry/exporter-setup.js +6 -1
- package/dist/telemetry/exporter.d.ts +2 -2
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +22 -41
- package/dist/telemetry/grade-mappers.d.ts +25 -0
- package/dist/telemetry/grade-mappers.d.ts.map +1 -0
- package/dist/telemetry/grade-mappers.js +64 -0
- package/dist/telemetry/grades-sql.d.ts +56 -0
- package/dist/telemetry/grades-sql.d.ts.map +1 -0
- package/dist/telemetry/grades-sql.js +156 -0
- package/dist/telemetry/ingest-protocol.d.ts +55 -48
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +48 -83
- package/dist/telemetry/org-identity.d.ts +44 -0
- package/dist/telemetry/org-identity.d.ts.map +1 -0
- package/dist/telemetry/org-identity.js +98 -0
- package/dist/telemetry/route-registration.d.ts +48 -0
- package/dist/telemetry/route-registration.d.ts.map +1 -0
- package/dist/telemetry/route-registration.js +77 -0
- package/dist/telemetry/sinks.d.ts +10 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +52 -6
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +64 -0
- package/dist/telemetry/store.d.ts +9 -6
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +8 -50
- package/dist/tenancy/api-key-format.d.ts +45 -1
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +50 -1
- package/dist/types/provider.d.ts +23 -3
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +26 -0
- package/dist/types/route.d.ts +154 -2
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +73 -48
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts.map +1 -1
- package/dist/validate-cli.js +32 -0
- package/dist/verify/verify.d.ts +5 -0
- package/dist/verify/verify.d.ts.map +1 -1
- package/dist/verify/verify.js +2 -0
- package/dist/watch/evaluate.d.ts +84 -1
- package/dist/watch/evaluate.d.ts.map +1 -1
- package/dist/watch/evaluate.js +28 -0
- package/dist/watch/monitor.d.ts +8 -2
- package/dist/watch/monitor.d.ts.map +1 -1
- package/dist/watch/monitor.js +13 -5
- package/dist/watch/regret-monitor-cli.d.ts +16 -0
- package/dist/watch/regret-monitor-cli.d.ts.map +1 -0
- package/dist/watch/regret-monitor-cli.js +137 -0
- package/dist/watch/regret-monitor.d.ts +168 -0
- package/dist/watch/regret-monitor.d.ts.map +1 -0
- package/dist/watch/regret-monitor.js +304 -0
- package/dist/watch/upgrade.d.ts +62 -0
- package/dist/watch/upgrade.d.ts.map +1 -0
- package/dist/watch/upgrade.js +57 -0
- package/docs/agent-skill/SKILL.md +181 -9
- package/docs/how-it-fits.md +3 -2
- package/docs/instrumentation.md +219 -46
- package/docs/llms.txt +158 -35
- package/docs/managed-serving.md +150 -0
- package/docs/mcp-oracle.md +322 -0
- package/docs/migration-playbook.md +5 -4
- package/docs/quickstart.md +263 -89
- package/package.json +18 -4
- package/registry/leaderboard.json +74 -0
- package/registry/registry.json +232 -112
- package/screen.d.ts +16 -0
- package/templates/call-disposition-qa.json +77 -0
- package/templates/catalog-cleansing.json +77 -0
- package/templates/compliance-review.json +76 -0
- package/templates/cs-next-action.json +76 -0
- package/templates/document-extraction.json +78 -0
- package/templates/financial-classification.json +75 -0
- package/templates/lead-qualification.json +76 -0
- package/templates/medical-classification.json +71 -0
- package/templates/ticket-triage.json +76 -0
- package/dist/feedback/feedback.d.ts +0 -35
- package/dist/feedback/feedback.d.ts.map +0 -1
- package/dist/feedback/feedback.js +0 -39
|
@@ -23,19 +23,21 @@
|
|
|
23
23
|
* stamped by whoever holds the principal — the exporter's config in direct
|
|
24
24
|
* mode, the resolved key in api mode.
|
|
25
25
|
*
|
|
26
|
-
* The single exception is `
|
|
27
|
-
* A
|
|
26
|
+
* The single exception is `grade.org_id`, and it is the reason WS2 exists.
|
|
27
|
+
* A judgment recorded on OUR hosted server belongs to the org of the person who
|
|
28
28
|
* recorded it, not to the org the server runs as, and one SQLite store holds
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* grades for many orgs. So a grade row may name its own org — and the ingest
|
|
30
|
+
* endpoint VERIFIES that name against the key rather than trusting it (see
|
|
31
|
+
* `foreignOrgGrades`). A service-role exporter needs no such check: it already
|
|
32
|
+
* holds a credential that can write any row in the database.
|
|
33
33
|
*/
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.DEFAULT_INGEST_URL = exports.INGEST_PROTOCOL_VERSION_V1 = exports.INGEST_PROTOCOL_VERSION = exports.INGEST_PATH = void 0;
|
|
35
|
+
exports.DEFAULT_INGEST_URL = exports.INGEST_PROTOCOL_VERSION_V1 = exports.INGEST_PROTOCOL_VERSION = exports.INGEST_PATH = exports.foreignOrgGrades = void 0;
|
|
36
36
|
exports.stampBatch = stampBatch;
|
|
37
|
-
exports.foreignOrgFeedback = foreignOrgFeedback;
|
|
38
37
|
const fingerprint_1 = require("../registry/fingerprint");
|
|
38
|
+
const grade_mappers_1 = require("./grade-mappers");
|
|
39
|
+
var grade_mappers_2 = require("./grade-mappers");
|
|
40
|
+
Object.defineProperty(exports, "foreignOrgGrades", { enumerable: true, get: function () { return grade_mappers_2.foreignOrgGrades; } });
|
|
39
41
|
/** The path the api sink posts to. Spelled once. */
|
|
40
42
|
exports.INGEST_PATH = "/v1/ingest";
|
|
41
43
|
/** The CURRENT protocol version — the maximum the SDK can speak (ruling d).
|
|
@@ -50,14 +52,7 @@ exports.INGEST_PROTOCOL_VERSION = 2;
|
|
|
50
52
|
exports.INGEST_PROTOCOL_VERSION_V1 = 1;
|
|
51
53
|
/** Default ingest host — telemetry-ingestion-boundary.md §2.1. */
|
|
52
54
|
exports.DEFAULT_INGEST_URL = "https://api.modelrig.ai";
|
|
53
|
-
|
|
54
|
-
* Turn a batch plus a tenant into the writes it becomes.
|
|
55
|
-
*
|
|
56
|
-
* THE conformance seam. Both transports call this; nothing else builds a
|
|
57
|
-
* telemetry row. If you are about to add a table to the export, add it here
|
|
58
|
-
* and both modes gain it at once — which is the point.
|
|
59
|
-
*/
|
|
60
|
-
function stampBatch(batch, tenancy) {
|
|
55
|
+
function stampBatch(batch, tenancy, opts = {}) {
|
|
61
56
|
const writes = [];
|
|
62
57
|
const now = new Date().toISOString();
|
|
63
58
|
if (batch.routes !== undefined && batch.routes.length > 0) {
|
|
@@ -101,28 +96,38 @@ function stampBatch(batch, tenancy) {
|
|
|
101
96
|
merge: true,
|
|
102
97
|
rows: taskRows,
|
|
103
98
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// `updated_at`, the row's metadata timestamp.
|
|
121
|
-
...(r.bundle !== undefined
|
|
122
|
-
? { bundle: r.bundle, bundle_sha: r.bundle_sha ?? null, synced_at: now }
|
|
123
|
-
: {}),
|
|
124
|
-
})),
|
|
99
|
+
const routeRow = (r) => ({
|
|
100
|
+
org_id: tenancy.org_id,
|
|
101
|
+
route: r.route,
|
|
102
|
+
version: r.version,
|
|
103
|
+
candidates: r.candidates,
|
|
104
|
+
capture: r.capture,
|
|
105
|
+
updated_at: now,
|
|
106
|
+
// Phase D (spec §13): the resolved bundle + its hash + the sync stamp.
|
|
107
|
+
// Written only when the route carries a bundle, so an older SDK's
|
|
108
|
+
// metadata-only mirror leaves any existing bundle intact (a merge
|
|
109
|
+
// upsert touches only the columns present in the row). `synced_at` is
|
|
110
|
+
// the BUNDLE's timestamp — stamped iff a bundle synced through a trusted
|
|
111
|
+
// path — distinct from `updated_at`, the row's metadata timestamp.
|
|
112
|
+
...(r.bundle !== undefined
|
|
113
|
+
? { bundle: r.bundle, bundle_sha: r.bundle_sha ?? null, synced_at: opts.declaredBundles === true ? null : now }
|
|
114
|
+
: {}),
|
|
125
115
|
});
|
|
116
|
+
// TWO writes, split by bundle presence: a PostgREST bulk insert requires
|
|
117
|
+
// every row to carry the SAME keys (PGRST102), so one route whose bundle was
|
|
118
|
+
// dropped must not fail — or null out — its neighbours' bundles.
|
|
119
|
+
const withBundle = batch.routes.filter((r) => r.bundle !== undefined);
|
|
120
|
+
const withoutBundle = batch.routes.filter((r) => r.bundle === undefined);
|
|
121
|
+
for (const group of [withBundle, withoutBundle]) {
|
|
122
|
+
if (group.length === 0)
|
|
123
|
+
continue;
|
|
124
|
+
writes.push({
|
|
125
|
+
table: "routes",
|
|
126
|
+
onConflict: "org_id,route,version",
|
|
127
|
+
merge: true,
|
|
128
|
+
rows: group.map(routeRow),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
126
131
|
}
|
|
127
132
|
if (batch.runs !== undefined && batch.runs.length > 0) {
|
|
128
133
|
writes.push({
|
|
@@ -225,28 +230,6 @@ function stampBatch(batch, tenancy) {
|
|
|
225
230
|
})),
|
|
226
231
|
});
|
|
227
232
|
}
|
|
228
|
-
if (batch.artifact_evaluations !== undefined && batch.artifact_evaluations.length > 0) {
|
|
229
|
-
writes.push({
|
|
230
|
-
table: "artifact_evaluations",
|
|
231
|
-
onConflict: "org_id,id",
|
|
232
|
-
merge: false,
|
|
233
|
-
rows: batch.artifact_evaluations.map((v) => ({
|
|
234
|
-
org_id: tenancy.org_id,
|
|
235
|
-
id: v.id,
|
|
236
|
-
artifact_id: v.artifact_id,
|
|
237
|
-
// Subject-one-of (00051): a run-subject row carries run_id, artifact_id
|
|
238
|
-
// null; the CHECK rejects a row that sets both or neither.
|
|
239
|
-
run_id: v.run_id ?? null,
|
|
240
|
-
evaluator: v.evaluator,
|
|
241
|
-
evaluator_kind: v.evaluator_kind,
|
|
242
|
-
score: v.score,
|
|
243
|
-
passed: v.passed,
|
|
244
|
-
label: v.label,
|
|
245
|
-
explanation: v.explanation,
|
|
246
|
-
created_by: v.created_by,
|
|
247
|
-
})),
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
233
|
if (batch.eval_cases !== undefined && batch.eval_cases.length > 0) {
|
|
251
234
|
writes.push({
|
|
252
235
|
// An eval case is created once (via promote); a retry must not rewrite it.
|
|
@@ -319,20 +302,14 @@ function stampBatch(batch, tenancy) {
|
|
|
319
302
|
],
|
|
320
303
|
});
|
|
321
304
|
}
|
|
322
|
-
if (batch.
|
|
305
|
+
if (batch.grades !== undefined && batch.grades.length > 0) {
|
|
306
|
+
// The SDK's current judgment write path. A grade may name its own org
|
|
307
|
+
// (feedback doctrine, verified by foreignOrgGrades before this).
|
|
323
308
|
writes.push({
|
|
324
|
-
table: "
|
|
325
|
-
onConflict: "id",
|
|
309
|
+
table: "grades",
|
|
310
|
+
onConflict: "org_id,id",
|
|
326
311
|
merge: false,
|
|
327
|
-
rows: batch.
|
|
328
|
-
// The verdict's own org wins when it has one — that is the whole of
|
|
329
|
-
// QA-P6-010's fix. Note that `source_id` is deliberately absent:
|
|
330
|
-
// `feedback.source` already means "who judged", and two columns a
|
|
331
|
-
// letter apart meaning different things is N2's cautionary tale.
|
|
332
|
-
org_id: org_id ?? tenancy.org_id,
|
|
333
|
-
project_id: tenancy.project_id,
|
|
334
|
-
...row,
|
|
335
|
-
})),
|
|
312
|
+
rows: batch.grades.map((g) => (0, grade_mappers_1.gradeToRow)(g, tenancy)),
|
|
336
313
|
});
|
|
337
314
|
}
|
|
338
315
|
if (batch.envelopes !== undefined && batch.envelopes.length > 0) {
|
|
@@ -352,15 +329,3 @@ function stampBatch(batch, tenancy) {
|
|
|
352
329
|
}
|
|
353
330
|
return writes;
|
|
354
331
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Feedback rows naming an org other than the presented key's.
|
|
357
|
-
*
|
|
358
|
-
* Exists as its own exported function because the ingest endpoint must drop
|
|
359
|
-
* these and COUNT them, and a check that lives inline in a request handler is
|
|
360
|
-
* a check nobody can test in isolation. Returns the offending ids.
|
|
361
|
-
*/
|
|
362
|
-
function foreignOrgFeedback(rows, keyOrgId) {
|
|
363
|
-
return rows
|
|
364
|
-
.filter((r) => r.org_id !== undefined && r.org_id !== keyOrgId)
|
|
365
|
-
.map((r) => r.id);
|
|
366
|
-
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first-flush org line + the `expectOrg` mismatch signal (REL-1, ruling M5).
|
|
3
|
+
*
|
|
4
|
+
* A VALID key minted in the WRONG org is the failure the AUTH FAILED latch in
|
|
5
|
+
* sinks.ts cannot see: every POST is a 200, and the runs land in someone else's
|
|
6
|
+
* console. (Six weeks of InferWealth pipeline telemetry went to Nunexa this
|
|
7
|
+
* way.) So on the first successful flush in each process the api sink asks
|
|
8
|
+
* `GET /v1/whoami` once — through the same `whoamiPreflight` helper that
|
|
9
|
+
* `modelrig doctor` uses — and logs which org it is exporting to:
|
|
10
|
+
*
|
|
11
|
+
* [modelrig] exporting to org «InferWealth» (inferwealth) as key «InferWealth2» …ab12
|
|
12
|
+
*
|
|
13
|
+
* With `createRig({ expectOrg: "<slug>" })` a different resolved slug logs
|
|
14
|
+
* `ORG MISMATCH` loudly on EVERY successful flush (ruling M5), because nobody
|
|
15
|
+
* reads boot logs. It is a code option, not an env var (P15).
|
|
16
|
+
*
|
|
17
|
+
* Contract: non-blocking (never awaited by the sink), once per process per
|
|
18
|
+
* key, never throws, never drops or delays telemetry. A failed whoami is
|
|
19
|
+
* SILENT: a 401 already has its own loud line, and a network blip must not
|
|
20
|
+
* become noise.
|
|
21
|
+
*/
|
|
22
|
+
import { type PreflightResult } from "../doctor-preflight";
|
|
23
|
+
/** The whoami call is aborted after this long, so it can never hold a
|
|
24
|
+
* process open (AbortSignal.timeout's timer is unref'd by Node). */
|
|
25
|
+
export declare const WHOAMI_TIMEOUT_MS = 5000;
|
|
26
|
+
/** TESTS ONLY — forget every key, so "once per process" cases are independent. */
|
|
27
|
+
export declare function __resetOrgIdentityForTest(): void;
|
|
28
|
+
export interface OrgIdentityInput {
|
|
29
|
+
readonly ingestUrl: string;
|
|
30
|
+
readonly apiKey: string;
|
|
31
|
+
readonly expectOrg?: string;
|
|
32
|
+
readonly fetchFn: typeof fetch;
|
|
33
|
+
/** Override WHOAMI_TIMEOUT_MS (tests). */
|
|
34
|
+
readonly timeoutMs?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Build the one-line org banner from a successful whoami. */
|
|
37
|
+
export declare function formatOrgLine(result: PreflightResult, apiKey: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Record one successful flush. Returns immediately; the whoami (first flush
|
|
40
|
+
* only) runs in the background. Exposed as a promise-returning helper ONLY so
|
|
41
|
+
* tests can await the background work — the sink calls it with `void`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function noteSuccessfulFlush(input: OrgIdentityInput): Promise<void>;
|
|
44
|
+
//# sourceMappingURL=org-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-identity.d.ts","sourceRoot":"","sources":["../../src/telemetry/org-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGjG;oEACoE;AACpE,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAYtC,kFAAkF;AAClF,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO7E;AAgBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B1E"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WHOAMI_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.__resetOrgIdentityForTest = __resetOrgIdentityForTest;
|
|
5
|
+
exports.formatOrgLine = formatOrgLine;
|
|
6
|
+
exports.noteSuccessfulFlush = noteSuccessfulFlush;
|
|
7
|
+
/**
|
|
8
|
+
* The first-flush org line + the `expectOrg` mismatch signal (REL-1, ruling M5).
|
|
9
|
+
*
|
|
10
|
+
* A VALID key minted in the WRONG org is the failure the AUTH FAILED latch in
|
|
11
|
+
* sinks.ts cannot see: every POST is a 200, and the runs land in someone else's
|
|
12
|
+
* console. (Six weeks of InferWealth pipeline telemetry went to Nunexa this
|
|
13
|
+
* way.) So on the first successful flush in each process the api sink asks
|
|
14
|
+
* `GET /v1/whoami` once — through the same `whoamiPreflight` helper that
|
|
15
|
+
* `modelrig doctor` uses — and logs which org it is exporting to:
|
|
16
|
+
*
|
|
17
|
+
* [modelrig] exporting to org «InferWealth» (inferwealth) as key «InferWealth2» …ab12
|
|
18
|
+
*
|
|
19
|
+
* With `createRig({ expectOrg: "<slug>" })` a different resolved slug logs
|
|
20
|
+
* `ORG MISMATCH` loudly on EVERY successful flush (ruling M5), because nobody
|
|
21
|
+
* reads boot logs. It is a code option, not an env var (P15).
|
|
22
|
+
*
|
|
23
|
+
* Contract: non-blocking (never awaited by the sink), once per process per
|
|
24
|
+
* key, never throws, never drops or delays telemetry. A failed whoami is
|
|
25
|
+
* SILENT: a 401 already has its own loud line, and a network blip must not
|
|
26
|
+
* become noise.
|
|
27
|
+
*/
|
|
28
|
+
const doctor_preflight_1 = require("../doctor-preflight");
|
|
29
|
+
const api_key_format_1 = require("../tenancy/api-key-format");
|
|
30
|
+
/** The whoami call is aborted after this long, so it can never hold a
|
|
31
|
+
* process open (AbortSignal.timeout's timer is unref'd by Node). */
|
|
32
|
+
exports.WHOAMI_TIMEOUT_MS = 5000;
|
|
33
|
+
/** Keyed by ingest origin + key, so two rigs sharing a key share one whoami. */
|
|
34
|
+
const states = new Map();
|
|
35
|
+
/** TESTS ONLY — forget every key, so "once per process" cases are independent. */
|
|
36
|
+
function __resetOrgIdentityForTest() {
|
|
37
|
+
states.clear();
|
|
38
|
+
}
|
|
39
|
+
/** Build the one-line org banner from a successful whoami. */
|
|
40
|
+
function formatOrgLine(result, apiKey) {
|
|
41
|
+
const org = result.whoami?.org ?? null;
|
|
42
|
+
const name = org?.name ?? org?.id ?? "?";
|
|
43
|
+
const slug = org?.slug ?? "?";
|
|
44
|
+
const keyName = result.whoami?.key?.name;
|
|
45
|
+
const keyLabel = typeof keyName === "string" && keyName !== "" ? `«${keyName}» ` : "";
|
|
46
|
+
return `[modelrig] exporting to org «${name}» (${slug}) as key ${keyLabel}${(0, api_key_format_1.redactApiKey)(apiKey)}`;
|
|
47
|
+
}
|
|
48
|
+
function mismatchLine(actual, expectOrg) {
|
|
49
|
+
return (`[modelrig] ORG MISMATCH: key resolves to «${actual}», expectOrg is «${expectOrg}» — ` +
|
|
50
|
+
`telemetry is landing in «${actual}»`);
|
|
51
|
+
}
|
|
52
|
+
function warnIfMismatch(result, expectOrg) {
|
|
53
|
+
const expected = expectOrg?.trim();
|
|
54
|
+
if (expected === undefined || expected === "")
|
|
55
|
+
return;
|
|
56
|
+
const actual = (0, doctor_preflight_1.expectedOrgMismatch)(result, expected);
|
|
57
|
+
if (actual !== null)
|
|
58
|
+
console.warn(mismatchLine(actual, expected));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Record one successful flush. Returns immediately; the whoami (first flush
|
|
62
|
+
* only) runs in the background. Exposed as a promise-returning helper ONLY so
|
|
63
|
+
* tests can await the background work — the sink calls it with `void`.
|
|
64
|
+
*/
|
|
65
|
+
function noteSuccessfulFlush(input) {
|
|
66
|
+
let origin;
|
|
67
|
+
try {
|
|
68
|
+
origin = new URL(input.ingestUrl).origin;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
}
|
|
73
|
+
const id = `${origin}|${input.apiKey}`;
|
|
74
|
+
let state = states.get(id);
|
|
75
|
+
if (state === undefined) {
|
|
76
|
+
state = { flushes: 0, resolved: null };
|
|
77
|
+
states.set(id, state);
|
|
78
|
+
}
|
|
79
|
+
state.flushes += 1;
|
|
80
|
+
const s = state;
|
|
81
|
+
if (s.flushes === 1) {
|
|
82
|
+
return (0, doctor_preflight_1.whoamiPreflight)(input.ingestUrl, input.apiKey, input.fetchFn, AbortSignal.timeout(input.timeoutMs ?? exports.WHOAMI_TIMEOUT_MS))
|
|
83
|
+
.then((result) => {
|
|
84
|
+
if (!result.ok)
|
|
85
|
+
return; // silent: a 401 has its own loud line
|
|
86
|
+
s.resolved = result;
|
|
87
|
+
console.log(formatOrgLine(result, input.apiKey));
|
|
88
|
+
warnIfMismatch(result, input.expectOrg);
|
|
89
|
+
})
|
|
90
|
+
.catch(() => {
|
|
91
|
+
// whoamiPreflight never throws, and logging must never break the sink.
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// M5: loud on EVERY flush once the org is known (flush 1 warns when whoami lands).
|
|
95
|
+
if (s.resolved !== null)
|
|
96
|
+
warnIfMismatch(s.resolved, input.expectOrg);
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RV-1 declare-on-startup — a rig with routes AND a control plane tells the
|
|
3
|
+
* control plane which routes it serves the moment it is created, so a route is
|
|
4
|
+
* visible (console, MCP `get_call_config`, `GET /v1/routes/:route/call-config
|
|
5
|
+
* ?include=effective`) BEFORE its first call.
|
|
6
|
+
*
|
|
7
|
+
* ── WHY A SEPARATE ONE-SHOT, WHEN THE EXPORTER ALREADY MIRRORS ROUTES ─────
|
|
8
|
+
* The exporter folds the routes mirror into its FIRST flush (one interval —
|
|
9
|
+
* 10s — after start). A short-lived process (a CLI, a cron, a lazily-built
|
|
10
|
+
* pipeline rig) that exits before that tick without `close()` never mirrors, and
|
|
11
|
+
* a long-lived one is invisible for the first interval. This sends the same
|
|
12
|
+
* metadata once, immediately. The exporter's header stays as the at-least-once
|
|
13
|
+
* retry path: the routes upsert is a merge on (org_id, route, version), so the
|
|
14
|
+
* duplicate is harmless.
|
|
15
|
+
*
|
|
16
|
+
* ── WHAT IT CARRIES (and does not) ────────────────────────────────────────
|
|
17
|
+
* route, version, candidates (provider/model), capture flag, AND the declared
|
|
18
|
+
* resolved bundle + its `bundle_sha` — the same payload the exporter's
|
|
19
|
+
* first-flush mirror has always carried (Matt's RV-1 ruling 1, 2026-10-10). The
|
|
20
|
+
* bundle is ROUTE CONFIGURATION (the authored prompt TEMPLATE with its
|
|
21
|
+
* placeholders, schema, candidates, policy — never per-call values); the control
|
|
22
|
+
* plane needs it so a call-config pin passes the eligibility gate (`loadBundle`)
|
|
23
|
+
* on a mirror-mode server. The server re-validates the bundle and recomputes the
|
|
24
|
+
* sha; once a PSR prompt has been promoted for the route, PSR's template wins
|
|
25
|
+
* over the declared one (modelrig-server ingest-route-config.ts). The org is
|
|
26
|
+
* never on the wire — the server stamps it from the key's principal (T3/T11).
|
|
27
|
+
* It travels through the SAME sink as telemetry, so in `api` mode it is a
|
|
28
|
+
* `POST /v1/ingest` with the rig's own key and needs only the `ingest` scope.
|
|
29
|
+
*
|
|
30
|
+
* ── FAIL-OPEN BY CONSTRUCTION ─────────────────────────────────────────────
|
|
31
|
+
* Fire-and-forget. Never awaited by createRig, never throws (sync or async),
|
|
32
|
+
* one console.warn on failure. It cannot change what THIS process serves (the
|
|
33
|
+
* SDK serves from its local bundles); it writes only the control-plane `routes`
|
|
34
|
+
* mirror, exactly as the exporter's first flush already does.
|
|
35
|
+
*/
|
|
36
|
+
import type { IngestBatch } from "./ingest-protocol";
|
|
37
|
+
import type { ExportedRouteMirror } from "./exporter";
|
|
38
|
+
import type { TelemetrySink } from "./sinks";
|
|
39
|
+
/** The routes-only batch (metadata + declared bundle). v1 so any server vintage
|
|
40
|
+
* accepts it (an older server keeps the metadata and ignores the bundle). */
|
|
41
|
+
export declare function buildRouteRegistrationBatch(sourceId: string, routes: readonly ExportedRouteMirror[]): IngestBatch;
|
|
42
|
+
/**
|
|
43
|
+
* Register `routes` with the control plane through `sink`, once, now. Returns
|
|
44
|
+
* the in-flight promise (always resolves — for tests and callers that want to
|
|
45
|
+
* await it); createRig ignores it. No routes ⇒ no request.
|
|
46
|
+
*/
|
|
47
|
+
export declare function registerRoutesOnStartup(sink: TelemetrySink, sourceId: string, routes: readonly ExportedRouteMirror[]): Promise<void>;
|
|
48
|
+
//# sourceMappingURL=route-registration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-registration.d.ts","sourceRoot":"","sources":["../../src/telemetry/route-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;6EAC6E;AAC7E,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,mBAAmB,EAAE,GACrC,WAAW,CAYb;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,mBAAmB,EAAE,GACrC,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RV-1 declare-on-startup — a rig with routes AND a control plane tells the
|
|
4
|
+
* control plane which routes it serves the moment it is created, so a route is
|
|
5
|
+
* visible (console, MCP `get_call_config`, `GET /v1/routes/:route/call-config
|
|
6
|
+
* ?include=effective`) BEFORE its first call.
|
|
7
|
+
*
|
|
8
|
+
* ── WHY A SEPARATE ONE-SHOT, WHEN THE EXPORTER ALREADY MIRRORS ROUTES ─────
|
|
9
|
+
* The exporter folds the routes mirror into its FIRST flush (one interval —
|
|
10
|
+
* 10s — after start). A short-lived process (a CLI, a cron, a lazily-built
|
|
11
|
+
* pipeline rig) that exits before that tick without `close()` never mirrors, and
|
|
12
|
+
* a long-lived one is invisible for the first interval. This sends the same
|
|
13
|
+
* metadata once, immediately. The exporter's header stays as the at-least-once
|
|
14
|
+
* retry path: the routes upsert is a merge on (org_id, route, version), so the
|
|
15
|
+
* duplicate is harmless.
|
|
16
|
+
*
|
|
17
|
+
* ── WHAT IT CARRIES (and does not) ────────────────────────────────────────
|
|
18
|
+
* route, version, candidates (provider/model), capture flag, AND the declared
|
|
19
|
+
* resolved bundle + its `bundle_sha` — the same payload the exporter's
|
|
20
|
+
* first-flush mirror has always carried (Matt's RV-1 ruling 1, 2026-10-10). The
|
|
21
|
+
* bundle is ROUTE CONFIGURATION (the authored prompt TEMPLATE with its
|
|
22
|
+
* placeholders, schema, candidates, policy — never per-call values); the control
|
|
23
|
+
* plane needs it so a call-config pin passes the eligibility gate (`loadBundle`)
|
|
24
|
+
* on a mirror-mode server. The server re-validates the bundle and recomputes the
|
|
25
|
+
* sha; once a PSR prompt has been promoted for the route, PSR's template wins
|
|
26
|
+
* over the declared one (modelrig-server ingest-route-config.ts). The org is
|
|
27
|
+
* never on the wire — the server stamps it from the key's principal (T3/T11).
|
|
28
|
+
* It travels through the SAME sink as telemetry, so in `api` mode it is a
|
|
29
|
+
* `POST /v1/ingest` with the rig's own key and needs only the `ingest` scope.
|
|
30
|
+
*
|
|
31
|
+
* ── FAIL-OPEN BY CONSTRUCTION ─────────────────────────────────────────────
|
|
32
|
+
* Fire-and-forget. Never awaited by createRig, never throws (sync or async),
|
|
33
|
+
* one console.warn on failure. It cannot change what THIS process serves (the
|
|
34
|
+
* SDK serves from its local bundles); it writes only the control-plane `routes`
|
|
35
|
+
* mirror, exactly as the exporter's first flush already does.
|
|
36
|
+
*/
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.buildRouteRegistrationBatch = buildRouteRegistrationBatch;
|
|
39
|
+
exports.registerRoutesOnStartup = registerRoutesOnStartup;
|
|
40
|
+
const ingest_protocol_1 = require("./ingest-protocol");
|
|
41
|
+
/** The routes-only batch (metadata + declared bundle). v1 so any server vintage
|
|
42
|
+
* accepts it (an older server keeps the metadata and ignores the bundle). */
|
|
43
|
+
function buildRouteRegistrationBatch(sourceId, routes) {
|
|
44
|
+
return {
|
|
45
|
+
v: ingest_protocol_1.INGEST_PROTOCOL_VERSION_V1,
|
|
46
|
+
source: sourceId,
|
|
47
|
+
routes: routes.map((r) => ({
|
|
48
|
+
route: r.route,
|
|
49
|
+
version: r.version,
|
|
50
|
+
candidates: r.candidates.map((c) => ({ provider: c.provider, model: c.model })),
|
|
51
|
+
capture: r.capture ?? false,
|
|
52
|
+
...(r.bundle !== undefined ? { bundle: r.bundle, bundle_sha: r.bundleSha } : {}),
|
|
53
|
+
})),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Register `routes` with the control plane through `sink`, once, now. Returns
|
|
58
|
+
* the in-flight promise (always resolves — for tests and callers that want to
|
|
59
|
+
* await it); createRig ignores it. No routes ⇒ no request.
|
|
60
|
+
*/
|
|
61
|
+
function registerRoutesOnStartup(sink, sourceId, routes) {
|
|
62
|
+
if (routes.length === 0)
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
let batch;
|
|
65
|
+
try {
|
|
66
|
+
batch = buildRouteRegistrationBatch(sourceId, routes);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return Promise.resolve();
|
|
70
|
+
}
|
|
71
|
+
return Promise.resolve()
|
|
72
|
+
.then(() => sink.send(batch))
|
|
73
|
+
.catch((err) => {
|
|
74
|
+
console.warn(`[modelrig] route registration (declare-on-startup) failed — routes will still be mirrored ` +
|
|
75
|
+
`by the exporter's first flush: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type { IngestBatch, IngestOutcome, Tenancy } from "./ingest-protocol";
|
|
23
23
|
import type { ApiExportConfig, DirectExportConfig } from "../config";
|
|
24
|
+
/** Reset the auth-failure latch — TESTS ONLY, so warn-once cases are independent
|
|
25
|
+
* of run order (mirrors deprecations.ts). */
|
|
26
|
+
export declare function __resetApiSinkWarningsForTest(): void;
|
|
24
27
|
export interface TelemetrySink {
|
|
25
28
|
/** Named so a boot banner and `/status` can say which lane is live without
|
|
26
29
|
* inspecting config — the "which mode am I in?" question got asked enough
|
|
@@ -77,6 +80,10 @@ export interface ApiSinkConfig {
|
|
|
77
80
|
* rows are being dropped must hear about it from their own process rather
|
|
78
81
|
* than by noticing a gap in a chart weeks later. */
|
|
79
82
|
readonly onOutcome?: (outcome: IngestOutcome) => void;
|
|
83
|
+
/** `createRig({ expectOrg })` (M5): the org slug this process expects its key
|
|
84
|
+
* to resolve to. A mismatch logs ORG MISMATCH on every flush (org-identity.ts);
|
|
85
|
+
* it never throws and never drops telemetry. */
|
|
86
|
+
readonly expectOrg?: string;
|
|
80
87
|
}
|
|
81
88
|
export declare function createApiSink(config: ApiSinkConfig): TelemetrySink;
|
|
82
89
|
/**
|
|
@@ -86,5 +93,7 @@ export declare function createApiSink(config: ApiSinkConfig): TelemetrySink;
|
|
|
86
93
|
* index.ts until Phase D; moved here — its natural home — to keep that file
|
|
87
94
|
* under the LOC cap.)
|
|
88
95
|
*/
|
|
89
|
-
export declare function buildTelemetrySink(exportConfig: ApiExportConfig | DirectExportConfig, sourceId: string
|
|
96
|
+
export declare function buildTelemetrySink(exportConfig: ApiExportConfig | DirectExportConfig, sourceId: string, opts?: {
|
|
97
|
+
readonly expectOrg?: string;
|
|
98
|
+
}): TelemetrySink;
|
|
90
99
|
//# sourceMappingURL=sinks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAmBrE;6CAC6C;AAC7C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,MAAM,WAAW,aAAa;IAC5B;;yDAEqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC;;6EAEyE;IACzE,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;qCAEiC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;CAMxD;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CA0CxE;AAMD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;4EACwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC;;;wDAGoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD;;oDAEgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAsElE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,eAAe,GAAG,kBAAkB,EAClD,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,aAAa,CA2Bf"}
|
package/dist/telemetry/sinks.js
CHANGED
|
@@ -22,12 +22,33 @@
|
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.SinkSendError = void 0;
|
|
25
|
+
exports.__resetApiSinkWarningsForTest = __resetApiSinkWarningsForTest;
|
|
25
26
|
exports.createDirectSink = createDirectSink;
|
|
26
27
|
exports.createApiSink = createApiSink;
|
|
27
28
|
exports.buildTelemetrySink = buildTelemetrySink;
|
|
28
29
|
const node_zlib_1 = require("node:zlib");
|
|
29
30
|
const ingest_protocol_1 = require("./ingest-protocol");
|
|
30
31
|
const constants_1 = require("../tenancy/constants");
|
|
32
|
+
const api_key_format_1 = require("../tenancy/api-key-format");
|
|
33
|
+
const org_identity_1 = require("./org-identity");
|
|
34
|
+
/**
|
|
35
|
+
* Auth-failure latch (once per FAILURE STREAK, not once per process).
|
|
36
|
+
*
|
|
37
|
+
* A 401/403 from ingest means the key is rejected and EVERY run is being dropped
|
|
38
|
+
* org-wide — categorically different from a single poison batch (422), and the
|
|
39
|
+
* exact silent failure that cost InferWealth days of invisible telemetry. The
|
|
40
|
+
* export path is fire-and-forget, so the only way this surfaces is a log line —
|
|
41
|
+
* and it must be loud, legible, and NOT drowned in per-batch noise. We warn ONCE
|
|
42
|
+
* and latch; a subsequent successful send clears the latch, so if the key breaks
|
|
43
|
+
* again later it re-alerts. No env var, no extra request — it rides the POST that
|
|
44
|
+
* is already failing.
|
|
45
|
+
*/
|
|
46
|
+
let apiAuthFailureWarned = false;
|
|
47
|
+
/** Reset the auth-failure latch — TESTS ONLY, so warn-once cases are independent
|
|
48
|
+
* of run order (mirrors deprecations.ts). */
|
|
49
|
+
function __resetApiSinkWarningsForTest() {
|
|
50
|
+
apiAuthFailureWarned = false;
|
|
51
|
+
}
|
|
31
52
|
/**
|
|
32
53
|
* A sink send failed — the typed error both transports throw (E2 hardening
|
|
33
54
|
* Phase 2, spec §2.3.1). It carries the HTTP status the sink observed (absent
|
|
@@ -127,14 +148,38 @@ function createApiSink(config) {
|
|
|
127
148
|
throw new SinkSendError("ingest POST → network error", undefined);
|
|
128
149
|
}
|
|
129
150
|
if (!response.ok) {
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
151
|
+
// A rejected KEY (401/403) is not one bad batch — it means every run is
|
|
152
|
+
// being dropped org-wide, silently. Say so, loudly, ONCE per failure
|
|
153
|
+
// streak. Safe to name here: the host is the public ingest endpoint and
|
|
154
|
+
// `redactApiKey` is the sanctioned last-four form (the same one the boot
|
|
155
|
+
// banner and `modelrig doctor` print) — never the body, never the secret.
|
|
156
|
+
if ((response.status === 401 || response.status === 403) && !apiAuthFailureWarned) {
|
|
157
|
+
apiAuthFailureWarned = true;
|
|
158
|
+
console.warn(`[modelrig] AUTH FAILED — telemetry is being DROPPED (${response.status} at ` +
|
|
159
|
+
`${new URL(url).origin}, key ${(0, api_key_format_1.redactApiKey)(config.apiKey)}). MODELRIG_API_KEY is not ` +
|
|
160
|
+
"accepted by this server/org; check the key and that it belongs to the org you expect " +
|
|
161
|
+
"at app.modelrig.ai. Runs will not appear until this is fixed. Confirm with `modelrig doctor`.");
|
|
162
|
+
}
|
|
163
|
+
// Status only in the THROWN error — never the response body. The key never
|
|
164
|
+
// appears in the message (C2's leak gate covers failure text), and an
|
|
165
|
+
// exporter's warnings are the likeliest place for a secret to reach
|
|
166
|
+
// someone else's log aggregator. The status is the exporter's transient-
|
|
167
|
+
// vs-poison signal: the server sends 422 for a deterministic control-plane
|
|
135
168
|
// rejection, 503 for a transient one (spec §2.2/§2.3.1).
|
|
136
169
|
throw new SinkSendError(`ingest POST → ${response.status}`, response.status);
|
|
137
170
|
}
|
|
171
|
+
// A good send clears the auth-failure latch, so a later key breakage
|
|
172
|
+
// re-alerts instead of staying silent behind a once-per-process guard.
|
|
173
|
+
apiAuthFailureWarned = false;
|
|
174
|
+
// M5: name the org this key exports to (first flush per process, via
|
|
175
|
+
// /v1/whoami) and log ORG MISMATCH on every flush when `expectOrg` disagrees. Fire-
|
|
176
|
+
// and-forget — never awaited, never throws, never touches the batch.
|
|
177
|
+
void (0, org_identity_1.noteSuccessfulFlush)({
|
|
178
|
+
ingestUrl: config.ingestUrl,
|
|
179
|
+
apiKey: config.apiKey,
|
|
180
|
+
fetchFn,
|
|
181
|
+
...(config.expectOrg !== undefined ? { expectOrg: config.expectOrg } : {}),
|
|
182
|
+
});
|
|
138
183
|
if (config.onOutcome !== undefined) {
|
|
139
184
|
const outcome = (await response.json().catch(() => null));
|
|
140
185
|
if (outcome !== null)
|
|
@@ -150,11 +195,12 @@ function createApiSink(config) {
|
|
|
150
195
|
* index.ts until Phase D; moved here — its natural home — to keep that file
|
|
151
196
|
* under the LOC cap.)
|
|
152
197
|
*/
|
|
153
|
-
function buildTelemetrySink(exportConfig, sourceId) {
|
|
198
|
+
function buildTelemetrySink(exportConfig, sourceId, opts = {}) {
|
|
154
199
|
return exportConfig.mode === "api"
|
|
155
200
|
? createApiSink({
|
|
156
201
|
ingestUrl: exportConfig.ingestUrl,
|
|
157
202
|
apiKey: exportConfig.apiKey,
|
|
203
|
+
...(opts.expectOrg !== undefined ? { expectOrg: opts.expectOrg } : {}),
|
|
158
204
|
// A batch the server partly refused comes back 200. Without this, a
|
|
159
205
|
// customer whose rows are being dropped would find out by noticing a
|
|
160
206
|
// gap in a chart weeks later.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;sCACsC;AACtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAgM9D"}
|