modelrig 0.1.0 → 0.3.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 +158 -0
- package/README.md +53 -11
- package/data/PROVENANCE.md +19 -0
- package/data/pricing-manual.json +22 -0
- package/data/pricing-map.json +46362 -0
- package/data/pricing-overlay.json +419 -0
- package/dist/adapters/anthropic.d.ts +50 -6
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +123 -29
- package/dist/adapters/build-adapters.d.ts +11 -0
- package/dist/adapters/build-adapters.d.ts.map +1 -0
- package/dist/adapters/build-adapters.js +45 -0
- package/dist/adapters/deepseek.d.ts +6 -0
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +54 -1
- package/dist/adapters/gemini.d.ts +10 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +78 -7
- package/dist/adapters/grok.d.ts +9 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +67 -3
- package/dist/adapters/openai-compat.d.ts +6 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +58 -1
- package/dist/adapters/openai-stream.d.ts +58 -0
- package/dist/adapters/openai-stream.d.ts.map +1 -0
- package/dist/adapters/openai-stream.js +120 -0
- package/dist/adapters/openai.d.ts +6 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +60 -1
- package/dist/adapters/sampling.d.ts +28 -0
- package/dist/adapters/sampling.d.ts.map +1 -0
- package/dist/adapters/sampling.js +34 -0
- package/dist/adapters/tool-mapping.d.ts +92 -0
- package/dist/adapters/tool-mapping.d.ts.map +1 -0
- package/dist/adapters/tool-mapping.js +143 -0
- package/dist/artifacts/artifact.d.ts +137 -0
- package/dist/artifacts/artifact.d.ts.map +1 -0
- package/dist/artifacts/artifact.js +377 -0
- package/dist/artifacts/content-uploader.d.ts +87 -0
- package/dist/artifacts/content-uploader.d.ts.map +1 -0
- package/dist/artifacts/content-uploader.js +124 -0
- package/dist/artifacts/index.d.ts +63 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +80 -0
- package/dist/artifacts/run-context.d.ts +129 -0
- package/dist/artifacts/run-context.d.ts.map +1 -0
- package/dist/artifacts/run-context.js +208 -0
- package/dist/artifacts/scrub.d.ts +60 -0
- package/dist/artifacts/scrub.d.ts.map +1 -0
- package/dist/artifacts/scrub.js +139 -0
- package/dist/artifacts/step.d.ts +26 -0
- package/dist/artifacts/step.d.ts.map +1 -0
- package/dist/artifacts/step.js +37 -0
- package/dist/artifacts/taint.d.ts +63 -0
- package/dist/artifacts/taint.d.ts.map +1 -0
- package/dist/artifacts/taint.js +76 -0
- package/dist/bakeoff/cache-sim.d.ts +99 -0
- package/dist/bakeoff/cache-sim.d.ts.map +1 -0
- package/dist/bakeoff/cache-sim.js +126 -0
- package/dist/bakeoff/eval-cases.d.ts +96 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases.js +105 -0
- package/dist/bakeoff/report.d.ts +41 -0
- package/dist/bakeoff/report.d.ts.map +1 -0
- package/dist/bakeoff/report.js +142 -0
- package/dist/bakeoff/samples.d.ts +15 -0
- package/dist/bakeoff/samples.d.ts.map +1 -1
- package/dist/bakeoff/samples.js +20 -0
- package/dist/cli.d.ts +1 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +134 -116
- package/dist/config.d.ts +100 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -0
- package/dist/gateway/http-protocol.d.ts +175 -0
- package/dist/gateway/http-protocol.d.ts.map +1 -0
- package/dist/gateway/http-protocol.js +105 -0
- package/dist/index.d.ts +106 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -138
- package/dist/init/finder.d.ts +15 -0
- package/dist/init/finder.d.ts.map +1 -1
- package/dist/init/finder.js +46 -0
- package/dist/init/init-cli.d.ts +10 -0
- package/dist/init/init-cli.d.ts.map +1 -0
- package/dist/init/init-cli.js +86 -0
- package/dist/init/observe-cli.d.ts +19 -0
- package/dist/init/observe-cli.d.ts.map +1 -0
- package/dist/init/observe-cli.js +73 -0
- package/dist/init/observe.d.ts +81 -0
- package/dist/init/observe.d.ts.map +1 -0
- package/dist/init/observe.js +245 -0
- package/dist/registry/build.d.ts +94 -1
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/build.js +125 -4
- package/dist/registry/cache-policy.d.ts +94 -0
- package/dist/registry/cache-policy.d.ts.map +1 -0
- package/dist/registry/cache-policy.js +238 -0
- package/dist/registry/call-notes.d.ts.map +1 -1
- package/dist/registry/call-notes.js +40 -2
- package/dist/registry/effective-rate.d.ts +151 -0
- package/dist/registry/effective-rate.d.ts.map +1 -0
- package/dist/registry/effective-rate.js +156 -0
- package/dist/registry/facts.d.ts.map +1 -1
- package/dist/registry/facts.js +16 -0
- package/dist/registry/parity.d.ts +26 -0
- package/dist/registry/parity.d.ts.map +1 -1
- package/dist/registry/parity.js +43 -2
- package/dist/registry/pricing-shape.d.ts +113 -0
- package/dist/registry/pricing-shape.d.ts.map +1 -0
- package/dist/registry/pricing-shape.js +123 -0
- package/dist/registry/pricing.d.ts +25 -1
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +64 -6
- package/dist/rigindex/bootstrap.d.ts +36 -0
- package/dist/rigindex/bootstrap.d.ts.map +1 -0
- package/dist/rigindex/bootstrap.js +97 -0
- package/dist/rigindex/fit.d.ts +24 -0
- package/dist/rigindex/fit.d.ts.map +1 -0
- package/dist/rigindex/fit.js +76 -0
- package/dist/rigindex/gates.d.ts +52 -0
- package/dist/rigindex/gates.d.ts.map +1 -0
- package/dist/rigindex/gates.js +109 -0
- package/dist/rigindex/graph.d.ts +31 -0
- package/dist/rigindex/graph.d.ts.map +1 -0
- package/dist/rigindex/graph.js +120 -0
- package/dist/rigindex/index.d.ts +23 -0
- package/dist/rigindex/index.d.ts.map +1 -0
- package/dist/rigindex/index.js +112 -0
- package/dist/rigindex/types.d.ts +76 -0
- package/dist/rigindex/types.d.ts.map +1 -0
- package/dist/rigindex/types.js +8 -0
- package/dist/routes/cache-lint.d.ts +41 -0
- package/dist/routes/cache-lint.d.ts.map +1 -0
- package/dist/routes/cache-lint.js +81 -0
- package/dist/routes/loader.d.ts +48 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +167 -0
- package/dist/routes/resolved-bundle.d.ts +79 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -0
- package/dist/routes/resolved-bundle.js +148 -0
- package/dist/routes/sampling-config.d.ts +32 -0
- package/dist/routes/sampling-config.d.ts.map +1 -0
- package/dist/routes/sampling-config.js +91 -0
- package/dist/run/ajv.d.ts +22 -0
- package/dist/run/ajv.d.ts.map +1 -0
- package/dist/run/ajv.js +32 -0
- package/dist/run/cache-handle.d.ts +58 -0
- package/dist/run/cache-handle.d.ts.map +1 -0
- package/dist/run/cache-handle.js +54 -0
- package/dist/run/cache-stamp.d.ts +65 -0
- package/dist/run/cache-stamp.d.ts.map +1 -0
- package/dist/run/cache-stamp.js +0 -0
- package/dist/run/capture-write.d.ts +33 -0
- package/dist/run/capture-write.d.ts.map +1 -0
- package/dist/run/capture-write.js +55 -0
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +1 -1
- package/dist/run/dispatch.d.ts +99 -0
- package/dist/run/dispatch.d.ts.map +1 -0
- package/dist/run/dispatch.js +191 -0
- package/dist/run/raw.d.ts +130 -0
- package/dist/run/raw.d.ts.map +1 -0
- package/dist/run/raw.js +363 -0
- package/dist/run/record-attempt.d.ts +40 -0
- package/dist/run/record-attempt.d.ts.map +1 -0
- package/dist/run/record-attempt.js +99 -0
- package/dist/run/resolve.d.ts +75 -2
- package/dist/run/resolve.d.ts.map +1 -1
- package/dist/run/resolve.js +126 -4
- package/dist/run/run.d.ts +14 -0
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +0 -0
- package/dist/run/stream.d.ts +49 -0
- package/dist/run/stream.d.ts.map +1 -0
- package/dist/run/stream.js +87 -0
- package/dist/run/validate.d.ts.map +1 -1
- package/dist/run/validate.js +6 -7
- package/dist/savings/cache-ledger.d.ts +108 -0
- package/dist/savings/cache-ledger.d.ts.map +1 -0
- package/dist/savings/cache-ledger.js +174 -0
- package/dist/savings/cache-reconcile.d.ts +64 -0
- package/dist/savings/cache-reconcile.d.ts.map +1 -0
- package/dist/savings/cache-reconcile.js +59 -0
- package/dist/savings/cache-stats.d.ts +63 -0
- package/dist/savings/cache-stats.d.ts.map +1 -0
- package/dist/savings/cache-stats.js +108 -0
- package/dist/savings/emit.d.ts +13 -0
- package/dist/savings/emit.d.ts.map +1 -1
- package/dist/savings/emit.js +25 -0
- package/dist/savings/swap-execute.d.ts +14 -0
- package/dist/savings/swap-execute.d.ts.map +1 -1
- package/dist/savings/swap-execute.js +44 -1
- package/dist/savings/swaps-client.d.ts +14 -0
- package/dist/savings/swaps-client.d.ts.map +1 -1
- package/dist/savings/swaps-client.js +16 -2
- package/dist/settings/api-fetcher.d.ts +39 -0
- package/dist/settings/api-fetcher.d.ts.map +1 -0
- package/dist/settings/api-fetcher.js +92 -0
- package/dist/settings/index.d.ts +10 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +15 -0
- package/dist/settings/task-settings.d.ts +91 -0
- package/dist/settings/task-settings.d.ts.map +1 -0
- package/dist/settings/task-settings.js +116 -0
- package/dist/status.d.ts +77 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +113 -0
- package/dist/telemetry/artifact-store.d.ts +48 -0
- package/dist/telemetry/artifact-store.d.ts.map +1 -0
- package/dist/telemetry/artifact-store.js +189 -0
- package/dist/telemetry/cache-miss-streak.d.ts +79 -0
- package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
- package/dist/telemetry/cache-miss-streak.js +99 -0
- package/dist/telemetry/capture.d.ts +3 -0
- package/dist/telemetry/capture.d.ts.map +1 -1
- package/dist/telemetry/capture.js +15 -8
- package/dist/telemetry/exporter-setup.d.ts +28 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -0
- package/dist/telemetry/exporter-setup.js +34 -0
- package/dist/telemetry/exporter.d.ts +75 -0
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +324 -47
- package/dist/telemetry/health.d.ts +76 -0
- package/dist/telemetry/health.d.ts.map +1 -0
- package/dist/telemetry/health.js +92 -0
- package/dist/telemetry/ingest-protocol.d.ts +152 -3
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +204 -4
- package/dist/telemetry/load-error-report.d.ts +24 -0
- package/dist/telemetry/load-error-report.d.ts.map +1 -0
- package/dist/telemetry/load-error-report.js +50 -0
- package/dist/telemetry/otel-map.d.ts +121 -0
- package/dist/telemetry/otel-map.d.ts.map +1 -0
- package/dist/telemetry/otel-map.js +224 -0
- package/dist/telemetry/otel-normalize.d.ts +104 -0
- package/dist/telemetry/otel-normalize.d.ts.map +1 -0
- package/dist/telemetry/otel-normalize.js +530 -0
- package/dist/telemetry/otlp-emitter.d.ts +52 -0
- package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
- package/dist/telemetry/otlp-emitter.js +144 -0
- package/dist/telemetry/otlp-proto.d.ts +29 -0
- package/dist/telemetry/otlp-proto.d.ts.map +1 -0
- package/dist/telemetry/otlp-proto.js +266 -0
- package/dist/telemetry/sinks.d.ts +37 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +121 -30
- package/dist/telemetry/store.d.ts +28 -1
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +218 -3
- package/dist/tenancy/api-key-format.d.ts +15 -5
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +24 -5
- package/dist/types/config-teaching.d.ts +62 -0
- package/dist/types/config-teaching.d.ts.map +1 -0
- package/dist/types/config-teaching.js +183 -0
- package/dist/types/provider.d.ts +147 -1
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +21 -1
- package/dist/types/route.d.ts +79 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +117 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts +12 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate-cli.js +57 -0
- package/dist/verify/capture.d.ts +66 -0
- package/dist/verify/capture.d.ts.map +1 -0
- package/dist/verify/capture.js +180 -0
- package/dist/verify/cli-run.d.ts +23 -0
- package/dist/verify/cli-run.d.ts.map +1 -0
- package/dist/verify/cli-run.js +57 -0
- package/dist/verify/diff.d.ts +48 -0
- package/dist/verify/diff.d.ts.map +1 -0
- package/dist/verify/diff.js +157 -0
- package/dist/verify/index.d.ts +17 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +24 -0
- package/dist/verify/report.d.ts +14 -0
- package/dist/verify/report.d.ts.map +1 -0
- package/dist/verify/report.js +88 -0
- package/dist/verify/types.d.ts +101 -0
- package/dist/verify/types.d.ts.map +1 -0
- package/dist/verify/types.js +17 -0
- package/dist/verify/verify.d.ts +29 -0
- package/dist/verify/verify.d.ts.map +1 -0
- package/dist/verify/verify.js +148 -0
- package/dist/watch/evaluate.d.ts +162 -3
- package/dist/watch/evaluate.d.ts.map +1 -1
- package/dist/watch/evaluate.js +61 -0
- package/dist/watch/monitor.d.ts +116 -0
- package/dist/watch/monitor.d.ts.map +1 -0
- package/dist/watch/monitor.js +199 -0
- package/dist/watch/proposals.d.ts +20 -4
- package/dist/watch/proposals.d.ts.map +1 -1
- package/dist/watch/proposals.js +31 -3
- package/dist/watch/run.d.ts +33 -0
- package/dist/watch/run.d.ts.map +1 -1
- package/dist/watch/run.js +39 -0
- package/package.json +7 -1
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/dist/telemetry/supabase-exporter.d.ts +0 -64
- package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
- package/dist/telemetry/supabase-exporter.js +0 -211
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-stability lint — cache-aware-routing WS-C step 4 (spec §5.3,
|
|
3
|
+
* [Briefing §10]). A prefix cache only pays off when the CACHED PREFIX is
|
|
4
|
+
* stable across calls. A route template that interpolates a volatile value
|
|
5
|
+
* (a timestamp, a run id, a random nonce) high in the prompt shifts the prefix
|
|
6
|
+
* on every call, so the provider caches something that never recurs — the route
|
|
7
|
+
* pays the write premium forever and never reads a hit ("paying full price and
|
|
8
|
+
* don't know it", the §5.6 smell, caught statically here instead of after the money).
|
|
9
|
+
*
|
|
10
|
+
* v1 is a pure, conservative NAME heuristic over the declared `{{variable}}`
|
|
11
|
+
* references (the run-loop's stable prefix IS the route template — spec §5.3).
|
|
12
|
+
* It flags variables whose NAME reads volatile AND that appear before the
|
|
13
|
+
* template's stable tail. It does NOT inspect rendered values (it can't — the
|
|
14
|
+
* lint runs at load, before any input) and it never fails the load: a false
|
|
15
|
+
* positive must not block a legitimate route, so this warns. The prompt compiler
|
|
16
|
+
* + Prefix Stability Score (real per-token volatility telemetry) is the deferred
|
|
17
|
+
* successor ([Briefing Phase 5]); this is the cheap static precursor.
|
|
18
|
+
*/
|
|
19
|
+
export interface CacheLintWarning {
|
|
20
|
+
/** The offending variable name. */
|
|
21
|
+
readonly variable: string;
|
|
22
|
+
/** Character offset of the reference in the raw template. */
|
|
23
|
+
readonly index: number;
|
|
24
|
+
/** Human-readable, teach-not-scold message. */
|
|
25
|
+
readonly message: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Lint a route's raw system template for cache-hostile volatile interpolations.
|
|
29
|
+
* Pure and total. Returns one warning per volatile-named reference that sits
|
|
30
|
+
* before the stable tail; `[]` for a clean template.
|
|
31
|
+
*
|
|
32
|
+
* "Before the stable tail" (v1 approximation): a volatile reference in the first
|
|
33
|
+
* `headFraction` of the template shifts the cacheable prefix; one at the very
|
|
34
|
+
* end (after the bulk of the stable instructions) shifts only the tail and is
|
|
35
|
+
* harmless for prefix caching. We warn only on head references to keep the
|
|
36
|
+
* signal high — a `{{now}}` in a trailing "as of {{now}}" line is fine.
|
|
37
|
+
*/
|
|
38
|
+
export declare function lintCacheTemplate(template: string, opts?: {
|
|
39
|
+
readonly headFraction?: number;
|
|
40
|
+
}): CacheLintWarning[];
|
|
41
|
+
//# sourceMappingURL=cache-lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-lint.d.ts","sourceRoot":"","sources":["../../src/routes/cache-lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAmCH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,gBAAgB,EAAE,CAmBpB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompt-stability lint — cache-aware-routing WS-C step 4 (spec §5.3,
|
|
4
|
+
* [Briefing §10]). A prefix cache only pays off when the CACHED PREFIX is
|
|
5
|
+
* stable across calls. A route template that interpolates a volatile value
|
|
6
|
+
* (a timestamp, a run id, a random nonce) high in the prompt shifts the prefix
|
|
7
|
+
* on every call, so the provider caches something that never recurs — the route
|
|
8
|
+
* pays the write premium forever and never reads a hit ("paying full price and
|
|
9
|
+
* don't know it", the §5.6 smell, caught statically here instead of after the money).
|
|
10
|
+
*
|
|
11
|
+
* v1 is a pure, conservative NAME heuristic over the declared `{{variable}}`
|
|
12
|
+
* references (the run-loop's stable prefix IS the route template — spec §5.3).
|
|
13
|
+
* It flags variables whose NAME reads volatile AND that appear before the
|
|
14
|
+
* template's stable tail. It does NOT inspect rendered values (it can't — the
|
|
15
|
+
* lint runs at load, before any input) and it never fails the load: a false
|
|
16
|
+
* positive must not block a legitimate route, so this warns. The prompt compiler
|
|
17
|
+
* + Prefix Stability Score (real per-token volatility telemetry) is the deferred
|
|
18
|
+
* successor ([Briefing Phase 5]); this is the cheap static precursor.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.lintCacheTemplate = lintCacheTemplate;
|
|
22
|
+
/** Variable names that read as per-call-volatile. Deliberately broad on the
|
|
23
|
+
* volatile side (a warning is cheap; a silently un-cached route is not) but
|
|
24
|
+
* anchored to whole \b tokens so `updated_at_note` matches yet `runtime` does
|
|
25
|
+
* not falsely trip on "run". */
|
|
26
|
+
const VOLATILE_NAME = new RegExp([
|
|
27
|
+
"\\btime(stamp)?\\b",
|
|
28
|
+
"\\bnow\\b",
|
|
29
|
+
"\\bdate(time)?\\b",
|
|
30
|
+
"run[_-]?id",
|
|
31
|
+
"request[_-]?id",
|
|
32
|
+
"trace[_-]?id",
|
|
33
|
+
"session[_-]?id",
|
|
34
|
+
"correlation[_-]?id",
|
|
35
|
+
"\\buuid\\b",
|
|
36
|
+
"\\bguid\\b",
|
|
37
|
+
"\\bnonce\\b",
|
|
38
|
+
"\\brandom\\b",
|
|
39
|
+
"\\brand\\b",
|
|
40
|
+
"\\bepoch\\b",
|
|
41
|
+
"\\bcurrent[_-]",
|
|
42
|
+
"generated[_-]?at",
|
|
43
|
+
"created[_-]?at",
|
|
44
|
+
"updated[_-]?at",
|
|
45
|
+
].join("|"), "i");
|
|
46
|
+
/** The `{{var}}` scanner from template.ts, duplicated as a global-match here so
|
|
47
|
+
* the lint can read each reference's POSITION (scanTemplateVariables dedupes to
|
|
48
|
+
* names only). Kept in sync with template.ts's VAR_TAG. */
|
|
49
|
+
const VAR_TAG = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
50
|
+
/**
|
|
51
|
+
* Lint a route's raw system template for cache-hostile volatile interpolations.
|
|
52
|
+
* Pure and total. Returns one warning per volatile-named reference that sits
|
|
53
|
+
* before the stable tail; `[]` for a clean template.
|
|
54
|
+
*
|
|
55
|
+
* "Before the stable tail" (v1 approximation): a volatile reference in the first
|
|
56
|
+
* `headFraction` of the template shifts the cacheable prefix; one at the very
|
|
57
|
+
* end (after the bulk of the stable instructions) shifts only the tail and is
|
|
58
|
+
* harmless for prefix caching. We warn only on head references to keep the
|
|
59
|
+
* signal high — a `{{now}}` in a trailing "as of {{now}}" line is fine.
|
|
60
|
+
*/
|
|
61
|
+
function lintCacheTemplate(template, opts = {}) {
|
|
62
|
+
const headFraction = opts.headFraction ?? 0.75;
|
|
63
|
+
const headCutoff = Math.floor(template.length * headFraction);
|
|
64
|
+
const warnings = [];
|
|
65
|
+
for (const m of template.matchAll(VAR_TAG)) {
|
|
66
|
+
const name = m[1];
|
|
67
|
+
const index = m.index;
|
|
68
|
+
if (index >= headCutoff)
|
|
69
|
+
continue; // in the stable tail — harmless
|
|
70
|
+
if (!VOLATILE_NAME.test(name))
|
|
71
|
+
continue;
|
|
72
|
+
warnings.push({
|
|
73
|
+
variable: name,
|
|
74
|
+
index,
|
|
75
|
+
message: `route template interpolates \`{{${name}}}\` before the stable prompt tail — a per-call ` +
|
|
76
|
+
`value here shifts the cacheable prefix and defeats prompt caching (the route would pay ` +
|
|
77
|
+
`full price every call). Move volatile values to the end of the prompt, or drop them.`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return warnings;
|
|
81
|
+
}
|
package/dist/routes/loader.d.ts
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
* resolves schema/prompt paths relative to the bundle file, freezes.
|
|
5
5
|
*/
|
|
6
6
|
import type { RouteBundle } from "../types/route";
|
|
7
|
+
/**
|
|
8
|
+
* CE-2 A5 co-item: warn (non-fatal) when a route's output schema has an ARRAY
|
|
9
|
+
* root. OpenAI strict structured output (`response_format: json_schema`, strict)
|
|
10
|
+
* requires a top-level OBJECT — an array-root schema cannot use strict
|
|
11
|
+
* enforcement on OpenAI candidates and silently falls back to non-strict there
|
|
12
|
+
* (the walkthrough §10 provider-matrix gap). The route still LOADS and serves as
|
|
13
|
+
* declared; the fix is to wrap the array in an object property so strict
|
|
14
|
+
* enforcement holds across every provider. Returns the warning line, or null.
|
|
15
|
+
* Pure — unit-tested directly.
|
|
16
|
+
*/
|
|
17
|
+
export declare function arrayRootSchemaWarning(route: string, outputSchema: object | null): string | null;
|
|
7
18
|
/**
|
|
8
19
|
* Structured config error thrown for any invalid bundle (never a raw Error).
|
|
9
20
|
* Load-time problems are deliberately NOT RigFailures — the failure taxonomy
|
|
@@ -22,5 +33,42 @@ export interface LoadedRoutes {
|
|
|
22
33
|
get(route: string): RouteBundle;
|
|
23
34
|
list(): readonly RouteBundle[];
|
|
24
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Merge two loaded route sets into one store — a UNION, collision is a loud
|
|
38
|
+
* error. Used to fold a deployment's bundled INTERNAL infra routes (e.g. the
|
|
39
|
+
* server's run-judge@v1 / analyst-summarize@v1) into the operator's configured
|
|
40
|
+
* routes without replacing them (index.ts createRig, extraRoutesDir). The
|
|
41
|
+
* reserved `@v<n>` infra namespace never collides with customer/demo routes;
|
|
42
|
+
* if it ever did, failing at boot is correct.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Load a routes directory and OPTIONALLY union a second (deployment-bundled
|
|
46
|
+
* infra) directory into it — the one call createRig makes so the extra-routes
|
|
47
|
+
* merge does not grow index.ts. `extraDir` absent/empty ⇒ exactly loadRoutes.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadRoutesWithExtra(routesDir: string, extraDir?: string): LoadedRoutes;
|
|
50
|
+
export declare function mergeLoadedRoutes(primary: LoadedRoutes, extra: LoadedRoutes): LoadedRoutes;
|
|
51
|
+
/** One route file's structural-validation outcome (CE-6, spec §1 / walkthrough
|
|
52
|
+
* §8.7a). `route` is the parsed handle, or null when the bundle failed before a
|
|
53
|
+
* name could be read. */
|
|
54
|
+
export interface RouteValidation {
|
|
55
|
+
readonly file: string;
|
|
56
|
+
readonly route: string | null;
|
|
57
|
+
readonly ok: boolean;
|
|
58
|
+
readonly problems: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Structurally validate every route bundle in a directory WITHOUT requiring any
|
|
62
|
+
* provider key (CE-6 `modelrig validate`). Unlike {@link loadRoutes}, one bad
|
|
63
|
+
* bundle does NOT abort the rest — each file is loaded independently and its
|
|
64
|
+
* outcome collected, so a newbie sees every problem in one pass. Duplicate route
|
|
65
|
+
* handles across files are reported per the later file. This deliberately does
|
|
66
|
+
* NOT run the serveable-candidate gate (serveability.ts) — that needs keys and
|
|
67
|
+
* stays fail-closed for SERVING; validation only checks authoring.
|
|
68
|
+
*
|
|
69
|
+
* A missing directory is still a hard error (nothing to validate) — it throws
|
|
70
|
+
* the same teaching RouteConfigError as loadRoutes.
|
|
71
|
+
*/
|
|
72
|
+
export declare function validateRoutesDir(routesDir: string): RouteValidation[];
|
|
25
73
|
export declare function loadRoutes(routesDir: string): LoadedRoutes;
|
|
26
74
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/routes/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAKV,WAAW,EAGZ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/routes/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAKV,WAAW,EAGZ,MAAM,gBAAgB,CAAC;AAMxB;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAWhG;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;gBAEzB,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAO;CAOxF;AAiCD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,IAAI,IAAI,SAAS,WAAW,EAAE,CAAC;CAChC;AA4ZD;;;;;;;GAOG;AACH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAItF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAyB1F;AAED;;yBAEyB;AACzB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CA6CtE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CA2C1D"}
|
package/dist/routes/loader.js
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.RouteConfigError = void 0;
|
|
9
|
+
exports.arrayRootSchemaWarning = arrayRootSchemaWarning;
|
|
10
|
+
exports.loadRoutesWithExtra = loadRoutesWithExtra;
|
|
11
|
+
exports.mergeLoadedRoutes = mergeLoadedRoutes;
|
|
12
|
+
exports.validateRoutesDir = validateRoutesDir;
|
|
9
13
|
exports.loadRoutes = loadRoutes;
|
|
10
14
|
const node_fs_1 = require("node:fs");
|
|
11
15
|
const node_path_1 = require("node:path");
|
|
@@ -13,6 +17,30 @@ const yaml_1 = require("yaml");
|
|
|
13
17
|
const failures_1 = require("../types/failures");
|
|
14
18
|
const fingerprint_1 = require("../registry/fingerprint");
|
|
15
19
|
const template_1 = require("./template");
|
|
20
|
+
const cache_lint_1 = require("./cache-lint");
|
|
21
|
+
const sampling_config_1 = require("./sampling-config");
|
|
22
|
+
/**
|
|
23
|
+
* CE-2 A5 co-item: warn (non-fatal) when a route's output schema has an ARRAY
|
|
24
|
+
* root. OpenAI strict structured output (`response_format: json_schema`, strict)
|
|
25
|
+
* requires a top-level OBJECT — an array-root schema cannot use strict
|
|
26
|
+
* enforcement on OpenAI candidates and silently falls back to non-strict there
|
|
27
|
+
* (the walkthrough §10 provider-matrix gap). The route still LOADS and serves as
|
|
28
|
+
* declared; the fix is to wrap the array in an object property so strict
|
|
29
|
+
* enforcement holds across every provider. Returns the warning line, or null.
|
|
30
|
+
* Pure — unit-tested directly.
|
|
31
|
+
*/
|
|
32
|
+
function arrayRootSchemaWarning(route, outputSchema) {
|
|
33
|
+
if (outputSchema === null)
|
|
34
|
+
return null;
|
|
35
|
+
const rootType = outputSchema.type;
|
|
36
|
+
if (rootType !== "array")
|
|
37
|
+
return null;
|
|
38
|
+
return (`route "${route}" declares an output schema whose ROOT is type: "array". OpenAI strict ` +
|
|
39
|
+
`structured output requires a top-level object, so strict enforcement cannot apply on OpenAI ` +
|
|
40
|
+
`candidates (they fall back to non-strict there). Wrap the array in an object property ` +
|
|
41
|
+
`(e.g. { "type": "object", "properties": { "items": <your array> } }) to keep strict ` +
|
|
42
|
+
`enforcement across providers. The route loads and serves as declared.`);
|
|
43
|
+
}
|
|
16
44
|
/**
|
|
17
45
|
* Structured config error thrown for any invalid bundle (never a raw Error).
|
|
18
46
|
* Load-time problems are deliberately NOT RigFailures — the failure taxonomy
|
|
@@ -136,6 +164,7 @@ function parsePolicy(raw, problems) {
|
|
|
136
164
|
if (typeof json !== "string" || !JSON_MODES.has(json)) {
|
|
137
165
|
problems.push(`policy.json "${String(json)}" is not one of [native, json_mode]`);
|
|
138
166
|
}
|
|
167
|
+
const sampling = (0, sampling_config_1.parseSampling)(raw.sampling, "policy.sampling", problems);
|
|
139
168
|
if (problems.length > 0)
|
|
140
169
|
return fallback;
|
|
141
170
|
return Object.freeze({
|
|
@@ -143,6 +172,7 @@ function parsePolicy(raw, problems) {
|
|
|
143
172
|
timeoutMs: timeoutMs,
|
|
144
173
|
tier: tier,
|
|
145
174
|
json: json,
|
|
175
|
+
...(sampling !== undefined ? { sampling } : {}),
|
|
146
176
|
});
|
|
147
177
|
}
|
|
148
178
|
/** "provider/model" string or {provider, model} mapping → CandidateSpec. */
|
|
@@ -328,6 +358,21 @@ function loadBundleFile(filePath) {
|
|
|
328
358
|
if (typeof capture !== "boolean") {
|
|
329
359
|
problems.push("capture must be a boolean");
|
|
330
360
|
}
|
|
361
|
+
// Request-path caching opt-in (cache-aware-routing WS-C). Exactly one value
|
|
362
|
+
// is supported in v1 — `cache: auto`. Anything else is a config error (a typo
|
|
363
|
+
// must not silently ship an un-cached route). Gemini is NOT excluded here: a
|
|
364
|
+
// mixed-provider route may opt in, and the run-loop's policy gate skips
|
|
365
|
+
// gemini's request path (cache-stamp.ts) while its implicit hit rate still
|
|
366
|
+
// arrives passively — the exclusion is code-enforced, not author-policed.
|
|
367
|
+
let cache = false;
|
|
368
|
+
if (doc.cache !== undefined && doc.cache !== null) {
|
|
369
|
+
if (doc.cache === "auto") {
|
|
370
|
+
cache = true;
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
problems.push(`cache "${String(doc.cache)}" is not supported — use "cache: auto" or omit it`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
331
376
|
let groundingQuery;
|
|
332
377
|
if (doc.grounding !== undefined) {
|
|
333
378
|
if (!isRecord(doc.grounding) || typeof doc.grounding.query !== "string" || doc.grounding.query === "") {
|
|
@@ -377,6 +422,28 @@ function loadBundleFile(filePath) {
|
|
|
377
422
|
if (problems.length > 0) {
|
|
378
423
|
throw new RouteConfigError("invalid route bundle", { file: filePath, problems });
|
|
379
424
|
}
|
|
425
|
+
// Prompt-stability lint (WS-C step 4, spec §5.3): a non-fatal warning — a
|
|
426
|
+
// volatile interpolation before the stable tail defeats prompt caching. Only
|
|
427
|
+
// meaningful once the route opted into `cache: auto`, so scoped to it to keep
|
|
428
|
+
// the signal high (same console-warn posture as manifest dimension warnings).
|
|
429
|
+
if (cache) {
|
|
430
|
+
for (const w of (0, cache_lint_1.lintCacheTemplate)(systemTemplate)) {
|
|
431
|
+
console.warn(`modelrig: route "${String(route)}" (${filePath}): ${w.message}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// CE-6 sampling advisory: a sub-1.0 temperature on a Gemini-3 candidate loads
|
|
435
|
+
// and is sent as declared, but Google recommends 1.0 for the family — warn so
|
|
436
|
+
// the choice is deliberate, never silent (spec §3/§6).
|
|
437
|
+
const geminiTempWarning = (0, sampling_config_1.gemini3LowTemperatureWarning)(route, policy, candidates);
|
|
438
|
+
if (geminiTempWarning !== null) {
|
|
439
|
+
console.warn(`modelrig: ${geminiTempWarning}`);
|
|
440
|
+
}
|
|
441
|
+
// CE-2 A5 co-item: an array-root schema cannot use OpenAI strict enforcement —
|
|
442
|
+
// warn once at load so the incompatibility is deliberate, never silent.
|
|
443
|
+
const arrayRootWarning = arrayRootSchemaWarning(route, outputSchema);
|
|
444
|
+
if (arrayRootWarning !== null) {
|
|
445
|
+
console.warn(`modelrig: ${arrayRootWarning}`);
|
|
446
|
+
}
|
|
380
447
|
return Object.freeze({
|
|
381
448
|
route: route,
|
|
382
449
|
version: version,
|
|
@@ -390,12 +457,112 @@ function loadBundleFile(filePath) {
|
|
|
390
457
|
...(repair !== undefined ? { repair } : {}),
|
|
391
458
|
variants: Object.freeze(variants),
|
|
392
459
|
capture: capture === true,
|
|
460
|
+
cache,
|
|
393
461
|
...(groundingQuery !== undefined ? { groundingQuery } : {}),
|
|
394
462
|
// Network-effect instrumentation (spec WS6): computed once at load,
|
|
395
463
|
// stamped on every telemetry row for this route.
|
|
396
464
|
fingerprintCell: (0, fingerprint_1.fingerprintSchema)(outputSchema)?.cellKey ?? null,
|
|
397
465
|
});
|
|
398
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Merge two loaded route sets into one store — a UNION, collision is a loud
|
|
469
|
+
* error. Used to fold a deployment's bundled INTERNAL infra routes (e.g. the
|
|
470
|
+
* server's run-judge@v1 / analyst-summarize@v1) into the operator's configured
|
|
471
|
+
* routes without replacing them (index.ts createRig, extraRoutesDir). The
|
|
472
|
+
* reserved `@v<n>` infra namespace never collides with customer/demo routes;
|
|
473
|
+
* if it ever did, failing at boot is correct.
|
|
474
|
+
*/
|
|
475
|
+
/**
|
|
476
|
+
* Load a routes directory and OPTIONALLY union a second (deployment-bundled
|
|
477
|
+
* infra) directory into it — the one call createRig makes so the extra-routes
|
|
478
|
+
* merge does not grow index.ts. `extraDir` absent/empty ⇒ exactly loadRoutes.
|
|
479
|
+
*/
|
|
480
|
+
function loadRoutesWithExtra(routesDir, extraDir) {
|
|
481
|
+
const base = loadRoutes(routesDir);
|
|
482
|
+
if (extraDir === undefined || extraDir === "")
|
|
483
|
+
return base;
|
|
484
|
+
return mergeLoadedRoutes(base, loadRoutes(extraDir));
|
|
485
|
+
}
|
|
486
|
+
function mergeLoadedRoutes(primary, extra) {
|
|
487
|
+
const byName = new Map();
|
|
488
|
+
for (const b of primary.list())
|
|
489
|
+
byName.set(b.route, b);
|
|
490
|
+
for (const b of extra.list()) {
|
|
491
|
+
if (byName.has(b.route)) {
|
|
492
|
+
throw new RouteConfigError(`duplicate route "${b.route}" — an internal infra route collides with a configured route`);
|
|
493
|
+
}
|
|
494
|
+
byName.set(b.route, b);
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
get(route) {
|
|
498
|
+
const bundle = byName.get(route);
|
|
499
|
+
if (!bundle) {
|
|
500
|
+
throw new RouteConfigError(`unknown route "${route}" — known routes: [${[...byName.keys()].join(", ")}]`);
|
|
501
|
+
}
|
|
502
|
+
return bundle;
|
|
503
|
+
},
|
|
504
|
+
list() {
|
|
505
|
+
return Object.freeze([...byName.values()]);
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Structurally validate every route bundle in a directory WITHOUT requiring any
|
|
511
|
+
* provider key (CE-6 `modelrig validate`). Unlike {@link loadRoutes}, one bad
|
|
512
|
+
* bundle does NOT abort the rest — each file is loaded independently and its
|
|
513
|
+
* outcome collected, so a newbie sees every problem in one pass. Duplicate route
|
|
514
|
+
* handles across files are reported per the later file. This deliberately does
|
|
515
|
+
* NOT run the serveable-candidate gate (serveability.ts) — that needs keys and
|
|
516
|
+
* stays fail-closed for SERVING; validation only checks authoring.
|
|
517
|
+
*
|
|
518
|
+
* A missing directory is still a hard error (nothing to validate) — it throws
|
|
519
|
+
* the same teaching RouteConfigError as loadRoutes.
|
|
520
|
+
*/
|
|
521
|
+
function validateRoutesDir(routesDir) {
|
|
522
|
+
if (!(0, node_fs_1.existsSync)(routesDir)) {
|
|
523
|
+
throw new RouteConfigError(`routes directory not found: ${routesDir}\n\n` +
|
|
524
|
+
"There are no route bundles to validate here. Create your first with\n" +
|
|
525
|
+
" modelrig init . draft routes from the provider calls in this repo\n" +
|
|
526
|
+
"or point elsewhere with MODELRIG_ROUTES_DIR. Docs: https://modelrig.dev/quickstart.html");
|
|
527
|
+
}
|
|
528
|
+
const files = (0, node_fs_1.readdirSync)(routesDir)
|
|
529
|
+
.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"))
|
|
530
|
+
.sort()
|
|
531
|
+
.map((f) => (0, node_path_1.join)(routesDir, f));
|
|
532
|
+
const results = [];
|
|
533
|
+
const seen = new Map();
|
|
534
|
+
for (const file of files) {
|
|
535
|
+
try {
|
|
536
|
+
const bundle = loadBundleFile(file);
|
|
537
|
+
const prior = seen.get(bundle.route);
|
|
538
|
+
if (prior !== undefined) {
|
|
539
|
+
results.push({
|
|
540
|
+
file,
|
|
541
|
+
route: bundle.route,
|
|
542
|
+
ok: false,
|
|
543
|
+
problems: [`duplicate route "${bundle.route}" — already declared in ${prior}`],
|
|
544
|
+
});
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
seen.set(bundle.route, file);
|
|
548
|
+
results.push({ file, route: bundle.route, ok: true, problems: [] });
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
if (err instanceof RouteConfigError) {
|
|
552
|
+
results.push({
|
|
553
|
+
file,
|
|
554
|
+
route: null,
|
|
555
|
+
ok: false,
|
|
556
|
+
problems: err.problems.length > 0 ? err.problems : [err.message],
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
results.push({ file, route: null, ok: false, problems: [err.message] });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return results;
|
|
565
|
+
}
|
|
399
566
|
function loadRoutes(routesDir) {
|
|
400
567
|
if (!(0, node_fs_1.existsSync)(routesDir)) {
|
|
401
568
|
// First-run moment (Phase 5 WS4): this is the most likely error a new
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RESOLVED route bundle — http-gateway spec §13 (Phase D).
|
|
3
|
+
*
|
|
4
|
+
* A `RouteBundle` in memory already has its file references FOLLOWED and
|
|
5
|
+
* INLINED: `loader.ts` reads the schema file into `outputSchema` (a parsed JSON
|
|
6
|
+
* object) and the prompt file into `systemTemplate` (a string), and a MISSING
|
|
7
|
+
* file fails there with a teaching `RouteConfigError` — at deploy, where the
|
|
8
|
+
* developer is, never at request where the customer is (§13.1 rule 6). So the
|
|
9
|
+
* "resolved bundle" the exporter mirrors is a pure, serialisable PROJECTION of
|
|
10
|
+
* an already-loaded bundle; this module is the ONE definition of that shape and
|
|
11
|
+
* of the round-trip both sides use.
|
|
12
|
+
*
|
|
13
|
+
* exporter (SDK) resolveRouteBundle(bundle) → ResolvedRouteBundle + sha
|
|
14
|
+
* → mirrored onto public.routes.bundle / bundle_sha
|
|
15
|
+
* gateway (server) bundleFromResolved(mirrored) → RouteBundle
|
|
16
|
+
* → rig.runBundle(bundle, opts) → the SAME verified pipeline
|
|
17
|
+
*
|
|
18
|
+
* `bundle_sha` is the content hash of the CANONICAL serialisation (keys sorted
|
|
19
|
+
* recursively, array order preserved), so the same bundle always hashes the
|
|
20
|
+
* same and the console can flag a mirror that no longer matches git (§13.1
|
|
21
|
+
* rule 5). The hash is computed once, at sync time, from the resolved bundle.
|
|
22
|
+
*
|
|
23
|
+
* `fingerprintCell` is NOT carried on the wire: it is derived from
|
|
24
|
+
* `outputSchema` and recomputed on rebuild, so it can never drift from the
|
|
25
|
+
* schema it fingerprints.
|
|
26
|
+
*/
|
|
27
|
+
import type { LoadedRoutes } from "./loader";
|
|
28
|
+
import type { CandidateSpec, Preference, RepairPolicy, Requirement, RouteBundle, RoutePolicy, RouteVariant } from "../types/route";
|
|
29
|
+
/**
|
|
30
|
+
* The canonical, JSON-serialisable form of a resolved route bundle. Every field
|
|
31
|
+
* here defines how the route BEHAVES — the gateway needs all of them to drive
|
|
32
|
+
* the verified pipeline without the customer's filesystem. `fingerprintCell` is
|
|
33
|
+
* deliberately absent (derived from `outputSchema`, recomputed on rebuild).
|
|
34
|
+
*/
|
|
35
|
+
export interface ResolvedRouteBundle {
|
|
36
|
+
readonly route: string;
|
|
37
|
+
readonly version: number;
|
|
38
|
+
/** Resolved, parsed JSON Schema (null = unstructured route). Inlined at sync. */
|
|
39
|
+
readonly outputSchema: object | null;
|
|
40
|
+
readonly candidates: readonly CandidateSpec[];
|
|
41
|
+
readonly require: readonly Requirement[];
|
|
42
|
+
readonly prefer: readonly Preference[];
|
|
43
|
+
/** Raw template text — the prompt FILE, inlined at sync. */
|
|
44
|
+
readonly systemTemplate: string;
|
|
45
|
+
readonly variables: readonly string[];
|
|
46
|
+
readonly policy: RoutePolicy;
|
|
47
|
+
readonly repair?: RepairPolicy;
|
|
48
|
+
readonly variants: readonly RouteVariant[];
|
|
49
|
+
readonly capture: boolean;
|
|
50
|
+
readonly groundingQuery?: string;
|
|
51
|
+
}
|
|
52
|
+
/** Project an already-loaded (resolved) bundle to its serialisable form. Pure —
|
|
53
|
+
* no filesystem; the loader already followed every file reference. */
|
|
54
|
+
export declare function resolveRouteBundle(bundle: RouteBundle): ResolvedRouteBundle;
|
|
55
|
+
/** The canonical serialisation whose hash becomes `bundle_sha`. */
|
|
56
|
+
export declare function canonicalBundleJson(bundle: ResolvedRouteBundle): string;
|
|
57
|
+
/** Content hash of the canonical bundle — `sha256:<hex>`. Self-describing so a
|
|
58
|
+
* future algorithm change is visible in the stored value rather than silent. */
|
|
59
|
+
export declare function hashResolvedBundle(bundle: ResolvedRouteBundle): string;
|
|
60
|
+
/**
|
|
61
|
+
* Rebuild a runnable `RouteBundle` from a mirrored resolved bundle. Recomputes
|
|
62
|
+
* `fingerprintCell` from `outputSchema` (never trusted from the wire) and
|
|
63
|
+
* freezes to match loader output. Throws `RouteConfigError` — the loader's own
|
|
64
|
+
* teaching class — if the mirrored payload is structurally unusable, so a
|
|
65
|
+
* corrupt mirror row surfaces as a teaching error, not a downstream crash.
|
|
66
|
+
*/
|
|
67
|
+
export declare function bundleFromResolved(raw: unknown): RouteBundle;
|
|
68
|
+
/**
|
|
69
|
+
* A `LoadedRoutes` holding exactly ONE bundle — the seam that lets the verified
|
|
70
|
+
* pipeline run over a mirrored bundle without a local routes directory.
|
|
71
|
+
*
|
|
72
|
+
* `run()`/`runStream()` resolve a route only through `deps.routes.get(name)`, so
|
|
73
|
+
* swapping in this store (and nothing else) runs the WHOLE existing pipeline —
|
|
74
|
+
* validate, repair, envelope, telemetry, ZDR (G-3), routing controls (G-2) —
|
|
75
|
+
* over the provided bundle. Asking for any other name is a teaching error, so a
|
|
76
|
+
* mismatched slug can never silently resolve to the wrong route.
|
|
77
|
+
*/
|
|
78
|
+
export declare function singleRouteStore(bundle: RouteBundle): LoadedRoutes;
|
|
79
|
+
//# sourceMappingURL=resolved-bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolved-bundle.d.ts","sourceRoot":"","sources":["../../src/routes/resolved-bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,4DAA4D;IAC5D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;sEACsE;AACtE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAgB3E;AAiBD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEvE;AAED;gFACgF;AAChF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAsC5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAclE"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The RESOLVED route bundle — http-gateway spec §13 (Phase D).
|
|
4
|
+
*
|
|
5
|
+
* A `RouteBundle` in memory already has its file references FOLLOWED and
|
|
6
|
+
* INLINED: `loader.ts` reads the schema file into `outputSchema` (a parsed JSON
|
|
7
|
+
* object) and the prompt file into `systemTemplate` (a string), and a MISSING
|
|
8
|
+
* file fails there with a teaching `RouteConfigError` — at deploy, where the
|
|
9
|
+
* developer is, never at request where the customer is (§13.1 rule 6). So the
|
|
10
|
+
* "resolved bundle" the exporter mirrors is a pure, serialisable PROJECTION of
|
|
11
|
+
* an already-loaded bundle; this module is the ONE definition of that shape and
|
|
12
|
+
* of the round-trip both sides use.
|
|
13
|
+
*
|
|
14
|
+
* exporter (SDK) resolveRouteBundle(bundle) → ResolvedRouteBundle + sha
|
|
15
|
+
* → mirrored onto public.routes.bundle / bundle_sha
|
|
16
|
+
* gateway (server) bundleFromResolved(mirrored) → RouteBundle
|
|
17
|
+
* → rig.runBundle(bundle, opts) → the SAME verified pipeline
|
|
18
|
+
*
|
|
19
|
+
* `bundle_sha` is the content hash of the CANONICAL serialisation (keys sorted
|
|
20
|
+
* recursively, array order preserved), so the same bundle always hashes the
|
|
21
|
+
* same and the console can flag a mirror that no longer matches git (§13.1
|
|
22
|
+
* rule 5). The hash is computed once, at sync time, from the resolved bundle.
|
|
23
|
+
*
|
|
24
|
+
* `fingerprintCell` is NOT carried on the wire: it is derived from
|
|
25
|
+
* `outputSchema` and recomputed on rebuild, so it can never drift from the
|
|
26
|
+
* schema it fingerprints.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.resolveRouteBundle = resolveRouteBundle;
|
|
30
|
+
exports.canonicalBundleJson = canonicalBundleJson;
|
|
31
|
+
exports.hashResolvedBundle = hashResolvedBundle;
|
|
32
|
+
exports.bundleFromResolved = bundleFromResolved;
|
|
33
|
+
exports.singleRouteStore = singleRouteStore;
|
|
34
|
+
const node_crypto_1 = require("node:crypto");
|
|
35
|
+
const fingerprint_1 = require("../registry/fingerprint");
|
|
36
|
+
const loader_1 = require("./loader");
|
|
37
|
+
/** Project an already-loaded (resolved) bundle to its serialisable form. Pure —
|
|
38
|
+
* no filesystem; the loader already followed every file reference. */
|
|
39
|
+
function resolveRouteBundle(bundle) {
|
|
40
|
+
return {
|
|
41
|
+
route: bundle.route,
|
|
42
|
+
version: bundle.version,
|
|
43
|
+
outputSchema: bundle.outputSchema,
|
|
44
|
+
candidates: bundle.candidates,
|
|
45
|
+
require: bundle.require,
|
|
46
|
+
prefer: bundle.prefer,
|
|
47
|
+
systemTemplate: bundle.systemTemplate,
|
|
48
|
+
variables: bundle.variables,
|
|
49
|
+
policy: bundle.policy,
|
|
50
|
+
...(bundle.repair !== undefined ? { repair: bundle.repair } : {}),
|
|
51
|
+
variants: bundle.variants,
|
|
52
|
+
capture: bundle.capture,
|
|
53
|
+
...(bundle.groundingQuery !== undefined ? { groundingQuery: bundle.groundingQuery } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Deterministic JSON: object keys sorted recursively, array order preserved.
|
|
57
|
+
* The whole point is that the SAME bundle always produces the SAME bytes, so
|
|
58
|
+
* the content hash is stable across processes and machines. */
|
|
59
|
+
function canonicalize(value) {
|
|
60
|
+
if (Array.isArray(value))
|
|
61
|
+
return value.map(canonicalize);
|
|
62
|
+
if (value !== null && typeof value === "object") {
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const key of Object.keys(value).sort()) {
|
|
65
|
+
out[key] = canonicalize(value[key]);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
/** The canonical serialisation whose hash becomes `bundle_sha`. */
|
|
72
|
+
function canonicalBundleJson(bundle) {
|
|
73
|
+
return JSON.stringify(canonicalize(bundle));
|
|
74
|
+
}
|
|
75
|
+
/** Content hash of the canonical bundle — `sha256:<hex>`. Self-describing so a
|
|
76
|
+
* future algorithm change is visible in the stored value rather than silent. */
|
|
77
|
+
function hashResolvedBundle(bundle) {
|
|
78
|
+
return `sha256:${(0, node_crypto_1.createHash)("sha256").update(canonicalBundleJson(bundle)).digest("hex")}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Rebuild a runnable `RouteBundle` from a mirrored resolved bundle. Recomputes
|
|
82
|
+
* `fingerprintCell` from `outputSchema` (never trusted from the wire) and
|
|
83
|
+
* freezes to match loader output. Throws `RouteConfigError` — the loader's own
|
|
84
|
+
* teaching class — if the mirrored payload is structurally unusable, so a
|
|
85
|
+
* corrupt mirror row surfaces as a teaching error, not a downstream crash.
|
|
86
|
+
*/
|
|
87
|
+
function bundleFromResolved(raw) {
|
|
88
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
89
|
+
throw new loader_1.RouteConfigError("mirrored route bundle is not an object");
|
|
90
|
+
}
|
|
91
|
+
const b = raw;
|
|
92
|
+
if (typeof b.route !== "string" || b.route === "") {
|
|
93
|
+
throw new loader_1.RouteConfigError("mirrored route bundle is missing `route`");
|
|
94
|
+
}
|
|
95
|
+
if (typeof b.version !== "number" || !Number.isInteger(b.version)) {
|
|
96
|
+
throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing a valid \`version\``);
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(b.candidates) || b.candidates.length === 0) {
|
|
99
|
+
throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" has no candidates`);
|
|
100
|
+
}
|
|
101
|
+
if (typeof b.systemTemplate !== "string") {
|
|
102
|
+
throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing \`systemTemplate\``);
|
|
103
|
+
}
|
|
104
|
+
if (b.policy === undefined || b.policy === null) {
|
|
105
|
+
throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing \`policy\``);
|
|
106
|
+
}
|
|
107
|
+
const outputSchema = b.outputSchema ?? null;
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
route: b.route,
|
|
110
|
+
version: b.version,
|
|
111
|
+
outputSchema: outputSchema === null ? null : Object.freeze(outputSchema),
|
|
112
|
+
candidates: Object.freeze([...b.candidates]),
|
|
113
|
+
require: Object.freeze([...(b.require ?? [])]),
|
|
114
|
+
prefer: Object.freeze([...(b.prefer ?? [])]),
|
|
115
|
+
systemTemplate: b.systemTemplate,
|
|
116
|
+
variables: Object.freeze([...(b.variables ?? [])]),
|
|
117
|
+
policy: b.policy,
|
|
118
|
+
...(b.repair !== undefined ? { repair: b.repair } : {}),
|
|
119
|
+
variants: Object.freeze([...(b.variants ?? [])]),
|
|
120
|
+
capture: b.capture === true,
|
|
121
|
+
...(b.groundingQuery !== undefined ? { groundingQuery: b.groundingQuery } : {}),
|
|
122
|
+
// Recomputed, never trusted from the wire — cannot drift from the schema.
|
|
123
|
+
fingerprintCell: (0, fingerprint_1.fingerprintSchema)(outputSchema)?.cellKey ?? null,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A `LoadedRoutes` holding exactly ONE bundle — the seam that lets the verified
|
|
128
|
+
* pipeline run over a mirrored bundle without a local routes directory.
|
|
129
|
+
*
|
|
130
|
+
* `run()`/`runStream()` resolve a route only through `deps.routes.get(name)`, so
|
|
131
|
+
* swapping in this store (and nothing else) runs the WHOLE existing pipeline —
|
|
132
|
+
* validate, repair, envelope, telemetry, ZDR (G-3), routing controls (G-2) —
|
|
133
|
+
* over the provided bundle. Asking for any other name is a teaching error, so a
|
|
134
|
+
* mismatched slug can never silently resolve to the wrong route.
|
|
135
|
+
*/
|
|
136
|
+
function singleRouteStore(bundle) {
|
|
137
|
+
return {
|
|
138
|
+
get(route) {
|
|
139
|
+
if (route !== bundle.route) {
|
|
140
|
+
throw new loader_1.RouteConfigError(`route "${route}" is not the mirrored bundle "${bundle.route}"`);
|
|
141
|
+
}
|
|
142
|
+
return bundle;
|
|
143
|
+
},
|
|
144
|
+
list() {
|
|
145
|
+
return Object.freeze([bundle]);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|