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.
Files changed (310) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +53 -11
  3. package/data/PROVENANCE.md +19 -0
  4. package/data/pricing-manual.json +22 -0
  5. package/data/pricing-map.json +46362 -0
  6. package/data/pricing-overlay.json +419 -0
  7. package/dist/adapters/anthropic.d.ts +50 -6
  8. package/dist/adapters/anthropic.d.ts.map +1 -1
  9. package/dist/adapters/anthropic.js +123 -29
  10. package/dist/adapters/build-adapters.d.ts +11 -0
  11. package/dist/adapters/build-adapters.d.ts.map +1 -0
  12. package/dist/adapters/build-adapters.js +45 -0
  13. package/dist/adapters/deepseek.d.ts +6 -0
  14. package/dist/adapters/deepseek.d.ts.map +1 -1
  15. package/dist/adapters/deepseek.js +54 -1
  16. package/dist/adapters/gemini.d.ts +10 -0
  17. package/dist/adapters/gemini.d.ts.map +1 -1
  18. package/dist/adapters/gemini.js +78 -7
  19. package/dist/adapters/grok.d.ts +9 -0
  20. package/dist/adapters/grok.d.ts.map +1 -1
  21. package/dist/adapters/grok.js +67 -3
  22. package/dist/adapters/openai-compat.d.ts +6 -0
  23. package/dist/adapters/openai-compat.d.ts.map +1 -1
  24. package/dist/adapters/openai-compat.js +58 -1
  25. package/dist/adapters/openai-stream.d.ts +58 -0
  26. package/dist/adapters/openai-stream.d.ts.map +1 -0
  27. package/dist/adapters/openai-stream.js +120 -0
  28. package/dist/adapters/openai.d.ts +6 -0
  29. package/dist/adapters/openai.d.ts.map +1 -1
  30. package/dist/adapters/openai.js +60 -1
  31. package/dist/adapters/sampling.d.ts +28 -0
  32. package/dist/adapters/sampling.d.ts.map +1 -0
  33. package/dist/adapters/sampling.js +34 -0
  34. package/dist/adapters/tool-mapping.d.ts +92 -0
  35. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  36. package/dist/adapters/tool-mapping.js +143 -0
  37. package/dist/artifacts/artifact.d.ts +137 -0
  38. package/dist/artifacts/artifact.d.ts.map +1 -0
  39. package/dist/artifacts/artifact.js +377 -0
  40. package/dist/artifacts/content-uploader.d.ts +87 -0
  41. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  42. package/dist/artifacts/content-uploader.js +124 -0
  43. package/dist/artifacts/index.d.ts +63 -0
  44. package/dist/artifacts/index.d.ts.map +1 -0
  45. package/dist/artifacts/index.js +80 -0
  46. package/dist/artifacts/run-context.d.ts +129 -0
  47. package/dist/artifacts/run-context.d.ts.map +1 -0
  48. package/dist/artifacts/run-context.js +208 -0
  49. package/dist/artifacts/scrub.d.ts +60 -0
  50. package/dist/artifacts/scrub.d.ts.map +1 -0
  51. package/dist/artifacts/scrub.js +139 -0
  52. package/dist/artifacts/step.d.ts +26 -0
  53. package/dist/artifacts/step.d.ts.map +1 -0
  54. package/dist/artifacts/step.js +37 -0
  55. package/dist/artifacts/taint.d.ts +63 -0
  56. package/dist/artifacts/taint.d.ts.map +1 -0
  57. package/dist/artifacts/taint.js +76 -0
  58. package/dist/bakeoff/cache-sim.d.ts +99 -0
  59. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  60. package/dist/bakeoff/cache-sim.js +126 -0
  61. package/dist/bakeoff/eval-cases.d.ts +96 -0
  62. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  63. package/dist/bakeoff/eval-cases.js +105 -0
  64. package/dist/bakeoff/report.d.ts +41 -0
  65. package/dist/bakeoff/report.d.ts.map +1 -0
  66. package/dist/bakeoff/report.js +142 -0
  67. package/dist/bakeoff/samples.d.ts +15 -0
  68. package/dist/bakeoff/samples.d.ts.map +1 -1
  69. package/dist/bakeoff/samples.js +20 -0
  70. package/dist/cli.d.ts +1 -3
  71. package/dist/cli.d.ts.map +1 -1
  72. package/dist/cli.js +134 -116
  73. package/dist/config.d.ts +100 -0
  74. package/dist/config.d.ts.map +1 -1
  75. package/dist/config.js +104 -0
  76. package/dist/deprecations.d.ts +32 -0
  77. package/dist/deprecations.d.ts.map +1 -0
  78. package/dist/deprecations.js +59 -0
  79. package/dist/gateway/http-protocol.d.ts +175 -0
  80. package/dist/gateway/http-protocol.d.ts.map +1 -0
  81. package/dist/gateway/http-protocol.js +105 -0
  82. package/dist/index.d.ts +106 -19
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +266 -138
  85. package/dist/init/finder.d.ts +15 -0
  86. package/dist/init/finder.d.ts.map +1 -1
  87. package/dist/init/finder.js +46 -0
  88. package/dist/init/init-cli.d.ts +10 -0
  89. package/dist/init/init-cli.d.ts.map +1 -0
  90. package/dist/init/init-cli.js +86 -0
  91. package/dist/init/observe-cli.d.ts +19 -0
  92. package/dist/init/observe-cli.d.ts.map +1 -0
  93. package/dist/init/observe-cli.js +73 -0
  94. package/dist/init/observe.d.ts +81 -0
  95. package/dist/init/observe.d.ts.map +1 -0
  96. package/dist/init/observe.js +245 -0
  97. package/dist/registry/build.d.ts +94 -1
  98. package/dist/registry/build.d.ts.map +1 -1
  99. package/dist/registry/build.js +125 -4
  100. package/dist/registry/cache-policy.d.ts +94 -0
  101. package/dist/registry/cache-policy.d.ts.map +1 -0
  102. package/dist/registry/cache-policy.js +238 -0
  103. package/dist/registry/call-notes.d.ts.map +1 -1
  104. package/dist/registry/call-notes.js +40 -2
  105. package/dist/registry/effective-rate.d.ts +151 -0
  106. package/dist/registry/effective-rate.d.ts.map +1 -0
  107. package/dist/registry/effective-rate.js +156 -0
  108. package/dist/registry/facts.d.ts.map +1 -1
  109. package/dist/registry/facts.js +16 -0
  110. package/dist/registry/parity.d.ts +26 -0
  111. package/dist/registry/parity.d.ts.map +1 -1
  112. package/dist/registry/parity.js +43 -2
  113. package/dist/registry/pricing-shape.d.ts +113 -0
  114. package/dist/registry/pricing-shape.d.ts.map +1 -0
  115. package/dist/registry/pricing-shape.js +123 -0
  116. package/dist/registry/pricing.d.ts +25 -1
  117. package/dist/registry/pricing.d.ts.map +1 -1
  118. package/dist/registry/pricing.js +64 -6
  119. package/dist/rigindex/bootstrap.d.ts +36 -0
  120. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  121. package/dist/rigindex/bootstrap.js +97 -0
  122. package/dist/rigindex/fit.d.ts +24 -0
  123. package/dist/rigindex/fit.d.ts.map +1 -0
  124. package/dist/rigindex/fit.js +76 -0
  125. package/dist/rigindex/gates.d.ts +52 -0
  126. package/dist/rigindex/gates.d.ts.map +1 -0
  127. package/dist/rigindex/gates.js +109 -0
  128. package/dist/rigindex/graph.d.ts +31 -0
  129. package/dist/rigindex/graph.d.ts.map +1 -0
  130. package/dist/rigindex/graph.js +120 -0
  131. package/dist/rigindex/index.d.ts +23 -0
  132. package/dist/rigindex/index.d.ts.map +1 -0
  133. package/dist/rigindex/index.js +112 -0
  134. package/dist/rigindex/types.d.ts +76 -0
  135. package/dist/rigindex/types.d.ts.map +1 -0
  136. package/dist/rigindex/types.js +8 -0
  137. package/dist/routes/cache-lint.d.ts +41 -0
  138. package/dist/routes/cache-lint.d.ts.map +1 -0
  139. package/dist/routes/cache-lint.js +81 -0
  140. package/dist/routes/loader.d.ts +48 -0
  141. package/dist/routes/loader.d.ts.map +1 -1
  142. package/dist/routes/loader.js +167 -0
  143. package/dist/routes/resolved-bundle.d.ts +79 -0
  144. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  145. package/dist/routes/resolved-bundle.js +148 -0
  146. package/dist/routes/sampling-config.d.ts +32 -0
  147. package/dist/routes/sampling-config.d.ts.map +1 -0
  148. package/dist/routes/sampling-config.js +91 -0
  149. package/dist/run/ajv.d.ts +22 -0
  150. package/dist/run/ajv.d.ts.map +1 -0
  151. package/dist/run/ajv.js +32 -0
  152. package/dist/run/cache-handle.d.ts +58 -0
  153. package/dist/run/cache-handle.d.ts.map +1 -0
  154. package/dist/run/cache-handle.js +54 -0
  155. package/dist/run/cache-stamp.d.ts +65 -0
  156. package/dist/run/cache-stamp.d.ts.map +1 -0
  157. package/dist/run/cache-stamp.js +0 -0
  158. package/dist/run/capture-write.d.ts +33 -0
  159. package/dist/run/capture-write.d.ts.map +1 -0
  160. package/dist/run/capture-write.js +55 -0
  161. package/dist/run/cost.d.ts.map +1 -1
  162. package/dist/run/cost.js +1 -1
  163. package/dist/run/dispatch.d.ts +99 -0
  164. package/dist/run/dispatch.d.ts.map +1 -0
  165. package/dist/run/dispatch.js +191 -0
  166. package/dist/run/raw.d.ts +130 -0
  167. package/dist/run/raw.d.ts.map +1 -0
  168. package/dist/run/raw.js +363 -0
  169. package/dist/run/record-attempt.d.ts +40 -0
  170. package/dist/run/record-attempt.d.ts.map +1 -0
  171. package/dist/run/record-attempt.js +99 -0
  172. package/dist/run/resolve.d.ts +75 -2
  173. package/dist/run/resolve.d.ts.map +1 -1
  174. package/dist/run/resolve.js +126 -4
  175. package/dist/run/run.d.ts +14 -0
  176. package/dist/run/run.d.ts.map +1 -1
  177. package/dist/run/run.js +0 -0
  178. package/dist/run/stream.d.ts +49 -0
  179. package/dist/run/stream.d.ts.map +1 -0
  180. package/dist/run/stream.js +87 -0
  181. package/dist/run/validate.d.ts.map +1 -1
  182. package/dist/run/validate.js +6 -7
  183. package/dist/savings/cache-ledger.d.ts +108 -0
  184. package/dist/savings/cache-ledger.d.ts.map +1 -0
  185. package/dist/savings/cache-ledger.js +174 -0
  186. package/dist/savings/cache-reconcile.d.ts +64 -0
  187. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  188. package/dist/savings/cache-reconcile.js +59 -0
  189. package/dist/savings/cache-stats.d.ts +63 -0
  190. package/dist/savings/cache-stats.d.ts.map +1 -0
  191. package/dist/savings/cache-stats.js +108 -0
  192. package/dist/savings/emit.d.ts +13 -0
  193. package/dist/savings/emit.d.ts.map +1 -1
  194. package/dist/savings/emit.js +25 -0
  195. package/dist/savings/swap-execute.d.ts +14 -0
  196. package/dist/savings/swap-execute.d.ts.map +1 -1
  197. package/dist/savings/swap-execute.js +44 -1
  198. package/dist/savings/swaps-client.d.ts +14 -0
  199. package/dist/savings/swaps-client.d.ts.map +1 -1
  200. package/dist/savings/swaps-client.js +16 -2
  201. package/dist/settings/api-fetcher.d.ts +39 -0
  202. package/dist/settings/api-fetcher.d.ts.map +1 -0
  203. package/dist/settings/api-fetcher.js +92 -0
  204. package/dist/settings/index.d.ts +10 -0
  205. package/dist/settings/index.d.ts.map +1 -0
  206. package/dist/settings/index.js +15 -0
  207. package/dist/settings/task-settings.d.ts +91 -0
  208. package/dist/settings/task-settings.d.ts.map +1 -0
  209. package/dist/settings/task-settings.js +116 -0
  210. package/dist/status.d.ts +77 -0
  211. package/dist/status.d.ts.map +1 -0
  212. package/dist/status.js +113 -0
  213. package/dist/telemetry/artifact-store.d.ts +48 -0
  214. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  215. package/dist/telemetry/artifact-store.js +189 -0
  216. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  217. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  218. package/dist/telemetry/cache-miss-streak.js +99 -0
  219. package/dist/telemetry/capture.d.ts +3 -0
  220. package/dist/telemetry/capture.d.ts.map +1 -1
  221. package/dist/telemetry/capture.js +15 -8
  222. package/dist/telemetry/exporter-setup.d.ts +28 -0
  223. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  224. package/dist/telemetry/exporter-setup.js +34 -0
  225. package/dist/telemetry/exporter.d.ts +75 -0
  226. package/dist/telemetry/exporter.d.ts.map +1 -1
  227. package/dist/telemetry/exporter.js +324 -47
  228. package/dist/telemetry/health.d.ts +76 -0
  229. package/dist/telemetry/health.d.ts.map +1 -0
  230. package/dist/telemetry/health.js +92 -0
  231. package/dist/telemetry/ingest-protocol.d.ts +152 -3
  232. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  233. package/dist/telemetry/ingest-protocol.js +204 -4
  234. package/dist/telemetry/load-error-report.d.ts +24 -0
  235. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  236. package/dist/telemetry/load-error-report.js +50 -0
  237. package/dist/telemetry/otel-map.d.ts +121 -0
  238. package/dist/telemetry/otel-map.d.ts.map +1 -0
  239. package/dist/telemetry/otel-map.js +224 -0
  240. package/dist/telemetry/otel-normalize.d.ts +104 -0
  241. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  242. package/dist/telemetry/otel-normalize.js +530 -0
  243. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  244. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  245. package/dist/telemetry/otlp-emitter.js +144 -0
  246. package/dist/telemetry/otlp-proto.d.ts +29 -0
  247. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  248. package/dist/telemetry/otlp-proto.js +266 -0
  249. package/dist/telemetry/sinks.d.ts +37 -1
  250. package/dist/telemetry/sinks.d.ts.map +1 -1
  251. package/dist/telemetry/sinks.js +121 -30
  252. package/dist/telemetry/store.d.ts +28 -1
  253. package/dist/telemetry/store.d.ts.map +1 -1
  254. package/dist/telemetry/store.js +218 -3
  255. package/dist/tenancy/api-key-format.d.ts +15 -5
  256. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  257. package/dist/tenancy/api-key-format.js +24 -5
  258. package/dist/types/config-teaching.d.ts +62 -0
  259. package/dist/types/config-teaching.d.ts.map +1 -0
  260. package/dist/types/config-teaching.js +183 -0
  261. package/dist/types/provider.d.ts +147 -1
  262. package/dist/types/provider.d.ts.map +1 -1
  263. package/dist/types/provider.js +21 -1
  264. package/dist/types/route.d.ts +79 -1
  265. package/dist/types/route.d.ts.map +1 -1
  266. package/dist/types/telemetry.d.ts +117 -0
  267. package/dist/types/telemetry.d.ts.map +1 -1
  268. package/dist/validate-cli.d.ts +12 -0
  269. package/dist/validate-cli.d.ts.map +1 -0
  270. package/dist/validate-cli.js +57 -0
  271. package/dist/verify/capture.d.ts +66 -0
  272. package/dist/verify/capture.d.ts.map +1 -0
  273. package/dist/verify/capture.js +180 -0
  274. package/dist/verify/cli-run.d.ts +23 -0
  275. package/dist/verify/cli-run.d.ts.map +1 -0
  276. package/dist/verify/cli-run.js +57 -0
  277. package/dist/verify/diff.d.ts +48 -0
  278. package/dist/verify/diff.d.ts.map +1 -0
  279. package/dist/verify/diff.js +157 -0
  280. package/dist/verify/index.d.ts +17 -0
  281. package/dist/verify/index.d.ts.map +1 -0
  282. package/dist/verify/index.js +24 -0
  283. package/dist/verify/report.d.ts +14 -0
  284. package/dist/verify/report.d.ts.map +1 -0
  285. package/dist/verify/report.js +88 -0
  286. package/dist/verify/types.d.ts +101 -0
  287. package/dist/verify/types.d.ts.map +1 -0
  288. package/dist/verify/types.js +17 -0
  289. package/dist/verify/verify.d.ts +29 -0
  290. package/dist/verify/verify.d.ts.map +1 -0
  291. package/dist/verify/verify.js +148 -0
  292. package/dist/watch/evaluate.d.ts +162 -3
  293. package/dist/watch/evaluate.d.ts.map +1 -1
  294. package/dist/watch/evaluate.js +61 -0
  295. package/dist/watch/monitor.d.ts +116 -0
  296. package/dist/watch/monitor.d.ts.map +1 -0
  297. package/dist/watch/monitor.js +199 -0
  298. package/dist/watch/proposals.d.ts +20 -4
  299. package/dist/watch/proposals.d.ts.map +1 -1
  300. package/dist/watch/proposals.js +31 -3
  301. package/dist/watch/run.d.ts +33 -0
  302. package/dist/watch/run.d.ts.map +1 -1
  303. package/dist/watch/run.js +39 -0
  304. package/package.json +7 -1
  305. package/registry/LICENSE +20 -0
  306. package/registry/leaderboard.json +3639 -0
  307. package/registry/registry.json +7263 -0
  308. package/dist/telemetry/supabase-exporter.d.ts +0 -64
  309. package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
  310. package/dist/telemetry/supabase-exporter.js +0 -211
@@ -5,9 +5,10 @@
5
5
  * CALL: the route schema becomes the single tool's input_schema and
6
6
  * tool_choice pins it, so the accumulated input_json_delta stream IS the
7
7
  * JSON output (Anthropic's recommended structured-output pattern; the run
8
- * loop still ajv-validates like every provider). Prompt caching is explicit
9
- * cache_control on Anthropic and is NOT wired yet — all cache capability
10
- * flags stay false until it is.
8
+ * loop still ajv-validates like every provider). Prompt caching (WS-C) is
9
+ * explicit `cache_control` on Anthropic: when the request opts in, the system
10
+ * prefix is marked with an ephemeral breakpoint (buildAnthropicParams) and the
11
+ * `context_cache_explicit` capability is advertised per the cache-policy registry.
11
12
  */
12
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
13
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -17,8 +18,11 @@ exports.isStrictSchemaRejection = isStrictSchemaRejection;
17
18
  exports.anthropicSupports = anthropicSupports;
18
19
  exports.classifyAnthropicFailure = classifyAnthropicFailure;
19
20
  exports.buildAnthropicParams = buildAnthropicParams;
21
+ exports.normalizeAnthropicInputUsage = normalizeAnthropicInputUsage;
20
22
  exports.createAnthropicAdapter = createAnthropicAdapter;
21
23
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
24
+ const tool_mapping_1 = require("./tool-mapping");
25
+ const sampling_1 = require("./sampling");
22
26
  const DEFAULT_MAX_TOKENS = 32000;
23
27
  const OUTPUT_TOOL_NAME = "route_output";
24
28
  /** Strict tool use (constrained decoding) — public beta. Schemas that
@@ -34,13 +38,14 @@ function anthropicSupports(feature, _model) {
34
38
  switch (feature) {
35
39
  case "structured_native": // forced tool call with the route schema
36
40
  case "json_mode":
41
+ case "context_cache_explicit": // cache_control ephemeral blocks (WS-C) — cache-policy registry truth
37
42
  return true;
38
43
  case "grounded_native": // web-search tool exists but is not wired
39
- case "context_cache_explicit": // cache_control not wired yet
40
- case "prompt_cache_key":
44
+ case "prompt_cache_key": // anthropic ignores keys — caching is prefix-based via cache_control
41
45
  case "prefix_cache_implicit":
42
46
  case "tier_flex":
43
47
  case "trace_visible": // extended thinking not enabled by this adapter
48
+ case "zero_retention": // never a static adapter attestation — set only from the credential-scoped registry facts (claims.ts)
44
49
  return false;
45
50
  }
46
51
  }
@@ -73,31 +78,85 @@ function classifyAnthropicFailure(message) {
73
78
  return { cls: "network" };
74
79
  }
75
80
  function buildAnthropicParams(model, req, strict = false) {
81
+ // Structured output is a FORCED tool call on the route schema (existing
82
+ // path). Otherwise, caller-supplied `tools` (spec §4.4) map to Anthropic's
83
+ // tool_use — the two are mutually exclusive here (a structured route does
84
+ // not also expose free tools in Phase A).
85
+ const toolBlock = req.outputSchema !== null
86
+ ? {
87
+ tools: [
88
+ {
89
+ name: OUTPUT_TOOL_NAME,
90
+ description: "Emit the structured output for this route.",
91
+ input_schema: req.outputSchema,
92
+ ...(strict ? { strict: true } : {}),
93
+ },
94
+ ],
95
+ tool_choice: { type: "tool", name: OUTPUT_TOOL_NAME },
96
+ }
97
+ : req.tools !== undefined && req.tools.length > 0
98
+ ? {
99
+ tools: (0, tool_mapping_1.toAnthropicTools)(req.tools),
100
+ ...((0, tool_mapping_1.toAnthropicToolChoice)(req.toolChoice) !== undefined
101
+ ? { tool_choice: (0, tool_mapping_1.toAnthropicToolChoice)(req.toolChoice) }
102
+ : {}),
103
+ }
104
+ : {};
105
+ // Prompt caching (WS-C): when the request opts in (`cache` set by the run
106
+ // loop's policy-gated stamp), mark the system prompt — the stable prefix in
107
+ // ModelRig's system-carries-the-task convention — with a `cache_control`
108
+ // ephemeral breakpoint. Anthropic caches the prefix up to and including the
109
+ // marked block (default 5m TTL, refreshes free on read); the `cache.key` is
110
+ // ignored (caching is prefix-based, not key-based — that is why the telemetry
111
+ // `cache_key` for anthropic is null). Extended 1h TTL is a documented
112
+ // follow-up (needs the extended-cache beta header; WS-A already accounts the
113
+ // 5m/1h split when the provider reports it).
114
+ const wantsCache = req.cache?.key !== undefined && req.cache.key !== "";
115
+ const systemField = req.systemPrompt === ""
116
+ ? {}
117
+ : wantsCache
118
+ ? {
119
+ system: [
120
+ { type: "text", text: req.systemPrompt, cache_control: { type: "ephemeral" } },
121
+ ],
122
+ }
123
+ : { system: req.systemPrompt };
76
124
  return {
77
125
  model,
78
- max_tokens: req.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
79
- ...(req.systemPrompt !== "" ? { system: req.systemPrompt } : {}),
126
+ max_tokens: (0, sampling_1.effectiveMaxOutputTokens)(req, DEFAULT_MAX_TOKENS),
127
+ ...(0, sampling_1.openAISamplingFields)(req),
128
+ ...systemField,
80
129
  messages: [{ role: "user", content: req.userPrompt }],
81
130
  stream: true,
82
- ...(req.outputSchema !== null
83
- ? {
84
- tools: [
85
- {
86
- name: OUTPUT_TOOL_NAME,
87
- description: "Emit the structured output for this route.",
88
- input_schema: req.outputSchema,
89
- ...(strict ? { strict: true } : {}),
90
- },
91
- ],
92
- tool_choice: { type: "tool", name: OUTPUT_TOOL_NAME },
93
- }
94
- : {}),
131
+ ...toolBlock,
132
+ };
133
+ }
134
+ /**
135
+ * Normalize Anthropic's `message_start` usage to ModelRig's convention
136
+ * (research §4 fix). Anthropic's `input_tokens` is the UNCACHED portion only;
137
+ * ModelRig requires `tokensIn` to INCLUDE cache reads and writes as subsets, so
138
+ * we add them back. Pure and total — the fixture-tested seam for the subset
139
+ * invariant and the 5m/1h split.
140
+ */
141
+ function normalizeAnthropicInputUsage(u) {
142
+ const uncached = u.input_tokens ?? 0;
143
+ const read = u.cache_read_input_tokens ?? 0;
144
+ const write = u.cache_creation_input_tokens ?? 0;
145
+ // 1h split when the extended-TTL beta reports it; clamp to the total write so
146
+ // a malformed payload can never make the 1h bucket exceed the write bucket.
147
+ const write1h = Math.min(u.cache_creation?.ephemeral_1h_input_tokens ?? 0, write);
148
+ return {
149
+ tokensIn: uncached + read + write,
150
+ tokensCached: read,
151
+ tokensCacheWrite: write,
152
+ tokensCacheWrite1h: write1h,
95
153
  };
96
154
  }
97
155
  function createAnthropicAdapter(config) {
98
156
  return {
99
157
  id: "anthropic",
100
158
  supports: anthropicSupports,
159
+ supportsTools: () => true,
101
160
  async call(req) {
102
161
  const model = req.candidate.model;
103
162
  // F1: post-stream failures still billed tokens — attach usage.
@@ -136,23 +195,47 @@ function createAnthropicAdapter(config) {
136
195
  stream = await client.messages.create(buildAnthropicParams(model, req));
137
196
  }
138
197
  const events = stream;
198
+ // Caller-supplied tools (spec §4.4) live on the non-structured path:
199
+ // accumulate each `tool_use` block's input_json_delta by block index.
200
+ const wantTools = req.outputSchema === null && req.tools !== undefined && req.tools.length > 0;
201
+ const toolBlocks = new Map();
139
202
  let text = "";
140
- let promptTokens = 0;
203
+ // Input-side usage, normalized at message_start (subset invariant +
204
+ // research §4 tokensIn fix + 5m/1h split live in normalizeAnthropicInputUsage).
205
+ let inputUsage = {
206
+ tokensIn: 0,
207
+ tokensCached: 0,
208
+ tokensCacheWrite: 0,
209
+ tokensCacheWrite1h: 0,
210
+ };
141
211
  let completionTokens = 0;
142
- let cachedTokens = 0;
143
212
  let stopReason = null;
144
213
  for await (const event of events) {
145
214
  if (event.type === "message_start" && event.message?.usage !== undefined) {
146
- promptTokens = event.message.usage.input_tokens ?? 0;
147
- cachedTokens = event.message.usage.cache_read_input_tokens ?? 0;
215
+ inputUsage = normalizeAnthropicInputUsage(event.message.usage);
216
+ }
217
+ else if (event.type === "content_block_start" &&
218
+ wantTools &&
219
+ event.content_block?.type === "tool_use") {
220
+ toolBlocks.set(event.index ?? 0, {
221
+ id: event.content_block.id ?? "",
222
+ name: event.content_block.name ?? "",
223
+ json: "",
224
+ });
148
225
  }
149
226
  else if (event.type === "content_block_delta") {
227
+ // Tools path: input_json_delta is the tool's arguments, per block.
150
228
  // Schema path: the forced tool's input_json_delta IS the output.
151
- // Plain path: text deltas. Never mix the two buffers.
152
- if (wantJson && event.delta?.type === "input_json_delta" && event.delta.partial_json) {
229
+ // Plain path: text deltas. Never mix the buffers.
230
+ if (wantTools && event.delta?.type === "input_json_delta" && event.delta.partial_json) {
231
+ const blk = toolBlocks.get(event.index ?? 0);
232
+ if (blk !== undefined)
233
+ blk.json += event.delta.partial_json;
234
+ }
235
+ else if (wantJson && event.delta?.type === "input_json_delta" && event.delta.partial_json) {
153
236
  text += event.delta.partial_json;
154
237
  }
155
- else if (!wantJson && event.delta?.type === "text_delta" && event.delta.text) {
238
+ else if (event.delta?.type === "text_delta" && event.delta.text) {
156
239
  text += event.delta.text;
157
240
  }
158
241
  }
@@ -165,12 +248,23 @@ function createAnthropicAdapter(config) {
165
248
  }
166
249
  }
167
250
  // Anthropic has no service tiers on this path — served is standard.
251
+ // The input side (tokensIn incl. cached+write, the write buckets) was
252
+ // normalized by normalizeAnthropicInputUsage (research §4 fix).
168
253
  const usage = {
169
- tokensIn: promptTokens,
254
+ ...inputUsage,
170
255
  tokensOut: completionTokens,
171
- tokensCached: cachedTokens,
172
256
  servedTier: "standard",
173
257
  };
258
+ // Tool-call turn (spec §4.4): `arguments` is the raw JSON string the
259
+ // model streamed (OpenAI parity) — text may be empty, skip the guards.
260
+ if (wantTools && toolBlocks.size > 0) {
261
+ const toolCalls = [...toolBlocks.values()].map((b, i) => ({
262
+ id: b.id !== "" ? b.id : `call_${i}`,
263
+ type: "function",
264
+ function: { name: b.name, arguments: b.json === "" ? "{}" : b.json },
265
+ }));
266
+ return { ok: true, text, usage, toolCalls };
267
+ }
174
268
  if (stopReason === "refusal") {
175
269
  return fail("refusal", "Anthropic refused this request (stop_reason=refusal)", undefined, usage);
176
270
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build the provider-adapter map from resolved config + registry facts.
3
+ * Extracted from index.ts (the SDK entrypoint) so that file stays under the LOC
4
+ * cap — the same reason wireRigArtifacts / buildExportedRouteMirrors live apart.
5
+ * Mechanical, one caller; behaviour is byte-identical to the prior inline block.
6
+ */
7
+ import type { RigConfig } from "../config";
8
+ import type { RegistryFacts } from "../registry/facts";
9
+ import type { CapabilityFlag, ProviderAdapter } from "../types/provider";
10
+ export declare function buildAdapters(config: RigConfig, registryFacts: RegistryFacts, compatStatic: ReadonlySet<CapabilityFlag>): Map<string, ProviderAdapter>;
11
+ //# sourceMappingURL=build-adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-adapters.d.ts","sourceRoot":"","sources":["../../src/adapters/build-adapters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAQzE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,GACxC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CA6B9B"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Build the provider-adapter map from resolved config + registry facts.
4
+ * Extracted from index.ts (the SDK entrypoint) so that file stays under the LOC
5
+ * cap — the same reason wireRigArtifacts / buildExportedRouteMirrors live apart.
6
+ * Mechanical, one caller; behaviour is byte-identical to the prior inline block.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.buildAdapters = buildAdapters;
10
+ const anthropic_1 = require("./anthropic");
11
+ const deepseek_1 = require("./deepseek");
12
+ const gemini_1 = require("./gemini");
13
+ const grok_1 = require("./grok");
14
+ const openai_1 = require("./openai");
15
+ const openai_compat_1 = require("./openai-compat");
16
+ function buildAdapters(config, registryFacts, compatStatic) {
17
+ const adapters = new Map();
18
+ if (config.keys.gemini)
19
+ adapters.set("gemini", (0, gemini_1.createGeminiAdapter)({ apiKey: config.keys.gemini }));
20
+ if (config.keys.openai)
21
+ adapters.set("openai", (0, openai_1.createOpenAIAdapter)({ apiKey: config.keys.openai }));
22
+ if (config.keys.deepseek) {
23
+ adapters.set("deepseek", (0, deepseek_1.createDeepSeekAdapter)({ apiKey: config.keys.deepseek }));
24
+ }
25
+ if (config.keys.anthropic) {
26
+ adapters.set("anthropic", (0, anthropic_1.createAnthropicAdapter)({ apiKey: config.keys.anthropic }));
27
+ }
28
+ if (config.keys.grok)
29
+ adapters.set("grok", (0, grok_1.createGrokAdapter)({ apiKey: config.keys.grok }));
30
+ // A1 aggregator hosts behind the generic openai-compatible adapter.
31
+ for (const hostId of ["deepinfra", "fireworks"]) {
32
+ const apiKey = config.keys[hostId];
33
+ if (!apiKey)
34
+ continue;
35
+ const host = openai_compat_1.OPENAI_COMPAT_HOSTS[hostId];
36
+ adapters.set(hostId, (0, openai_compat_1.createOpenAICompatAdapter)({
37
+ providerId: hostId,
38
+ baseUrl: host.baseUrl,
39
+ apiKey,
40
+ ...(host.shapeModelId !== undefined ? { shapeModelId: host.shapeModelId } : {}),
41
+ capabilityProfile: (model) => registryFacts.flagsFor(hostId, model, compatStatic) ?? compatStatic,
42
+ }));
43
+ }
44
+ return adapters;
45
+ }
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import type { CapabilityFlag, ProviderAdapter } from "../types/provider";
16
16
  import type { ClassifiedFailure, ShapingInput } from "./gemini";
17
+ import type { OpenAIWireTool, OpenAIWireToolChoice } from "./tool-mapping";
17
18
  export interface DeepSeekAdapterConfig {
18
19
  readonly apiKey: string;
19
20
  }
@@ -31,9 +32,14 @@ export interface DeepSeekShapedParams {
31
32
  readonly stream_options: {
32
33
  readonly include_usage: true;
33
34
  };
35
+ /** CE-6 declared sampling — present only when the route/raw asks for them. */
36
+ readonly temperature?: number;
37
+ readonly top_p?: number;
34
38
  readonly response_format?: {
35
39
  readonly type: "json_object";
36
40
  };
41
+ readonly tools?: readonly OpenAIWireTool[];
42
+ readonly tool_choice?: OpenAIWireToolChoice;
37
43
  }
38
44
  export declare function buildDeepSeekParams(model: string, req: ShapingInput): DeepSeekShapedParams;
39
45
  export declare function createDeepSeekAdapter(config: DeepSeekAdapterConfig): ProviderAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/adapters/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMhF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAiB1E;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;CAC7D;AAOD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,oBAAoB,CAkB1F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAkHpF"}
1
+ {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/adapters/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMhE,OAAO,KAAK,EAAuB,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAMhG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMhF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAiB1E;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAOD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,oBAAoB,CA2B1F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CA8JpF"}
@@ -22,6 +22,9 @@ exports.classifyDeepSeekFailure = classifyDeepSeekFailure;
22
22
  exports.buildDeepSeekParams = buildDeepSeekParams;
23
23
  exports.createDeepSeekAdapter = createDeepSeekAdapter;
24
24
  const openai_1 = __importDefault(require("openai"));
25
+ const tool_mapping_1 = require("./tool-mapping");
26
+ const openai_stream_1 = require("./openai-stream");
27
+ const sampling_1 = require("./sampling");
25
28
  const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
26
29
  const DEFAULT_MAX_TOKENS = 64000; // R1/V3 max — full capacity for long reports (ported)
27
30
  function deepseekSupports(feature, model) {
@@ -69,17 +72,27 @@ function buildDeepSeekParams(model, req) {
69
72
  ];
70
73
  return {
71
74
  model,
72
- max_tokens: req.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
75
+ max_tokens: (0, sampling_1.effectiveMaxOutputTokens)(req, DEFAULT_MAX_TOKENS),
73
76
  messages,
74
77
  stream: true,
75
78
  stream_options: { include_usage: true },
79
+ ...(0, sampling_1.openAISamplingFields)(req),
76
80
  ...(wantJson ? { response_format: { type: "json_object" } } : {}),
81
+ ...(req.tools !== undefined && req.tools.length > 0
82
+ ? {
83
+ tools: (0, tool_mapping_1.toOpenAITools)(req.tools),
84
+ ...((0, tool_mapping_1.toOpenAIToolChoice)(req.toolChoice) !== undefined
85
+ ? { tool_choice: (0, tool_mapping_1.toOpenAIToolChoice)(req.toolChoice) }
86
+ : {}),
87
+ }
88
+ : {}),
77
89
  };
78
90
  }
79
91
  function createDeepSeekAdapter(config) {
80
92
  return {
81
93
  id: "deepseek",
82
94
  supports: deepseekSupports,
95
+ supportsTools: () => true,
83
96
  async call(req) {
84
97
  const model = req.candidate.model;
85
98
  // F1: post-stream failures still billed tokens — attach usage.
@@ -110,6 +123,7 @@ function createDeepSeekAdapter(config) {
110
123
  let finishReason = null;
111
124
  // TTFB (Phase 4 WS3): first streamed chunk = first body byte.
112
125
  let ttfbMs;
126
+ const toolCallAcc = (0, tool_mapping_1.createOpenAIToolCallAccumulator)();
113
127
  // Only delta.content is accumulated — a reasoner's reasoning_content
114
128
  // (chain-of-thought) must never leak into the JSON buffer (ported).
115
129
  for await (const chunk of stream) {
@@ -118,6 +132,7 @@ function createDeepSeekAdapter(config) {
118
132
  const choice = chunk.choices[0];
119
133
  if (choice?.delta?.content)
120
134
  text += choice.delta.content;
135
+ toolCallAcc.push(choice?.delta?.tool_calls);
121
136
  if (choice?.finish_reason)
122
137
  finishReason = choice.finish_reason;
123
138
  if (chunk.usage) {
@@ -135,6 +150,11 @@ function createDeepSeekAdapter(config) {
135
150
  servedTier: "standard",
136
151
  ...(ttfbMs !== undefined ? { ttfbMs } : {}),
137
152
  };
153
+ // Tool-call turn (spec §4.4): surface normalized calls, skip content guards.
154
+ const toolCalls = toolCallAcc.finalize();
155
+ if (toolCalls !== undefined) {
156
+ return { ok: true, text, usage, toolCalls };
157
+ }
138
158
  if (text.trim().length === 0) {
139
159
  return fail("content_invalid", `empty response from DeepSeek (finish_reason=${finishReason ?? "none"})`, undefined, usage);
140
160
  }
@@ -159,5 +179,38 @@ function createDeepSeekAdapter(config) {
159
179
  };
160
180
  }
161
181
  },
182
+ async *stream(req) {
183
+ const model = req.candidate.model;
184
+ try {
185
+ const client = new openai_1.default({ apiKey: config.apiKey, baseURL: DEEPSEEK_BASE_URL, timeout: req.timeoutMs });
186
+ const params = buildDeepSeekParams(model, req);
187
+ const dispatchedAt = Date.now();
188
+ const stream = await client.chat.completions.create(params);
189
+ // DeepSeek: cached tokens surface as prompt_cache_hit_tokens; no tiers.
190
+ yield* (0, openai_stream_1.streamOpenAIDialect)(stream, req, dispatchedAt, {
191
+ provider: "deepseek",
192
+ cachedTokenSource: "cache_hit",
193
+ mapServedTier: () => "standard",
194
+ });
195
+ }
196
+ catch (error) {
197
+ const message = error instanceof Error ? error.message : String(error);
198
+ const { cls, fixHint } = classifyDeepSeekFailure(message);
199
+ yield {
200
+ kind: "final",
201
+ result: {
202
+ ok: false,
203
+ failure: {
204
+ class: cls,
205
+ message,
206
+ provider: "deepseek",
207
+ model,
208
+ cause: error,
209
+ ...(fixHint !== undefined ? { fixHint } : {}),
210
+ },
211
+ },
212
+ };
213
+ }
214
+ },
162
215
  };
163
216
  }
@@ -33,6 +33,16 @@ export declare function classifyGeminiFailure(message: string): ClassifiedFailur
33
33
  /** The candidate-independent part of an AdapterRequest (also what the pure
34
34
  * shaping helpers take, so unit tests never need a branded CandidateRef). */
35
35
  export type ShapingInput = Omit<AdapterRequest, "candidate">;
36
+ /**
37
+ * CE-2 F3 fail-closed guard: `reasoning` declared on a Gemini model that is NOT
38
+ * thinking-capable would 400 at the provider (thinkingConfig on a 2.x model is
39
+ * rejected). Pre-empt with a classified TERMINAL failure so the declaration is
40
+ * never silently dropped and never turns into an unguarded provider 400 — the
41
+ * same fail-closed discipline as the raw-lane provider guard (spec §3 F1/F3).
42
+ * Returns the failure class + fixHint, or null when the request is fine (no
43
+ * reasoning declared, or a thinking-capable model). Pure — unit-tested directly.
44
+ */
45
+ export declare function geminiReasoningUnsupported(model: string, req: ShapingInput): ClassifiedFailure | null;
36
46
  export interface GeminiShapedRequest {
37
47
  readonly model: string;
38
48
  readonly contents: string;
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,cAAc,EAGd,cAAc,EACd,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAyBD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAyCxE;AAED;6EAC6E;AAC7E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,mBAAmB,CAkCxF;AA0BD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CAyHhF"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,cAAc,EAGd,cAAc,EACd,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAKlE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAkCD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAyCxE;AAED;6EAC6E;AAC7E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,iBAAiB,GAAG,IAAI,CAQrG;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,mBAAmB,CAgDxF;AA0BD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CA+JhF"}
@@ -17,9 +17,12 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.geminiSupports = geminiSupports;
19
19
  exports.classifyGeminiFailure = classifyGeminiFailure;
20
+ exports.geminiReasoningUnsupported = geminiReasoningUnsupported;
20
21
  exports.buildGeminiRequest = buildGeminiRequest;
21
22
  exports.createGeminiAdapter = createGeminiAdapter;
22
23
  const genai_1 = require("@google/genai");
24
+ const tool_mapping_1 = require("./tool-mapping");
25
+ const sampling_1 = require("./sampling");
23
26
  /** Models that accept thinkingConfig — passing it to 2.x models 400s.
24
27
  * Ported from the production THINKING_CAPABLE_MODELS set. */
25
28
  const THINKING_CAPABLE_MODELS = new Set([
@@ -27,7 +30,16 @@ const THINKING_CAPABLE_MODELS = new Set([
27
30
  "gemini-3.0-pro-preview",
28
31
  "gemini-3.1-flash-preview",
29
32
  ]);
30
- /** Gemini 3 requires temperature=1.0 — lower values cause looping (ported). */
33
+ /** Gemini-3 default. Google STRONGLY recommends temperature=1.0 for all
34
+ * Gemini 3 models and warns that values below 1.0 can cause looping / degraded
35
+ * output — concentrated on complex reasoning/math tasks, not simple
36
+ * classification (verified 2026-08-20 against ai.google.dev/gemini-api/docs/gemini-3;
37
+ * the source pipeline ran gemini-3.1-pro at 0.1 for CLASSIFICATION without
38
+ * looping, which the reasoning-task carve-out explains). It is a recommendation,
39
+ * not a hard API cap: the accepted range stays 0–2. This is the DEFAULT the
40
+ * adapter sends when a route declares no `sampling.temperature`; a declared
41
+ * value overrides it and is sent as-is (CE-6), with a load-time warning emitted
42
+ * for a sub-1.0 value on a Gemini-3 candidate (routes/loader.ts). */
31
43
  const GEMINI_TEMPERATURE = 1.0;
32
44
  const DEFAULT_MAX_OUTPUT_TOKENS = 64000; // thinking tokens count against this
33
45
  const TOP_P = 0.95;
@@ -88,15 +100,41 @@ function classifyGeminiFailure(message) {
88
100
  }
89
101
  return { cls: "network" };
90
102
  }
103
+ /**
104
+ * CE-2 F3 fail-closed guard: `reasoning` declared on a Gemini model that is NOT
105
+ * thinking-capable would 400 at the provider (thinkingConfig on a 2.x model is
106
+ * rejected). Pre-empt with a classified TERMINAL failure so the declaration is
107
+ * never silently dropped and never turns into an unguarded provider 400 — the
108
+ * same fail-closed discipline as the raw-lane provider guard (spec §3 F1/F3).
109
+ * Returns the failure class + fixHint, or null when the request is fine (no
110
+ * reasoning declared, or a thinking-capable model). Pure — unit-tested directly.
111
+ */
112
+ function geminiReasoningUnsupported(model, req) {
113
+ if (req.reasoning !== undefined && !THINKING_CAPABLE_MODELS.has(model)) {
114
+ return {
115
+ cls: "invariant_violation",
116
+ fixHint: `reasoning control requires a thinking-capable Gemini model; "${model}" is not one — remove reasoning or choose a thinking-capable model`,
117
+ };
118
+ }
119
+ return null;
120
+ }
91
121
  /** Pure request shaping — everything except the abort signal. */
92
122
  function buildGeminiRequest(model, req) {
93
123
  const usingCache = req.cache?.key !== undefined && req.cache.key !== "";
124
+ // CE-6: a declared sampling field OVERRIDES the ported constant; absent, the
125
+ // constant stands (byte-identical to pre-CE-6). topK stays internal (gemini-only,
126
+ // not on the customer surface — spec §1 non-goals).
127
+ // CE-2 F3: a declared reasoning level replaces the hardcoded `"high"` for the
128
+ // thinking-capable models; absent, `"high"` stands (byte-identical to
129
+ // pre-CE-2). A declared level on a NON-capable model is rejected fail-closed in
130
+ // call() (geminiReasoningUnsupported) — it never reaches this shaping.
131
+ const thinkingLevel = req.reasoning?.level ?? "high";
94
132
  const config = {
95
- temperature: GEMINI_TEMPERATURE,
96
- maxOutputTokens: req.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
97
- topP: TOP_P,
133
+ temperature: req.sampling?.temperature ?? GEMINI_TEMPERATURE,
134
+ maxOutputTokens: (0, sampling_1.effectiveMaxOutputTokens)(req, DEFAULT_MAX_OUTPUT_TOKENS),
135
+ topP: req.sampling?.topP ?? TOP_P,
98
136
  topK: TOP_K,
99
- ...(THINKING_CAPABLE_MODELS.has(model) ? { thinkingConfig: { thinkingLevel: "high" } } : {}),
137
+ ...(THINKING_CAPABLE_MODELS.has(model) ? { thinkingConfig: { thinkingLevel } } : {}),
100
138
  };
101
139
  if (req.outputSchema !== null) {
102
140
  config.responseMimeType = "application/json";
@@ -115,6 +153,14 @@ function buildGeminiRequest(model, req) {
115
153
  if (req.grounding === true) {
116
154
  config.tools = [{ googleSearch: {} }];
117
155
  }
156
+ else if (req.tools !== undefined && req.tools.length > 0) {
157
+ // Caller-supplied tools (spec §4.4) → Gemini functionDeclarations.
158
+ // Grounding wins the single `tools` slot when both are asked for.
159
+ config.tools = (0, tool_mapping_1.toGeminiTools)(req.tools);
160
+ const toolConfig = (0, tool_mapping_1.toGeminiToolConfig)(req.toolChoice);
161
+ if (toolConfig !== undefined)
162
+ config.toolConfig = toolConfig;
163
+ }
118
164
  }
119
165
  if (req.requestedTier === "flex") {
120
166
  config.serviceTier = "flex";
@@ -139,6 +185,7 @@ function createGeminiAdapter(config) {
139
185
  return {
140
186
  id: "gemini",
141
187
  supports: geminiSupports,
188
+ supportsTools: () => true,
142
189
  async call(req) {
143
190
  const model = req.candidate.model;
144
191
  const abortController = new AbortController();
@@ -156,6 +203,13 @@ function createGeminiAdapter(config) {
156
203
  },
157
204
  ...(usage !== undefined ? { usage } : {}),
158
205
  });
206
+ // CE-2 F3: reject reasoning on a non-thinking model BEFORE any dispatch —
207
+ // classified, terminal, never a silent drop (spec §3 F3).
208
+ const reasoningUnsupported = geminiReasoningUnsupported(model, req);
209
+ if (reasoningUnsupported !== null) {
210
+ clearTimeout(timeoutId);
211
+ return fail(reasoningUnsupported.cls, `reasoning control declared on the non-thinking Gemini model "${model}"`, reasoningUnsupported.fixHint);
212
+ }
159
213
  try {
160
214
  const shaped = buildGeminiRequest(model, req);
161
215
  const dispatchedAt = Date.now();
@@ -176,9 +230,26 @@ function createGeminiAdapter(config) {
176
230
  const tokensOut = (usageMetadata.candidatesTokenCount ?? 0) + (usageMetadata.thoughtsTokenCount ?? 0);
177
231
  const tokensCached = usageMetadata.cachedContentTokenCount ?? 0;
178
232
  const usage = { tokensIn, tokensOut, tokensCached, servedTier, ttfbMs };
179
- const candidates = response
180
- .candidates;
233
+ const candidates = response.candidates;
181
234
  const finishReason = candidates?.[0]?.finishReason;
235
+ // Tool-call turn (spec §4.4): collect functionCall parts. When present,
236
+ // surface normalized calls and skip the text/empty/truncation guards.
237
+ // Read any accompanying text from the parts (not the `response.text`
238
+ // getter, which can throw/warn on a functionCall-only response).
239
+ if (req.tools !== undefined && req.tools.length > 0) {
240
+ const parts = candidates?.[0]?.content?.parts ?? [];
241
+ const fnCalls = parts
242
+ .map((p) => p.functionCall)
243
+ .filter((fc) => fc !== undefined && fc !== null);
244
+ const toolCalls = (0, tool_mapping_1.geminiFunctionCallsToToolCalls)(fnCalls);
245
+ if (toolCalls !== undefined) {
246
+ const toolText = parts
247
+ .map((p) => p.text)
248
+ .filter((t) => typeof t === "string")
249
+ .join("");
250
+ return { ok: true, text: toolText, usage, toolCalls };
251
+ }
252
+ }
182
253
  if (finishReason === "SAFETY" || finishReason === "PROHIBITED_CONTENT") {
183
254
  return fail("refusal", `Gemini blocked output via safety filter (finishReason=${finishReason})`, undefined, usage);
184
255
  }
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import type { CapabilityFlag, ProviderAdapter } from "../types/provider";
14
14
  import type { ClassifiedFailure, ShapingInput } from "./gemini";
15
+ import type { OpenAIWireTool, OpenAIWireToolChoice } from "./tool-mapping";
15
16
  export interface GrokAdapterConfig {
16
17
  readonly apiKey: string;
17
18
  }
@@ -29,6 +30,12 @@ export interface GrokShapedParams {
29
30
  readonly stream_options: {
30
31
  readonly include_usage: true;
31
32
  };
33
+ /** CE-6 declared sampling — present only when the route/raw asks for them. */
34
+ readonly temperature?: number;
35
+ readonly top_p?: number;
36
+ /** Prompt-cache routing hint (WS-C) — the stable-prefix fingerprint the run
37
+ * loop stamps; xAI keeps a warm cache keyed to it (writes free today). */
38
+ readonly prompt_cache_key?: string;
32
39
  readonly response_format?: {
33
40
  readonly type: "json_schema";
34
41
  readonly json_schema: {
@@ -37,6 +44,8 @@ export interface GrokShapedParams {
37
44
  readonly strict: true;
38
45
  };
39
46
  };
47
+ readonly tools?: readonly OpenAIWireTool[];
48
+ readonly tool_choice?: OpenAIWireToolChoice;
40
49
  }
41
50
  export declare function buildGrokParams(model: string, req: ShapingInput): GrokShapedParams;
42
51
  export declare function createGrokAdapter(config: GrokAdapterConfig): ProviderAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"grok.d.ts","sourceRoot":"","sources":["../../src/adapters/grok.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAa7E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAsBtE;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE;QACzB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAC7B,QAAQ,CAAC,WAAW,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;SAAE,CAAC;KACjG,CAAC;CACH;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,gBAAgB,CAwBlF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CA4G5E"}
1
+ {"version":3,"file":"grok.d.ts","sourceRoot":"","sources":["../../src/adapters/grok.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMhE,OAAO,KAAK,EAAuB,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAMhG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAc7E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAsBtE;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;8EAC0E;IAC1E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE;QACzB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAC7B,QAAQ,CAAC,WAAW,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;SAAE,CAAC;KACjG,CAAC;IACF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,gBAAgB,CAsClF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAgK5E"}