@earendil-works/pi-ai 1.4.0 → 1.5.1

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 (318) hide show
  1. package/README.md +58 -39
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +282 -87
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.js +1 -7
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +127 -103
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/bedrock-converse-stream.lazy.d.ts +7 -0
  12. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
  13. package/dist/api/bedrock-converse-stream.lazy.js +18 -5
  14. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
  15. package/dist/api/constrained-sampling.d.ts +13 -1
  16. package/dist/api/constrained-sampling.d.ts.map +1 -1
  17. package/dist/api/constrained-sampling.js +38 -18
  18. package/dist/api/constrained-sampling.js.map +1 -1
  19. package/dist/api/google-generative-ai.d.ts.map +1 -1
  20. package/dist/api/google-generative-ai.js +0 -6
  21. package/dist/api/google-generative-ai.js.map +1 -1
  22. package/dist/api/google-shared.d.ts +2 -1
  23. package/dist/api/google-shared.d.ts.map +1 -1
  24. package/dist/api/google-shared.js +57 -2
  25. package/dist/api/google-shared.js.map +1 -1
  26. package/dist/api/google-vertex.d.ts.map +1 -1
  27. package/dist/api/google-vertex.js +0 -6
  28. package/dist/api/google-vertex.js.map +1 -1
  29. package/dist/api/lazy.d.ts +1 -1
  30. package/dist/api/lazy.d.ts.map +1 -1
  31. package/dist/api/lazy.js +9 -8
  32. package/dist/api/lazy.js.map +1 -1
  33. package/dist/api/mistral-conversations.d.ts.map +1 -1
  34. package/dist/api/mistral-conversations.js +17 -44
  35. package/dist/api/mistral-conversations.js.map +1 -1
  36. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  37. package/dist/api/openai-codex-responses.js +7 -8
  38. package/dist/api/openai-codex-responses.js.map +1 -1
  39. package/dist/api/openai-completions.d.ts +2 -1
  40. package/dist/api/openai-completions.d.ts.map +1 -1
  41. package/dist/api/openai-completions.js +55 -59
  42. package/dist/api/openai-completions.js.map +1 -1
  43. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  44. package/dist/api/openai-responses-shared.js +58 -57
  45. package/dist/api/openai-responses-shared.js.map +1 -1
  46. package/dist/api/openai-responses.d.ts.map +1 -1
  47. package/dist/api/openai-responses.js +4 -8
  48. package/dist/api/openai-responses.js.map +1 -1
  49. package/dist/api/openrouter-images.js +1 -0
  50. package/dist/api/openrouter-images.js.map +1 -1
  51. package/dist/api/pi-messages.d.ts +2 -0
  52. package/dist/api/pi-messages.d.ts.map +1 -1
  53. package/dist/api/pi-messages.js +24 -9
  54. package/dist/api/pi-messages.js.map +1 -1
  55. package/dist/api/simple-options.d.ts +1 -0
  56. package/dist/api/simple-options.d.ts.map +1 -1
  57. package/dist/api/simple-options.js +16 -2
  58. package/dist/api/simple-options.js.map +1 -1
  59. package/dist/api/strict-tool-limits.d.ts +13 -0
  60. package/dist/api/strict-tool-limits.d.ts.map +1 -0
  61. package/dist/api/strict-tool-limits.js +50 -0
  62. package/dist/api/strict-tool-limits.js.map +1 -0
  63. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  64. package/dist/auth/oauth/github-copilot.js +4 -11
  65. package/dist/auth/oauth/github-copilot.js.map +1 -1
  66. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  67. package/dist/auth/oauth/kimi-coding.js +4 -22
  68. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  69. package/dist/auth/oauth/load.d.ts.map +1 -1
  70. package/dist/auth/oauth/load.js +5 -2
  71. package/dist/auth/oauth/load.js.map +1 -1
  72. package/dist/auth/oauth/meta.d.ts.map +1 -1
  73. package/dist/auth/oauth/meta.js +1 -14
  74. package/dist/auth/oauth/meta.js.map +1 -1
  75. package/dist/auth/oauth/radius.d.ts.map +1 -1
  76. package/dist/auth/oauth/radius.js +9 -3
  77. package/dist/auth/oauth/radius.js.map +1 -1
  78. package/dist/auth/oauth/trusted-url.d.ts +7 -0
  79. package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
  80. package/dist/auth/oauth/trusted-url.js +12 -0
  81. package/dist/auth/oauth/trusted-url.js.map +1 -0
  82. package/dist/auth/types.d.ts +4 -0
  83. package/dist/auth/types.d.ts.map +1 -1
  84. package/dist/auth/types.js.map +1 -1
  85. package/dist/cache-warming.d.ts +53 -0
  86. package/dist/cache-warming.d.ts.map +1 -0
  87. package/dist/cache-warming.js +82 -0
  88. package/dist/cache-warming.js.map +1 -0
  89. package/dist/cli.js +6 -2
  90. package/dist/cli.js.map +1 -1
  91. package/dist/compat/extension-oauth-types.d.ts +4 -0
  92. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  93. package/dist/compat/extension-oauth-types.js.map +1 -1
  94. package/dist/env-api-keys.d.ts.map +1 -1
  95. package/dist/env-api-keys.js +1 -0
  96. package/dist/env-api-keys.js.map +1 -1
  97. package/dist/index.d.ts +3 -3
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3 -2
  100. package/dist/index.js.map +1 -1
  101. package/dist/lazy-bedrock-provider.d.ts +10 -0
  102. package/dist/lazy-bedrock-provider.d.ts.map +1 -0
  103. package/dist/lazy-bedrock-provider.js +13 -0
  104. package/dist/lazy-bedrock-provider.js.map +1 -0
  105. package/dist/lazy-oauth.d.ts +10 -0
  106. package/dist/lazy-oauth.d.ts.map +1 -0
  107. package/dist/lazy-oauth.js +23 -0
  108. package/dist/lazy-oauth.js.map +1 -0
  109. package/dist/models.d.ts +7 -0
  110. package/dist/models.d.ts.map +1 -1
  111. package/dist/models.generated.d.ts +4 -0
  112. package/dist/models.generated.d.ts.map +1 -1
  113. package/dist/models.generated.js +5 -1
  114. package/dist/models.generated.js.map +1 -1
  115. package/dist/models.js +20 -11
  116. package/dist/models.js.map +1 -1
  117. package/dist/providers/all.d.ts.map +1 -1
  118. package/dist/providers/all.js +2 -0
  119. package/dist/providers/all.js.map +1 -1
  120. package/dist/providers/amazon-bedrock.models.js +1 -1
  121. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  122. package/dist/providers/ant-ling.models.js +1 -1
  123. package/dist/providers/ant-ling.models.js.map +1 -1
  124. package/dist/providers/anthropic.models.js +1 -1
  125. package/dist/providers/anthropic.models.js.map +1 -1
  126. package/dist/providers/azure.models.js +1 -1
  127. package/dist/providers/azure.models.js.map +1 -1
  128. package/dist/providers/baseten.models.js +1 -1
  129. package/dist/providers/baseten.models.js.map +1 -1
  130. package/dist/providers/cerebras.models.js +1 -1
  131. package/dist/providers/cerebras.models.js.map +1 -1
  132. package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
  133. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  134. package/dist/providers/cloudflare-workers-ai.models.js +1 -1
  135. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  136. package/dist/providers/compat-schema.d.ts +444 -456
  137. package/dist/providers/compat-schema.d.ts.map +1 -1
  138. package/dist/providers/compat-schema.js +166 -112
  139. package/dist/providers/compat-schema.js.map +1 -1
  140. package/dist/providers/data/.manifest.json +1 -1
  141. package/dist/providers/data/amazon-bedrock.json +1 -1
  142. package/dist/providers/data/anthropic.json +1 -1
  143. package/dist/providers/data/azure.json +1 -1
  144. package/dist/providers/data/baseten.json +1 -1
  145. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  146. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  147. package/dist/providers/data/fireworks.json +1 -1
  148. package/dist/providers/data/github-copilot.json +1 -1
  149. package/dist/providers/data/google-vertex.json +1 -1
  150. package/dist/providers/data/google.json +1 -1
  151. package/dist/providers/data/huggingface.json +1 -1
  152. package/dist/providers/data/kimi-coding.json +1 -1
  153. package/dist/providers/data/mistral.json +1 -1
  154. package/dist/providers/data/moonshotai-cn.json +1 -1
  155. package/dist/providers/data/moonshotai.json +1 -1
  156. package/dist/providers/data/nvidia.json +1 -1
  157. package/dist/providers/data/openai-codex.json +1 -1
  158. package/dist/providers/data/openai.json +1 -1
  159. package/dist/providers/data/opencode-go.json +1 -1
  160. package/dist/providers/data/opencode.json +1 -1
  161. package/dist/providers/data/openrouter.json +1 -1
  162. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  163. package/dist/providers/data/qwen-token-plan-individual-cn.json +1 -0
  164. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  165. package/dist/providers/data/qwen-token-plan.json +1 -1
  166. package/dist/providers/data/radius.json +1 -1
  167. package/dist/providers/data/together.json +1 -1
  168. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  169. package/dist/providers/data/xai.json +1 -1
  170. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  171. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  172. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  173. package/dist/providers/data/xiaomi.json +1 -1
  174. package/dist/providers/data/zai-coding-cn.json +1 -1
  175. package/dist/providers/data/zai.json +1 -1
  176. package/dist/providers/deepseek.models.js +1 -1
  177. package/dist/providers/deepseek.models.js.map +1 -1
  178. package/dist/providers/faux.d.ts.map +1 -1
  179. package/dist/providers/faux.js +7 -2
  180. package/dist/providers/faux.js.map +1 -1
  181. package/dist/providers/fireworks.models.js +1 -1
  182. package/dist/providers/fireworks.models.js.map +1 -1
  183. package/dist/providers/github-copilot.d.ts +3 -1
  184. package/dist/providers/github-copilot.d.ts.map +1 -1
  185. package/dist/providers/github-copilot.js +13 -2
  186. package/dist/providers/github-copilot.js.map +1 -1
  187. package/dist/providers/github-copilot.models.js +1 -1
  188. package/dist/providers/github-copilot.models.js.map +1 -1
  189. package/dist/providers/google-vertex.models.js +1 -1
  190. package/dist/providers/google-vertex.models.js.map +1 -1
  191. package/dist/providers/google.models.js +1 -1
  192. package/dist/providers/google.models.js.map +1 -1
  193. package/dist/providers/groq.models.js +1 -1
  194. package/dist/providers/groq.models.js.map +1 -1
  195. package/dist/providers/huggingface.models.js +1 -1
  196. package/dist/providers/huggingface.models.js.map +1 -1
  197. package/dist/providers/kimi-coding.models.js +1 -1
  198. package/dist/providers/kimi-coding.models.js.map +1 -1
  199. package/dist/providers/meta.models.js +1 -1
  200. package/dist/providers/meta.models.js.map +1 -1
  201. package/dist/providers/minimax-cn.models.js +1 -1
  202. package/dist/providers/minimax-cn.models.js.map +1 -1
  203. package/dist/providers/minimax.models.js +1 -1
  204. package/dist/providers/minimax.models.js.map +1 -1
  205. package/dist/providers/mistral.models.js +1 -1
  206. package/dist/providers/mistral.models.js.map +1 -1
  207. package/dist/providers/model-schema.d.ts +65 -65
  208. package/dist/providers/model-schema.d.ts.map +1 -1
  209. package/dist/providers/model-schema.js +41 -30
  210. package/dist/providers/model-schema.js.map +1 -1
  211. package/dist/providers/moonshotai-cn.models.js +1 -1
  212. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  213. package/dist/providers/moonshotai.models.js +1 -1
  214. package/dist/providers/moonshotai.models.js.map +1 -1
  215. package/dist/providers/nvidia.models.js +1 -1
  216. package/dist/providers/nvidia.models.js.map +1 -1
  217. package/dist/providers/openai-codex.js +1 -1
  218. package/dist/providers/openai-codex.js.map +1 -1
  219. package/dist/providers/openai-codex.models.js +1 -1
  220. package/dist/providers/openai-codex.models.js.map +1 -1
  221. package/dist/providers/openai.models.js +1 -1
  222. package/dist/providers/openai.models.js.map +1 -1
  223. package/dist/providers/opencode-go.models.js +1 -1
  224. package/dist/providers/opencode-go.models.js.map +1 -1
  225. package/dist/providers/opencode.js +1 -1
  226. package/dist/providers/opencode.js.map +1 -1
  227. package/dist/providers/opencode.models.js +1 -1
  228. package/dist/providers/opencode.models.js.map +1 -1
  229. package/dist/providers/openrouter.models.js +1 -1
  230. package/dist/providers/openrouter.models.js.map +1 -1
  231. package/dist/providers/qwen-token-plan-cn.models.js +1 -1
  232. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  233. package/dist/providers/qwen-token-plan-individual-cn.d.ts +3 -0
  234. package/dist/providers/qwen-token-plan-individual-cn.d.ts.map +1 -0
  235. package/dist/providers/qwen-token-plan-individual-cn.js +15 -0
  236. package/dist/providers/qwen-token-plan-individual-cn.js.map +1 -0
  237. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts +6 -0
  238. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts.map +1 -0
  239. package/dist/providers/qwen-token-plan-individual-cn.models.js +8 -0
  240. package/dist/providers/qwen-token-plan-individual-cn.models.js.map +1 -0
  241. package/dist/providers/qwen-token-plan-individual.models.js +1 -1
  242. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  243. package/dist/providers/qwen-token-plan.models.js +1 -1
  244. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  245. package/dist/providers/radius-config.d.ts.map +1 -1
  246. package/dist/providers/radius-config.js +2 -1
  247. package/dist/providers/radius-config.js.map +1 -1
  248. package/dist/providers/radius.models.js +1 -1
  249. package/dist/providers/radius.models.js.map +1 -1
  250. package/dist/providers/schema-builders.d.ts +10 -0
  251. package/dist/providers/schema-builders.d.ts.map +1 -0
  252. package/dist/providers/schema-builders.js +18 -0
  253. package/dist/providers/schema-builders.js.map +1 -0
  254. package/dist/providers/together.models.js +1 -1
  255. package/dist/providers/together.models.js.map +1 -1
  256. package/dist/providers/typesafe.models.js +1 -1
  257. package/dist/providers/typesafe.models.js.map +1 -1
  258. package/dist/providers/vercel-ai-gateway.models.js +1 -1
  259. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  260. package/dist/providers/xai.models.js +1 -1
  261. package/dist/providers/xai.models.js.map +1 -1
  262. package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
  263. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  264. package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
  265. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  266. package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
  267. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  268. package/dist/providers/xiaomi.models.js +1 -1
  269. package/dist/providers/xiaomi.models.js.map +1 -1
  270. package/dist/providers/zai-coding-cn.models.js +1 -1
  271. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  272. package/dist/providers/zai.models.js +1 -1
  273. package/dist/providers/zai.models.js.map +1 -1
  274. package/dist/telemetry.d.ts.map +1 -1
  275. package/dist/telemetry.js +2 -1
  276. package/dist/telemetry.js.map +1 -1
  277. package/dist/types.d.ts +20 -3
  278. package/dist/types.d.ts.map +1 -1
  279. package/dist/types.js.map +1 -1
  280. package/dist/utils/error-body.d.ts +8 -0
  281. package/dist/utils/error-body.d.ts.map +1 -1
  282. package/dist/utils/error-body.js +30 -2
  283. package/dist/utils/error-body.js.map +1 -1
  284. package/dist/utils/openrouter-cost.d.ts +24 -0
  285. package/dist/utils/openrouter-cost.d.ts.map +1 -0
  286. package/dist/utils/openrouter-cost.js +34 -0
  287. package/dist/utils/openrouter-cost.js.map +1 -0
  288. package/dist/utils/overflow.d.ts.map +1 -1
  289. package/dist/utils/overflow.js +2 -0
  290. package/dist/utils/overflow.js.map +1 -1
  291. package/dist/utils/retry.d.ts.map +1 -1
  292. package/dist/utils/retry.js +6 -0
  293. package/dist/utils/retry.js.map +1 -1
  294. package/dist/utils/streaming-json.d.ts +84 -0
  295. package/dist/utils/streaming-json.d.ts.map +1 -0
  296. package/dist/utils/streaming-json.js +513 -0
  297. package/dist/utils/streaming-json.js.map +1 -0
  298. package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
  299. package/dist/utils/string-enum.d.ts.map +1 -0
  300. package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
  301. package/dist/utils/string-enum.js.map +1 -0
  302. package/dist/utils/text.d.ts +7 -0
  303. package/dist/utils/text.d.ts.map +1 -1
  304. package/dist/utils/text.js +9 -0
  305. package/dist/utils/text.js.map +1 -1
  306. package/dist/utils/transcript.d.ts +1 -1
  307. package/dist/utils/transcript.js +1 -1
  308. package/dist/utils/transcript.js.map +1 -1
  309. package/dist/utils/validation.d.ts +5 -3
  310. package/dist/utils/validation.d.ts.map +1 -1
  311. package/dist/utils/validation.js +27 -277
  312. package/dist/utils/validation.js.map +1 -1
  313. package/dist/utils/version.d.ts +1 -1
  314. package/dist/utils/version.js +1 -1
  315. package/dist/utils/version.js.map +1 -1
  316. package/package.json +19 -10
  317. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  318. package/dist/utils/typebox-helpers.js.map +0 -1
@@ -5,13 +5,14 @@ import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
5
  import { shortHash } from "../utils/hash.js";
6
6
  import { headersToRecord } from "../utils/headers.js";
7
7
  import { getIdentityHeaders } from "../utils/identity.js";
8
- import { parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
8
+ import { applyOpenRouterReportedCost, isOpenRouterModel, } from "../utils/openrouter-cost.js";
9
9
  import { getProviderEnvValue } from "../utils/provider-env.js";
10
10
  import { retryProviderRequest } from "../utils/provider-retry.js";
11
11
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
12
+ import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
12
13
  import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
13
14
  import { getDeclaredTools, resolveTranscript, resolveTranscriptTools, } from "../utils/transcript.js";
14
- import { appendGrammarToolInputJsonDelta, createGrammarToolInputProperties, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
15
+ import { appendGrammarToolInputJsonDelta, closeGrammarToolInputJson, createGrammarToolInputProperties, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
15
16
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
16
17
  import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
17
18
  import { buildBaseOptions, clampThinkingBudgetToAnswerRoom, resolveSamplingParams, thinkingBudgetForLevel, } from "./simple-options.js";
@@ -206,23 +207,14 @@ export const stream = (model, context, options) => {
206
207
  let hasFinishReason = false;
207
208
  const toolCallBlocksByIndex = new Map();
208
209
  const toolCallBlocksById = new Map();
210
+ const toolCallStates = new Map();
209
211
  const blocks = output.content;
210
212
  const getContentIndex = (block) => blocks.indexOf(block);
211
- const getCustomToolCallInput = (block) => {
212
- const property = block.customInput?.property;
213
- if (property === undefined)
214
- return "";
215
- const value = block.arguments[property];
216
- return typeof value === "string" ? value : "";
217
- };
218
- const appendCustomToolCallInput = (block, nextInput, close) => {
219
- const customInput = block.customInput;
220
- if (!customInput)
221
- return undefined;
222
- const delta = appendGrammarToolInputJsonDelta(customInput.jsonBuffer, customInput.property, nextInput, close);
223
- block.arguments = { [customInput.property]: nextInput };
224
- return delta;
225
- };
213
+ const customInput = (property) => ({
214
+ kind: "custom",
215
+ property,
216
+ buffer: { input: "", started: false, closed: false },
217
+ });
226
218
  const finishBlock = (block) => {
227
219
  const contentIndex = getContentIndex(block);
228
220
  if (contentIndex === -1) {
@@ -246,8 +238,10 @@ export const stream = (model, context, options) => {
246
238
  });
247
239
  }
248
240
  else if (block.type === "toolCall") {
249
- if (block.customInput) {
250
- const delta = appendCustomToolCallInput(block, getCustomToolCallInput(block), true);
241
+ const input = toolCallStates.get(block).input;
242
+ toolCallStates.delete(block);
243
+ if (input.kind === "custom") {
244
+ const delta = closeGrammarToolInputJson(input.buffer, input.property, undefined);
251
245
  if (delta !== undefined) {
252
246
  stream.push({
253
247
  type: "toolcall_delta",
@@ -258,13 +252,8 @@ export const stream = (model, context, options) => {
258
252
  }
259
253
  }
260
254
  else {
261
- Object.assign(block, parseToolCallArguments(block.partialArgs));
255
+ Object.assign(block, input.arguments.finish());
262
256
  }
263
- // Finalize in-place and strip the scratch buffers so replay only
264
- // carries parsed arguments.
265
- delete block.partialArgs;
266
- delete block.customInput;
267
- delete block.streamIndex;
268
257
  stream.push({
269
258
  type: "toolcall_end",
270
259
  contentIndex,
@@ -304,18 +293,18 @@ export const stream = (model, context, options) => {
304
293
  // Note: the "input" fallback here should/must not be taken. in case the LLM makes up
305
294
  // a tool we don't knwo about, we at least have a place to stash our stuff.
306
295
  const customInputProperty = toolCall.custom && !toolCall.function ? (grammarToolInputProperties.get(name) ?? "input") : undefined;
307
- const hasCustomInput = customInputProperty !== undefined;
308
296
  block = {
309
297
  type: "toolCall",
310
298
  id: toolCall.id || "",
311
299
  name,
312
- arguments: hasCustomInput ? { [customInputProperty]: "" } : {},
313
- partialArgs: hasCustomInput ? undefined : "",
314
- customInput: hasCustomInput
315
- ? { property: customInputProperty, jsonBuffer: { input: "", started: false, closed: false } }
316
- : undefined,
317
- streamIndex,
300
+ arguments: customInputProperty !== undefined ? { [customInputProperty]: "" } : {},
318
301
  };
302
+ toolCallStates.set(block, {
303
+ streamIndex,
304
+ input: customInputProperty !== undefined
305
+ ? customInput(customInputProperty)
306
+ : { kind: "json", arguments: new ToolCallArgumentsStream() },
307
+ });
319
308
  if (streamIndex !== undefined) {
320
309
  toolCallBlocksByIndex.set(streamIndex, block);
321
310
  }
@@ -329,8 +318,9 @@ export const stream = (model, context, options) => {
329
318
  partial: output,
330
319
  });
331
320
  }
332
- if (streamIndex !== undefined && block.streamIndex === undefined) {
333
- block.streamIndex = streamIndex;
321
+ const state = toolCallStates.get(block);
322
+ if (streamIndex !== undefined && state.streamIndex === undefined) {
323
+ state.streamIndex = streamIndex;
334
324
  toolCallBlocksByIndex.set(streamIndex, block);
335
325
  }
336
326
  if (toolCall.id) {
@@ -339,16 +329,12 @@ export const stream = (model, context, options) => {
339
329
  if (!block.name && name) {
340
330
  block.name = name;
341
331
  }
342
- if (toolCall.custom && !toolCall.function && !block.customInput) {
332
+ if (toolCall.custom && !toolCall.function && state.input.kind !== "custom") {
343
333
  const customInputProperty = grammarToolInputProperties.get(block.name) ?? "input";
344
334
  block.arguments = { [customInputProperty]: "" };
345
- block.customInput = {
346
- property: customInputProperty,
347
- jsonBuffer: { input: "", started: false, closed: false },
348
- };
349
- delete block.partialArgs;
335
+ state.input = customInput(customInputProperty);
350
336
  }
351
- return block;
337
+ return { block, state };
352
338
  };
353
339
  for await (const chunk of openaiStream) {
354
340
  await options?.onProviderStreamEvent?.(chunk, model);
@@ -440,7 +426,7 @@ export const stream = (model, context, options) => {
440
426
  }
441
427
  if (choice?.delta?.tool_calls) {
442
428
  for (const toolCall of choice.delta.tool_calls) {
443
- const block = ensureToolCallBlock(toolCall);
429
+ const { block, state } = ensureToolCallBlock(toolCall);
444
430
  if (!block.id && toolCall.id) {
445
431
  block.id = toolCall.id;
446
432
  toolCallBlocksById.set(toolCall.id, block);
@@ -450,14 +436,15 @@ export const stream = (model, context, options) => {
450
436
  block.name = name;
451
437
  }
452
438
  let delta = "";
453
- if (toolCall.function?.arguments) {
439
+ const input = state.input;
440
+ if (toolCall.function?.arguments && input.kind === "json") {
454
441
  delta = toolCall.function.arguments;
455
- block.partialArgs = (block.partialArgs ?? "") + toolCall.function.arguments;
456
- block.arguments = parseStreamingJson(block.partialArgs);
442
+ block.arguments = input.arguments.append(delta);
457
443
  }
458
- else if (toolCall.custom?.input) {
459
- const nextInput = getCustomToolCallInput(block) + toolCall.custom.input;
460
- delta = appendCustomToolCallInput(block, nextInput, false) ?? "";
444
+ else if (toolCall.custom?.input && input.kind === "custom") {
445
+ delta =
446
+ appendGrammarToolInputJsonDelta(input.buffer, input.property, toolCall.custom.input) ?? "";
447
+ block.arguments = { [input.property]: input.buffer.input };
461
448
  }
462
449
  stream.push({
463
450
  type: "toolcall_delta",
@@ -495,11 +482,6 @@ export const stream = (model, context, options) => {
495
482
  if (block.type === "thinking") {
496
483
  applyStreamedReasoningDetails(block);
497
484
  }
498
- delete block.index;
499
- // Streaming scratch buffers are only used during parsing; never persist them.
500
- delete block.partialArgs;
501
- delete block.customInput;
502
- delete block.streamIndex;
503
485
  }
504
486
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
505
487
  output.errorMessage = formatProviderError(normalizeProviderError(error));
@@ -573,12 +555,16 @@ function buildParams(model, context, options, compat = getCompat(model), cacheRe
573
555
  grammarToolInputProperties,
574
556
  });
575
557
  const cacheControl = getCompatCacheControl(compat, cacheRetention);
558
+ const promptCacheKeyMode = compat.promptCacheKeyMode ?? "auto";
559
+ const supportsPromptCacheKey = promptCacheKeyMode === "enabled" ||
560
+ (promptCacheKeyMode === "auto" &&
561
+ (model.baseUrl.includes("api.openai.com") ||
562
+ (cacheRetention === "long" && compat.supportsLongCacheRetention)));
576
563
  const params = {
577
564
  model: model.id,
578
565
  messages,
579
566
  stream: true,
580
- prompt_cache_key: (model.baseUrl.includes("api.openai.com") && cacheRetention !== "none") ||
581
- (cacheRetention === "long" && compat.supportsLongCacheRetention)
567
+ prompt_cache_key: cacheRetention !== "none" && supportsPromptCacheKey
582
568
  ? clampOpenAIPromptCacheKey(options?.sessionId)
583
569
  : undefined,
584
570
  prompt_cache_retention: cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : undefined,
@@ -607,8 +593,7 @@ function buildParams(model, context, options, compat = getCompat(model), cacheRe
607
593
  params.tool_stream = true;
608
594
  }
609
595
  }
610
- else if (hasToolHistory(context.messages)) {
611
- // Anthropic (via LiteLLM/proxy) requires tools param when conversation has tool_calls/tool_results
596
+ else if (compat.requiresToolsWithToolHistory && hasToolHistory(context.messages)) {
612
597
  params.tools = [];
613
598
  }
614
599
  if (cacheControl) {
@@ -968,7 +953,7 @@ export function convertMessages(model, context, compat, options) {
968
953
  // Some providers don't accept null content, use empty string instead
969
954
  const assistantMsg = {
970
955
  role: "assistant",
971
- content: compat.requiresAssistantAfterToolResult ? "" : null,
956
+ content: compat.requiresAssistantAfterToolResult || compat.requiresNonNullAssistantContent ? "" : null,
972
957
  };
973
958
  const assistantTextParts = msg.content
974
959
  .filter(isTextContentBlock)
@@ -1168,7 +1153,10 @@ function convertTools(tools, compat) {
1168
1153
  };
1169
1154
  });
1170
1155
  }
1171
- function parseChunkUsage(rawUsage, model) {
1156
+ function parseChunkUsage(
1157
+ // OpenRouter adds billing fields to streamed usage at runtime,
1158
+ // although the OpenAI SDK does not type those fields.
1159
+ rawUsage, model) {
1172
1160
  const promptTokens = rawUsage.prompt_tokens || 0;
1173
1161
  const cacheReadTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? rawUsage.prompt_cache_hit_tokens ?? rawUsage.cached_tokens ?? 0;
1174
1162
  const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;
@@ -1196,6 +1184,9 @@ function parseChunkUsage(rawUsage, model) {
1196
1184
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
1197
1185
  };
1198
1186
  calculateCost(model, usage);
1187
+ if (isOpenRouterModel(model)) {
1188
+ applyOpenRouterReportedCost(usage, rawUsage);
1189
+ }
1199
1190
  return usage;
1200
1191
  }
1201
1192
  function mapStopReason(reason) {
@@ -1276,6 +1267,8 @@ function detectCompat(model) {
1276
1267
  maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
1277
1268
  requiresToolResultName: false,
1278
1269
  requiresAssistantAfterToolResult: false,
1270
+ requiresNonNullAssistantContent: false,
1271
+ requiresToolsWithToolHistory: false,
1279
1272
  requiresThinkingAsText: false,
1280
1273
  requiresReasoningContentOnAssistantMessages: isDeepSeek,
1281
1274
  thinkingFormat: isDeepSeek
@@ -1328,6 +1321,8 @@ function getCompat(model) {
1328
1321
  maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField,
1329
1322
  requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName,
1330
1323
  requiresAssistantAfterToolResult: model.compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
1324
+ requiresNonNullAssistantContent: model.compat.requiresNonNullAssistantContent ?? detected.requiresNonNullAssistantContent,
1325
+ requiresToolsWithToolHistory: model.compat.requiresToolsWithToolHistory ?? detected.requiresToolsWithToolHistory,
1331
1326
  requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
1332
1327
  requiresReasoningContentOnAssistantMessages: model.compat.requiresReasoningContentOnAssistantMessages ??
1333
1328
  detected.requiresReasoningContentOnAssistantMessages,
@@ -1347,6 +1342,7 @@ function getCompat(model) {
1347
1342
  sendSessionAffinityHeaders: model.compat.sendSessionAffinityHeaders ?? detected.sendSessionAffinityHeaders,
1348
1343
  sessionAffinityFormat: model.compat.sessionAffinityFormat ?? detected.sessionAffinityFormat,
1349
1344
  supportsLongCacheRetention: model.compat.supportsLongCacheRetention ?? detected.supportsLongCacheRetention,
1345
+ promptCacheKeyMode: model.compat.promptCacheKeyMode,
1350
1346
  vllmPriority: model.compat.vllmPriority,
1351
1347
  };
1352
1348
  }