modelrig 0.9.0 → 0.11.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.
Files changed (231) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +51 -27
  3. package/data/PROVENANCE.md +14 -0
  4. package/data/pricing-map.json +26 -0
  5. package/dist/adapters/attempt-meta-map.d.ts +8 -1
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
  7. package/dist/adapters/attempt-meta-map.js +4 -0
  8. package/dist/adapters/build-adapters.js +2 -2
  9. package/dist/adapters/gemini.d.ts +28 -1
  10. package/dist/adapters/gemini.d.ts.map +1 -1
  11. package/dist/adapters/gemini.js +38 -9
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +53 -4
  14. package/dist/adapters/openai-compat.d.ts +8 -2
  15. package/dist/adapters/openai-compat.d.ts.map +1 -1
  16. package/dist/adapters/openai-compat.js +8 -1
  17. package/dist/adapters/openai-stream.d.ts +10 -2
  18. package/dist/adapters/openai-stream.d.ts.map +1 -1
  19. package/dist/adapters/openai-stream.js +5 -1
  20. package/dist/adapters/openai.d.ts +10 -1
  21. package/dist/adapters/openai.d.ts.map +1 -1
  22. package/dist/adapters/openai.js +57 -7
  23. package/dist/adapters/responses-api.d.ts +61 -0
  24. package/dist/adapters/responses-api.d.ts.map +1 -0
  25. package/dist/adapters/responses-api.js +124 -0
  26. package/dist/artifacts/artifact.d.ts +20 -0
  27. package/dist/artifacts/artifact.d.ts.map +1 -1
  28. package/dist/artifacts/artifact.js +207 -171
  29. package/dist/artifacts/index.d.ts +3 -1
  30. package/dist/artifacts/index.d.ts.map +1 -1
  31. package/dist/artifacts/index.js +8 -1
  32. package/dist/bakeoff/groundedness.d.ts +58 -0
  33. package/dist/bakeoff/groundedness.d.ts.map +1 -0
  34. package/dist/bakeoff/groundedness.js +165 -0
  35. package/dist/bakeoff/quality.d.ts +14 -1
  36. package/dist/bakeoff/quality.d.ts.map +1 -1
  37. package/dist/bakeoff/quality.js +10 -0
  38. package/dist/bakeoff/replay.d.ts.map +1 -1
  39. package/dist/bakeoff/replay.js +4 -0
  40. package/dist/cli.d.ts.map +1 -1
  41. package/dist/cli.js +8 -0
  42. package/dist/config.d.ts +31 -0
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +23 -0
  45. package/dist/doctor-cli.d.ts +7 -0
  46. package/dist/doctor-cli.d.ts.map +1 -0
  47. package/dist/doctor-cli.js +68 -0
  48. package/dist/doctor-preflight.d.ts +64 -0
  49. package/dist/doctor-preflight.d.ts.map +1 -0
  50. package/dist/doctor-preflight.js +167 -0
  51. package/dist/feedback/coverage.d.ts.map +1 -1
  52. package/dist/feedback/coverage.js +9 -2
  53. package/dist/feedback/grade.d.ts +8 -1
  54. package/dist/feedback/grade.d.ts.map +1 -1
  55. package/dist/feedback/grade.js +4 -0
  56. package/dist/gateway/http-protocol.d.ts +19 -0
  57. package/dist/gateway/http-protocol.d.ts.map +1 -1
  58. package/dist/gateway/serve-client.d.ts +99 -0
  59. package/dist/gateway/serve-client.d.ts.map +1 -0
  60. package/dist/gateway/serve-client.js +360 -0
  61. package/dist/index.d.ts +31 -3
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +71 -8
  64. package/dist/prompts/manifest.d.ts +128 -0
  65. package/dist/prompts/manifest.d.ts.map +1 -0
  66. package/dist/prompts/manifest.js +214 -0
  67. package/dist/prompts/prompt-fetch.d.ts +84 -0
  68. package/dist/prompts/prompt-fetch.d.ts.map +1 -0
  69. package/dist/prompts/prompt-fetch.js +120 -0
  70. package/dist/prompts/prompt-resolve.d.ts +76 -0
  71. package/dist/prompts/prompt-resolve.d.ts.map +1 -0
  72. package/dist/prompts/prompt-resolve.js +93 -0
  73. package/dist/prompts/slot-resolver.d.ts +91 -0
  74. package/dist/prompts/slot-resolver.d.ts.map +1 -0
  75. package/dist/prompts/slot-resolver.js +135 -0
  76. package/dist/prompts/tools-register.d.ts +60 -0
  77. package/dist/prompts/tools-register.d.ts.map +1 -0
  78. package/dist/prompts/tools-register.js +48 -0
  79. package/dist/prompts/version-hash.d.ts +29 -0
  80. package/dist/prompts/version-hash.d.ts.map +1 -0
  81. package/dist/prompts/version-hash.js +54 -0
  82. package/dist/prompts-export-cli.d.ts +21 -0
  83. package/dist/prompts-export-cli.d.ts.map +1 -0
  84. package/dist/prompts-export-cli.js +130 -0
  85. package/dist/registry/cache-policy.d.ts.map +1 -1
  86. package/dist/registry/cache-policy.js +22 -0
  87. package/dist/registry/pricing-shape.d.ts +38 -8
  88. package/dist/registry/pricing-shape.d.ts.map +1 -1
  89. package/dist/registry/pricing-shape.js +40 -12
  90. package/dist/registry/pricing.d.ts +6 -5
  91. package/dist/registry/pricing.d.ts.map +1 -1
  92. package/dist/registry/pricing.js +9 -5
  93. package/dist/routes/destination-loader.d.ts +16 -0
  94. package/dist/routes/destination-loader.d.ts.map +1 -0
  95. package/dist/routes/destination-loader.js +29 -0
  96. package/dist/routes/draft.d.ts +7 -0
  97. package/dist/routes/draft.d.ts.map +1 -1
  98. package/dist/routes/draft.js +12 -0
  99. package/dist/routes/loader.d.ts.map +1 -1
  100. package/dist/routes/loader.js +27 -54
  101. package/dist/routes/output-loader.d.ts +42 -0
  102. package/dist/routes/output-loader.d.ts.map +1 -0
  103. package/dist/routes/output-loader.js +112 -0
  104. package/dist/routes/prompt-loader.d.ts +49 -0
  105. package/dist/routes/prompt-loader.d.ts.map +1 -0
  106. package/dist/routes/prompt-loader.js +113 -0
  107. package/dist/routes/resolved-bundle.d.ts +19 -0
  108. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  109. package/dist/routes/resolved-bundle.js +24 -0
  110. package/dist/routes/screen-loader.d.ts +1 -1
  111. package/dist/routes/screen-loader.d.ts.map +1 -1
  112. package/dist/routes/screen-loader.js +8 -1
  113. package/dist/run/accept.d.ts +3 -0
  114. package/dist/run/accept.d.ts.map +1 -1
  115. package/dist/run/accept.js +1 -0
  116. package/dist/run/cost.d.ts +31 -2
  117. package/dist/run/cost.d.ts.map +1 -1
  118. package/dist/run/cost.js +57 -2
  119. package/dist/run/destination.d.ts +39 -0
  120. package/dist/run/destination.d.ts.map +1 -0
  121. package/dist/run/destination.js +45 -0
  122. package/dist/run/dispatch.d.ts +25 -3
  123. package/dist/run/dispatch.d.ts.map +1 -1
  124. package/dist/run/dispatch.js +26 -4
  125. package/dist/run/raw-defaults.d.ts.map +1 -1
  126. package/dist/run/raw-defaults.js +6 -1
  127. package/dist/run/raw-finalize.d.ts +96 -0
  128. package/dist/run/raw-finalize.d.ts.map +1 -0
  129. package/dist/run/raw-finalize.js +283 -0
  130. package/dist/run/raw-knobs.d.ts +36 -0
  131. package/dist/run/raw-knobs.d.ts.map +1 -0
  132. package/dist/run/raw-knobs.js +99 -0
  133. package/dist/run/raw-request.d.ts +24 -0
  134. package/dist/run/raw-request.d.ts.map +1 -0
  135. package/dist/run/raw-request.js +69 -0
  136. package/dist/run/raw-schema.d.ts +76 -0
  137. package/dist/run/raw-schema.d.ts.map +1 -0
  138. package/dist/run/raw-schema.js +166 -0
  139. package/dist/run/raw-search-cost.d.ts +49 -0
  140. package/dist/run/raw-search-cost.d.ts.map +1 -0
  141. package/dist/run/raw-search-cost.js +79 -0
  142. package/dist/run/raw-support.d.ts +64 -6
  143. package/dist/run/raw-support.d.ts.map +1 -1
  144. package/dist/run/raw-support.js +84 -11
  145. package/dist/run/raw.d.ts +25 -26
  146. package/dist/run/raw.d.ts.map +1 -1
  147. package/dist/run/raw.js +30 -351
  148. package/dist/run/record-attempt.d.ts +8 -0
  149. package/dist/run/record-attempt.d.ts.map +1 -1
  150. package/dist/run/record-attempt.js +26 -12
  151. package/dist/run/record-call.d.ts +104 -0
  152. package/dist/run/record-call.d.ts.map +1 -0
  153. package/dist/run/record-call.js +277 -0
  154. package/dist/run/repair-attempt.d.ts +68 -0
  155. package/dist/run/repair-attempt.d.ts.map +1 -0
  156. package/dist/run/repair-attempt.js +180 -0
  157. package/dist/run/resolve-destination.d.ts +49 -0
  158. package/dist/run/resolve-destination.d.ts.map +1 -0
  159. package/dist/run/resolve-destination.js +76 -0
  160. package/dist/run/routing-docs.d.ts.map +1 -1
  161. package/dist/run/routing-docs.js +9 -4
  162. package/dist/run/run.d.ts +30 -1
  163. package/dist/run/run.d.ts.map +1 -1
  164. package/dist/run/run.js +75 -148
  165. package/dist/run/telemetry-row.d.ts +92 -0
  166. package/dist/run/telemetry-row.d.ts.map +1 -0
  167. package/dist/run/telemetry-row.js +81 -0
  168. package/dist/run/validate.d.ts +12 -1
  169. package/dist/run/validate.d.ts.map +1 -1
  170. package/dist/run/validate.js +31 -0
  171. package/dist/run/web-search-pricing.d.ts +43 -0
  172. package/dist/run/web-search-pricing.d.ts.map +1 -0
  173. package/dist/run/web-search-pricing.js +56 -0
  174. package/dist/settings/grading-guide.d.ts +27 -1
  175. package/dist/settings/grading-guide.d.ts.map +1 -1
  176. package/dist/status.d.ts +6 -0
  177. package/dist/status.d.ts.map +1 -1
  178. package/dist/status.js +4 -1
  179. package/dist/telemetry/artifact-store.d.ts.map +1 -1
  180. package/dist/telemetry/artifact-store.js +11 -0
  181. package/dist/telemetry/attempt-meta.d.ts +66 -1
  182. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  183. package/dist/telemetry/attempt-meta.js +27 -1
  184. package/dist/telemetry/exporter-setup.d.ts +2 -0
  185. package/dist/telemetry/exporter-setup.d.ts.map +1 -1
  186. package/dist/telemetry/exporter-setup.js +6 -1
  187. package/dist/telemetry/grade-mappers.d.ts +5 -2
  188. package/dist/telemetry/grade-mappers.d.ts.map +1 -1
  189. package/dist/telemetry/grade-mappers.js +8 -2
  190. package/dist/telemetry/grades-sql.d.ts +4 -1
  191. package/dist/telemetry/grades-sql.d.ts.map +1 -1
  192. package/dist/telemetry/grades-sql.js +26 -3
  193. package/dist/telemetry/ingest-protocol.d.ts +26 -2
  194. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  195. package/dist/telemetry/ingest-protocol.js +32 -29
  196. package/dist/telemetry/lanes.d.ts +31 -0
  197. package/dist/telemetry/lanes.d.ts.map +1 -0
  198. package/dist/telemetry/lanes.js +41 -0
  199. package/dist/telemetry/org-identity.d.ts +47 -0
  200. package/dist/telemetry/org-identity.d.ts.map +1 -0
  201. package/dist/telemetry/org-identity.js +100 -0
  202. package/dist/telemetry/route-registration.d.ts +48 -0
  203. package/dist/telemetry/route-registration.d.ts.map +1 -0
  204. package/dist/telemetry/route-registration.js +77 -0
  205. package/dist/telemetry/sinks.d.ts +10 -1
  206. package/dist/telemetry/sinks.d.ts.map +1 -1
  207. package/dist/telemetry/sinks.js +52 -6
  208. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  209. package/dist/telemetry/store-migrations.js +13 -0
  210. package/dist/tenancy/api-key-format.d.ts +16 -1
  211. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  212. package/dist/tenancy/api-key-format.js +17 -0
  213. package/dist/types/provider.d.ts +20 -3
  214. package/dist/types/provider.d.ts.map +1 -1
  215. package/dist/types/provider.js +1 -0
  216. package/dist/types/route.d.ts +76 -0
  217. package/dist/types/route.d.ts.map +1 -1
  218. package/dist/types/telemetry.d.ts +30 -2
  219. package/dist/types/telemetry.d.ts.map +1 -1
  220. package/docs/agent-skill/SKILL.md +75 -5
  221. package/docs/instrumentation.md +98 -36
  222. package/docs/llms.txt +96 -16
  223. package/docs/managed-serving.md +150 -0
  224. package/docs/mcp-oracle.md +75 -2
  225. package/docs/migration-playbook.md +5 -4
  226. package/docs/quickstart.md +169 -83
  227. package/package.json +3 -2
  228. package/registry/registry.json +82 -0
  229. package/dist/feedback/feedback.d.ts +0 -35
  230. package/dist/feedback/feedback.d.ts.map +0 -1
  231. package/dist/feedback/feedback.js +0 -39
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /**
3
+ * R2 (GitLab issue #6) — `responseSchema` on the raw lane: the schema helpers
4
+ * the guard (raw-knobs.ts) and the finalize path (raw-finalize.ts) share.
5
+ *
6
+ * What the knob IS: a JSON Schema the caller declares on `rig.runRaw`. It is
7
+ * forwarded as the provider's NATIVE structured-output directive (the adapters'
8
+ * existing `AdapterRequest.outputSchema` path — gemini `responseJsonSchema`,
9
+ * openai/grok strict `json_schema`, anthropic a forced tool call) AND the
10
+ * returned text is checked client-side by the SAME Ajv instance the routed lane
11
+ * uses (`createModelrigAjv`, 2020-12 + draft-07). A miss is `content_invalid`.
12
+ *
13
+ * What the knob is NOT: a ladder. The raw lane never repairs, never retries,
14
+ * and never falls through — a schema miss is a terminal, billed `content_invalid`
15
+ * row and a thrown `RigFailureError` whose `fixHint` names the first errors. F4
16
+ * is narrowed, not reversed: `meta.validated` stays `false` because no ladder
17
+ * stands behind the check (routing-docs.ts B2 states the boundary).
18
+ *
19
+ * Why its own file: raw-finalize.ts and raw-knobs.ts both sit under the 700-LOC
20
+ * cap (P1) and neither should pull Ajv + hashing into its header story; this
21
+ * module keeps the raw lane's no-route/registry dependency invariant (it imports
22
+ * only the shared Ajv factory and failure types).
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.RAW_SCHEMA_MAX_ERRORS = void 0;
26
+ exports.canonicalJson = canonicalJson;
27
+ exports.rawSchemaSha = rawSchemaSha;
28
+ exports.schemaRootType = schemaRootType;
29
+ exports.assertRawResponseSchemaShape = assertRawResponseSchemaShape;
30
+ exports.validateRawOutput = validateRawOutput;
31
+ exports.rawSchemaMismatchFailure = rawSchemaMismatchFailure;
32
+ const node_crypto_1 = require("node:crypto");
33
+ const ajv_1 = require("./ajv");
34
+ const failures_1 = require("../types/failures");
35
+ /** Canonical JSON — object keys sorted recursively, arrays in order, no
36
+ * whitespace — so two authored spellings of ONE schema hash identically and a
37
+ * reordered key never reads as drift. Pure; exported for the hash tests. */
38
+ function canonicalJson(value) {
39
+ if (Array.isArray(value))
40
+ return `[${value.map(canonicalJson).join(",")}]`;
41
+ if (value !== null && typeof value === "object") {
42
+ const entries = Object.keys(value)
43
+ .sort()
44
+ .map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`);
45
+ return `{${entries.join(",")}}`;
46
+ }
47
+ return JSON.stringify(value);
48
+ }
49
+ /** `sha256:<hex>` of the canonical schema JSON — the value stamped on
50
+ * `inferences.meta.schema_sha` (a hash, never content; meta-safe by shape). */
51
+ function rawSchemaSha(schema) {
52
+ return `sha256:${(0, node_crypto_1.createHash)("sha256").update(canonicalJson(schema), "utf8").digest("hex")}`;
53
+ }
54
+ /** Root `type` of a schema when it declares one (the openai strict-mode
55
+ * object-root check reads this). `undefined` when absent or non-string. */
56
+ function schemaRootType(schema) {
57
+ const t = schema.type;
58
+ return typeof t === "string" ? t : undefined;
59
+ }
60
+ /** Compiled validators, keyed by schema sha — a pipeline re-declares the same
61
+ * schema on every call, and Ajv compilation is the expensive step. Bounded:
62
+ * the raw lane sees a handful of distinct schemas per process. */
63
+ const compiled = new Map();
64
+ const COMPILED_CAP = 256;
65
+ function compileRawSchema(schema, sha) {
66
+ const hit = compiled.get(sha);
67
+ if (hit !== undefined)
68
+ return hit;
69
+ // A fresh instance per schema: Ajv de-duplicates on `$id`, so two schemas
70
+ // sharing an `$id` on one instance would throw "already exists".
71
+ const fn = (0, ajv_1.createModelrigAjv)().compile(schema);
72
+ if (compiled.size >= COMPILED_CAP)
73
+ compiled.clear();
74
+ compiled.set(sha, fn);
75
+ return fn;
76
+ }
77
+ /**
78
+ * Pre-dispatch SHAPE check (called from the knob guard): the schema must be a
79
+ * plain JSON-Schema object that Ajv can compile. Anything else throws the
80
+ * existing terminal class `invariant_violation` BEFORE any adapter is built —
81
+ * no meter, no provider call, no telemetry row — with a teaching fixHint. Also
82
+ * the one place the openai strict-root warning lives: strict `json_schema`
83
+ * requires an object root; a non-object root is WARNED (the provider enforces
84
+ * and its rejection is classified teachably by the adapter), never silently
85
+ * rewritten. Idempotent; side-effect-free apart from the warning + compile cache.
86
+ */
87
+ function assertRawResponseSchemaShape(input) {
88
+ const schema = input.responseSchema;
89
+ if (schema === undefined)
90
+ return;
91
+ const base = { class: "invariant_violation", provider: input.provider, model: input.model };
92
+ if (input.responseFormat !== undefined) {
93
+ throw new failures_1.RigFailureError({
94
+ ...base,
95
+ message: "responseSchema and responseFormat were both declared on the raw lane",
96
+ fixHint: "declare ONE of responseSchema (a JSON Schema — implies JSON output) or responseFormat: \"json_object\" (mime-shaping only); remove the other",
97
+ });
98
+ }
99
+ if (schema === null || typeof schema !== "object" || Array.isArray(schema)) {
100
+ throw new failures_1.RigFailureError({
101
+ ...base,
102
+ message: "responseSchema on the raw lane must be a JSON Schema object",
103
+ fixHint: "pass the schema as a plain object (e.g. { type: \"object\", properties: {…}, required: […] }), not a string, array, or null",
104
+ });
105
+ }
106
+ const sha = rawSchemaSha(schema);
107
+ try {
108
+ compileRawSchema(schema, sha);
109
+ }
110
+ catch (err) {
111
+ const reason = err instanceof Error ? err.message : String(err);
112
+ throw new failures_1.RigFailureError({
113
+ ...base,
114
+ message: `responseSchema on the raw lane does not compile: ${reason}`,
115
+ fixHint: "fix the schema so Ajv (JSON Schema 2020-12, draft-07 accepted; `format` annotation-only) compiles it — the same instance validates route schemas, so a schema that loads on a route loads here",
116
+ });
117
+ }
118
+ if (input.provider === "openai" && schemaRootType(schema) !== "object") {
119
+ console.warn(`[modelrig] responseSchema on openai/${input.model}: strict json_schema requires an object root (got type: ${schemaRootType(schema) ?? "absent"}); the provider will reject it — wrap the schema in { type: "object", properties: { items: … } }`);
120
+ }
121
+ }
122
+ /** The one terminal-error rendering the raw lane makes (parity with run.ts's
123
+ * `content_invalid` message shape); exported for the finalize tests. */
124
+ exports.RAW_SCHEMA_MAX_ERRORS = 5;
125
+ /**
126
+ * Validate the raw output text against the declared schema with the shared
127
+ * Ajv. Parity with the routed lane's `validateOutput`: a non-JSON body is a
128
+ * schema miss (one `parse` error), and Ajv errors render as `instancePath
129
+ * message`. Never throws — the shape check above already compiled the schema;
130
+ * a late compile failure (cache cleared + a schema that stopped compiling, which
131
+ * cannot happen for the same object) is reported as a miss, not a crash.
132
+ */
133
+ function validateRawOutput(schema, text) {
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(text);
137
+ }
138
+ catch {
139
+ return { ok: false, errors: ["output is not valid JSON (the provider returned non-JSON text)"] };
140
+ }
141
+ let fn;
142
+ try {
143
+ fn = compileRawSchema(schema, rawSchemaSha(schema));
144
+ }
145
+ catch (err) {
146
+ return { ok: false, errors: [`schema did not compile: ${err instanceof Error ? err.message : String(err)}`] };
147
+ }
148
+ if (fn(parsed))
149
+ return { ok: true };
150
+ const errors = (fn.errors ?? []).map((e) => `${e.instancePath || "/"} ${e.message ?? "invalid"}`);
151
+ return { ok: false, errors: errors.length > 0 ? errors : ["output failed schema validation"] };
152
+ }
153
+ /** The `content_invalid` failure a raw schema miss raises — the EXISTING class
154
+ * (its table teaching + docsUrl apply via `teach()`), with a raw-lane fixHint:
155
+ * this lane never repairs, so the fix is the prompt/schema or a route. */
156
+ function rawSchemaMismatchFailure(input, errors) {
157
+ const shown = errors.slice(0, exports.RAW_SCHEMA_MAX_ERRORS);
158
+ const more = errors.length > shown.length ? ` (+${errors.length - shown.length} more)` : "";
159
+ return {
160
+ class: "content_invalid",
161
+ message: `raw output failed responseSchema validation: ${shown.join("; ")}${more}`,
162
+ provider: input.provider,
163
+ model: input.model,
164
+ fixHint: "the raw lane validates but never repairs or retries — tighten the prompt or loosen the schema, or graduate this call to a route (policy.retries.content_invalid + a repair rung) if you want ModelRig to recover it",
165
+ };
166
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Native-search cost row for the RAW lane (Native Web Search lane, spec N3) —
3
+ * ZERO DDL. On a successful native-grounded raw attempt (openai/grok Responses
4
+ * API), the model's token cost rides the normal raw row while the `web_search`
5
+ * tool SURCHARGE rides one extra, distinct, legible line here — exactly like the
6
+ * inject rung's synthetic search-cost row (`dispatch.ts recordSearchCostRow`),
7
+ * but shaped for the raw lane (which has no `RouteBundle` / `LadderKeyMinter`):
8
+ * the row records the REAL provider id and a `web_search` pseudo-model under
9
+ * `rung:"search"`, reusing the existing `inferences` telemetry family — no new
10
+ * table, no schema change (spec §2, conservative-schema).
11
+ *
12
+ * Search cost is never folded into token cost. The rate is a static, drift-locked
13
+ * ESTIMATE (`WEB_SEARCH_PRICING`, `web-search-pricing.ts`) — placeholders until the
14
+ * post-merge live-invoice reconciliation (spec §6 live gate) un-tags it.
15
+ *
16
+ * Extracted into its own module (not inlined in `raw.ts`) to keep `raw.ts` under
17
+ * its 700-LOC ceiling (P-cap); the recorder mirrors `recordRawRow`'s minimal
18
+ * telemetry shape so a raw search-cost line reads identically to a raw token line.
19
+ */
20
+ import type { ProviderId } from "../types/provider";
21
+ import type { RunTags } from "../types/route";
22
+ import type { TelemetryStore } from "../types/telemetry";
23
+ /** The `web_search` pseudo-model the native-search cost row records under (spec
24
+ * N3) — parallels the inject rung's synthetic search provider, but the ROW's
25
+ * provider is the real dispatch provider so the surcharge is attributed to it. */
26
+ export declare const NATIVE_WEB_SEARCH_MODEL = "web_search";
27
+ export interface RawSearchCostArgs {
28
+ /** The real dispatch provider (openai / grok) — the row's `provider`. An
29
+ * OBS-1 recorded call may name a provider ModelRig has no rate for (⇒ $0). */
30
+ readonly provider: ProviderId | string;
31
+ /** The `provider/model` handle the caller used — the row's `route`. */
32
+ readonly rawModelId: string;
33
+ /** The effective tags already stamped on the primary raw row (lane:"raw",
34
+ * run_id, step) — the search row rides the same set + `rung:"search"`. */
35
+ readonly tags: RunTags;
36
+ /** The `call_id` shared with the primary raw row (ladder correlation, R0). */
37
+ readonly callId: string;
38
+ /** Native `web_search` tool calls billed on this attempt (from the Responses
39
+ * usage when present, else 1 — disclosed). Clamped to ≥ 1. */
40
+ readonly searchCalls: number;
41
+ }
42
+ /**
43
+ * Record the raw-lane native-search cost row. `costEstimateUsd =
44
+ * webSearchPerCallUsd(provider) × searchCalls`. Zero tokens (the surcharge is not
45
+ * token-metered), `rung:"search"`, `attemptSeq: 2` so it never collides with the
46
+ * primary raw row's `attemptSeq: 1` on the shared `call_id`.
47
+ */
48
+ export declare function recordRawSearchCostRow(telemetry: TelemetryStore, args: RawSearchCostArgs): void;
49
+ //# sourceMappingURL=raw-search-cost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw-search-cost.d.ts","sourceRoot":"","sources":["../../src/run/raw-search-cost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIvE;;kFAEkF;AAClF,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC;kFAC8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC;IACvC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;8EAC0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;kEAC8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CA0C/F"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Native-search cost row for the RAW lane (Native Web Search lane, spec N3) —
4
+ * ZERO DDL. On a successful native-grounded raw attempt (openai/grok Responses
5
+ * API), the model's token cost rides the normal raw row while the `web_search`
6
+ * tool SURCHARGE rides one extra, distinct, legible line here — exactly like the
7
+ * inject rung's synthetic search-cost row (`dispatch.ts recordSearchCostRow`),
8
+ * but shaped for the raw lane (which has no `RouteBundle` / `LadderKeyMinter`):
9
+ * the row records the REAL provider id and a `web_search` pseudo-model under
10
+ * `rung:"search"`, reusing the existing `inferences` telemetry family — no new
11
+ * table, no schema change (spec §2, conservative-schema).
12
+ *
13
+ * Search cost is never folded into token cost. The rate is a static, drift-locked
14
+ * ESTIMATE (`WEB_SEARCH_PRICING`, `web-search-pricing.ts`) — placeholders until the
15
+ * post-merge live-invoice reconciliation (spec §6 live gate) un-tags it.
16
+ *
17
+ * Extracted into its own module (not inlined in `raw.ts`) to keep `raw.ts` under
18
+ * its 700-LOC ceiling (P-cap); the recorder mirrors `recordRawRow`'s minimal
19
+ * telemetry shape so a raw search-cost line reads identically to a raw token line.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.NATIVE_WEB_SEARCH_MODEL = void 0;
23
+ exports.recordRawSearchCostRow = recordRawSearchCostRow;
24
+ const node_crypto_1 = require("node:crypto");
25
+ const telemetry_row_1 = require("./telemetry-row");
26
+ const web_search_pricing_1 = require("./web-search-pricing");
27
+ /** The `web_search` pseudo-model the native-search cost row records under (spec
28
+ * N3) — parallels the inject rung's synthetic search provider, but the ROW's
29
+ * provider is the real dispatch provider so the surcharge is attributed to it. */
30
+ exports.NATIVE_WEB_SEARCH_MODEL = "web_search";
31
+ /**
32
+ * Record the raw-lane native-search cost row. `costEstimateUsd =
33
+ * webSearchPerCallUsd(provider) × searchCalls`. Zero tokens (the surcharge is not
34
+ * token-metered), `rung:"search"`, `attemptSeq: 2` so it never collides with the
35
+ * primary raw row's `attemptSeq: 1` on the shared `call_id`.
36
+ */
37
+ function recordRawSearchCostRow(telemetry, args) {
38
+ const searchCalls = Math.max(1, args.searchCalls);
39
+ // An unknown provider has no WEB_SEARCH_PRICING entry ⇒ webSearchPerCallUsd's `?.` yields 0.
40
+ const costEstimateUsd = (0, web_search_pricing_1.webSearchPerCallUsd)(args.provider) * searchCalls;
41
+ // U4: assembled by the ONE shared builder (./telemetry-row.ts). `rung:"search"`
42
+ // and `attemptSeq: 2` (so it never collides with the primary raw row's
43
+ // attemptSeq 1 on the shared call_id) are explicit inputs; `meta: null` matches
44
+ // the former row's shape exactly (present, null).
45
+ const row = (0, telemetry_row_1.buildTelemetryRow)({
46
+ id: (0, node_crypto_1.randomUUID)(),
47
+ route: args.rawModelId,
48
+ routeVersion: 0,
49
+ provider: args.provider,
50
+ model: exports.NATIVE_WEB_SEARCH_MODEL,
51
+ requestedTier: "standard",
52
+ servedTier: "standard",
53
+ tokensIn: 0,
54
+ tokensOut: 0,
55
+ tokensCached: 0,
56
+ tokensCacheWrite: 0,
57
+ tokensCacheWrite1h: 0,
58
+ costEstimateUsd,
59
+ latencyMs: 0,
60
+ ttfbMs: null,
61
+ failureClass: null,
62
+ attemptsJson: "{}",
63
+ baseTags: args.tags,
64
+ rung: "search",
65
+ fingerprintCell: null,
66
+ servedVariant: null,
67
+ repairedBy: null,
68
+ // A static list-rate estimate is never "pricing missing" — the drift test /
69
+ // live gate catches an absent entry for a supported provider.
70
+ pricingMissing: false,
71
+ cacheKey: null,
72
+ prefixFingerprint: null,
73
+ callId: args.callId,
74
+ attemptSeq: 2,
75
+ candidateIndex: 0,
76
+ meta: null,
77
+ });
78
+ telemetry.record(row);
79
+ }
@@ -1,6 +1,11 @@
1
1
  /**
2
- * CE-9 (spec R5) — the raw-lane provider × knob support matrix, the SINGLE
3
- * source of truth for which knobs each provider honors on `rig.runRaw`.
2
+ * CE-9 (spec R5) / Unified Route U1 — **the one provider × capability
3
+ * vocabulary**. Originally the raw-lane knob support matrix, this table is now
4
+ * the SINGLE source of truth BOTH lanes and the route contract consult for what
5
+ * each provider can do: which knobs it honors on `rig.runRaw`, AND which serving
6
+ * `destination`s it can be reached through (`direct` / `modelrig-served`, ruling
7
+ * D-2). One table, so the guard, the docs matrix, and the destination lookups
8
+ * (`src/run/destination.ts`) can never disagree.
4
9
  *
5
10
  * BOTH the runtime guard (`assertRawKnobsSupported`, run/raw.ts) and the docs
6
11
  * generator (scripts/build-docs-site.ts) import `RAW_KNOB_SUPPORT` — so the
@@ -9,10 +14,14 @@
9
14
  * inline literals; adding a provider (or widening a knob) is one edit here.
10
15
  *
11
16
  * The knob discipline this table encodes (spec R1/R2/R4):
12
- * - grounding: gemini honors native web grounding (grok's Live Search was retired by
13
- * xAI — 410 on the CE-9 live gate — so grok is fail-closed until CE-9b lands the
14
- * Responses-API web_search path); every other provider
15
- * FAILS CLOSED pre-dispatch (guarded).
17
+ * - grounding: gemini, openai, and grok honor native web grounding — gemini via
18
+ * Google Search, openai + grok via the native `web_search` tool on the Responses
19
+ * API (Native Web Search MVP; grok's replacement for the retired Live Search
20
+ * surface). Both openai AND grok are RELEASED (each cleared its own live
21
+ * recency+citations+price gate — openai 2026-09-09, grok 2026-09-09: 15/15
22
+ * dispatched, 100% citation presence, avg 3.80 cites/response, 15/15 recency,
23
+ * clearing the ≥50% bar and retiring the prior ~20-27% flag). Every other
24
+ * provider FAILS CLOSED pre-dispatch (guarded).
16
25
  * - reasoning: NO per-model allowlist outside gemini (the drift cost the
17
26
  * customer flagged). openai/deepinfra/fireworks forward `level` verbatim as
18
27
  * `reasoning_effort` and let the provider enforce (a rejection is classified
@@ -24,11 +33,21 @@
24
33
  * (mime-shaping only) honor it; anthropic FAILS CLOSED (guarded). This is
25
34
  * mime-shaping, NOT schema enforcement — F4 stands (no validation/repair,
26
35
  * `meta.validated` stays false).
36
+ * - responseSchema (R2, issue #6): a JSON Schema forwarded as the provider's
37
+ * NATIVE structured-output directive — gemini `responseJsonSchema` + JSON
38
+ * mime, openai/grok strict `json_schema`, anthropic a forced tool call
39
+ * (strict-first) — and checked client-side by the SAME Ajv the routed lane
40
+ * uses: a miss is `content_invalid` (terminal on this lane — never repaired,
41
+ * never retried). deepseek (json_object only — the schema would be DROPPED)
42
+ * and the OpenAI-compat hosts (deepinfra/fireworks/zai — the raw profile has
43
+ * no probed `structured_native`, so the schema would degrade to json_object
44
+ * plus a prompt guard) FAIL CLOSED pre-dispatch.
27
45
  * - cache + serviceTier are INFORMATIONAL here (never a hard fail): the cache
28
46
  * directive is provider-neutral (a resource HANDLE on gemini, a routing HINT
29
47
  * on the prompt_cache_key providers) and serviceTier forwards everywhere.
30
48
  */
31
49
  import type { ProviderId, ReasoningLevel } from "../types/provider";
50
+ import type { Destination } from "./destination";
32
51
  /** How a provider treats a declared `reasoning.level` on the raw lane (R1). */
33
52
  export type ReasoningSupportKind =
34
53
  /** openai / deepinfra / fireworks — `level` → `reasoning_effort` 1:1,
@@ -78,6 +97,22 @@ export interface CacheFact {
78
97
  readonly semantics: CacheSemantics;
79
98
  readonly note: string;
80
99
  }
100
+ /**
101
+ * Which serving `destination`s a provider can be reached through (Unified Route
102
+ * U1, ruling D-2) — the ONE source of truth the `src/run/destination.ts` lookups
103
+ * read. `direct` for every provider (today's behavior; ABSENT ⇒ byte-identical);
104
+ * `modelrig-served` additionally for the ModelRig-keyed hosts (fireworks,
105
+ * deepinfra — `config.ts` `PROVIDER_KEY_ENV`). INFORMATIONAL in U1 — this is a
106
+ * vocabulary cell only; no dispatch reads it here. Managed serving is gated
107
+ * behind the `config.managedServing` flag (set in code via createRig, NOT an env
108
+ * var; default off), wired in U3 (run/resolve-destination.ts).
109
+ */
110
+ export interface ServingFact {
111
+ /** The destinations this provider supports, `direct` always first. */
112
+ readonly destinations: readonly Destination[];
113
+ /** Human teaching string rendered in the docs matrix `destination` cell. */
114
+ readonly note: string;
115
+ }
81
116
  export interface RawKnobSupport {
82
117
  /** GUARDED — a declared `grounding` on an unsupported provider throws. */
83
118
  readonly grounding: KnobFact;
@@ -85,11 +120,21 @@ export interface RawKnobSupport {
85
120
  readonly reasoning: ReasoningFact;
86
121
  /** GUARDED — a declared `responseFormat` on an unsupported provider throws. */
87
122
  readonly responseFormat: KnobFact;
123
+ /** GUARDED — a declared `responseSchema` (JSON Schema) on a provider whose
124
+ * adapter has no NATIVE schema path throws (R2). Where supported the schema is
125
+ * ALSO validated client-side (Ajv) — a miss is `content_invalid`. */
126
+ readonly responseSchema: KnobFact;
88
127
  /** INFORMATIONAL — the cache directive never hard-fails on the raw lane. */
89
128
  readonly cache: CacheFact;
90
129
  /** INFORMATIONAL — serviceTier forwards to every adapter; the served tier
91
130
  * reports the truth. */
92
131
  readonly serviceTier: KnobFact;
132
+ /** VOCABULARY + RELEASED capability — which serving `destination`s the
133
+ * provider supports (`direct` / `modelrig-served`, ruling D-2). Read by
134
+ * `destination.ts`. Managed serving is RELEASED (GA), gated by
135
+ * `config.managedServing` (default off); the guard + dispatch wiring lives in
136
+ * run/resolve-destination.ts. */
137
+ readonly serving: ServingFact;
93
138
  }
94
139
  /**
95
140
  * The grok reasoning coarsening (R1): xAI's `reasoning_effort` accepts only
@@ -113,6 +158,19 @@ export declare function rawKnobSupport(provider: ProviderId): RawKnobSupport;
113
158
  * can never drift from the table). */
114
159
  export declare function groundingSupportedProviders(): readonly ProviderId[];
115
160
  export declare function responseFormatSupportedProviders(): readonly ProviderId[];
161
+ /** R2: providers whose adapter carries a NATIVE schema path on the raw lane. */
162
+ export declare function responseSchemaSupportedProviders(): readonly ProviderId[];
163
+ /**
164
+ * The serving `destination` cell for a provider (Unified Route U1) — the raw
165
+ * capability fact from the one table. The typed vocabulary lookups
166
+ * (`servedDestinations` / `destinationSupported`, `src/run/destination.ts`) read
167
+ * this, so there is no second serving list to drift. VOCABULARY only in U1.
168
+ */
169
+ export declare function destinationSupport(provider: ProviderId): ServingFact;
170
+ /** Providers that can be served through a given destination, in PROVIDER_IDS
171
+ * order — the matrix source for the eventual (U3) fail-closed fixHint that names
172
+ * the served-capable set. `direct` returns every provider. VOCABULARY only. */
173
+ export declare function providersServing(destination: Destination): readonly ProviderId[];
116
174
  /** Matrix-level notes rendered below the table (spec §3.2 restated in docs). */
117
175
  export declare const RAW_KNOB_MATRIX_NOTES: readonly string[];
118
176
  /** Render the provider × knob matrix as a Markdown table + notes, FROM the
@@ -1 +1 @@
1
- {"version":3,"file":"raw-support.d.ts","sourceRoot":"","sources":["../../src/run/raw-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB;AAC9B;;wDAEwD;AACtD,SAAS;AACX;6EAC6E;GAC3E,SAAS;AACX;mEACmE;GACjE,aAAa;AACf;iDACiD;GAC/C,aAAa,CAAC;AAElB;uDACuD;AACvD,MAAM,MAAM,cAAc;AACxB;mDACmD;AACjD,QAAQ;AACV;2EAC2E;GACzE,MAAM;AACR,8DAA8D;GAC5D,UAAU;AACZ;2CAC2C;GACzC,QAAQ,CAAC;AAEb;;yEAEyE;AACzE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;yCACyC;AACzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B;4BACwB;IACxB,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,GAAG,MAAM,CAEhF;AAED;+EAC+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,cAAc,CA+D/D,CAAC;AAEF;;qBAEqB;AACrB,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,cAAc,CAEnE;AAED;;sCAEsC;AACtC,wBAAgB,2BAA2B,IAAI,SAAS,UAAU,EAAE,CAEnE;AAED,wBAAgB,gCAAgC,IAAI,SAAS,UAAU,EAAE,CAExE;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAKlD,CAAC;AAEF;;;kCAGkC;AAClC,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C"}
1
+ {"version":3,"file":"raw-support.d.ts","sourceRoot":"","sources":["../../src/run/raw-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB;AAC9B;;wDAEwD;AACtD,SAAS;AACX;6EAC6E;GAC3E,SAAS;AACX;mEACmE;GACjE,aAAa;AACf;iDACiD;GAC/C,aAAa,CAAC;AAElB;uDACuD;AACvD,MAAM,MAAM,cAAc;AACxB;mDACmD;AACjD,QAAQ;AACV;2EAC2E;GACzE,MAAM;AACR,8DAA8D;GAC5D,UAAU;AACZ;2CAC2C;GACzC,QAAQ,CAAC;AAEb;;yEAEyE;AACzE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;yCACyC;AACzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAClC;;yEAEqE;IACrE,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B;4BACwB;IACxB,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;IAC/B;;;;qCAIiC;IACjC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,GAAG,MAAM,CAEhF;AAED;+EAC+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,cAAc,CA4F/D,CAAC;AAEF;;qBAEqB;AACrB,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,cAAc,CAEnE;AAED;;sCAEsC;AACtC,wBAAgB,2BAA2B,IAAI,SAAS,UAAU,EAAE,CAEnE;AAED,wBAAgB,gCAAgC,IAAI,SAAS,UAAU,EAAE,CAExE;AAED,gFAAgF;AAChF,wBAAgB,gCAAgC,IAAI,SAAS,UAAU,EAAE,CAExE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAEpE;AAED;;+EAE+E;AAC/E,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,UAAU,EAAE,CAEhF;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAOlD,CAAC;AAEF;;;kCAGkC;AAClC,wBAAgB,mBAAmB,IAAI,MAAM,CAU5C"}
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  /**
3
- * CE-9 (spec R5) — the raw-lane provider × knob support matrix, the SINGLE
4
- * source of truth for which knobs each provider honors on `rig.runRaw`.
3
+ * CE-9 (spec R5) / Unified Route U1 — **the one provider × capability
4
+ * vocabulary**. Originally the raw-lane knob support matrix, this table is now
5
+ * the SINGLE source of truth BOTH lanes and the route contract consult for what
6
+ * each provider can do: which knobs it honors on `rig.runRaw`, AND which serving
7
+ * `destination`s it can be reached through (`direct` / `modelrig-served`, ruling
8
+ * D-2). One table, so the guard, the docs matrix, and the destination lookups
9
+ * (`src/run/destination.ts`) can never disagree.
5
10
  *
6
11
  * BOTH the runtime guard (`assertRawKnobsSupported`, run/raw.ts) and the docs
7
12
  * generator (scripts/build-docs-site.ts) import `RAW_KNOB_SUPPORT` — so the
@@ -10,10 +15,14 @@
10
15
  * inline literals; adding a provider (or widening a knob) is one edit here.
11
16
  *
12
17
  * The knob discipline this table encodes (spec R1/R2/R4):
13
- * - grounding: gemini honors native web grounding (grok's Live Search was retired by
14
- * xAI — 410 on the CE-9 live gate — so grok is fail-closed until CE-9b lands the
15
- * Responses-API web_search path); every other provider
16
- * FAILS CLOSED pre-dispatch (guarded).
18
+ * - grounding: gemini, openai, and grok honor native web grounding — gemini via
19
+ * Google Search, openai + grok via the native `web_search` tool on the Responses
20
+ * API (Native Web Search MVP; grok's replacement for the retired Live Search
21
+ * surface). Both openai AND grok are RELEASED (each cleared its own live
22
+ * recency+citations+price gate — openai 2026-09-09, grok 2026-09-09: 15/15
23
+ * dispatched, 100% citation presence, avg 3.80 cites/response, 15/15 recency,
24
+ * clearing the ≥50% bar and retiring the prior ~20-27% flag). Every other
25
+ * provider FAILS CLOSED pre-dispatch (guarded).
17
26
  * - reasoning: NO per-model allowlist outside gemini (the drift cost the
18
27
  * customer flagged). openai/deepinfra/fireworks forward `level` verbatim as
19
28
  * `reasoning_effort` and let the provider enforce (a rejection is classified
@@ -25,6 +34,15 @@
25
34
  * (mime-shaping only) honor it; anthropic FAILS CLOSED (guarded). This is
26
35
  * mime-shaping, NOT schema enforcement — F4 stands (no validation/repair,
27
36
  * `meta.validated` stays false).
37
+ * - responseSchema (R2, issue #6): a JSON Schema forwarded as the provider's
38
+ * NATIVE structured-output directive — gemini `responseJsonSchema` + JSON
39
+ * mime, openai/grok strict `json_schema`, anthropic a forced tool call
40
+ * (strict-first) — and checked client-side by the SAME Ajv the routed lane
41
+ * uses: a miss is `content_invalid` (terminal on this lane — never repaired,
42
+ * never retried). deepseek (json_object only — the schema would be DROPPED)
43
+ * and the OpenAI-compat hosts (deepinfra/fireworks/zai — the raw profile has
44
+ * no probed `structured_native`, so the schema would degrade to json_object
45
+ * plus a prompt guard) FAIL CLOSED pre-dispatch.
28
46
  * - cache + serviceTier are INFORMATIONAL here (never a hard fail): the cache
29
47
  * directive is provider-neutral (a resource HANDLE on gemini, a routing HINT
30
48
  * on the prompt_cache_key providers) and serviceTier forwards everywhere.
@@ -35,6 +53,9 @@ exports.coarsenGrokReasoningEffort = coarsenGrokReasoningEffort;
35
53
  exports.rawKnobSupport = rawKnobSupport;
36
54
  exports.groundingSupportedProviders = groundingSupportedProviders;
37
55
  exports.responseFormatSupportedProviders = responseFormatSupportedProviders;
56
+ exports.responseSchemaSupportedProviders = responseSchemaSupportedProviders;
57
+ exports.destinationSupport = destinationSupport;
58
+ exports.providersServing = providersServing;
38
59
  exports.renderRawKnobMatrix = renderRawKnobMatrix;
39
60
  const provider_1 = require("../types/provider");
40
61
  /**
@@ -56,22 +77,28 @@ exports.RAW_KNOB_SUPPORT = {
56
77
  grounding: { supported: true, note: "native — Google Search (`googleSearch` tool)" },
57
78
  reasoning: { kind: "model-gated", note: "`level` → `thinkingConfig.thinkingLevel`, thinking-capable models only (see `GEMINI_THINKING_CAPABLE_MODELS`)" },
58
79
  responseFormat: { supported: true, note: "`responseMimeType: application/json` (mime-shaping only — no schema, `validated` stays false)" },
80
+ responseSchema: { supported: true, note: "`responseJsonSchema` + `responseMimeType: application/json` (native); output Ajv-checked, a miss is `content_invalid`" },
59
81
  cache: { semantics: "handle", note: "`cache.key` = a `cachedContent` resource handle (customer-owned lifecycle)" },
60
82
  serviceTier: { supported: true, note: "`flex` / `standard` / `priority` honored" },
83
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
61
84
  },
62
85
  openai: {
63
- grounding: { supported: false, note: "fail-closed" },
86
+ grounding: { supported: true, note: "native — `web_search` tool (Responses API) — released (live gate passed 2026-09-09)" },
64
87
  reasoning: { kind: "forward", note: "`level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably" },
65
88
  responseFormat: { supported: true, note: "`response_format: { type: json_object }`" },
89
+ responseSchema: { supported: true, note: "`response_format: { type: json_schema, strict: true }` (native; strict needs an object root — a non-object root is warned pre-dispatch); output Ajv-checked" },
66
90
  cache: { semantics: "hint", note: "`cache.key` → `prompt_cache_key`; `ttlSeconds ≥ 86400` → `prompt_cache_retention: 24h`" },
67
91
  serviceTier: { supported: true, note: "`flex` (allowlist) / `priority`; flex→standard is a customer-side degrade (§3.2)" },
92
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
68
93
  },
69
94
  grok: {
70
- grounding: { supported: false, note: "fail-closed — xAI retired Live Search (HTTP 410, 2026-08-21); native `web_search` needs the Responses API, tracked as CE-9b" },
95
+ grounding: { supported: true, note: "native — `web_search` tool (xAI Agent Tools / Responses API); replaces retired Live Search — released (live gate passed 2026-09-09)" },
71
96
  reasoning: { kind: "coarsen", note: 'coarsened (disclosed): `minimal,low → "low"`; `medium,high → "high"` → `reasoning_effort`' },
72
97
  responseFormat: { supported: true, note: "`response_format: { type: json_object }`" },
98
+ responseSchema: { supported: true, note: "`response_format: { type: json_schema, strict: true }` (native); output Ajv-checked" },
73
99
  cache: { semantics: "hint", note: "`cache.key` → `prompt_cache_key` (routing hint)" },
74
100
  serviceTier: { supported: false, note: "no tiers — always `standard`" },
101
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
75
102
  },
76
103
  deepseek: {
77
104
  grounding: { supported: false, note: "fail-closed" },
@@ -81,8 +108,10 @@ exports.RAW_KNOB_SUPPORT = {
81
108
  fixHint: "DeepSeek has no reasoning-effort control — select the deepseek-reasoner model instead and remove reasoning",
82
109
  },
83
110
  responseFormat: { supported: true, note: "`response_format: { type: json_object }` — the prompt must contain the word \"JSON\" (caller's responsibility on the raw lane)" },
111
+ responseSchema: { supported: false, note: "fail-closed — json_object only; a schema would be silently dropped" },
84
112
  cache: { semantics: "implicit", note: "automatic prefix cache — the key is not used" },
85
113
  serviceTier: { supported: false, note: "no tiers — always `standard`" },
114
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
86
115
  },
87
116
  anthropic: {
88
117
  grounding: { supported: false, note: "fail-closed" },
@@ -95,22 +124,43 @@ exports.RAW_KNOB_SUPPORT = {
95
124
  supported: false,
96
125
  note: "fail-closed — no json_object equivalent (structured output is a forced tool call)",
97
126
  },
127
+ responseSchema: { supported: true, note: "forced tool call carrying the schema as `input_schema` (strict-first via the structured-outputs beta, advisory on a strict compile rejection); output Ajv-checked" },
98
128
  cache: { semantics: "marker", note: "prefix caching via `cache_control` markers — the key is not used" },
99
129
  serviceTier: { supported: false, note: "no tiers — always `standard`" },
130
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
100
131
  },
101
132
  deepinfra: {
102
133
  grounding: { supported: false, note: "fail-closed" },
103
134
  reasoning: { kind: "forward", note: "`level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably" },
104
135
  responseFormat: { supported: true, note: "`response_format: { type: json_object }`" },
136
+ responseSchema: { supported: false, note: "fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard" },
105
137
  cache: { semantics: "hint", note: "`cache.key` → `prompt_cache_key` (routing hint)" },
106
138
  serviceTier: { supported: false, note: "no tiers — always `standard`" },
139
+ serving: { destinations: ["direct", "modelrig-served"], note: "`direct` · `modelrig-served` — ModelRig-owned key (managed serving RELEASED — GA, gated by `config.managedServing`, default off)" },
107
140
  },
108
141
  fireworks: {
109
142
  grounding: { supported: false, note: "fail-closed" },
110
143
  reasoning: { kind: "forward", note: "`level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably" },
111
144
  responseFormat: { supported: true, note: "`response_format: { type: json_object }`" },
145
+ responseSchema: { supported: false, note: "fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard" },
112
146
  cache: { semantics: "hint", note: "`cache.key` → `prompt_cache_key` (routing hint; caching is automatic host-side)" },
113
147
  serviceTier: { supported: false, note: "no tiers — always `standard`" },
148
+ serving: { destinations: ["direct", "modelrig-served"], note: "`direct` · `modelrig-served` — ModelRig-owned key (managed serving RELEASED — GA, gated by `config.managedServing`, default off)" },
149
+ },
150
+ // Z.ai / Zhipu (B4a) — the GLM family via the OpenAI-compat adapter (same row
151
+ // shape as fireworks/deepinfra). Grounding FAILS CLOSED this phase: native GLM
152
+ // `web_search` is B4b (not built), so a declared `grounding` on zai is rejected
153
+ // pre-dispatch with the teaching fixHint that names the supported set. Serving
154
+ // is `direct` only — `modelrig-served` for zai is deferred (U3/B2), so ABSENT ⇒
155
+ // byte-identical to every other direct-only provider.
156
+ zai: {
157
+ grounding: { supported: false, note: "fail-closed — native GLM web_search wired in B4b" },
158
+ reasoning: { kind: "forward", note: "`level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably" },
159
+ responseFormat: { supported: true, note: "response_format json_object" },
160
+ responseSchema: { supported: false, note: "fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard" },
161
+ cache: { semantics: "hint", note: "`cache.key` → `prompt_cache_key` (routing hint)" },
162
+ serviceTier: { supported: false, note: "no tiers — always `standard`" },
163
+ serving: { destinations: ["direct"], note: "`direct` only — vendor key (BYOK / configured)" },
114
164
  },
115
165
  };
116
166
  /** Pure lookup into the matrix (spec R6, exported from the package root). A
@@ -128,23 +178,46 @@ function groundingSupportedProviders() {
128
178
  function responseFormatSupportedProviders() {
129
179
  return provider_1.PROVIDER_IDS.filter((p) => exports.RAW_KNOB_SUPPORT[p].responseFormat.supported);
130
180
  }
181
+ /** R2: providers whose adapter carries a NATIVE schema path on the raw lane. */
182
+ function responseSchemaSupportedProviders() {
183
+ return provider_1.PROVIDER_IDS.filter((p) => exports.RAW_KNOB_SUPPORT[p].responseSchema.supported);
184
+ }
185
+ /**
186
+ * The serving `destination` cell for a provider (Unified Route U1) — the raw
187
+ * capability fact from the one table. The typed vocabulary lookups
188
+ * (`servedDestinations` / `destinationSupported`, `src/run/destination.ts`) read
189
+ * this, so there is no second serving list to drift. VOCABULARY only in U1.
190
+ */
191
+ function destinationSupport(provider) {
192
+ return exports.RAW_KNOB_SUPPORT[provider].serving;
193
+ }
194
+ /** Providers that can be served through a given destination, in PROVIDER_IDS
195
+ * order — the matrix source for the eventual (U3) fail-closed fixHint that names
196
+ * the served-capable set. `direct` returns every provider. VOCABULARY only. */
197
+ function providersServing(destination) {
198
+ return provider_1.PROVIDER_IDS.filter((p) => exports.RAW_KNOB_SUPPORT[p].serving.destinations.includes(destination));
199
+ }
131
200
  /** Matrix-level notes rendered below the table (spec §3.2 restated in docs). */
132
201
  exports.RAW_KNOB_MATRIX_NOTES = [
133
202
  "**Fail-closed is discoverable, not a surprise.** A knob a provider cannot honor throws `RigFailureError` `invariant_violation` PRE-DISPATCH (no meter, no provider call, no telemetry row) — read this table before you call, not after.",
134
203
  "**reasoning has NO per-model allowlist outside gemini.** openai/deepinfra/fireworks forward `level` verbatim and let the provider enforce; a rejection surfaces as a classified failure that names the fix. grok coarsens (disclosed above). deepseek reasoning is model selection (`deepseek-reasoner`); anthropic extended thinking is unwired.",
135
204
  "**responseFormat is mime-shaping, not schema enforcement.** `json_object` forces syntactic JSON only — the raw lane still never validates or repairs, and `meta.validated` stays `false` (F4).",
205
+ "**responseSchema validates, never repairs.** A JSON Schema on `rig.runRaw` is forwarded as the provider's NATIVE structured-output directive AND checked client-side by the same Ajv the routed lane uses: a miss is thrown as `content_invalid` (a billed row with that class, `fixHint` naming the first errors) — the raw lane never repairs, never retries, never falls through, and `meta.validated` stays `false` (no ladder stands behind it). The row's `meta.schema_sha` is the sha256 of the canonical (sorted-key) schema JSON, so declared-vs-served drift is queryable. Declare ONE of `responseFormat` / `responseSchema`, not both.",
136
206
  "**§3.2 flex degrade is customer-side.** ModelRig does not replicate the gemini direct-path infra-retry wrapper on the raw lane; under `flex` a capacity rejection surfaces classified (`capacity_shed`) for your own `→ standard` retry ladder — a NAMED difference for your migration record, not a silent one.",
207
+ "**`destination` (managed serving) is RELEASED (GA).** The serving-destination column names which providers *can* be `modelrig-served` (reached on ModelRig's own key) vs `direct` only. Managed serving is gated behind a config flag (`config.managedServing`, default off, set in code — not an env var); it defaults off, so until you enable it every call is `direct` (ABSENT ⇒ byte-identical).",
137
208
  ];
138
209
  /** Render the provider × knob matrix as a Markdown table + notes, FROM the
139
210
  * matrix (never a hand-written table — R5). Consumed by the docs generator
140
211
  * (scripts/build-docs-site.ts) so the published matrix and the runtime guard
141
212
  * derive from the same source. */
142
213
  function renderRawKnobMatrix() {
143
- const header = "| Provider | grounding | reasoning | responseFormat | cache | serviceTier |";
144
- const sep = "|---|---|---|---|---|---|";
214
+ // `destination` (managed serving) is RELEASED (GA, gated by
215
+ // `config.managedServing`, default off) — detailed in the notes below.
216
+ const header = "| Provider | grounding | reasoning | responseFormat | responseSchema | cache | serviceTier | destination |";
217
+ const sep = "|---|---|---|---|---|---|---|---|";
145
218
  const rows = provider_1.PROVIDER_IDS.map((p) => {
146
219
  const s = exports.RAW_KNOB_SUPPORT[p];
147
- return `| \`${p}\` | ${s.grounding.note} | ${s.reasoning.note} | ${s.responseFormat.note} | ${s.cache.note} | ${s.serviceTier.note} |`;
220
+ return `| \`${p}\` | ${s.grounding.note} | ${s.reasoning.note} | ${s.responseFormat.note} | ${s.responseSchema.note} | ${s.cache.note} | ${s.serviceTier.note} | ${s.serving.note} |`;
148
221
  });
149
222
  return [header, sep, ...rows, "", ...exports.RAW_KNOB_MATRIX_NOTES].join("\n");
150
223
  }