@linxiraos/pi-ai 1.0.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 (639) hide show
  1. package/CHANGELOG.md +5066 -0
  2. package/README.md +1195 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth/sqlite-credential-store.d.ts +117 -0
  5. package/dist/types/auth-broker/client.d.ts +93 -0
  6. package/dist/types/auth-broker/discover.d.ts +39 -0
  7. package/dist/types/auth-broker/index.d.ts +7 -0
  8. package/dist/types/auth-broker/refresher.d.ts +25 -0
  9. package/dist/types/auth-broker/remote-store.d.ts +136 -0
  10. package/dist/types/auth-broker/server.d.ts +48 -0
  11. package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
  12. package/dist/types/auth-broker/types.d.ts +152 -0
  13. package/dist/types/auth-broker/wire-schema-resource.d.ts +53 -0
  14. package/dist/types/auth-broker/wire-schemas.d.ts +5 -0
  15. package/dist/types/auth-gateway/http.d.ts +56 -0
  16. package/dist/types/auth-gateway/index.d.ts +3 -0
  17. package/dist/types/auth-gateway/server.d.ts +36 -0
  18. package/dist/types/auth-gateway/types.d.ts +127 -0
  19. package/dist/types/auth-retry.d.ts +150 -0
  20. package/dist/types/auth-storage.d.ts +1258 -0
  21. package/dist/types/dialect/anthropic.d.ts +15 -0
  22. package/dist/types/dialect/catalog.d.ts +3 -0
  23. package/dist/types/dialect/coercion.d.ts +23 -0
  24. package/dist/types/dialect/deepseek.d.ts +14 -0
  25. package/dist/types/dialect/demotion.d.ts +30 -0
  26. package/dist/types/dialect/examples.d.ts +18 -0
  27. package/dist/types/dialect/factory.d.ts +3 -0
  28. package/dist/types/dialect/fenced-thinking.d.ts +53 -0
  29. package/dist/types/dialect/gemini.d.ts +17 -0
  30. package/dist/types/dialect/gemma.d.ts +15 -0
  31. package/dist/types/dialect/glm.d.ts +9 -0
  32. package/dist/types/dialect/harmony.d.ts +8 -0
  33. package/dist/types/dialect/hermes.d.ts +9 -0
  34. package/dist/types/dialect/history.d.ts +3 -0
  35. package/dist/types/dialect/index.d.ts +11 -0
  36. package/dist/types/dialect/inventory.d.ts +9 -0
  37. package/dist/types/dialect/kimi.d.ts +14 -0
  38. package/dist/types/dialect/minimax.d.ts +3 -0
  39. package/dist/types/dialect/owned-stream.d.ts +4 -0
  40. package/dist/types/dialect/qwen3.d.ts +9 -0
  41. package/dist/types/dialect/rendering.d.ts +54 -0
  42. package/dist/types/dialect/thinking.d.ts +6 -0
  43. package/dist/types/dialect/types.d.ts +68 -0
  44. package/dist/types/dialect/xml.d.ts +9 -0
  45. package/dist/types/error/abort.d.ts +14 -0
  46. package/dist/types/error/auth-classify.d.ts +20 -0
  47. package/dist/types/error/auth.d.ts +27 -0
  48. package/dist/types/error/aws.d.ts +27 -0
  49. package/dist/types/error/classes.d.ts +106 -0
  50. package/dist/types/error/finalize.d.ts +39 -0
  51. package/dist/types/error/flags.d.ts +94 -0
  52. package/dist/types/error/format.d.ts +20 -0
  53. package/dist/types/error/gateway.d.ts +20 -0
  54. package/dist/types/error/index.d.ts +14 -0
  55. package/dist/types/error/oauth.d.ts +43 -0
  56. package/dist/types/error/provider.d.ts +42 -0
  57. package/dist/types/error/rate-limit.d.ts +80 -0
  58. package/dist/types/error/retryable.d.ts +27 -0
  59. package/dist/types/error/validation.d.ts +32 -0
  60. package/dist/types/index.d.ts +51 -0
  61. package/dist/types/provider-details.d.ts +24 -0
  62. package/dist/types/providers/amazon-bedrock.d.ts +39 -0
  63. package/dist/types/providers/anthropic-client.d.ts +106 -0
  64. package/dist/types/providers/anthropic-messages-server-schema.d.ts +937 -0
  65. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  66. package/dist/types/providers/anthropic-wire.d.ts +345 -0
  67. package/dist/types/providers/anthropic.d.ts +261 -0
  68. package/dist/types/providers/aws-credentials.d.ts +48 -0
  69. package/dist/types/providers/aws-eventstream.d.ts +39 -0
  70. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  71. package/dist/types/providers/azure-openai-responses.d.ts +16 -0
  72. package/dist/types/providers/bedrock-mantle.d.ts +13 -0
  73. package/dist/types/providers/claude-code-fingerprint.d.ts +19 -0
  74. package/dist/types/providers/cowork-fetch.d.ts +3 -0
  75. package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
  76. package/dist/types/providers/cursor-pi-args.d.ts +105 -0
  77. package/dist/types/providers/cursor.d.ts +221 -0
  78. package/dist/types/providers/devin.d.ts +12 -0
  79. package/dist/types/providers/error-message.d.ts +25 -0
  80. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  81. package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
  82. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  83. package/dist/types/providers/google-auth.d.ts +24 -0
  84. package/dist/types/providers/google-gemini-cli.d.ts +120 -0
  85. package/dist/types/providers/google-shared.d.ts +203 -0
  86. package/dist/types/providers/google-types.d.ts +155 -0
  87. package/dist/types/providers/google-vertex.d.ts +7 -0
  88. package/dist/types/providers/google.d.ts +4 -0
  89. package/dist/types/providers/grammar.d.ts +1 -0
  90. package/dist/types/providers/kimi.d.ts +27 -0
  91. package/dist/types/providers/mock.d.ts +179 -0
  92. package/dist/types/providers/ollama.d.ts +8 -0
  93. package/dist/types/providers/openai-anthropic-shim.d.ts +35 -0
  94. package/dist/types/providers/openai-chat-server-schema.d.ts +1311 -0
  95. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  96. package/dist/types/providers/openai-chat-wire.d.ts +669 -0
  97. package/dist/types/providers/openai-codex/request-transformer.d.ts +109 -0
  98. package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
  99. package/dist/types/providers/openai-codex-responses.d.ts +238 -0
  100. package/dist/types/providers/openai-completions.d.ts +48 -0
  101. package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
  102. package/dist/types/providers/openai-responses-server-schema.d.ts +1314 -0
  103. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  104. package/dist/types/providers/openai-responses-wire.d.ts +6099 -0
  105. package/dist/types/providers/openai-responses.d.ts +131 -0
  106. package/dist/types/providers/openai-shared.d.ts +624 -0
  107. package/dist/types/providers/pi-native-client.d.ts +13 -0
  108. package/dist/types/providers/pi-native-server.d.ts +69 -0
  109. package/dist/types/providers/register-builtins.d.ts +37 -0
  110. package/dist/types/providers/synthetic.d.ts +26 -0
  111. package/dist/types/providers/transform-messages.d.ts +32 -0
  112. package/dist/types/providers/vision-guard.d.ts +20 -0
  113. package/dist/types/registry/aiand.d.ts +7 -0
  114. package/dist/types/registry/aimlapi.d.ts +4 -0
  115. package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
  116. package/dist/types/registry/alibaba-token-plan.d.ts +18 -0
  117. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  118. package/dist/types/registry/anthropic.d.ts +10 -0
  119. package/dist/types/registry/api-key-login.d.ts +42 -0
  120. package/dist/types/registry/api-key-validation.d.ts +43 -0
  121. package/dist/types/registry/aws.d.ts +13 -0
  122. package/dist/types/registry/azure.d.ts +4 -0
  123. package/dist/types/registry/baseten.d.ts +7 -0
  124. package/dist/types/registry/bedrock-mantle.d.ts +22 -0
  125. package/dist/types/registry/cerebras.d.ts +7 -0
  126. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  127. package/dist/types/registry/coreweave.d.ts +7 -0
  128. package/dist/types/registry/cursor.d.ts +7 -0
  129. package/dist/types/registry/deepseek.d.ts +8 -0
  130. package/dist/types/registry/derived.d.ts +5 -0
  131. package/dist/types/registry/devin.d.ts +8 -0
  132. package/dist/types/registry/exa.d.ts +8 -0
  133. package/dist/types/registry/firepass.d.ts +16 -0
  134. package/dist/types/registry/fireworks.d.ts +7 -0
  135. package/dist/types/registry/github-copilot.d.ts +7 -0
  136. package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
  137. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  138. package/dist/types/registry/gmi-cloud.d.ts +7 -0
  139. package/dist/types/registry/google-antigravity.d.ts +9 -0
  140. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  141. package/dist/types/registry/google-vertex.d.ts +5 -0
  142. package/dist/types/registry/google.d.ts +4 -0
  143. package/dist/types/registry/groq.d.ts +4 -0
  144. package/dist/types/registry/huggingface.d.ts +7 -0
  145. package/dist/types/registry/index.d.ts +4 -0
  146. package/dist/types/registry/kagi.d.ts +14 -0
  147. package/dist/types/registry/kilo.d.ts +7 -0
  148. package/dist/types/registry/kimi-code.d.ts +7 -0
  149. package/dist/types/registry/litellm.d.ts +13 -0
  150. package/dist/types/registry/llama-cpp.d.ts +8 -0
  151. package/dist/types/registry/lm-studio.d.ts +8 -0
  152. package/dist/types/registry/meta.d.ts +7 -0
  153. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  154. package/dist/types/registry/minimax-code.d.ts +6 -0
  155. package/dist/types/registry/minimax.d.ts +4 -0
  156. package/dist/types/registry/mistral.d.ts +4 -0
  157. package/dist/types/registry/moonshot.d.ts +7 -0
  158. package/dist/types/registry/nanogpt.d.ts +7 -0
  159. package/dist/types/registry/novita.d.ts +6 -0
  160. package/dist/types/registry/nvidia.d.ts +7 -0
  161. package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
  162. package/dist/types/registry/oauth/anthropic.d.ts +24 -0
  163. package/dist/types/registry/oauth/callback-server.d.ts +74 -0
  164. package/dist/types/registry/oauth/cursor.d.ts +16 -0
  165. package/dist/types/registry/oauth/device-code.d.ts +25 -0
  166. package/dist/types/registry/oauth/devin.d.ts +5 -0
  167. package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
  168. package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
  169. package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
  170. package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
  171. package/dist/types/registry/oauth/google-gemini-cli.d.ts +22 -0
  172. package/dist/types/registry/oauth/google-oauth-shared.d.ts +56 -0
  173. package/dist/types/registry/oauth/index.d.ts +45 -0
  174. package/dist/types/registry/oauth/kimi.d.ts +21 -0
  175. package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
  176. package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
  177. package/dist/types/registry/oauth/opencode.d.ts +18 -0
  178. package/dist/types/registry/oauth/perplexity.d.ts +9 -0
  179. package/dist/types/registry/oauth/pkce.d.ts +8 -0
  180. package/dist/types/registry/oauth/types.d.ts +87 -0
  181. package/dist/types/registry/oauth/wafer.d.ts +1 -0
  182. package/dist/types/registry/oauth/xai-oauth.d.ts +46 -0
  183. package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
  184. package/dist/types/registry/oauth/zai.d.ts +25 -0
  185. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  186. package/dist/types/registry/ollama.d.ts +12 -0
  187. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  188. package/dist/types/registry/openai-codex.d.ts +9 -0
  189. package/dist/types/registry/openai.d.ts +4 -0
  190. package/dist/types/registry/opencode-go.d.ts +6 -0
  191. package/dist/types/registry/opencode-zen.d.ts +6 -0
  192. package/dist/types/registry/openrouter.d.ts +13 -0
  193. package/dist/types/registry/parallel.d.ts +14 -0
  194. package/dist/types/registry/perplexity.d.ts +7 -0
  195. package/dist/types/registry/qianfan.d.ts +7 -0
  196. package/dist/types/registry/qwen-portal.d.ts +7 -0
  197. package/dist/types/registry/registry.d.ts +367 -0
  198. package/dist/types/registry/sakana.d.ts +7 -0
  199. package/dist/types/registry/siliconflow-cn.d.ts +7 -0
  200. package/dist/types/registry/siliconflow.d.ts +7 -0
  201. package/dist/types/registry/synthetic.d.ts +6 -0
  202. package/dist/types/registry/tavily.d.ts +14 -0
  203. package/dist/types/registry/together.d.ts +6 -0
  204. package/dist/types/registry/types.d.ts +75 -0
  205. package/dist/types/registry/umans.d.ts +7 -0
  206. package/dist/types/registry/venice.d.ts +13 -0
  207. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  208. package/dist/types/registry/vllm.d.ts +7 -0
  209. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  210. package/dist/types/registry/xai-oauth.d.ts +7 -0
  211. package/dist/types/registry/xai.d.ts +7 -0
  212. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  213. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  214. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  215. package/dist/types/registry/xiaomi.d.ts +6 -0
  216. package/dist/types/registry/zai.d.ts +15 -0
  217. package/dist/types/registry/zenmux.d.ts +7 -0
  218. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  219. package/dist/types/stream.d.ts +46 -0
  220. package/dist/types/types.d.ts +1064 -0
  221. package/dist/types/usage/alibaba-token-plan.d.ts +3 -0
  222. package/dist/types/usage/claude.d.ts +4 -0
  223. package/dist/types/usage/cursor.d.ts +4 -0
  224. package/dist/types/usage/gemini.d.ts +2 -0
  225. package/dist/types/usage/github-copilot.d.ts +7 -0
  226. package/dist/types/usage/google-antigravity.d.ts +15 -0
  227. package/dist/types/usage/kimi.d.ts +2 -0
  228. package/dist/types/usage/minimax-code.d.ts +3 -0
  229. package/dist/types/usage/ollama.d.ts +5 -0
  230. package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
  231. package/dist/types/usage/openai-codex-reset.d.ts +88 -0
  232. package/dist/types/usage/openai-codex.d.ts +10 -0
  233. package/dist/types/usage/opencode-go.d.ts +2 -0
  234. package/dist/types/usage/shared.d.ts +1 -0
  235. package/dist/types/usage/synthetic.d.ts +2 -0
  236. package/dist/types/usage/umans.d.ts +2 -0
  237. package/dist/types/usage/xai-oauth.d.ts +12 -0
  238. package/dist/types/usage/zai.d.ts +3 -0
  239. package/dist/types/usage.d.ts +527 -0
  240. package/dist/types/utils/abort.d.ts +25 -0
  241. package/dist/types/utils/anthropic-auth.d.ts +35 -0
  242. package/dist/types/utils/aws-profile.d.ts +17 -0
  243. package/dist/types/utils/block-symbols.d.ts +62 -0
  244. package/dist/types/utils/deterministic-id.d.ts +16 -0
  245. package/dist/types/utils/empty-completion-retry.d.ts +21 -0
  246. package/dist/types/utils/event-stream.d.ts +39 -0
  247. package/dist/types/utils/foundry.d.ts +1 -0
  248. package/dist/types/utils/google-validation.d.ts +2 -0
  249. package/dist/types/utils/harmony-leak.d.ts +135 -0
  250. package/dist/types/utils/http-inspector.d.ts +49 -0
  251. package/dist/types/utils/idle-iterator.d.ts +149 -0
  252. package/dist/types/utils/leaked-thinking-stream.d.ts +33 -0
  253. package/dist/types/utils/openai-http.d.ts +48 -0
  254. package/dist/types/utils/openrouter-headers.d.ts +1 -0
  255. package/dist/types/utils/parse-bind.d.ts +23 -0
  256. package/dist/types/utils/provider-response.d.ts +3 -0
  257. package/dist/types/utils/proxy.d.ts +39 -0
  258. package/dist/types/utils/request-debug.d.ts +29 -0
  259. package/dist/types/utils/retry-after.d.ts +4 -0
  260. package/dist/types/utils/retry.d.ts +14 -0
  261. package/dist/types/utils/schema/adapt.d.ts +24 -0
  262. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  263. package/dist/types/utils/schema/dereference.d.ts +11 -0
  264. package/dist/types/utils/schema/draft.d.ts +10 -0
  265. package/dist/types/utils/schema/equality.d.ts +4 -0
  266. package/dist/types/utils/schema/fields.d.ts +54 -0
  267. package/dist/types/utils/schema/index.d.ts +14 -0
  268. package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
  269. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  270. package/dist/types/utils/schema/normalize.d.ts +153 -0
  271. package/dist/types/utils/schema/spill.d.ts +8 -0
  272. package/dist/types/utils/schema/stamps.d.ts +17 -0
  273. package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
  274. package/dist/types/utils/schema/types.d.ts +4 -0
  275. package/dist/types/utils/schema/typescript.d.ts +24 -0
  276. package/dist/types/utils/schema/wire.d.ts +52 -0
  277. package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
  278. package/dist/types/utils/sse-debug.d.ts +5 -0
  279. package/dist/types/utils/stream-markup-healing.d.ts +87 -0
  280. package/dist/types/utils/thinking-loop.d.ts +102 -0
  281. package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
  282. package/dist/types/utils/tool-choice.d.ts +52 -0
  283. package/dist/types/utils/validation.d.ts +28 -0
  284. package/dist/types/utils.d.ts +57 -0
  285. package/package.json +138 -0
  286. package/src/api-registry.ts +109 -0
  287. package/src/auth/sqlite-credential-store.ts +2066 -0
  288. package/src/auth-broker/client.ts +471 -0
  289. package/src/auth-broker/discover.ts +310 -0
  290. package/src/auth-broker/index.ts +7 -0
  291. package/src/auth-broker/refresher.ts +117 -0
  292. package/src/auth-broker/remote-store.ts +1332 -0
  293. package/src/auth-broker/server.ts +898 -0
  294. package/src/auth-broker/snapshot-cache.ts +200 -0
  295. package/src/auth-broker/types.ts +193 -0
  296. package/src/auth-broker/wire-schema-resource.ts +487 -0
  297. package/src/auth-broker/wire-schemas.ts +43 -0
  298. package/src/auth-gateway/http.ts +227 -0
  299. package/src/auth-gateway/index.ts +3 -0
  300. package/src/auth-gateway/server.ts +836 -0
  301. package/src/auth-gateway/types.ts +153 -0
  302. package/src/auth-retry.ts +401 -0
  303. package/src/auth-storage.ts +6540 -0
  304. package/src/dialect/anthropic.md +31 -0
  305. package/src/dialect/anthropic.ts +608 -0
  306. package/src/dialect/catalog.ts +29 -0
  307. package/src/dialect/coercion.ts +136 -0
  308. package/src/dialect/deepseek.md +24 -0
  309. package/src/dialect/deepseek.ts +609 -0
  310. package/src/dialect/demotion.ts +40 -0
  311. package/src/dialect/examples.ts +71 -0
  312. package/src/dialect/factory.ts +34 -0
  313. package/src/dialect/fenced-thinking.ts +184 -0
  314. package/src/dialect/gemini.md +44 -0
  315. package/src/dialect/gemini.ts +583 -0
  316. package/src/dialect/gemma.md +33 -0
  317. package/src/dialect/gemma.ts +387 -0
  318. package/src/dialect/glm.md +32 -0
  319. package/src/dialect/glm.ts +579 -0
  320. package/src/dialect/harmony.md +31 -0
  321. package/src/dialect/harmony.ts +345 -0
  322. package/src/dialect/hermes.md +25 -0
  323. package/src/dialect/hermes.ts +206 -0
  324. package/src/dialect/history.ts +81 -0
  325. package/src/dialect/index.ts +15 -0
  326. package/src/dialect/inventory.ts +30 -0
  327. package/src/dialect/kimi.md +24 -0
  328. package/src/dialect/kimi.ts +340 -0
  329. package/src/dialect/minimax.md +31 -0
  330. package/src/dialect/minimax.ts +95 -0
  331. package/src/dialect/owned-stream.ts +481 -0
  332. package/src/dialect/prompt-template.md +12 -0
  333. package/src/dialect/qwen3.md +28 -0
  334. package/src/dialect/qwen3.ts +240 -0
  335. package/src/dialect/rendering.ts +304 -0
  336. package/src/dialect/thinking.ts +292 -0
  337. package/src/dialect/types.ts +56 -0
  338. package/src/dialect/xml.md +22 -0
  339. package/src/dialect/xml.ts +90 -0
  340. package/src/error/abort.ts +18 -0
  341. package/src/error/auth-classify.ts +47 -0
  342. package/src/error/auth.ts +48 -0
  343. package/src/error/aws.ts +35 -0
  344. package/src/error/classes.ts +281 -0
  345. package/src/error/finalize.ts +69 -0
  346. package/src/error/flags.ts +602 -0
  347. package/src/error/format.ts +45 -0
  348. package/src/error/gateway.ts +96 -0
  349. package/src/error/index.ts +14 -0
  350. package/src/error/oauth.ts +58 -0
  351. package/src/error/provider.ts +63 -0
  352. package/src/error/rate-limit.ts +303 -0
  353. package/src/error/retryable.ts +70 -0
  354. package/src/error/validation.ts +44 -0
  355. package/src/index.ts +51 -0
  356. package/src/provider-details.ts +90 -0
  357. package/src/providers/amazon-bedrock.ts +1064 -0
  358. package/src/providers/anthropic-client.ts +317 -0
  359. package/src/providers/anthropic-messages-server-schema.ts +252 -0
  360. package/src/providers/anthropic-messages-server.ts +818 -0
  361. package/src/providers/anthropic-wire.ts +359 -0
  362. package/src/providers/anthropic.ts +4539 -0
  363. package/src/providers/aws-credentials.ts +772 -0
  364. package/src/providers/aws-eventstream.ts +181 -0
  365. package/src/providers/aws-sigv4.ts +218 -0
  366. package/src/providers/azure-openai-responses.ts +438 -0
  367. package/src/providers/bedrock-mantle.ts +110 -0
  368. package/src/providers/claude-code-fingerprint.ts +20 -0
  369. package/src/providers/cowork-fetch.ts +201 -0
  370. package/src/providers/cursor/exec-modern.ts +496 -0
  371. package/src/providers/cursor/proto/agent.proto +4533 -0
  372. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  373. package/src/providers/cursor/proto/buf.yaml +17 -0
  374. package/src/providers/cursor-pi-args.ts +165 -0
  375. package/src/providers/cursor.ts +4689 -0
  376. package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
  377. package/src/providers/devin/proto/buf.gen.yaml +33 -0
  378. package/src/providers/devin/proto/buf.yaml +17 -0
  379. package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
  380. package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
  381. package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
  382. package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
  383. package/src/providers/devin/proto/cel/expr/value.proto +41 -0
  384. package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
  385. package/src/providers/devin/proto/errorspb/errors.proto +56 -0
  386. package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
  387. package/src/providers/devin/proto/errorspb/markers.proto +10 -0
  388. package/src/providers/devin/proto/errorspb/tags.proto +12 -0
  389. package/src/providers/devin/proto/errorspb/testing.proto +6 -0
  390. package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
  391. package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
  392. package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
  393. package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
  394. package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
  395. package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
  396. package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
  397. package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
  398. package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
  399. package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
  400. package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
  401. package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
  402. package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
  403. package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
  404. package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
  405. package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
  406. package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
  407. package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
  408. package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
  409. package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
  410. package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
  411. package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
  412. package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
  413. package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
  414. package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
  415. package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
  416. package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
  417. package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
  418. package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
  419. package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
  420. package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
  421. package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
  422. package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
  423. package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
  424. package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
  425. package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
  426. package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
  427. package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
  428. package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
  429. package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
  430. package/src/providers/devin.ts +679 -0
  431. package/src/providers/error-message.ts +23 -0
  432. package/src/providers/github-copilot-headers.ts +141 -0
  433. package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
  434. package/src/providers/gitlab-duo-workflow.ts +3135 -0
  435. package/src/providers/gitlab-duo.ts +399 -0
  436. package/src/providers/google-auth.ts +330 -0
  437. package/src/providers/google-gemini-cli.ts +1370 -0
  438. package/src/providers/google-shared.ts +1122 -0
  439. package/src/providers/google-types.ts +180 -0
  440. package/src/providers/google-vertex.ts +135 -0
  441. package/src/providers/google.ts +47 -0
  442. package/src/providers/grammar.ts +70 -0
  443. package/src/providers/kimi.ts +51 -0
  444. package/src/providers/mock.ts +514 -0
  445. package/src/providers/ollama.ts +776 -0
  446. package/src/providers/openai-anthropic-shim.ts +166 -0
  447. package/src/providers/openai-chat-server-schema.ts +243 -0
  448. package/src/providers/openai-chat-server.ts +752 -0
  449. package/src/providers/openai-chat-wire.ts +859 -0
  450. package/src/providers/openai-codex/request-transformer.ts +491 -0
  451. package/src/providers/openai-codex/response-handler.ts +102 -0
  452. package/src/providers/openai-codex-responses.ts +4716 -0
  453. package/src/providers/openai-completions.ts +2389 -0
  454. package/src/providers/openai-reasoning-fallback.ts +269 -0
  455. package/src/providers/openai-responses-server-schema.ts +397 -0
  456. package/src/providers/openai-responses-server.ts +1466 -0
  457. package/src/providers/openai-responses-wire.ts +6416 -0
  458. package/src/providers/openai-responses.ts +1393 -0
  459. package/src/providers/openai-shared.ts +3500 -0
  460. package/src/providers/pi-native-client.ts +275 -0
  461. package/src/providers/pi-native-server.ts +245 -0
  462. package/src/providers/register-builtins.ts +503 -0
  463. package/src/providers/synthetic.ts +50 -0
  464. package/src/providers/transform-messages.ts +1083 -0
  465. package/src/providers/vision-guard.ts +54 -0
  466. package/src/registry/aiand.ts +22 -0
  467. package/src/registry/aimlapi.ts +6 -0
  468. package/src/registry/alibaba-coding-plan.ts +104 -0
  469. package/src/registry/alibaba-token-plan.ts +125 -0
  470. package/src/registry/amazon-bedrock.ts +22 -0
  471. package/src/registry/anthropic.ts +26 -0
  472. package/src/registry/api-key-login.ts +115 -0
  473. package/src/registry/api-key-validation.ts +145 -0
  474. package/src/registry/aws.ts +57 -0
  475. package/src/registry/azure.ts +6 -0
  476. package/src/registry/baseten.ts +22 -0
  477. package/src/registry/bedrock-mantle.ts +34 -0
  478. package/src/registry/cerebras.ts +23 -0
  479. package/src/registry/cloudflare-ai-gateway.ts +45 -0
  480. package/src/registry/coreweave.ts +40 -0
  481. package/src/registry/cursor.ts +20 -0
  482. package/src/registry/deepseek.ts +46 -0
  483. package/src/registry/derived.ts +9 -0
  484. package/src/registry/devin.ts +15 -0
  485. package/src/registry/exa.ts +19 -0
  486. package/src/registry/firepass.ts +32 -0
  487. package/src/registry/fireworks.ts +28 -0
  488. package/src/registry/github-copilot.ts +22 -0
  489. package/src/registry/gitlab-duo-workflow.ts +20 -0
  490. package/src/registry/gitlab-duo.ts +19 -0
  491. package/src/registry/gmi-cloud.ts +22 -0
  492. package/src/registry/google-antigravity.ts +22 -0
  493. package/src/registry/google-gemini-cli.ts +22 -0
  494. package/src/registry/google-vertex.ts +38 -0
  495. package/src/registry/google.ts +6 -0
  496. package/src/registry/groq.ts +6 -0
  497. package/src/registry/huggingface.ts +29 -0
  498. package/src/registry/index.ts +4 -0
  499. package/src/registry/kagi.ts +46 -0
  500. package/src/registry/kilo.ts +114 -0
  501. package/src/registry/kimi-code.ts +17 -0
  502. package/src/registry/litellm.ts +45 -0
  503. package/src/registry/llama-cpp.ts +35 -0
  504. package/src/registry/lm-studio.ts +31 -0
  505. package/src/registry/meta.ts +22 -0
  506. package/src/registry/minimax-code-cn.ts +12 -0
  507. package/src/registry/minimax-code.ts +12 -0
  508. package/src/registry/minimax.ts +6 -0
  509. package/src/registry/mistral.ts +6 -0
  510. package/src/registry/moonshot.ts +28 -0
  511. package/src/registry/nanogpt.ts +22 -0
  512. package/src/registry/novita.ts +25 -0
  513. package/src/registry/nvidia.ts +61 -0
  514. package/src/registry/oauth/anthropic-constants.ts +12 -0
  515. package/src/registry/oauth/anthropic.ts +346 -0
  516. package/src/registry/oauth/callback-server.ts +438 -0
  517. package/src/registry/oauth/cursor.ts +187 -0
  518. package/src/registry/oauth/device-code.ts +92 -0
  519. package/src/registry/oauth/devin.ts +124 -0
  520. package/src/registry/oauth/github-copilot.ts +369 -0
  521. package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
  522. package/src/registry/oauth/gitlab-duo.ts +222 -0
  523. package/src/registry/oauth/google-antigravity.ts +225 -0
  524. package/src/registry/oauth/google-gemini-cli.ts +297 -0
  525. package/src/registry/oauth/google-oauth-shared.ts +211 -0
  526. package/src/registry/oauth/index.ts +187 -0
  527. package/src/registry/oauth/kimi.ts +297 -0
  528. package/src/registry/oauth/minimax-code.ts +53 -0
  529. package/src/registry/oauth/oauth.html +317 -0
  530. package/src/registry/oauth/openai-codex.ts +384 -0
  531. package/src/registry/oauth/opencode.ts +50 -0
  532. package/src/registry/oauth/perplexity.ts +228 -0
  533. package/src/registry/oauth/pkce.ts +18 -0
  534. package/src/registry/oauth/types.ts +96 -0
  535. package/src/registry/oauth/wafer.ts +24 -0
  536. package/src/registry/oauth/xai-oauth.ts +559 -0
  537. package/src/registry/oauth/xiaomi.ts +211 -0
  538. package/src/registry/oauth/zai.ts +285 -0
  539. package/src/registry/ollama-cloud.ts +36 -0
  540. package/src/registry/ollama.ts +43 -0
  541. package/src/registry/openai-codex-device.ts +18 -0
  542. package/src/registry/openai-codex.ts +19 -0
  543. package/src/registry/openai.ts +6 -0
  544. package/src/registry/opencode-go.ts +12 -0
  545. package/src/registry/opencode-zen.ts +12 -0
  546. package/src/registry/openrouter.ts +28 -0
  547. package/src/registry/parallel.ts +45 -0
  548. package/src/registry/perplexity.ts +13 -0
  549. package/src/registry/qianfan.ts +27 -0
  550. package/src/registry/qwen-portal.ts +50 -0
  551. package/src/registry/registry.ts +182 -0
  552. package/src/registry/sakana.ts +22 -0
  553. package/src/registry/siliconflow-cn.ts +22 -0
  554. package/src/registry/siliconflow.ts +22 -0
  555. package/src/registry/synthetic.ts +21 -0
  556. package/src/registry/tavily.ts +45 -0
  557. package/src/registry/together.ts +22 -0
  558. package/src/registry/types.ts +86 -0
  559. package/src/registry/umans.ts +23 -0
  560. package/src/registry/venice.ts +33 -0
  561. package/src/registry/vercel-ai-gateway.ts +38 -0
  562. package/src/registry/vllm.ts +34 -0
  563. package/src/registry/wafer-serverless.ts +12 -0
  564. package/src/registry/xai-oauth.ts +17 -0
  565. package/src/registry/xai.ts +22 -0
  566. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  567. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  568. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  569. package/src/registry/xiaomi.ts +12 -0
  570. package/src/registry/zai.ts +41 -0
  571. package/src/registry/zenmux.ts +22 -0
  572. package/src/registry/zhipu-coding-plan.ts +27 -0
  573. package/src/stream.ts +1944 -0
  574. package/src/types.ts +1243 -0
  575. package/src/usage/alibaba-token-plan.ts +230 -0
  576. package/src/usage/claude.ts +830 -0
  577. package/src/usage/cursor.ts +335 -0
  578. package/src/usage/gemini.ts +258 -0
  579. package/src/usage/github-copilot.ts +424 -0
  580. package/src/usage/google-antigravity.ts +497 -0
  581. package/src/usage/kimi.ts +277 -0
  582. package/src/usage/minimax-code.ts +291 -0
  583. package/src/usage/ollama.ts +41 -0
  584. package/src/usage/openai-codex-base-url.ts +35 -0
  585. package/src/usage/openai-codex-reset.ts +205 -0
  586. package/src/usage/openai-codex.ts +627 -0
  587. package/src/usage/opencode-go.ts +89 -0
  588. package/src/usage/shared.ts +10 -0
  589. package/src/usage/synthetic.ts +180 -0
  590. package/src/usage/umans.ts +192 -0
  591. package/src/usage/xai-oauth.ts +414 -0
  592. package/src/usage/zai.ts +370 -0
  593. package/src/usage.ts +411 -0
  594. package/src/utils/abort.ts +67 -0
  595. package/src/utils/anthropic-auth.ts +93 -0
  596. package/src/utils/aws-profile.ts +88 -0
  597. package/src/utils/block-symbols.ts +78 -0
  598. package/src/utils/deterministic-id.ts +20 -0
  599. package/src/utils/empty-completion-retry.ts +161 -0
  600. package/src/utils/event-stream.ts +202 -0
  601. package/src/utils/foundry.ts +8 -0
  602. package/src/utils/google-validation.ts +25 -0
  603. package/src/utils/harmony-leak.ts +500 -0
  604. package/src/utils/http-inspector.ts +196 -0
  605. package/src/utils/idle-iterator.ts +531 -0
  606. package/src/utils/leaked-thinking-stream.ts +483 -0
  607. package/src/utils/openai-http.ts +119 -0
  608. package/src/utils/openrouter-headers.ts +12 -0
  609. package/src/utils/parse-bind.ts +56 -0
  610. package/src/utils/provider-response.ts +30 -0
  611. package/src/utils/proxy.ts +314 -0
  612. package/src/utils/request-debug.ts +351 -0
  613. package/src/utils/retry-after.ts +121 -0
  614. package/src/utils/retry.ts +77 -0
  615. package/src/utils/schema/CONSTRAINTS.md +168 -0
  616. package/src/utils/schema/adapt.ts +36 -0
  617. package/src/utils/schema/compatibility.ts +435 -0
  618. package/src/utils/schema/dereference.ts +98 -0
  619. package/src/utils/schema/draft.ts +341 -0
  620. package/src/utils/schema/equality.ts +97 -0
  621. package/src/utils/schema/fields.ts +210 -0
  622. package/src/utils/schema/index.ts +14 -0
  623. package/src/utils/schema/json-schema-validator.ts +595 -0
  624. package/src/utils/schema/meta-validator.ts +167 -0
  625. package/src/utils/schema/normalize.ts +2314 -0
  626. package/src/utils/schema/spill.ts +43 -0
  627. package/src/utils/schema/stamps.ts +109 -0
  628. package/src/utils/schema/strict-tool-validation.ts +117 -0
  629. package/src/utils/schema/types.ts +10 -0
  630. package/src/utils/schema/typescript.ts +212 -0
  631. package/src/utils/schema/wire.ts +662 -0
  632. package/src/utils/sdk-stream-timeout.ts +43 -0
  633. package/src/utils/sse-debug.ts +18 -0
  634. package/src/utils/stream-markup-healing.ts +247 -0
  635. package/src/utils/thinking-loop.ts +552 -0
  636. package/src/utils/tool-call-loop-guard.ts +107 -0
  637. package/src/utils/tool-choice.ts +101 -0
  638. package/src/utils/validation.ts +1932 -0
  639. package/src/utils.ts +492 -0
@@ -0,0 +1,752 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { type } from "@linxiraos/pi-omptype";
3
+ import { resolvePromptCacheKey } from "../auth-gateway/http";
4
+ /**
5
+ * Parsed inbound OpenAI chat-completions request, ready to feed into pi-ai
6
+ * `stream(model, context, options)`.
7
+ */
8
+ import type { AuthGatewayStreamControl, AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
9
+ import * as AIError from "../error";
10
+ import type {
11
+ AssistantMessage,
12
+ AssistantMessageEventStream,
13
+ Context,
14
+ ImageContent,
15
+ Message,
16
+ ServiceTier,
17
+ StopReason,
18
+ TextContent,
19
+ Tool,
20
+ ToolCall,
21
+ ToolResultMessage,
22
+ TSchema,
23
+ } from "../types";
24
+ import {
25
+ type OpenAIChatContentPart,
26
+ type OpenAIChatMessage,
27
+ type OpenAIChatTool,
28
+ type OpenAIChatToolCall,
29
+ type OpenAIChatToolChoice,
30
+ openaiChatRequestSchema,
31
+ } from "./openai-chat-server-schema";
32
+
33
+ export type { ParsedRequest };
34
+
35
+ type ReasoningEffort = NonNullable<ParsedRequest["options"]["reasoning"]>;
36
+
37
+ function isReasoningEffort(value: unknown): value is ReasoningEffort {
38
+ return (
39
+ value === "minimal" ||
40
+ value === "low" ||
41
+ value === "medium" ||
42
+ value === "high" ||
43
+ value === "xhigh" ||
44
+ value === "max"
45
+ );
46
+ }
47
+
48
+ function isServiceTier(value: unknown): value is ServiceTier {
49
+ return value === "auto" || value === "default" || value === "flex" || value === "scale" || value === "priority";
50
+ }
51
+
52
+ const UNSUPPORTED_EXPLICIT_PROMPT_CACHE_MESSAGE =
53
+ "openai-chat: prompt_cache_options and prompt_cache_breakpoint are unsupported by this auth-gateway route; use /v1/pi/stream with options.promptCache instead";
54
+
55
+ function hasUnsupportedExplicitPromptCacheFields(body: unknown): boolean {
56
+ if (typeof body !== "object" || body === null || Array.isArray(body)) return false;
57
+ const request = body as Record<string, unknown>;
58
+ if ("prompt_cache_options" in request || "prompt_cache_breakpoint" in request) return true;
59
+ if (!Array.isArray(request.messages)) return false;
60
+
61
+ return request.messages.some(message => {
62
+ if (typeof message !== "object" || message === null || Array.isArray(message)) return false;
63
+ const wireMessage = message as Record<string, unknown>;
64
+ if ("prompt_cache_breakpoint" in wireMessage) return true;
65
+ return (
66
+ Array.isArray(wireMessage.content) &&
67
+ wireMessage.content.some(
68
+ part =>
69
+ typeof part === "object" && part !== null && !Array.isArray(part) && "prompt_cache_breakpoint" in part,
70
+ )
71
+ );
72
+ });
73
+ }
74
+
75
+ function rejectUnsupportedExplicitPromptCacheFields(body: unknown): void {
76
+ if (hasUnsupportedExplicitPromptCacheFields(body)) {
77
+ throw new AIError.ValidationError(UNSUPPORTED_EXPLICIT_PROMPT_CACHE_MESSAGE);
78
+ }
79
+ }
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // parseRequest
83
+ // ---------------------------------------------------------------------------
84
+
85
+ export function parseRequest(body: unknown, headers?: Headers): ParsedRequest {
86
+ // Header capture is centralized in `auth-gateway/server.ts` (allow-listed
87
+ // headers like openai-organization/openai-project/openai-beta/x-stainless-*
88
+ // land on `options.headers` automatically). We consult `headers` here too
89
+ // for `resolvePromptCacheKey` to pull a cache identity out of inbound
90
+ // vendor-neutral headers when the body doesn't carry one.
91
+ rejectUnsupportedExplicitPromptCacheFields(body);
92
+ const parsed = openaiChatRequestSchema(body);
93
+ if (parsed instanceof type.errors) {
94
+ throw new AIError.ValidationError(`openai-chat: ${parsed.summary}`);
95
+ }
96
+ const data = parsed;
97
+
98
+ const now = Date.now();
99
+ const systemParts: string[] = [];
100
+ const messages: Message[] = [];
101
+ // Map of `tool_call_id` → function name, populated as we walk assistant
102
+ // turns. The OpenAI wire spec drops `name` from `role:"tool"` messages,
103
+ // but downstream providers (notably Google: `functionResponse.name` is
104
+ // required) need it. We back-resolve from the matching call. If the
105
+ // client did send a wire `name` we still prefer that (forward-compat).
106
+ const toolNamesById = new Map<string, string>();
107
+
108
+ for (const m of data.messages as OpenAIChatMessage[]) {
109
+ switch (m.role) {
110
+ case "system": {
111
+ const text = stringifyContent(m.content);
112
+ if (text.length > 0) systemParts.push(text);
113
+ break;
114
+ }
115
+ case "developer":
116
+ messages.push({ role: "developer", content: parseUserLikeContent(m.content), timestamp: now });
117
+ break;
118
+ case "user":
119
+ messages.push({ role: "user", content: parseUserLikeContent(m.content), timestamp: now });
120
+ break;
121
+ case "assistant":
122
+ if (m.tool_calls) {
123
+ for (const raw of m.tool_calls) {
124
+ if (raw.type !== undefined && raw.type !== "function") continue;
125
+ const fn = (raw as { function?: { name?: string } }).function;
126
+ if (raw.id && fn?.name) toolNamesById.set(raw.id, fn.name);
127
+ }
128
+ }
129
+ messages.push(
130
+ buildAssistantMessage(
131
+ (m.content ?? undefined) as string | OpenAIChatContentPart[] | undefined,
132
+ m.tool_calls,
133
+ (m as { reasoning_content?: string | null }).reasoning_content ?? undefined,
134
+ data.model,
135
+ now,
136
+ ),
137
+ );
138
+ break;
139
+ case "tool": {
140
+ // Prefer the wire `name` when present; otherwise back-resolve from
141
+ // the assistant `tool_calls` map. Falls through to "" only when no
142
+ // prior call shares this id, which is the well-known broken case.
143
+ const wireName = (m as { name?: string }).name;
144
+ const resolvedName = wireName ?? (m.tool_call_id ? toolNamesById.get(m.tool_call_id) : undefined);
145
+ pushToolResultMessages(messages, m.content, m.tool_call_id, resolvedName, now);
146
+ break;
147
+ }
148
+ case "function": {
149
+ // Legacy `function` role (pre-tools API): the message carries the tool's
150
+ // name on `name` and its output on `content`. Translate to a canonical
151
+ // `toolResult` with a synthetic id (no original id on the wire).
152
+ const fn = m as { role: "function"; name: string; content: string | null };
153
+ pushToolResultMessages(messages, fn.content ?? "", undefined, fn.name, now);
154
+ break;
155
+ }
156
+ }
157
+ }
158
+
159
+ const tools = data.tools ? buildTools(data.tools as OpenAIChatTool[]) : undefined;
160
+
161
+ const context: Context = {
162
+ messages,
163
+ ...(systemParts.length > 0 ? { systemPrompt: [systemParts.join("\n\n")] } : {}),
164
+ ...(tools ? { tools } : {}),
165
+ };
166
+
167
+ // Prefer max_completion_tokens (newer) over max_tokens.
168
+ const maxOutputTokens = data.max_completion_tokens ?? data.max_tokens;
169
+ const stopSequences = normalizeStop(data.stop);
170
+ // Schema accepts the Anthropic-style {type:'tool', name} variant that the SDK
171
+ // union doesn't model; the normalizer collapses it to a plain name lookup.
172
+ const toolChoice = normalizeToolChoice(data.tool_choice as Parameters<typeof normalizeToolChoice>[0]);
173
+ const includeStreamingUsage = data.stream_options?.include_usage === true;
174
+
175
+ // `includeStreamingUsage` is the one genuinely-opaque flag — the streaming
176
+ // encoder reads it later off `options.extra`. Everything else now lives on
177
+ // a typed field; `extra` stays undefined when only typed values are set.
178
+ const extra: Record<string, unknown> = {};
179
+ let hasExtra = false;
180
+ if (includeStreamingUsage) {
181
+ extra.includeStreamingUsage = true;
182
+ hasExtra = true;
183
+ }
184
+
185
+ const options: ParsedRequest["options"] = {};
186
+ if (maxOutputTokens !== undefined) options.maxOutputTokens = maxOutputTokens;
187
+ if (data.temperature !== undefined) options.temperature = data.temperature;
188
+ if (data.top_p !== undefined) options.topP = data.top_p;
189
+ if (stopSequences) options.stopSequences = stopSequences;
190
+ if (toolChoice !== undefined) options.toolChoice = toolChoice;
191
+ if (data.presence_penalty !== undefined) options.presencePenalty = data.presence_penalty;
192
+ if (data.frequency_penalty !== undefined) options.frequencyPenalty = data.frequency_penalty;
193
+ if (data.seed !== undefined) options.seed = data.seed;
194
+ if (data.logit_bias !== undefined) options.logitBias = data.logit_bias;
195
+ if (data.user !== undefined) options.user = data.user;
196
+ if (data.response_format !== undefined) options.responseFormat = data.response_format;
197
+ if (data.parallel_tool_calls !== undefined) options.parallelToolCalls = data.parallel_tool_calls;
198
+ if (data.reasoning_effort !== undefined && isReasoningEffort(data.reasoning_effort)) {
199
+ options.reasoning = data.reasoning_effort;
200
+ }
201
+ if (data.service_tier !== undefined && isServiceTier(data.service_tier)) {
202
+ options.serviceTier = data.service_tier;
203
+ }
204
+ if (data.metadata !== undefined) options.metadata = data.metadata;
205
+ const cacheKey = resolvePromptCacheKey(body, headers);
206
+ if (cacheKey !== undefined) options.promptCacheKey = cacheKey;
207
+ if (hasExtra) options.extra = extra;
208
+
209
+ return {
210
+ modelId: data.model,
211
+ context,
212
+ stream: data.stream === true,
213
+ options,
214
+ };
215
+ }
216
+
217
+ function stringifyContent(content: string | OpenAIChatContentPart[] | undefined): string {
218
+ if (content === undefined) return "";
219
+ if (typeof content === "string") return content;
220
+ const out: string[] = [];
221
+ for (const part of content) {
222
+ if (part.type === "text") out.push(part.text);
223
+ }
224
+ return out.join("");
225
+ }
226
+
227
+ function parseUserLikeContent(
228
+ content: string | OpenAIChatContentPart[] | undefined,
229
+ ): string | (TextContent | ImageContent)[] {
230
+ if (content === undefined) return "";
231
+ if (typeof content === "string") return content;
232
+ const parts: (TextContent | ImageContent)[] = [];
233
+ for (const part of content) {
234
+ if (part.type === "text") {
235
+ parts.push({ type: "text", text: part.text });
236
+ continue;
237
+ }
238
+ if (part.type !== "image_url") continue;
239
+ // input_audio / file / refusal / unknown-type parts are accepted by the
240
+ // schema for forward-compat but dropped here — pi-ai's canonical user
241
+ // content only models text and image today.
242
+ const url = typeof part.image_url === "string" ? part.image_url : part.image_url.url;
243
+ const decoded = decodeDataUri(url);
244
+ if (decoded) {
245
+ parts.push({ type: "image", data: decoded.data, mimeType: decoded.mimeType });
246
+ } else {
247
+ // No image fetcher available in the gateway; surface as a text placeholder so
248
+ // downstream providers still receive a coherent message.
249
+ parts.push({ type: "text", text: `[image: ${url}]` });
250
+ }
251
+ }
252
+ return parts;
253
+ }
254
+
255
+ function decodeDataUri(url: string): { data: string; mimeType: string } | undefined {
256
+ if (!url.startsWith("data:")) return undefined;
257
+ const comma = url.indexOf(",");
258
+ if (comma < 0) return undefined;
259
+ const header = url.slice(5, comma);
260
+ const payload = url.slice(comma + 1);
261
+ const isBase64 = header.endsWith(";base64");
262
+ const mimeType = (isBase64 ? header.slice(0, -";base64".length) : header) || "application/octet-stream";
263
+ const data = isBase64 ? payload : Buffer.from(decodeURIComponent(payload), "utf8").toString("base64");
264
+ return { data, mimeType };
265
+ }
266
+
267
+ function buildAssistantMessage(
268
+ content: string | OpenAIChatContentPart[] | undefined,
269
+ toolCalls: OpenAIChatToolCall[] | undefined,
270
+ reasoningContent: string | undefined,
271
+ modelId: string,
272
+ now: number,
273
+ ): AssistantMessage {
274
+ const parts: AssistantMessage["content"] = [];
275
+ if (reasoningContent !== undefined && reasoningContent.length > 0) {
276
+ // Replayed reasoning channel. The signature names the wire field so
277
+ // completions providers that demand exact `reasoning_content` replay
278
+ // (DeepSeek/Kimi) echo the model's actual reasoning back verbatim.
279
+ parts.push({ type: "thinking", thinking: reasoningContent, thinkingSignature: "reasoning_content" });
280
+ }
281
+ const text = stringifyContent(content);
282
+ if (text.length > 0) parts.push({ type: "text", text });
283
+ if (toolCalls) {
284
+ for (const raw of toolCalls) {
285
+ // Schema only accepts type:"function" (or omitted); narrow the SDK
286
+ // union here so the custom-tool variant doesn't trip TS.
287
+ if (raw.type !== undefined && raw.type !== "function") continue;
288
+ const fn = (raw as { function: { name: string; arguments: string } }).function;
289
+ const argsStr = fn.arguments;
290
+ let args: Record<string, unknown> = {};
291
+ if (argsStr.length > 0) {
292
+ try {
293
+ const v: unknown = JSON.parse(argsStr);
294
+ args =
295
+ v && typeof v === "object" && !Array.isArray(v) ? (v as Record<string, unknown>) : { __raw: argsStr };
296
+ } catch {
297
+ args = { __raw: argsStr };
298
+ }
299
+ }
300
+ const call: ToolCall = { type: "toolCall", id: raw.id, name: fn.name, arguments: args };
301
+ parts.push(call);
302
+ }
303
+ }
304
+ return {
305
+ role: "assistant",
306
+ content: parts,
307
+ api: "openai-completions",
308
+ provider: "openai",
309
+ model: modelId,
310
+ usage: {
311
+ input: 0,
312
+ output: 0,
313
+ cacheRead: 0,
314
+ cacheWrite: 0,
315
+ totalTokens: 0,
316
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
317
+ },
318
+ stopReason: "stop",
319
+ timestamp: now,
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Walk a wire `tool` (or legacy `function`) message into canonical messages.
325
+ * Tool-result content may carry images alongside text; pi-ai's
326
+ * `ToolResultMessage` accepts both, but most downstream providers ignore
327
+ * images on tool results. To mirror Rust's `encode_messages` behavior we
328
+ * keep text inside the tool-result message and hoist any image parts into a
329
+ * follow-up `user` message so they still reach the model.
330
+ */
331
+ function pushToolResultMessages(
332
+ messages: Message[],
333
+ content: string | OpenAIChatContentPart[] | undefined | null,
334
+ toolCallId: string | undefined,
335
+ toolName: string | undefined,
336
+ now: number,
337
+ ): void {
338
+ const textParts: TextContent[] = [];
339
+ const imageParts: ImageContent[] = [];
340
+
341
+ if (typeof content === "string") {
342
+ if (content.length > 0) textParts.push({ type: "text", text: content });
343
+ } else if (Array.isArray(content)) {
344
+ for (const part of content) {
345
+ if (part.type === "text") {
346
+ textParts.push({ type: "text", text: part.text });
347
+ continue;
348
+ }
349
+ if (part.type !== "image_url") continue;
350
+ const url = typeof part.image_url === "string" ? part.image_url : part.image_url.url;
351
+ const decoded = decodeDataUri(url);
352
+ if (decoded) {
353
+ imageParts.push({ type: "image", data: decoded.data, mimeType: decoded.mimeType });
354
+ } else {
355
+ // No fetcher available; degrade gracefully to a text placeholder.
356
+ textParts.push({ type: "text", text: `[image: ${url}]` });
357
+ }
358
+ }
359
+ }
360
+
361
+ const toolMsg: ToolResultMessage = {
362
+ role: "toolResult",
363
+ toolCallId: toolCallId ?? "",
364
+ // OpenAI's `tool` role omits the tool name on the wire; the legacy
365
+ // `function` role supplies it. Downstream providers tolerate empty.
366
+ toolName: toolName ?? "",
367
+ content: textParts.length > 0 ? textParts : [{ type: "text", text: "" }],
368
+ isError: false,
369
+ timestamp: now,
370
+ };
371
+ messages.push(toolMsg);
372
+
373
+ if (imageParts.length > 0) {
374
+ messages.push({
375
+ role: "user",
376
+ content: imageParts,
377
+ timestamp: now,
378
+ });
379
+ }
380
+ }
381
+
382
+ function buildTools(tools: OpenAIChatTool[]): Tool[] | undefined {
383
+ if (tools.length === 0) return undefined;
384
+ const out: Tool[] = [];
385
+ for (const t of tools) {
386
+ if (t.type !== "function") continue;
387
+ out.push({
388
+ name: t.function.name,
389
+ description: t.function.description ?? "",
390
+ parameters: (t.function.parameters ?? {}) as Record<string, unknown> as TSchema,
391
+ });
392
+ }
393
+ return out;
394
+ }
395
+
396
+ function normalizeStop(value: string | string[] | undefined): string[] | undefined {
397
+ if (value === undefined) return undefined;
398
+ if (typeof value === "string") return [value];
399
+ return value.length > 0 ? value : undefined;
400
+ }
401
+
402
+ function normalizeToolChoice(value: OpenAIChatToolChoice | undefined): ParsedRequest["options"]["toolChoice"] {
403
+ if (value === undefined) return undefined;
404
+ if (value === "auto" || value === "none" || value === "required") return value;
405
+ if (typeof value === "object" && value !== null) {
406
+ // OpenAI canonical: { type: 'function', function: { name } }
407
+ if ("function" in value && value.function) return { name: value.function.name };
408
+ // Anthropic-style passthrough (schema-allowed): { type: 'tool', name }
409
+ const anthropicLike = value as unknown as { type?: string; name?: string };
410
+ if (anthropicLike.type === "tool" && typeof anthropicLike.name === "string") {
411
+ return { name: anthropicLike.name };
412
+ }
413
+ }
414
+ return undefined;
415
+ }
416
+
417
+ // ---------------------------------------------------------------------------
418
+ // encodeResponse (non-streaming)
419
+ // ---------------------------------------------------------------------------
420
+
421
+ export function encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown> {
422
+ const { text, reasoning, toolCalls } = flattenAssistant(message);
423
+
424
+ const responseMessage: Record<string, unknown> = {
425
+ role: "assistant",
426
+ content: text.length > 0 ? text : null,
427
+ // pi-ai does not surface real refusals yet; emit `null` so SDKs that
428
+ // probe `.refusal` see the documented field shape rather than missing.
429
+ refusal: null,
430
+ };
431
+ if (reasoning.length > 0) {
432
+ // DeepSeek-style / o-series reasoning channel.
433
+ responseMessage.reasoning_content = reasoning;
434
+ }
435
+ if (toolCalls.length > 0) {
436
+ responseMessage.tool_calls = toolCalls.map(tc => ({
437
+ id: tc.id,
438
+ type: "function",
439
+ function: { name: tc.name, arguments: stringifyArgs(tc.arguments) },
440
+ }));
441
+ }
442
+
443
+ return {
444
+ id: makeId(),
445
+ object: "chat.completion",
446
+ created: Math.floor(Date.now() / 1000),
447
+ model: requestedModelId,
448
+ // Real OpenAI always emits this key, even when the value is null. Mirror
449
+ // the contract so probing SDKs do not throw on a missing field.
450
+ system_fingerprint: null,
451
+ choices: [
452
+ {
453
+ index: 0,
454
+ message: responseMessage,
455
+ finish_reason: mapFinishReason(message.stopReason, toolCalls.length > 0),
456
+ logprobs: null,
457
+ },
458
+ ],
459
+ usage: buildUsage(message),
460
+ };
461
+ }
462
+
463
+ function buildUsage(message: AssistantMessage): Record<string, unknown> {
464
+ const promptTokens = message.usage.input + message.usage.cacheRead + message.usage.cacheWrite;
465
+ const usage: Record<string, unknown> = {
466
+ prompt_tokens: promptTokens,
467
+ completion_tokens: message.usage.output,
468
+ total_tokens: promptTokens + message.usage.output,
469
+ prompt_tokens_details: { cached_tokens: message.usage.cacheRead },
470
+ };
471
+ if (message.usage.reasoningTokens !== undefined) {
472
+ usage.completion_tokens_details = { reasoning_tokens: message.usage.reasoningTokens };
473
+ }
474
+ return usage;
475
+ }
476
+
477
+ function flattenAssistant(message: AssistantMessage): {
478
+ text: string;
479
+ reasoning: string;
480
+ toolCalls: ToolCall[];
481
+ } {
482
+ let text = "";
483
+ let reasoning = "";
484
+ const toolCalls: ToolCall[] = [];
485
+ for (const part of message.content) {
486
+ switch (part.type) {
487
+ case "text":
488
+ text += part.text;
489
+ break;
490
+ case "thinking":
491
+ reasoning += part.thinking;
492
+ break;
493
+ case "redactedThinking":
494
+ // Opaque blob — surface verbatim on the reasoning channel so the
495
+ // concatenation round-trips through clients that just echo it.
496
+ reasoning += part.data;
497
+ break;
498
+ case "toolCall":
499
+ toolCalls.push(part);
500
+ break;
501
+ }
502
+ }
503
+ return { text, reasoning, toolCalls };
504
+ }
505
+
506
+ function isOnlyRaw(args: Record<string, unknown>): boolean {
507
+ for (const k in args) {
508
+ if (k !== "__raw") return false;
509
+ }
510
+ return true;
511
+ }
512
+
513
+ function stringifyArgs(args: Record<string, unknown>): string {
514
+ // `__raw` is our fallback marker for un-parseable inbound args; preserve it verbatim on the way out.
515
+ if (typeof args.__raw === "string" && isOnlyRaw(args)) return args.__raw;
516
+ try {
517
+ return JSON.stringify(args);
518
+ } catch {
519
+ return "{}";
520
+ }
521
+ }
522
+
523
+ function mapFinishReason(reason: StopReason, hasToolCalls: boolean): string {
524
+ if (reason === "toolUse" || (hasToolCalls && reason === "stop")) return "tool_calls";
525
+ if (reason === "length") return "length";
526
+ // pi-ai's StopReason does not currently carry a content-filter signal;
527
+ // when it does, map it to "content_filter" here.
528
+ return "stop";
529
+ }
530
+
531
+ function makeId(): string {
532
+ return `chatcmpl-${randomUUID()}`;
533
+ }
534
+
535
+ // ---------------------------------------------------------------------------
536
+ // encodeStream (SSE)
537
+ // ---------------------------------------------------------------------------
538
+
539
+ export function encodeStream(
540
+ events: AssistantMessageEventStream,
541
+ requestedModelId: string,
542
+ options?: ParsedRequest["options"],
543
+ control?: AuthGatewayStreamControl,
544
+ ): ReadableStream<Uint8Array> {
545
+ const encoder = new TextEncoder();
546
+ const id = makeId();
547
+ const created = Math.floor(Date.now() / 1000);
548
+ const includeUsage = options?.extra?.includeStreamingUsage === true;
549
+ let cancelled = control?.signal?.aborted === true;
550
+ const markCancelled = () => {
551
+ cancelled = true;
552
+ };
553
+ control?.signal?.addEventListener("abort", markCancelled, { once: true });
554
+
555
+ const baseChunk = (delta: Record<string, unknown>, finishReason: string | null) => ({
556
+ id,
557
+ object: "chat.completion.chunk",
558
+ created,
559
+ model: requestedModelId,
560
+ system_fingerprint: null,
561
+ choices: [{ index: 0, delta, finish_reason: finishReason, logprobs: null }],
562
+ ...(includeUsage ? { usage: null } : {}),
563
+ });
564
+
565
+ const writeSse = (controller: ReadableStreamDefaultController<Uint8Array>, payload: unknown): void => {
566
+ if (!cancelled) controller.enqueue(encoder.encode(`data: ${JSON.stringify(payload)}\n\n`));
567
+ };
568
+
569
+ const writeUsage = (controller: ReadableStreamDefaultController<Uint8Array>, message: AssistantMessage): void => {
570
+ writeSse(controller, {
571
+ id,
572
+ object: "chat.completion.chunk",
573
+ created,
574
+ model: requestedModelId,
575
+ system_fingerprint: null,
576
+ choices: [],
577
+ usage: buildUsage(message),
578
+ });
579
+ };
580
+
581
+ return new ReadableStream<Uint8Array>({
582
+ async start(controller) {
583
+ // contentIndex (from pi-ai events) -> tool_calls index on the wire.
584
+ const toolIndexByContentIndex = new Map<number, number>();
585
+ // wire index -> id/name emitted on the start chunk, to detect late-arriving
586
+ // upstream id/name that needs a corrective chunk before the finish.
587
+ const sentToolMeta = new Map<number, { id: string; name: string }>();
588
+ let nextToolIndex = 0;
589
+ let hasToolCalls = false;
590
+ let finishReason: string = "stop";
591
+
592
+ try {
593
+ if (cancelled) {
594
+ controller.close();
595
+ return;
596
+ }
597
+ // Initial role chunk.
598
+ writeSse(controller, baseChunk({ role: "assistant" }, null));
599
+
600
+ for await (const event of events) {
601
+ if (cancelled) return;
602
+ switch (event.type) {
603
+ case "text_delta":
604
+ if (event.delta.length > 0) {
605
+ writeSse(controller, baseChunk({ content: event.delta }, null));
606
+ }
607
+ break;
608
+
609
+ case "thinking_delta":
610
+ // DeepSeek-style / o-series reasoning channel. Clients that don't
611
+ // understand it ignore the unknown delta key.
612
+ if (event.delta.length > 0) {
613
+ writeSse(controller, baseChunk({ reasoning_content: event.delta }, null));
614
+ }
615
+ break;
616
+
617
+ case "toolcall_start": {
618
+ hasToolCalls = true;
619
+ const idx = nextToolIndex++;
620
+ toolIndexByContentIndex.set(event.contentIndex, idx);
621
+ const partial = event.partial.content[event.contentIndex];
622
+ const call = partial && partial.type === "toolCall" ? partial : undefined;
623
+ sentToolMeta.set(idx, { id: call?.id ?? "", name: call?.name ?? "" });
624
+ writeSse(
625
+ controller,
626
+ baseChunk(
627
+ {
628
+ tool_calls: [
629
+ {
630
+ index: idx,
631
+ id: call?.id ?? "",
632
+ type: "function",
633
+ function: { name: call?.name ?? "", arguments: "" },
634
+ },
635
+ ],
636
+ },
637
+ null,
638
+ ),
639
+ );
640
+ break;
641
+ }
642
+
643
+ case "toolcall_delta": {
644
+ const idx = toolIndexByContentIndex.get(event.contentIndex);
645
+ if (idx === undefined) break;
646
+ writeSse(
647
+ controller,
648
+ baseChunk({ tool_calls: [{ index: idx, function: { arguments: event.delta } }] }, null),
649
+ );
650
+ break;
651
+ }
652
+
653
+ case "toolcall_end": {
654
+ const idx = toolIndexByContentIndex.get(event.contentIndex);
655
+ if (idx === undefined) break;
656
+ const sent = sentToolMeta.get(idx);
657
+ if (sent === undefined) break;
658
+ // Upstream completions providers can receive the real id/name in a
659
+ // later chunk than toolcall_start. Emit a corrective chunk only when
660
+ // the streamed value was empty: accumulating clients concatenate
661
+ // string fields, so "" + value is the only safe correction.
662
+ const correctId = sent.id === "" && event.toolCall.id !== "" ? event.toolCall.id : undefined;
663
+ const correctName =
664
+ sent.name === "" && event.toolCall.name !== "" ? event.toolCall.name : undefined;
665
+ if (correctId !== undefined || correctName !== undefined) {
666
+ writeSse(
667
+ controller,
668
+ baseChunk(
669
+ {
670
+ tool_calls: [
671
+ {
672
+ index: idx,
673
+ ...(correctId !== undefined ? { id: correctId } : {}),
674
+ ...(correctName !== undefined ? { function: { name: correctName } } : {}),
675
+ },
676
+ ],
677
+ },
678
+ null,
679
+ ),
680
+ );
681
+ }
682
+ break;
683
+ }
684
+
685
+ case "done":
686
+ finishReason =
687
+ event.reason === "toolUse"
688
+ ? "tool_calls"
689
+ : event.reason === "length"
690
+ ? "length"
691
+ : hasToolCalls
692
+ ? "tool_calls"
693
+ : "stop";
694
+ writeSse(controller, baseChunk({}, finishReason));
695
+ if (includeUsage) writeUsage(controller, event.message);
696
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
697
+ controller.close();
698
+ return;
699
+
700
+ case "error": {
701
+ const msg = event.error.errorMessage ?? "stream error";
702
+ writeSse(controller, { error: { message: msg, type: "upstream_error" } });
703
+ controller.close();
704
+ return;
705
+ }
706
+
707
+ // Drop start / *_start and text/thinking *_end — chat-completions
708
+ // wire only surfaces deltas and the terminal finish_reason.
709
+ default:
710
+ break;
711
+ }
712
+ }
713
+
714
+ // Stream ended without a terminal `done` (defensive). Close gracefully.
715
+ if (!cancelled) {
716
+ writeSse(controller, baseChunk({}, hasToolCalls ? "tool_calls" : "stop"));
717
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
718
+ controller.close();
719
+ }
720
+ } catch (err) {
721
+ if (!cancelled) {
722
+ const msg = err instanceof Error ? err.message : String(err);
723
+ writeSse(controller, { error: { message: msg, type: "upstream_error" } });
724
+ controller.close();
725
+ }
726
+ } finally {
727
+ control?.signal?.removeEventListener("abort", markCancelled);
728
+ }
729
+ },
730
+ cancel(reason) {
731
+ cancelled = true;
732
+ control?.signal?.removeEventListener("abort", markCancelled);
733
+ control?.onCancel?.(reason);
734
+ },
735
+ });
736
+ }
737
+
738
+ // ---------------------------------------------------------------------------
739
+ // formatError
740
+ // ---------------------------------------------------------------------------
741
+
742
+ /**
743
+ * OpenAI chat-completions error envelope:
744
+ * `{ error: { message, type } }`
745
+ * Matches the shape the official SDK auto-parses into `APIError`.
746
+ */
747
+ export function formatError(status: number, type: string, message: string): Response {
748
+ return new Response(JSON.stringify({ error: { message, type } }), {
749
+ status,
750
+ headers: { "Content-Type": "application/json" },
751
+ });
752
+ }