openai 7.5.0 → 7.6.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 (597) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +46 -2
  3. package/auth/index.d.mts +1 -1
  4. package/auth/index.d.mts.map +1 -1
  5. package/auth/index.d.ts +1 -1
  6. package/auth/index.d.ts.map +1 -1
  7. package/auth/index.js.map +1 -1
  8. package/auth/index.mjs.map +1 -1
  9. package/auth/subject-token-providers.d.mts.map +1 -1
  10. package/auth/subject-token-providers.d.ts.map +1 -1
  11. package/auth/subject-token-providers.js +176 -4
  12. package/auth/subject-token-providers.js.map +1 -1
  13. package/auth/subject-token-providers.mjs +176 -4
  14. package/auth/subject-token-providers.mjs.map +1 -1
  15. package/auth/types.d.mts +17 -0
  16. package/auth/types.d.mts.map +1 -1
  17. package/auth/types.d.ts +17 -0
  18. package/auth/types.d.ts.map +1 -1
  19. package/auth/workload-identity-auth.d.mts.map +1 -1
  20. package/auth/workload-identity-auth.d.ts.map +1 -1
  21. package/auth/workload-identity-auth.js +80 -7
  22. package/auth/workload-identity-auth.js.map +1 -1
  23. package/auth/workload-identity-auth.mjs +80 -7
  24. package/auth/workload-identity-auth.mjs.map +1 -1
  25. package/auth/x509-transport.d.mts +5 -0
  26. package/auth/x509-transport.d.mts.map +1 -0
  27. package/auth/x509-transport.d.ts +5 -0
  28. package/auth/x509-transport.d.ts.map +1 -0
  29. package/auth/x509-transport.js +39 -0
  30. package/auth/x509-transport.js.map +1 -0
  31. package/auth/x509-transport.mjs +36 -0
  32. package/auth/x509-transport.mjs.map +1 -0
  33. package/azure.d.mts +11 -2
  34. package/azure.d.mts.map +1 -1
  35. package/azure.d.ts +11 -2
  36. package/azure.d.ts.map +1 -1
  37. package/azure.js +7 -1
  38. package/azure.js.map +1 -1
  39. package/azure.mjs +7 -1
  40. package/azure.mjs.map +1 -1
  41. package/bedrock.d.mts +6 -2
  42. package/bedrock.d.mts.map +1 -1
  43. package/bedrock.d.ts +6 -2
  44. package/bedrock.d.ts.map +1 -1
  45. package/bedrock.js +34 -4
  46. package/bedrock.js.map +1 -1
  47. package/bedrock.mjs +35 -5
  48. package/bedrock.mjs.map +1 -1
  49. package/beta/realtime/internal-base.d.mts +2 -0
  50. package/beta/realtime/internal-base.d.mts.map +1 -1
  51. package/beta/realtime/internal-base.d.ts +2 -0
  52. package/beta/realtime/internal-base.d.ts.map +1 -1
  53. package/beta/realtime/internal-base.js +15 -0
  54. package/beta/realtime/internal-base.js.map +1 -1
  55. package/beta/realtime/internal-base.mjs +14 -0
  56. package/beta/realtime/internal-base.mjs.map +1 -1
  57. package/beta/realtime/websocket.d.mts +11 -5
  58. package/beta/realtime/websocket.d.mts.map +1 -1
  59. package/beta/realtime/websocket.d.ts +11 -5
  60. package/beta/realtime/websocket.d.ts.map +1 -1
  61. package/beta/realtime/websocket.js +106 -31
  62. package/beta/realtime/websocket.js.map +1 -1
  63. package/beta/realtime/websocket.mjs +107 -32
  64. package/beta/realtime/websocket.mjs.map +1 -1
  65. package/beta/realtime/ws.d.mts +0 -2
  66. package/beta/realtime/ws.d.mts.map +1 -1
  67. package/beta/realtime/ws.d.ts +0 -2
  68. package/beta/realtime/ws.d.ts.map +1 -1
  69. package/beta/realtime/ws.js +24 -5
  70. package/beta/realtime/ws.js.map +1 -1
  71. package/beta/realtime/ws.mjs +25 -6
  72. package/beta/realtime/ws.mjs.map +1 -1
  73. package/client.d.mts +17 -2
  74. package/client.d.mts.map +1 -1
  75. package/client.d.ts +17 -2
  76. package/client.d.ts.map +1 -1
  77. package/client.js +422 -70
  78. package/client.js.map +1 -1
  79. package/client.mjs +423 -71
  80. package/client.mjs.map +1 -1
  81. package/core/EventEmitter.d.mts.map +1 -1
  82. package/core/EventEmitter.d.ts.map +1 -1
  83. package/core/EventEmitter.js +67 -9
  84. package/core/EventEmitter.js.map +1 -1
  85. package/core/EventEmitter.mjs +68 -10
  86. package/core/EventEmitter.mjs.map +1 -1
  87. package/core/streaming.d.mts +1 -0
  88. package/core/streaming.d.mts.map +1 -1
  89. package/core/streaming.d.ts +1 -0
  90. package/core/streaming.d.ts.map +1 -1
  91. package/core/streaming.js +58 -19
  92. package/core/streaming.js.map +1 -1
  93. package/core/streaming.mjs +58 -19
  94. package/core/streaming.mjs.map +1 -1
  95. package/helpers/audio.d.mts +5 -5
  96. package/helpers/audio.d.mts.map +1 -1
  97. package/helpers/audio.d.ts +5 -5
  98. package/helpers/audio.d.ts.map +1 -1
  99. package/helpers/audio.js +93 -22
  100. package/helpers/audio.js.map +1 -1
  101. package/helpers/audio.mjs +93 -22
  102. package/helpers/audio.mjs.map +1 -1
  103. package/helpers/standard-schema.d.mts.map +1 -1
  104. package/helpers/standard-schema.d.ts.map +1 -1
  105. package/helpers/standard-schema.js +34 -14
  106. package/helpers/standard-schema.js.map +1 -1
  107. package/helpers/standard-schema.mjs +35 -15
  108. package/helpers/standard-schema.mjs.map +1 -1
  109. package/helpers/zod-v3-strict-schema.d.mts +7 -0
  110. package/helpers/zod-v3-strict-schema.d.mts.map +1 -0
  111. package/helpers/zod-v3-strict-schema.d.ts +7 -0
  112. package/helpers/zod-v3-strict-schema.d.ts.map +1 -0
  113. package/helpers/zod-v3-strict-schema.js +403 -0
  114. package/helpers/zod-v3-strict-schema.js.map +1 -0
  115. package/helpers/zod-v3-strict-schema.mjs +399 -0
  116. package/helpers/zod-v3-strict-schema.mjs.map +1 -0
  117. package/helpers/zod.d.mts.map +1 -1
  118. package/helpers/zod.d.ts.map +1 -1
  119. package/helpers/zod.js +11 -3
  120. package/helpers/zod.js.map +1 -1
  121. package/helpers/zod.mjs +12 -4
  122. package/helpers/zod.mjs.map +1 -1
  123. package/index.d.mts +1 -1
  124. package/index.d.mts.map +1 -1
  125. package/index.d.ts +1 -1
  126. package/index.d.ts.map +1 -1
  127. package/index.js.map +1 -1
  128. package/index.mjs.map +1 -1
  129. package/internal/assistant-stream-delta.d.mts.map +1 -1
  130. package/internal/assistant-stream-delta.d.ts.map +1 -1
  131. package/internal/assistant-stream-delta.js +28 -8
  132. package/internal/assistant-stream-delta.js.map +1 -1
  133. package/internal/assistant-stream-delta.mjs +28 -8
  134. package/internal/assistant-stream-delta.mjs.map +1 -1
  135. package/internal/auth/x509-token-exchange.d.mts +16 -0
  136. package/internal/auth/x509-token-exchange.d.mts.map +1 -0
  137. package/internal/auth/x509-token-exchange.d.ts +16 -0
  138. package/internal/auth/x509-token-exchange.d.ts.map +1 -0
  139. package/internal/auth/x509-token-exchange.js +246 -0
  140. package/internal/auth/x509-token-exchange.js.map +1 -0
  141. package/internal/auth/x509-token-exchange.mjs +243 -0
  142. package/internal/auth/x509-token-exchange.mjs.map +1 -0
  143. package/internal/auth/x509-transport-capability.d.mts +37 -0
  144. package/internal/auth/x509-transport-capability.d.mts.map +1 -0
  145. package/internal/auth/x509-transport-capability.d.ts +37 -0
  146. package/internal/auth/x509-transport-capability.d.ts.map +1 -0
  147. package/internal/auth/x509-transport-capability.js +169 -0
  148. package/internal/auth/x509-transport-capability.js.map +1 -0
  149. package/internal/auth/x509-transport-capability.mjs +164 -0
  150. package/internal/auth/x509-transport-capability.mjs.map +1 -0
  151. package/internal/auth/x509-transport-registry.d.mts +63 -0
  152. package/internal/auth/x509-transport-registry.d.mts.map +1 -0
  153. package/internal/auth/x509-transport-registry.d.ts +63 -0
  154. package/internal/auth/x509-transport-registry.d.ts.map +1 -0
  155. package/internal/auth/x509-transport-registry.js +53 -0
  156. package/internal/auth/x509-transport-registry.js.map +1 -0
  157. package/internal/auth/x509-transport-registry.mjs +48 -0
  158. package/internal/auth/x509-transport-registry.mjs.map +1 -0
  159. package/internal/auth/x509-transport-state-browser.d.mts +21 -0
  160. package/internal/auth/x509-transport-state-browser.d.mts.map +1 -0
  161. package/internal/auth/x509-transport-state-browser.d.ts +21 -0
  162. package/internal/auth/x509-transport-state-browser.d.ts.map +1 -0
  163. package/internal/auth/x509-transport-state-browser.js +30 -0
  164. package/internal/auth/x509-transport-state-browser.js.map +1 -0
  165. package/internal/auth/x509-transport-state-browser.mjs +27 -0
  166. package/internal/auth/x509-transport-state-browser.mjs.map +1 -0
  167. package/internal/auth/x509-transport-state.cjs +30 -0
  168. package/internal/auth/x509-transport-state.cjs.map +1 -0
  169. package/internal/auth/x509-transport-state.d.cts +21 -0
  170. package/internal/auth/x509-transport-state.d.cts.map +1 -0
  171. package/internal/auth/x509-workload-identity-auth.d.mts +128 -0
  172. package/internal/auth/x509-workload-identity-auth.d.mts.map +1 -0
  173. package/internal/auth/x509-workload-identity-auth.d.ts +128 -0
  174. package/internal/auth/x509-workload-identity-auth.d.ts.map +1 -0
  175. package/internal/auth/x509-workload-identity-auth.js +767 -0
  176. package/internal/auth/x509-workload-identity-auth.js.map +1 -0
  177. package/internal/auth/x509-workload-identity-auth.mjs +757 -0
  178. package/internal/auth/x509-workload-identity-auth.mjs.map +1 -0
  179. package/internal/bedrock.d.mts +2 -0
  180. package/internal/bedrock.d.mts.map +1 -1
  181. package/internal/bedrock.d.ts +2 -0
  182. package/internal/bedrock.d.ts.map +1 -1
  183. package/internal/bedrock.js +169 -5
  184. package/internal/bedrock.js.map +1 -1
  185. package/internal/bedrock.mjs +168 -5
  186. package/internal/bedrock.mjs.map +1 -1
  187. package/internal/data-residency.d.mts +12 -0
  188. package/internal/data-residency.d.mts.map +1 -0
  189. package/internal/data-residency.d.ts +12 -0
  190. package/internal/data-residency.d.ts.map +1 -0
  191. package/internal/data-residency.js +34 -0
  192. package/internal/data-residency.js.map +1 -0
  193. package/internal/data-residency.mjs +30 -0
  194. package/internal/data-residency.mjs.map +1 -0
  195. package/internal/multipart-encoding.d.mts +8 -0
  196. package/internal/multipart-encoding.d.mts.map +1 -0
  197. package/internal/multipart-encoding.d.ts +8 -0
  198. package/internal/multipart-encoding.d.ts.map +1 -0
  199. package/internal/multipart-encoding.js +51 -0
  200. package/internal/multipart-encoding.js.map +1 -0
  201. package/internal/multipart-encoding.mjs +48 -0
  202. package/internal/multipart-encoding.mjs.map +1 -0
  203. package/internal/qs/utils.d.mts.map +1 -1
  204. package/internal/qs/utils.d.ts.map +1 -1
  205. package/internal/qs/utils.js +307 -10
  206. package/internal/qs/utils.js.map +1 -1
  207. package/internal/qs/utils.mjs +307 -10
  208. package/internal/qs/utils.mjs.map +1 -1
  209. package/internal/request-options.d.mts +7 -0
  210. package/internal/request-options.d.mts.map +1 -1
  211. package/internal/request-options.d.ts +7 -0
  212. package/internal/request-options.d.ts.map +1 -1
  213. package/internal/request-options.js +43 -1
  214. package/internal/request-options.js.map +1 -1
  215. package/internal/request-options.mjs +42 -1
  216. package/internal/request-options.mjs.map +1 -1
  217. package/internal/responses/canonical-output-text.js +3 -3
  218. package/internal/responses/canonical-output-text.js.map +1 -1
  219. package/internal/responses/canonical-output-text.mjs +3 -3
  220. package/internal/responses/canonical-output-text.mjs.map +1 -1
  221. package/internal/responses/response-accumulator.d.mts +1 -1
  222. package/internal/responses/response-accumulator.d.mts.map +1 -1
  223. package/internal/responses/response-accumulator.d.ts +1 -1
  224. package/internal/responses/response-accumulator.d.ts.map +1 -1
  225. package/internal/responses/response-accumulator.js +331 -25
  226. package/internal/responses/response-accumulator.js.map +1 -1
  227. package/internal/responses/response-accumulator.mjs +331 -25
  228. package/internal/responses/response-accumulator.mjs.map +1 -1
  229. package/internal/tslib.js +6 -0
  230. package/internal/tslib.mjs +6 -1
  231. package/internal/uploads.d.mts +10 -6
  232. package/internal/uploads.d.mts.map +1 -1
  233. package/internal/uploads.d.ts +10 -6
  234. package/internal/uploads.d.ts.map +1 -1
  235. package/internal/uploads.js +65 -24
  236. package/internal/uploads.js.map +1 -1
  237. package/internal/uploads.mjs +65 -24
  238. package/internal/uploads.mjs.map +1 -1
  239. package/internal/utils/env.d.mts.map +1 -1
  240. package/internal/utils/env.d.ts.map +1 -1
  241. package/internal/utils/env.js +9 -4
  242. package/internal/utils/env.js.map +1 -1
  243. package/internal/utils/env.mjs +9 -4
  244. package/internal/utils/env.mjs.map +1 -1
  245. package/internal/ws.d.mts.map +1 -1
  246. package/internal/ws.d.ts.map +1 -1
  247. package/internal/ws.js +23 -1
  248. package/internal/ws.js.map +1 -1
  249. package/internal/ws.mjs +23 -1
  250. package/internal/ws.mjs.map +1 -1
  251. package/lib/AbstractChatCompletionRunner.d.mts.map +1 -1
  252. package/lib/AbstractChatCompletionRunner.d.ts.map +1 -1
  253. package/lib/AbstractChatCompletionRunner.js +32 -7
  254. package/lib/AbstractChatCompletionRunner.js.map +1 -1
  255. package/lib/AbstractChatCompletionRunner.mjs +34 -9
  256. package/lib/AbstractChatCompletionRunner.mjs.map +1 -1
  257. package/lib/AssistantStream.d.mts.map +1 -1
  258. package/lib/AssistantStream.d.ts.map +1 -1
  259. package/lib/AssistantStream.js +211 -23
  260. package/lib/AssistantStream.js.map +1 -1
  261. package/lib/AssistantStream.mjs +211 -23
  262. package/lib/AssistantStream.mjs.map +1 -1
  263. package/lib/ChatCompletionStream.d.mts +1 -1
  264. package/lib/ChatCompletionStream.d.mts.map +1 -1
  265. package/lib/ChatCompletionStream.d.ts +1 -1
  266. package/lib/ChatCompletionStream.d.ts.map +1 -1
  267. package/lib/ChatCompletionStream.js +1118 -67
  268. package/lib/ChatCompletionStream.js.map +1 -1
  269. package/lib/ChatCompletionStream.mjs +1119 -68
  270. package/lib/ChatCompletionStream.mjs.map +1 -1
  271. package/lib/EventEmitter.d.mts.map +1 -1
  272. package/lib/EventEmitter.d.ts.map +1 -1
  273. package/lib/EventEmitter.js +67 -9
  274. package/lib/EventEmitter.js.map +1 -1
  275. package/lib/EventEmitter.mjs +68 -10
  276. package/lib/EventEmitter.mjs.map +1 -1
  277. package/lib/EventStream.d.mts +4 -0
  278. package/lib/EventStream.d.mts.map +1 -1
  279. package/lib/EventStream.d.ts +4 -0
  280. package/lib/EventStream.d.ts.map +1 -1
  281. package/lib/EventStream.js +1096 -11
  282. package/lib/EventStream.js.map +1 -1
  283. package/lib/EventStream.mjs +1097 -12
  284. package/lib/EventStream.mjs.map +1 -1
  285. package/lib/ResponsesParser.d.mts.map +1 -1
  286. package/lib/ResponsesParser.d.ts.map +1 -1
  287. package/lib/ResponsesParser.js +1 -1
  288. package/lib/ResponsesParser.js.map +1 -1
  289. package/lib/ResponsesParser.mjs +1 -1
  290. package/lib/ResponsesParser.mjs.map +1 -1
  291. package/lib/Util.d.mts +2 -0
  292. package/lib/Util.d.mts.map +1 -1
  293. package/lib/Util.d.ts +2 -0
  294. package/lib/Util.d.ts.map +1 -1
  295. package/lib/Util.js +7 -4
  296. package/lib/Util.js.map +1 -1
  297. package/lib/Util.mjs +7 -4
  298. package/lib/Util.mjs.map +1 -1
  299. package/lib/assistant-run-polling.d.mts +12 -0
  300. package/lib/assistant-run-polling.d.mts.map +1 -0
  301. package/lib/assistant-run-polling.d.ts +12 -0
  302. package/lib/assistant-run-polling.d.ts.map +1 -0
  303. package/lib/assistant-run-polling.js +17 -0
  304. package/lib/assistant-run-polling.js.map +1 -0
  305. package/lib/assistant-run-polling.mjs +14 -0
  306. package/lib/assistant-run-polling.mjs.map +1 -0
  307. package/lib/embeddings.d.mts +18 -0
  308. package/lib/embeddings.d.mts.map +1 -0
  309. package/lib/embeddings.d.ts +18 -0
  310. package/lib/embeddings.d.ts.map +1 -0
  311. package/lib/embeddings.js +50 -0
  312. package/lib/embeddings.js.map +1 -0
  313. package/lib/embeddings.mjs +47 -0
  314. package/lib/embeddings.mjs.map +1 -0
  315. package/lib/file-processing.d.mts +10 -0
  316. package/lib/file-processing.d.mts.map +1 -0
  317. package/lib/file-processing.d.ts +10 -0
  318. package/lib/file-processing.d.ts.map +1 -0
  319. package/lib/file-processing.js +30 -0
  320. package/lib/file-processing.js.map +1 -0
  321. package/lib/file-processing.mjs +27 -0
  322. package/lib/file-processing.mjs.map +1 -0
  323. package/lib/parser.d.mts.map +1 -1
  324. package/lib/parser.d.ts.map +1 -1
  325. package/lib/parser.js +16 -4
  326. package/lib/parser.js.map +1 -1
  327. package/lib/parser.mjs +16 -4
  328. package/lib/parser.mjs.map +1 -1
  329. package/lib/polling.d.mts +20 -0
  330. package/lib/polling.d.mts.map +1 -0
  331. package/lib/polling.d.ts +20 -0
  332. package/lib/polling.d.ts.map +1 -0
  333. package/lib/polling.js +130 -0
  334. package/lib/polling.js.map +1 -0
  335. package/lib/polling.mjs +127 -0
  336. package/lib/polling.mjs.map +1 -0
  337. package/lib/responses/ResponseStream.d.mts.map +1 -1
  338. package/lib/responses/ResponseStream.d.ts.map +1 -1
  339. package/lib/responses/ResponseStream.js +21 -11
  340. package/lib/responses/ResponseStream.js.map +1 -1
  341. package/lib/responses/ResponseStream.mjs +21 -11
  342. package/lib/responses/ResponseStream.mjs.map +1 -1
  343. package/lib/vector-store-polling.d.mts +22 -0
  344. package/lib/vector-store-polling.d.mts.map +1 -0
  345. package/lib/vector-store-polling.d.ts +22 -0
  346. package/lib/vector-store-polling.d.ts.map +1 -0
  347. package/lib/vector-store-polling.js +24 -0
  348. package/lib/vector-store-polling.js.map +1 -0
  349. package/lib/vector-store-polling.mjs +20 -0
  350. package/lib/vector-store-polling.mjs.map +1 -0
  351. package/lib/vector-store-upload.d.mts +17 -0
  352. package/lib/vector-store-upload.d.mts.map +1 -0
  353. package/lib/vector-store-upload.d.ts +17 -0
  354. package/lib/vector-store-upload.d.ts.map +1 -0
  355. package/lib/vector-store-upload.js +37 -0
  356. package/lib/vector-store-upload.js.map +1 -0
  357. package/lib/vector-store-upload.mjs +34 -0
  358. package/lib/vector-store-upload.mjs.map +1 -0
  359. package/lib/webhook-signature.d.mts +15 -0
  360. package/lib/webhook-signature.d.mts.map +1 -0
  361. package/lib/webhook-signature.d.ts +15 -0
  362. package/lib/webhook-signature.d.ts.map +1 -0
  363. package/lib/webhook-signature.js +125 -0
  364. package/lib/webhook-signature.js.map +1 -0
  365. package/lib/webhook-signature.mjs +121 -0
  366. package/lib/webhook-signature.mjs.map +1 -0
  367. package/package.json +14 -1
  368. package/realtime/internal-base.d.mts +2 -0
  369. package/realtime/internal-base.d.mts.map +1 -1
  370. package/realtime/internal-base.d.ts +2 -0
  371. package/realtime/internal-base.d.ts.map +1 -1
  372. package/realtime/internal-base.js +15 -0
  373. package/realtime/internal-base.js.map +1 -1
  374. package/realtime/internal-base.mjs +14 -0
  375. package/realtime/internal-base.mjs.map +1 -1
  376. package/realtime/websocket.d.mts +3 -2
  377. package/realtime/websocket.d.mts.map +1 -1
  378. package/realtime/websocket.d.ts +3 -2
  379. package/realtime/websocket.d.ts.map +1 -1
  380. package/realtime/websocket.js +90 -26
  381. package/realtime/websocket.js.map +1 -1
  382. package/realtime/websocket.mjs +91 -27
  383. package/realtime/websocket.mjs.map +1 -1
  384. package/realtime/ws.d.mts.map +1 -1
  385. package/realtime/ws.d.ts.map +1 -1
  386. package/realtime/ws.js +9 -1
  387. package/realtime/ws.js.map +1 -1
  388. package/realtime/ws.mjs +10 -2
  389. package/realtime/ws.mjs.map +1 -1
  390. package/resources/admin/organization/index.d.mts +2 -2
  391. package/resources/admin/organization/index.d.mts.map +1 -1
  392. package/resources/admin/organization/index.d.ts +2 -2
  393. package/resources/admin/organization/index.d.ts.map +1 -1
  394. package/resources/admin/organization/index.js.map +1 -1
  395. package/resources/admin/organization/index.mjs.map +1 -1
  396. package/resources/admin/organization/organization.d.mts +4 -4
  397. package/resources/admin/organization/organization.d.mts.map +1 -1
  398. package/resources/admin/organization/organization.d.ts +4 -4
  399. package/resources/admin/organization/organization.d.ts.map +1 -1
  400. package/resources/admin/organization/organization.js.map +1 -1
  401. package/resources/admin/organization/organization.mjs.map +1 -1
  402. package/resources/admin/organization/projects/index.d.mts +1 -1
  403. package/resources/admin/organization/projects/index.d.mts.map +1 -1
  404. package/resources/admin/organization/projects/index.d.ts +1 -1
  405. package/resources/admin/organization/projects/index.d.ts.map +1 -1
  406. package/resources/admin/organization/projects/index.js.map +1 -1
  407. package/resources/admin/organization/projects/index.mjs.map +1 -1
  408. package/resources/admin/organization/projects/projects.d.mts +22 -5
  409. package/resources/admin/organization/projects/projects.d.mts.map +1 -1
  410. package/resources/admin/organization/projects/projects.d.ts +22 -5
  411. package/resources/admin/organization/projects/projects.d.ts.map +1 -1
  412. package/resources/admin/organization/projects/projects.js.map +1 -1
  413. package/resources/admin/organization/projects/projects.mjs.map +1 -1
  414. package/resources/admin/organization/usage.d.mts +62 -1
  415. package/resources/admin/organization/usage.d.mts.map +1 -1
  416. package/resources/admin/organization/usage.d.ts +62 -1
  417. package/resources/admin/organization/usage.d.ts.map +1 -1
  418. package/resources/admin/organization/usage.js.map +1 -1
  419. package/resources/admin/organization/usage.mjs.map +1 -1
  420. package/resources/beta/responses/internal-base.d.mts.map +1 -1
  421. package/resources/beta/responses/internal-base.d.ts.map +1 -1
  422. package/resources/beta/responses/internal-base.js +2 -0
  423. package/resources/beta/responses/internal-base.js.map +1 -1
  424. package/resources/beta/responses/internal-base.mjs +2 -0
  425. package/resources/beta/responses/internal-base.mjs.map +1 -1
  426. package/resources/beta/responses/responses.d.mts +9 -12
  427. package/resources/beta/responses/responses.d.mts.map +1 -1
  428. package/resources/beta/responses/responses.d.ts +9 -12
  429. package/resources/beta/responses/responses.d.ts.map +1 -1
  430. package/resources/beta/responses/responses.js.map +1 -1
  431. package/resources/beta/responses/responses.mjs.map +1 -1
  432. package/resources/beta/threads/runs/index.d.mts +1 -1
  433. package/resources/beta/threads/runs/index.d.mts.map +1 -1
  434. package/resources/beta/threads/runs/index.d.ts +1 -1
  435. package/resources/beta/threads/runs/index.d.ts.map +1 -1
  436. package/resources/beta/threads/runs/runs.d.mts.map +1 -1
  437. package/resources/beta/threads/runs/runs.d.ts.map +1 -1
  438. package/resources/beta/threads/runs/runs.js +2 -43
  439. package/resources/beta/threads/runs/runs.js.map +1 -1
  440. package/resources/beta/threads/runs/runs.mjs +2 -43
  441. package/resources/beta/threads/runs/runs.mjs.map +1 -1
  442. package/resources/chat/completions/completions.d.mts +6 -0
  443. package/resources/chat/completions/completions.d.mts.map +1 -1
  444. package/resources/chat/completions/completions.d.ts +6 -0
  445. package/resources/chat/completions/completions.d.ts.map +1 -1
  446. package/resources/chat/completions/completions.js.map +1 -1
  447. package/resources/chat/completions/completions.mjs.map +1 -1
  448. package/resources/embeddings.d.mts.map +1 -1
  449. package/resources/embeddings.d.ts.map +1 -1
  450. package/resources/embeddings.js +2 -36
  451. package/resources/embeddings.js.map +1 -1
  452. package/resources/embeddings.mjs +2 -36
  453. package/resources/embeddings.mjs.map +1 -1
  454. package/resources/files.d.mts.map +1 -1
  455. package/resources/files.d.ts.map +1 -1
  456. package/resources/files.js +2 -15
  457. package/resources/files.js.map +1 -1
  458. package/resources/files.mjs +2 -15
  459. package/resources/files.mjs.map +1 -1
  460. package/resources/images.d.mts +12 -22
  461. package/resources/images.d.mts.map +1 -1
  462. package/resources/images.d.ts +12 -22
  463. package/resources/images.d.ts.map +1 -1
  464. package/resources/realtime/calls.d.mts +23 -1
  465. package/resources/realtime/calls.d.mts.map +1 -1
  466. package/resources/realtime/calls.d.ts +23 -1
  467. package/resources/realtime/calls.d.ts.map +1 -1
  468. package/resources/realtime/calls.js +24 -0
  469. package/resources/realtime/calls.js.map +1 -1
  470. package/resources/realtime/calls.mjs +24 -0
  471. package/resources/realtime/calls.mjs.map +1 -1
  472. package/resources/realtime/index.d.mts +1 -1
  473. package/resources/realtime/index.d.mts.map +1 -1
  474. package/resources/realtime/index.d.ts +1 -1
  475. package/resources/realtime/index.d.ts.map +1 -1
  476. package/resources/realtime/index.js.map +1 -1
  477. package/resources/realtime/index.mjs +1 -1
  478. package/resources/realtime/index.mjs.map +1 -1
  479. package/resources/realtime/realtime.d.mts +5 -4
  480. package/resources/realtime/realtime.d.mts.map +1 -1
  481. package/resources/realtime/realtime.d.ts +5 -4
  482. package/resources/realtime/realtime.d.ts.map +1 -1
  483. package/resources/realtime/realtime.js.map +1 -1
  484. package/resources/realtime/realtime.mjs.map +1 -1
  485. package/resources/responses/internal-base.d.mts.map +1 -1
  486. package/resources/responses/internal-base.d.ts.map +1 -1
  487. package/resources/responses/internal-base.js +2 -0
  488. package/resources/responses/internal-base.js.map +1 -1
  489. package/resources/responses/internal-base.mjs +2 -0
  490. package/resources/responses/internal-base.mjs.map +1 -1
  491. package/resources/responses/responses.d.mts +9 -12
  492. package/resources/responses/responses.d.mts.map +1 -1
  493. package/resources/responses/responses.d.ts +9 -12
  494. package/resources/responses/responses.d.ts.map +1 -1
  495. package/resources/responses/responses.js.map +1 -1
  496. package/resources/responses/responses.mjs.map +1 -1
  497. package/resources/vector-stores/file-batches.d.mts.map +1 -1
  498. package/resources/vector-stores/file-batches.d.ts.map +1 -1
  499. package/resources/vector-stores/file-batches.js +4 -61
  500. package/resources/vector-stores/file-batches.js.map +1 -1
  501. package/resources/vector-stores/file-batches.mjs +4 -61
  502. package/resources/vector-stores/file-batches.mjs.map +1 -1
  503. package/resources/vector-stores/files.d.mts +1 -1
  504. package/resources/vector-stores/files.d.mts.map +1 -1
  505. package/resources/vector-stores/files.d.ts +1 -1
  506. package/resources/vector-stores/files.d.ts.map +1 -1
  507. package/resources/vector-stores/files.js +2 -35
  508. package/resources/vector-stores/files.js.map +1 -1
  509. package/resources/vector-stores/files.mjs +2 -35
  510. package/resources/vector-stores/files.mjs.map +1 -1
  511. package/resources/webhooks/webhooks.d.mts.map +1 -1
  512. package/resources/webhooks/webhooks.d.ts.map +1 -1
  513. package/resources/webhooks/webhooks.js +5 -43
  514. package/resources/webhooks/webhooks.js.map +1 -1
  515. package/resources/webhooks/webhooks.mjs +5 -43
  516. package/resources/webhooks/webhooks.mjs.map +1 -1
  517. package/src/auth/index.ts +6 -1
  518. package/src/auth/subject-token-providers.ts +196 -4
  519. package/src/auth/types.ts +24 -0
  520. package/src/auth/workload-identity-auth.ts +104 -7
  521. package/src/auth/x509-transport.ts +49 -0
  522. package/src/azure.ts +22 -1
  523. package/src/bedrock.ts +49 -5
  524. package/src/beta/realtime/internal-base.ts +15 -0
  525. package/src/beta/realtime/websocket.ts +148 -40
  526. package/src/beta/realtime/ws.ts +31 -13
  527. package/src/client.ts +536 -92
  528. package/src/core/EventEmitter.ts +91 -7
  529. package/src/core/streaming.ts +71 -20
  530. package/src/helpers/audio.ts +93 -22
  531. package/src/helpers/standard-schema.ts +50 -16
  532. package/src/helpers/zod-v3-strict-schema.ts +514 -0
  533. package/src/helpers/zod.ts +17 -4
  534. package/src/index.ts +1 -1
  535. package/src/internal/assistant-stream-delta.ts +33 -10
  536. package/src/internal/auth/x509-token-exchange.ts +310 -0
  537. package/src/internal/auth/x509-transport-capability.ts +219 -0
  538. package/src/internal/auth/x509-transport-registry.ts +118 -0
  539. package/src/internal/auth/x509-transport-state-browser.ts +36 -0
  540. package/src/internal/auth/x509-transport-state.cts +36 -0
  541. package/src/internal/auth/x509-workload-identity-auth.ts +958 -0
  542. package/src/internal/bedrock.ts +182 -5
  543. package/src/internal/data-residency.ts +44 -0
  544. package/src/internal/multipart-encoding.ts +53 -0
  545. package/src/internal/qs/utils.ts +375 -13
  546. package/src/internal/request-options.ts +52 -1
  547. package/src/internal/responses/canonical-output-text.ts +3 -3
  548. package/src/internal/responses/response-accumulator.ts +411 -24
  549. package/src/internal/uploads.ts +81 -26
  550. package/src/internal/utils/env.ts +9 -5
  551. package/src/internal/ws.ts +24 -3
  552. package/src/lib/AbstractChatCompletionRunner.ts +33 -6
  553. package/src/lib/AssistantStream.ts +262 -22
  554. package/src/lib/ChatCompletionStream.ts +1467 -103
  555. package/src/lib/EventEmitter.ts +91 -7
  556. package/src/lib/EventStream.ts +1483 -10
  557. package/src/lib/ResponsesParser.ts +4 -1
  558. package/src/lib/Util.ts +7 -5
  559. package/src/lib/assistant-run-polling.ts +27 -0
  560. package/src/lib/embeddings.ts +65 -0
  561. package/src/lib/file-processing.ts +36 -0
  562. package/src/lib/parser.ts +19 -4
  563. package/src/lib/polling.ts +144 -0
  564. package/src/lib/responses/ResponseStream.ts +22 -10
  565. package/src/lib/vector-store-polling.ts +46 -0
  566. package/src/lib/vector-store-upload.ts +53 -0
  567. package/src/lib/webhook-signature.ts +164 -0
  568. package/src/realtime/internal-base.ts +15 -0
  569. package/src/realtime/websocket.ts +130 -27
  570. package/src/realtime/ws.ts +15 -2
  571. package/src/resources/admin/organization/index.ts +2 -0
  572. package/src/resources/admin/organization/organization.ts +4 -0
  573. package/src/resources/admin/organization/projects/index.ts +1 -0
  574. package/src/resources/admin/organization/projects/projects.ts +37 -4
  575. package/src/resources/admin/organization/usage.ts +83 -0
  576. package/src/resources/beta/responses/internal-base.ts +2 -0
  577. package/src/resources/beta/responses/responses.ts +9 -12
  578. package/src/resources/beta/threads/runs/index.ts +1 -1
  579. package/src/resources/beta/threads/runs/runs.ts +2 -45
  580. package/src/resources/chat/completions/completions.ts +7 -0
  581. package/src/resources/embeddings.ts +2 -42
  582. package/src/resources/files.ts +2 -19
  583. package/src/resources/images.ts +12 -22
  584. package/src/resources/realtime/api.md +1 -0
  585. package/src/resources/realtime/calls.ts +46 -0
  586. package/src/resources/realtime/index.ts +7 -1
  587. package/src/resources/realtime/realtime.ts +5 -3
  588. package/src/resources/responses/internal-base.ts +2 -0
  589. package/src/resources/responses/responses.ts +9 -12
  590. package/src/resources/vector-stores/file-batches.ts +8 -77
  591. package/src/resources/vector-stores/files.ts +3 -43
  592. package/src/resources/webhooks/webhooks.ts +5 -61
  593. package/src/version.ts +1 -1
  594. package/version.d.mts +1 -1
  595. package/version.d.ts +1 -1
  596. package/version.js +1 -1
  597. package/version.mjs +1 -1
@@ -1,4 +1,4 @@
1
- import { partialParse } from '../_vendor/partial-json-parser/parser';
1
+ import { MalformedJSON, partialParse } from '../_vendor/partial-json-parser/parser';
2
2
  import {
3
3
  APIUserAbortError,
4
4
  ContentFilterFinishReasonError,
@@ -6,7 +6,7 @@ import {
6
6
  OpenAIError,
7
7
  } from '../error';
8
8
  import type OpenAI from '../index';
9
- import type { RequestOptions } from '../internal/request-options';
9
+ import { observeJSONRequestBody, type RequestOptions } from '../internal/request-options';
10
10
  import type { ReadableStream } from '../internal/shim-types';
11
11
  import { uuid4 } from '../internal/utils/uuid';
12
12
  import { hasOwn } from '../internal/utils/values';
@@ -36,6 +36,18 @@ import { Stream } from '../streaming';
36
36
  import { AbstractChatCompletionRunner } from './AbstractChatCompletionRunner';
37
37
  import type { AbstractChatCompletionRunnerEvents } from './AbstractChatCompletionRunner';
38
38
 
39
+ function parseStructuredStreamingJSON(content: string): unknown {
40
+ try {
41
+ return partialParse(content);
42
+ } catch (error) {
43
+ if (error instanceof MalformedJSON || error instanceof SyntaxError) {
44
+ return parseResponseFormatContent({ type: 'json_schema', $parseRaw: undefined }, content);
45
+ }
46
+
47
+ throw error;
48
+ }
49
+ }
50
+
39
51
  /** An incremental assistant-text event and its accumulated state. */
40
52
  export interface ContentDeltaEvent {
41
53
  /** The new text received in this chunk. */
@@ -256,11 +268,64 @@ type PartialToolCallSnapshot = {
256
268
 
257
269
  interface ChoiceEventState {
258
270
  content_done: boolean;
271
+ content_parse_state: PartialJSONParseState | undefined;
259
272
  refusal_done: boolean;
260
273
  logprobs_content_done: boolean;
261
274
  logprobs_refusal_done: boolean;
262
275
  current_tool_call_index: number | null;
263
276
  done_tool_calls: Set<number>;
277
+ tool_call_parse_states: Map<number, PartialJSONParseState>;
278
+ tool_call_identities: Map<number, ToolCallIdentity>;
279
+ }
280
+
281
+ interface CapturedToolCallDeltaFrame {
282
+ readonly index: number;
283
+ readonly arguments_delta: string;
284
+ }
285
+
286
+ interface CapturedChoiceToolCallFrames {
287
+ readonly index: number;
288
+ readonly tool_calls: readonly CapturedToolCallDeltaFrame[];
289
+ }
290
+
291
+ interface ValidatedChoiceSnapshot {
292
+ message: ChatCompletionSnapshot.Choice.Message;
293
+ content: string | null | undefined;
294
+ refusal: string | null | undefined;
295
+ toolCallCollection: ChatCompletionSnapshot.Choice.Message.ToolCall[] | undefined;
296
+ toolCalls: ReadonlyMap<number, ValidatedToolCallSnapshot>;
297
+ }
298
+
299
+ interface ValidatedToolCallSnapshot {
300
+ tool: ChatCompletionSnapshot.Choice.Message.ToolCall;
301
+ function: ChatCompletionSnapshot.Choice.Message.ToolCall.Function;
302
+ type: 'function';
303
+ name: string;
304
+ arguments: string;
305
+ }
306
+
307
+ interface ToolCallIdentity {
308
+ type: 'function';
309
+ name: string;
310
+ parseable: boolean;
311
+ }
312
+
313
+ interface PartialJSONParseState {
314
+ bytes: number;
315
+ depth: number;
316
+ fragments: number;
317
+ work: number;
318
+ escaped: boolean;
319
+ has_non_whitespace: boolean;
320
+ in_string: boolean;
321
+ last_parsed_bytes: number;
322
+ pending_high_surrogate: boolean;
323
+ }
324
+
325
+ interface PartialJSONParseBudget {
326
+ bytes: number;
327
+ fragments: number;
328
+ work: number;
264
329
  }
265
330
 
266
331
  // The Chat Completions schema limits n to 128. Replayed streams do not retain
@@ -268,6 +333,287 @@ interface ChoiceEventState {
268
333
  // conservative ceiling to prevent sparse tool-call arrays from growing unbounded.
269
334
  const MAX_STREAM_CHOICES = 128;
270
335
  const MAX_STREAM_TOOL_CALLS = 128;
336
+ const MAX_PARTIAL_JSON_BYTES = 16 * 1024 * 1024;
337
+ const MAX_PARTIAL_JSON_FRAGMENTS = 65_536;
338
+ const MAX_PARTIAL_JSON_DEPTH = 128;
339
+ const MAX_PARTIAL_JSON_PARSE_WORK = 64 * 1024 * 1024;
340
+ const EAGER_PARTIAL_JSON_BYTES = 1024;
341
+
342
+ function createPartialJSONParseState(): PartialJSONParseState {
343
+ return {
344
+ bytes: 0,
345
+ depth: 0,
346
+ fragments: 0,
347
+ work: 0,
348
+ escaped: false,
349
+ has_non_whitespace: false,
350
+ in_string: false,
351
+ last_parsed_bytes: 0,
352
+ pending_high_surrogate: false,
353
+ };
354
+ }
355
+
356
+ function recordPartialJSONFragment(
357
+ state: PartialJSONParseState,
358
+ budget: PartialJSONParseBudget,
359
+ fragment: string,
360
+ validationWorkBudget?: PartialJSONParseBudget,
361
+ ): boolean {
362
+ if (budget.fragments >= MAX_PARTIAL_JSON_FRAGMENTS) {
363
+ throw new OpenAIError('Chat completion stream exceeded its structured JSON fragment limit');
364
+ }
365
+
366
+ let bytes = 0;
367
+ let { depth, escaped, has_non_whitespace: hasNonWhitespace, in_string: inString } = state;
368
+ let completed = false;
369
+ let firstCharacter = true;
370
+
371
+ for (const character of fragment) {
372
+ const previousBytes = bytes;
373
+ const codePoint = character.codePointAt(0)!;
374
+ if (firstCharacter && state.pending_high_surrogate && codePoint >= 0xdc_00 && codePoint <= 0xdf_ff) {
375
+ bytes += 1;
376
+ } else if (codePoint <= 0x7f) {
377
+ bytes += 1;
378
+ } else if (codePoint <= 0x7_ff) {
379
+ bytes += 2;
380
+ } else if (codePoint <= 0xff_ff) {
381
+ bytes += 3;
382
+ } else {
383
+ bytes += 4;
384
+ }
385
+ firstCharacter = false;
386
+ if (budget.bytes + bytes > MAX_PARTIAL_JSON_BYTES) {
387
+ throw new OpenAIError('Chat completion stream exceeded its structured JSON byte limit');
388
+ }
389
+ if (validationWorkBudget && validationWorkBudget.work + bytes > MAX_PARTIAL_JSON_PARSE_WORK) {
390
+ validationWorkBudget.work += previousBytes;
391
+ throw new OpenAIError('Chat completion stream exceeded its structured JSON parse-work limit');
392
+ }
393
+
394
+ if (character !== ' ' && character !== '\n' && character !== '\r' && character !== '\t') {
395
+ hasNonWhitespace = true;
396
+ }
397
+
398
+ if (inString) {
399
+ if (escaped) {
400
+ escaped = false;
401
+ } else if (character === '\\') {
402
+ escaped = true;
403
+ } else if (character === '"') {
404
+ inString = false;
405
+ completed ||= depth === 0;
406
+ }
407
+ continue;
408
+ }
409
+
410
+ if (character === '"') {
411
+ inString = true;
412
+ } else if (character === '{' || character === '[') {
413
+ depth += 1;
414
+ if (depth > MAX_PARTIAL_JSON_DEPTH) {
415
+ throw new OpenAIError('Chat completion stream exceeded its structured JSON nesting depth limit');
416
+ }
417
+ } else if ((character === '}' || character === ']') && depth > 0) {
418
+ depth -= 1;
419
+ completed ||= depth === 0;
420
+ }
421
+ }
422
+
423
+ state.bytes += bytes;
424
+ state.fragments += 1;
425
+ state.depth = depth;
426
+ state.escaped = escaped;
427
+ state.has_non_whitespace = hasNonWhitespace;
428
+ state.in_string = inString;
429
+ if (fragment.length > 0) {
430
+ const finalCodeUnit = fragment.codePointAt(fragment.length - 1) ?? 0;
431
+ state.pending_high_surrogate = finalCodeUnit >= 0xd8_00 && finalCodeUnit <= 0xdb_ff;
432
+ }
433
+ budget.bytes += bytes;
434
+ budget.fragments += 1;
435
+ if (validationWorkBudget) {
436
+ validationWorkBudget.work += bytes;
437
+ }
438
+
439
+ if (!hasNonWhitespace || bytes === 0) {
440
+ return false;
441
+ }
442
+
443
+ const minimumGrowth = Math.max(EAGER_PARTIAL_JSON_BYTES, Math.floor(state.last_parsed_bytes / 2));
444
+ if (
445
+ state.bytes > EAGER_PARTIAL_JSON_BYTES &&
446
+ !completed &&
447
+ state.bytes - state.last_parsed_bytes < minimumGrowth
448
+ ) {
449
+ return false;
450
+ }
451
+
452
+ return true;
453
+ }
454
+
455
+ function reservePartialJSONParse(state: PartialJSONParseState, budget: PartialJSONParseBudget): boolean {
456
+ if (budget.work + state.bytes > MAX_PARTIAL_JSON_PARSE_WORK) {
457
+ return false;
458
+ }
459
+
460
+ budget.work += state.bytes;
461
+ state.work += state.bytes;
462
+ state.last_parsed_bytes = state.bytes;
463
+ return true;
464
+ }
465
+
466
+ function captureStructuredJSONSnapshot(
467
+ snapshot: object,
468
+ property: 'content' | 'arguments' | 'refusal',
469
+ ): string | null | undefined {
470
+ const descriptor = Object.getOwnPropertyDescriptor(snapshot, property);
471
+ if (!descriptor) {
472
+ let prototype = Object.getPrototypeOf(snapshot) as object | null;
473
+ for (let depth = 0; prototype !== null; depth += 1) {
474
+ if (depth >= MAX_PARTIAL_JSON_DEPTH || Object.getOwnPropertyDescriptor(prototype, property)) {
475
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
476
+ }
477
+ prototype = Object.getPrototypeOf(prototype) as object | null;
478
+ }
479
+ return undefined;
480
+ }
481
+ if (
482
+ !('value' in descriptor) ||
483
+ (typeof descriptor.value !== 'string' && descriptor.value !== null && descriptor.value !== undefined)
484
+ ) {
485
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
486
+ }
487
+
488
+ return descriptor.value as string | null | undefined;
489
+ }
490
+
491
+ function captureStructuredMessageSnapshot(
492
+ choice: ChatCompletionSnapshot.Choice,
493
+ ): ChatCompletionSnapshot.Choice.Message {
494
+ const descriptor = Object.getOwnPropertyDescriptor(choice, 'message');
495
+ if (
496
+ !descriptor ||
497
+ !('value' in descriptor) ||
498
+ typeof descriptor.value !== 'object' ||
499
+ descriptor.value === null
500
+ ) {
501
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
502
+ }
503
+
504
+ return descriptor.value as ChatCompletionSnapshot.Choice.Message;
505
+ }
506
+
507
+ function captureSnapshotArray<Item>(
508
+ snapshot: object,
509
+ property: 'choices' | 'tool_calls',
510
+ maximum: number,
511
+ kind: 'choice' | 'tool-call',
512
+ ): Item[] | undefined {
513
+ const descriptor = Object.getOwnPropertyDescriptor(snapshot, property);
514
+ if (!descriptor) {
515
+ let prototype = Object.getPrototypeOf(snapshot) as object | null;
516
+ for (let depth = 0; prototype !== null; depth += 1) {
517
+ if (depth >= MAX_PARTIAL_JSON_DEPTH || Object.getOwnPropertyDescriptor(prototype, property)) {
518
+ throw new OpenAIError(`Chat completion stream contains an unsafe snapshot ${kind} collection`);
519
+ }
520
+ prototype = Object.getPrototypeOf(prototype) as object | null;
521
+ }
522
+ return undefined;
523
+ }
524
+ if (!('value' in descriptor) || !Array.isArray(descriptor.value)) {
525
+ throw new OpenAIError(`Chat completion stream contains an unsafe snapshot ${kind} collection`);
526
+ }
527
+
528
+ const length = Object.getOwnPropertyDescriptor(descriptor.value, 'length');
529
+ if (!length || !('value' in length) || !Number.isSafeInteger(length.value) || length.value > maximum) {
530
+ throw new OpenAIError(`Chat completion stream exceeded its snapshot ${kind} limit`);
531
+ }
532
+
533
+ return descriptor.value as Item[];
534
+ }
535
+
536
+ function captureSnapshotArrayItem<Item>(array: Item[], index: number): Item | undefined {
537
+ const descriptor = Object.getOwnPropertyDescriptor(array, index);
538
+ if (!descriptor) {
539
+ return undefined;
540
+ }
541
+ if (!('value' in descriptor)) {
542
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
543
+ }
544
+
545
+ return descriptor.value as Item;
546
+ }
547
+
548
+ function mapCapturedSnapshotArray<Item, Mapped>(
549
+ array: Item[],
550
+ maximum: number,
551
+ kind: 'choice' | 'tool-call',
552
+ map: (item: Item, index: number) => Mapped,
553
+ ): Mapped[] {
554
+ const descriptor = Object.getOwnPropertyDescriptor(array, 'length');
555
+ const length: unknown = descriptor && 'value' in descriptor ? descriptor.value : undefined;
556
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0 || length > maximum) {
557
+ throw new OpenAIError(`Chat completion stream exceeded its snapshot ${kind} limit`);
558
+ }
559
+
560
+ const mapped: Mapped[] = [];
561
+ mapped.length = length;
562
+ for (let index = 0; index < length; index += 1) {
563
+ const item = Object.getOwnPropertyDescriptor(array, index);
564
+ if (!item) {
565
+ continue;
566
+ }
567
+ if (!('value' in item)) {
568
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
569
+ }
570
+ mapped[index] = map(item.value as Item, index);
571
+ }
572
+
573
+ return mapped;
574
+ }
575
+
576
+ function validateStructuredJSONSnapshot(
577
+ value: string,
578
+ budget?: PartialJSONParseBudget,
579
+ validationWorkBudget?: PartialJSONParseBudget,
580
+ ): string {
581
+ const state = createPartialJSONParseState();
582
+ const parseBudget = budget ?? { bytes: 0, fragments: 0, work: 0 };
583
+ recordPartialJSONFragment(state, parseBudget, value, validationWorkBudget);
584
+ if (!reservePartialJSONParse(state, parseBudget)) {
585
+ throw new OpenAIError('Chat completion stream exceeded its structured JSON parse-work limit');
586
+ }
587
+
588
+ return value;
589
+ }
590
+
591
+ function ownFunctionToolIdentity(
592
+ toolCall: PartialToolCallSnapshot,
593
+ ): Pick<ToolCallIdentity, 'type' | 'name'> | undefined {
594
+ const type = Object.getOwnPropertyDescriptor(toolCall, 'type');
595
+ const fn = Object.getOwnPropertyDescriptor(toolCall, 'function');
596
+ if (!type || !('value' in type) || type.value !== 'function' || !fn || !('value' in fn)) {
597
+ return undefined;
598
+ }
599
+ if (typeof fn.value !== 'object' || fn.value === null) {
600
+ return undefined;
601
+ }
602
+
603
+ const name = Object.getOwnPropertyDescriptor(fn.value, 'name');
604
+ if (!name || !('value' in name) || typeof name.value !== 'string' || name.value.length === 0) {
605
+ return undefined;
606
+ }
607
+
608
+ return { type: 'function', name: name.value };
609
+ }
610
+
611
+ function assertBoundToolCallIdentity(toolCall: PartialToolCallSnapshot, identity: ToolCallIdentity): void {
612
+ const current = ownFunctionToolIdentity(toolCall);
613
+ if (!current || current.name !== identity.name || current.type !== identity.type) {
614
+ throw new OpenAIError('Chat completion stream contains a changed tool call identity');
615
+ }
616
+ }
271
617
 
272
618
  function assignOwnProperties<T extends object>(target: T, source: object): T {
273
619
  if (Object.prototype.propertyIsEnumerable.call(source, '__proto__') && !hasOwn(target, '__proto__')) {
@@ -282,6 +628,625 @@ function assignOwnProperties<T extends object>(target: T, source: object): T {
282
628
  return Object.assign(target, source);
283
629
  }
284
630
 
631
+ function cloneParserConfigObject<Value extends object>(
632
+ value: Value,
633
+ stableFields: readonly PropertyKey[] = [],
634
+ ): Value {
635
+ const descriptors: PropertyDescriptorMap = Object.getOwnPropertyDescriptors(value);
636
+ for (const field of stableFields) {
637
+ const descriptor = descriptors[field];
638
+ if (!descriptor && !(field in value)) {
639
+ continue;
640
+ }
641
+
642
+ descriptors[field] = {
643
+ value: descriptor && 'value' in descriptor ? descriptor.value : Reflect.get(value, field, value),
644
+ enumerable: descriptor?.enumerable ?? false,
645
+ configurable: descriptor?.configurable ?? true,
646
+ writable: descriptor && 'writable' in descriptor ? descriptor.writable : false,
647
+ };
648
+ }
649
+
650
+ return Object.create(Object.getPrototypeOf(value), descriptors) as Value;
651
+ }
652
+
653
+ function snapshotChatCompletionParserParams(params: ChatCompletionCreateParams): ChatCompletionCreateParams {
654
+ const snapshot = cloneParserConfigObject(params);
655
+
656
+ if (params.tools) {
657
+ const stableTools: NonNullable<ChatCompletionCreateParams['tools']> = [];
658
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(params.tools, 'length');
659
+ const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
660
+ const toolCount =
661
+ typeof length === 'number' && Number.isSafeInteger(length) && length >= 0
662
+ ? Math.min(length, MAX_STREAM_TOOL_CALLS)
663
+ : 0;
664
+
665
+ for (let index = 0; index < toolCount; index += 1) {
666
+ const item = Object.getOwnPropertyDescriptor(params.tools, String(index));
667
+ if (!item || !('value' in item)) {
668
+ stableTools.length = index + 1;
669
+ continue;
670
+ }
671
+
672
+ const tool = item.value as NonNullable<ChatCompletionCreateParams['tools']>[number];
673
+ const stableTool = cloneParserConfigObject(tool, [
674
+ 'type',
675
+ '$brand',
676
+ '$parseRaw',
677
+ '$callback',
678
+ 'function',
679
+ ]);
680
+ const descriptors = Object.getOwnPropertyDescriptors(stableTool);
681
+
682
+ if (isChatCompletionFunctionTool(stableTool)) {
683
+ const descriptor = descriptors.function;
684
+ descriptors.function = {
685
+ ...(descriptor && 'value' in descriptor
686
+ ? descriptor
687
+ : { configurable: true, enumerable: true, writable: true }),
688
+ value: cloneParserConfigObject(stableTool.function, ['name', 'strict']),
689
+ };
690
+ }
691
+
692
+ stableTools[index] = Object.create(Object.getPrototypeOf(tool), descriptors) as typeof tool;
693
+ }
694
+ snapshot.tools = stableTools;
695
+ }
696
+
697
+ if (params.response_format) {
698
+ snapshot.response_format = cloneParserConfigObject(params.response_format, [
699
+ 'type',
700
+ '$brand',
701
+ '$parseRaw',
702
+ ]);
703
+ }
704
+
705
+ return snapshot;
706
+ }
707
+
708
+ type ChatCompletionInputTool = NonNullable<ChatCompletionCreateParams['tools']>[number];
709
+ type ChatCompletionResponseFormat = NonNullable<ChatCompletionCreateParams['response_format']>;
710
+
711
+ interface SerializedFunctionParserConfig {
712
+ source: ChatCompletionInputTool | undefined;
713
+ schemaMatches: boolean;
714
+ name?: string;
715
+ strict?: boolean;
716
+ }
717
+
718
+ interface SerializedToolParserConfig {
719
+ source: ChatCompletionInputTool | undefined;
720
+ type?: string;
721
+ function?: SerializedFunctionParserConfig;
722
+ }
723
+
724
+ interface SerializedResponseParserConfig {
725
+ source: ChatCompletionResponseFormat | undefined;
726
+ schemaMatches: boolean;
727
+ type?: string;
728
+ }
729
+
730
+ interface SerializedParserSchemaBudget {
731
+ nodes: number;
732
+ bytes: number;
733
+ }
734
+
735
+ const MAX_SERIALIZED_PARSER_SCHEMA_NODES = 4096;
736
+ const stringifyParserSchemaValue = JSON.stringify;
737
+ const MAX_SERIALIZED_PARSER_SCHEMA_BYTES = 1024 * 1024;
738
+ const MAX_SERIALIZED_PARSER_SCHEMA_DEPTH = 64;
739
+ const OMITTED_SERIALIZED_PARSER_VALUE = Symbol('omitted serialized parser value');
740
+ const UNSAFE_SERIALIZED_PARSER_VALUE = Symbol('unsafe serialized parser value');
741
+
742
+ type CanonicalSerializedParserValue =
743
+ | string
744
+ | typeof OMITTED_SERIALIZED_PARSER_VALUE
745
+ | typeof UNSAFE_SERIALIZED_PARSER_VALUE;
746
+
747
+ function canonicalSerializedParserSchema(
748
+ value: unknown,
749
+ budget: SerializedParserSchemaBudget,
750
+ ): string | undefined {
751
+ const ancestors = new WeakSet<object>();
752
+
753
+ const charge = (bytes: number): boolean => {
754
+ if (
755
+ !Number.isSafeInteger(bytes) ||
756
+ bytes < 0 ||
757
+ budget.bytes + bytes > MAX_SERIALIZED_PARSER_SCHEMA_BYTES
758
+ ) {
759
+ return false;
760
+ }
761
+ budget.bytes += bytes;
762
+ return true;
763
+ };
764
+
765
+ const visit = (current: unknown, depth: number): CanonicalSerializedParserValue => {
766
+ if (depth > MAX_SERIALIZED_PARSER_SCHEMA_DEPTH || budget.nodes >= MAX_SERIALIZED_PARSER_SCHEMA_NODES) {
767
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
768
+ }
769
+ budget.nodes += 1;
770
+
771
+ if (current === undefined || typeof current === 'function' || typeof current === 'symbol') {
772
+ return OMITTED_SERIALIZED_PARSER_VALUE;
773
+ }
774
+ if (typeof current === 'bigint') {
775
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
776
+ }
777
+ if (current === null || typeof current === 'boolean' || typeof current === 'number') {
778
+ const serialized = stringifyParserSchemaValue(current);
779
+ return typeof serialized === 'string' && charge(serialized.length)
780
+ ? serialized
781
+ : UNSAFE_SERIALIZED_PARSER_VALUE;
782
+ }
783
+ if (typeof current === 'string') {
784
+ if (!charge(current.length * 6 + 2)) {
785
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
786
+ }
787
+ return stringifyParserSchemaValue(current);
788
+ }
789
+ if (typeof current !== 'object' || ancestors.has(current)) {
790
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
791
+ }
792
+
793
+ const array = Array.isArray(current);
794
+ const prototype = Object.getPrototypeOf(current) as object | null;
795
+ if (
796
+ (array && prototype !== Array.prototype) ||
797
+ (!array && prototype !== null && prototype !== Object.prototype)
798
+ ) {
799
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
800
+ }
801
+
802
+ for (
803
+ let owner: object | null = current;
804
+ owner !== null;
805
+ owner = Object.getPrototypeOf(owner) as object | null
806
+ ) {
807
+ const serializer = Object.getOwnPropertyDescriptor(owner, 'toJSON');
808
+ if (!serializer) {
809
+ continue;
810
+ }
811
+ if (!('value' in serializer) || typeof serializer.value === 'function') {
812
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
813
+ }
814
+ break;
815
+ }
816
+
817
+ ancestors.add(current);
818
+ try {
819
+ if (!charge(2)) {
820
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
821
+ }
822
+
823
+ if (array) {
824
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(current, 'length');
825
+ const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
826
+ if (
827
+ typeof length !== 'number' ||
828
+ !Number.isSafeInteger(length) ||
829
+ length < 0 ||
830
+ length > MAX_SERIALIZED_PARSER_SCHEMA_NODES - budget.nodes
831
+ ) {
832
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
833
+ }
834
+
835
+ const items: string[] = [];
836
+ for (let index = 0; index < length; index += 1) {
837
+ const key = String(index);
838
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
839
+ if (!descriptor) {
840
+ if (
841
+ Object.getOwnPropertyDescriptor(Array.prototype, key) ||
842
+ Object.getOwnPropertyDescriptor(Object.prototype, key)
843
+ ) {
844
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
845
+ }
846
+ budget.nodes += 1;
847
+ if (!charge(4)) {
848
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
849
+ }
850
+ items.push('null');
851
+ continue;
852
+ }
853
+ if (!('value' in descriptor)) {
854
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
855
+ }
856
+
857
+ const item = visit(descriptor.value, depth + 1);
858
+ if (item === UNSAFE_SERIALIZED_PARSER_VALUE) {
859
+ return item;
860
+ }
861
+ items.push(item === OMITTED_SERIALIZED_PARSER_VALUE ? 'null' : item);
862
+ }
863
+ return `[${items.join(',')}]`;
864
+ }
865
+
866
+ const keys = Reflect.ownKeys(current);
867
+ if (keys.length > MAX_SERIALIZED_PARSER_SCHEMA_NODES - budget.nodes) {
868
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
869
+ }
870
+
871
+ const entries: [string, unknown][] = [];
872
+ for (const key of keys) {
873
+ if (typeof key !== 'string') {
874
+ continue;
875
+ }
876
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
877
+ if (!descriptor) {
878
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
879
+ }
880
+ if (!descriptor.enumerable) {
881
+ continue;
882
+ }
883
+ if (!('value' in descriptor)) {
884
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
885
+ }
886
+ entries.push([key, descriptor.value]);
887
+ }
888
+ entries.sort(([left], [right]) => {
889
+ if (left === right) {
890
+ return 0;
891
+ }
892
+ return left < right ? -1 : 1;
893
+ });
894
+
895
+ const fields: string[] = [];
896
+ for (const [key, entry] of entries) {
897
+ const normalized = visit(entry, depth + 1);
898
+ if (normalized === UNSAFE_SERIALIZED_PARSER_VALUE) {
899
+ return normalized;
900
+ }
901
+ if (normalized === OMITTED_SERIALIZED_PARSER_VALUE) {
902
+ continue;
903
+ }
904
+ if (!charge(key.length * 6 + 3)) {
905
+ return UNSAFE_SERIALIZED_PARSER_VALUE;
906
+ }
907
+ fields.push(`${stringifyParserSchemaValue(key)}:${normalized}`);
908
+ }
909
+ return `{${fields.join(',')}}`;
910
+ } finally {
911
+ ancestors.delete(current);
912
+ }
913
+ };
914
+
915
+ try {
916
+ const normalized = visit(value, 0);
917
+ return typeof normalized === 'string' ? normalized : undefined;
918
+ } catch {
919
+ return undefined;
920
+ }
921
+ }
922
+
923
+ function rememberSerializedParserSchema(
924
+ signatures: WeakMap<object, string>,
925
+ source: object,
926
+ holder: object,
927
+ key: string,
928
+ ): void {
929
+ const parser = Object.getOwnPropertyDescriptor(source, '$parseRaw');
930
+ const schema = Object.getOwnPropertyDescriptor(holder, key);
931
+ if (
932
+ !parser ||
933
+ !('value' in parser) ||
934
+ typeof parser.value !== 'function' ||
935
+ !schema ||
936
+ !('value' in schema)
937
+ ) {
938
+ return;
939
+ }
940
+
941
+ const normalized = canonicalSerializedParserSchema(schema.value, { nodes: 0, bytes: 0 });
942
+ if (normalized !== undefined) {
943
+ signatures.set(source, normalized);
944
+ }
945
+ }
946
+
947
+ function hasMatchingSerializedParserSchema(
948
+ signatures: WeakMap<object, string>,
949
+ source: object | undefined,
950
+ holder: object,
951
+ key: string,
952
+ value: unknown,
953
+ ): boolean {
954
+ const expected = source && signatures.get(source);
955
+ const descriptor = Object.getOwnPropertyDescriptor(holder, key);
956
+ return (
957
+ expected !== undefined &&
958
+ descriptor !== undefined &&
959
+ 'value' in descriptor &&
960
+ canonicalSerializedParserSchema(value, { nodes: 0, bytes: 0 }) === expected
961
+ );
962
+ }
963
+
964
+ function serializedParserDescriptor(
965
+ descriptor: PropertyDescriptor | undefined,
966
+ value: unknown,
967
+ ): PropertyDescriptor {
968
+ return descriptor && 'value' in descriptor
969
+ ? { ...descriptor, value }
970
+ : { configurable: true, enumerable: true, writable: true, value };
971
+ }
972
+
973
+ function shadowSerializedParserMetadata(
974
+ descriptors: PropertyDescriptorMap,
975
+ source: object,
976
+ fields: readonly string[],
977
+ ): void {
978
+ for (const field of fields) {
979
+ const descriptor = descriptors[field];
980
+ if (!descriptor && !(field in source)) {
981
+ continue;
982
+ }
983
+ descriptors[field] =
984
+ descriptor && 'value' in descriptor
985
+ ? { ...descriptor, value: undefined }
986
+ : {
987
+ configurable: descriptor?.configurable ?? true,
988
+ enumerable: descriptor?.enumerable ?? false,
989
+ writable: false,
990
+ value: undefined,
991
+ };
992
+ }
993
+ }
994
+
995
+ function snapshotSerializedParserTool(serialized: SerializedToolParserConfig): ChatCompletionInputTool {
996
+ const source =
997
+ serialized.source ??
998
+ ({
999
+ type: serialized.type,
1000
+ ...(serialized.type === 'function' ? { function: {} } : {}),
1001
+ } as ChatCompletionInputTool);
1002
+ const descriptors = Object.getOwnPropertyDescriptors(source);
1003
+ descriptors.type = serializedParserDescriptor(descriptors.type, serialized.type);
1004
+
1005
+ if (serialized.type !== 'function' || !serialized.function) {
1006
+ if (descriptors.function) {
1007
+ descriptors.function = serializedParserDescriptor(descriptors.function, undefined);
1008
+ }
1009
+ shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw', '$callback']);
1010
+ return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionInputTool;
1011
+ }
1012
+
1013
+ const descriptor = descriptors.function;
1014
+ const original =
1015
+ descriptor && 'value' in descriptor && typeof descriptor.value === 'object' && descriptor.value !== null
1016
+ ? (descriptor.value as object)
1017
+ : {};
1018
+ const functionDescriptors = Object.getOwnPropertyDescriptors(original);
1019
+ functionDescriptors['name'] = serializedParserDescriptor(
1020
+ functionDescriptors['name'],
1021
+ serialized.function.name,
1022
+ );
1023
+ functionDescriptors['strict'] = serializedParserDescriptor(
1024
+ functionDescriptors['strict'],
1025
+ serialized.function.strict,
1026
+ );
1027
+ descriptors.function = serializedParserDescriptor(
1028
+ descriptor,
1029
+ Object.create(Object.getPrototypeOf(original), functionDescriptors),
1030
+ );
1031
+ if (!serialized.function.schemaMatches) {
1032
+ shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw', '$callback']);
1033
+ }
1034
+
1035
+ return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionInputTool;
1036
+ }
1037
+
1038
+ function snapshotSerializedResponseFormat(
1039
+ serialized: SerializedResponseParserConfig,
1040
+ ): ChatCompletionResponseFormat {
1041
+ const source = serialized.source ?? ({ type: serialized.type } as ChatCompletionResponseFormat);
1042
+ const descriptors = Object.getOwnPropertyDescriptors(source);
1043
+ descriptors.type = serializedParserDescriptor(descriptors.type, serialized.type);
1044
+ if (serialized.type !== 'json_schema' || !serialized.source || !serialized.schemaMatches) {
1045
+ shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw']);
1046
+ }
1047
+ return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionResponseFormat;
1048
+ }
1049
+
1050
+ function ownSerializedParserObject(holder: object, key: string): object | undefined {
1051
+ const descriptor = Object.getOwnPropertyDescriptor(holder, key);
1052
+ if (!descriptor || !('value' in descriptor)) {
1053
+ return undefined;
1054
+ }
1055
+ const { value } = descriptor;
1056
+ return typeof value === 'object' && value !== null ? value : undefined;
1057
+ }
1058
+
1059
+ function observeSerializedChatCompletionParserParams(
1060
+ body: ChatCompletionCreateParams,
1061
+ initial: ChatCompletionCreateParams,
1062
+ update: (params: ChatCompletionCreateParams) => void,
1063
+ ): () => void {
1064
+ const originalToolOwners = new WeakMap<object, ChatCompletionInputTool>();
1065
+ const originalSchemaSignatures = new WeakMap<object, string>();
1066
+ // At most 128 tools plus one response format each receive an independent
1067
+ // 4,096-node/1 MiB source and wire allowance (129 MiB maximum per pass).
1068
+ if (body.tools) {
1069
+ for (let index = 0; index < body.tools.length && index < MAX_STREAM_TOOL_CALLS; index += 1) {
1070
+ const owner = ownSerializedParserObject(body.tools, String(index));
1071
+ const source = initial.tools?.[index];
1072
+ if (owner && source) {
1073
+ originalToolOwners.set(owner, source);
1074
+ const originalFunction = ownSerializedParserObject(source, 'function');
1075
+ if (originalFunction) {
1076
+ rememberSerializedParserSchema(originalSchemaSignatures, source, originalFunction, 'parameters');
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ if (initial.response_format) {
1082
+ rememberSerializedParserSchema(
1083
+ originalSchemaSignatures,
1084
+ initial.response_format,
1085
+ initial.response_format,
1086
+ 'json_schema',
1087
+ );
1088
+ }
1089
+ let root: object | undefined;
1090
+ let tools: object | undefined;
1091
+ let responseFormat: object | undefined;
1092
+ let responseFrame: SerializedResponseParserConfig | undefined;
1093
+ let frames: (SerializedToolParserConfig | undefined)[] = [];
1094
+ let toolFrames = new WeakMap<object, SerializedToolParserConfig>();
1095
+ let actualToolOwners = new Map<number, object | undefined>();
1096
+ let functionFrames = new WeakMap<object, SerializedFunctionParserConfig>();
1097
+
1098
+ return observeJSONRequestBody(body, {
1099
+ value(holder, key, value) {
1100
+ if (!root && key === '' && typeof value === 'object' && value !== null) {
1101
+ root = value;
1102
+ tools = undefined;
1103
+ responseFormat = undefined;
1104
+ responseFrame = undefined;
1105
+ frames = [];
1106
+ toolFrames = new WeakMap();
1107
+ actualToolOwners = new Map();
1108
+ functionFrames = new WeakMap();
1109
+ return;
1110
+ }
1111
+
1112
+ if (holder === root && key === 'response_format') {
1113
+ if (typeof value === 'object' && value !== null) {
1114
+ responseFormat = value;
1115
+ const owner = ownSerializedParserObject(holder, key);
1116
+ responseFrame = {
1117
+ source: owner === body.response_format ? initial.response_format : undefined,
1118
+ schemaMatches: false,
1119
+ };
1120
+ }
1121
+ return;
1122
+ }
1123
+
1124
+ if (holder === root && key === 'tools') {
1125
+ if (Array.isArray(value)) {
1126
+ tools = new Proxy(value, {
1127
+ get(target, property) {
1128
+ const actual = Reflect.get(target, property, target) as unknown;
1129
+ if (typeof property === 'string') {
1130
+ const index = Number(property);
1131
+ if (
1132
+ Number.isSafeInteger(index) &&
1133
+ index >= 0 &&
1134
+ index < MAX_STREAM_TOOL_CALLS &&
1135
+ String(index) === property
1136
+ ) {
1137
+ actualToolOwners.set(
1138
+ index,
1139
+ typeof actual === 'object' && actual !== null ? actual : undefined,
1140
+ );
1141
+ }
1142
+ }
1143
+ return actual;
1144
+ },
1145
+ });
1146
+ return tools;
1147
+ }
1148
+ return;
1149
+ }
1150
+
1151
+ if (holder === tools) {
1152
+ const index = Number(key);
1153
+ if (
1154
+ !Number.isSafeInteger(index) ||
1155
+ index < 0 ||
1156
+ index >= MAX_STREAM_TOOL_CALLS ||
1157
+ typeof value !== 'object' ||
1158
+ value === null
1159
+ ) {
1160
+ return;
1161
+ }
1162
+ const owner = actualToolOwners.get(index);
1163
+ const source = owner ? originalToolOwners.get(owner) : undefined;
1164
+ const frame: SerializedToolParserConfig = { source };
1165
+ frames[index] = frame;
1166
+ toolFrames.set(value, frame);
1167
+ return;
1168
+ }
1169
+
1170
+ const tool = toolFrames.get(holder);
1171
+ if (tool) {
1172
+ if (key === 'type' && typeof value === 'string') {
1173
+ tool.type = value;
1174
+ } else if (key === 'function' && typeof value === 'object' && value !== null) {
1175
+ const fn: SerializedFunctionParserConfig = { source: tool.source, schemaMatches: false };
1176
+ tool.function = fn;
1177
+ functionFrames.set(value, fn);
1178
+ }
1179
+ return;
1180
+ }
1181
+
1182
+ if (holder === responseFormat && responseFrame) {
1183
+ if (key === 'type' && typeof value === 'string') {
1184
+ responseFrame.type = value;
1185
+ } else if (key === 'json_schema') {
1186
+ responseFrame.schemaMatches = hasMatchingSerializedParserSchema(
1187
+ originalSchemaSignatures,
1188
+ responseFrame.source,
1189
+ holder,
1190
+ key,
1191
+ value,
1192
+ );
1193
+ }
1194
+ return;
1195
+ }
1196
+
1197
+ const fn = functionFrames.get(holder);
1198
+ if (fn) {
1199
+ if (key === 'name' && typeof value === 'string') {
1200
+ fn.name = value;
1201
+ } else if (key === 'strict' && typeof value === 'boolean') {
1202
+ fn.strict = value;
1203
+ } else if (key === 'parameters') {
1204
+ fn.schemaMatches = hasMatchingSerializedParserSchema(
1205
+ originalSchemaSignatures,
1206
+ fn.source,
1207
+ holder,
1208
+ key,
1209
+ value,
1210
+ );
1211
+ }
1212
+ }
1213
+ return undefined;
1214
+ },
1215
+ complete() {
1216
+ if (!root) {
1217
+ return;
1218
+ }
1219
+ const snapshot = cloneParserConfigObject(initial);
1220
+ if (tools) {
1221
+ const serializedTools: ChatCompletionInputTool[] = [];
1222
+ for (let index = 0; index < frames.length; index += 1) {
1223
+ const frame = frames[index];
1224
+ if (frame) {
1225
+ serializedTools[index] = snapshotSerializedParserTool(frame);
1226
+ }
1227
+ }
1228
+ snapshot.tools = serializedTools;
1229
+ } else {
1230
+ delete snapshot.tools;
1231
+ }
1232
+ if (responseFrame) {
1233
+ snapshot.response_format = snapshotSerializedResponseFormat(responseFrame);
1234
+ } else {
1235
+ delete snapshot.response_format;
1236
+ }
1237
+ update(snapshot);
1238
+ root = undefined;
1239
+ tools = undefined;
1240
+ responseFormat = undefined;
1241
+ responseFrame = undefined;
1242
+ frames = [];
1243
+ toolFrames = new WeakMap();
1244
+ actualToolOwners = new Map();
1245
+ functionFrames = new WeakMap();
1246
+ },
1247
+ });
1248
+ }
1249
+
285
1250
  /** Streams chat completion chunks while accumulating snapshots, parsed output, and events. */
286
1251
  export class ChatCompletionStream<ParsedT = null>
287
1252
  extends AbstractChatCompletionRunner<ChatCompletionStreamEvents<ParsedT>, ParsedT>
@@ -291,6 +1256,8 @@ export class ChatCompletionStream<ParsedT = null>
291
1256
  #audioDoneChoiceIndexes: Set<number>;
292
1257
  #choiceEventStates: ChoiceEventState[];
293
1258
  #currentChatCompletionSnapshot: ChatCompletionSnapshot | undefined;
1259
+ #hasAutoParseableTool: boolean;
1260
+ #partialJSONParseBudget: PartialJSONParseBudget;
294
1261
 
295
1262
  /** Creates an unstarted stream, retaining request parameters for structured-output parsing. */
296
1263
  constructor(params: ChatCompletionCreateParams | null) {
@@ -298,6 +1265,27 @@ export class ChatCompletionStream<ParsedT = null>
298
1265
  this.#params = params;
299
1266
  this.#audioDoneChoiceIndexes = new Set();
300
1267
  this.#choiceEventStates = [];
1268
+ this.#hasAutoParseableTool = false;
1269
+ const tools = params?.tools;
1270
+ const lengthDescriptor = tools && Object.getOwnPropertyDescriptor(tools, 'length');
1271
+ const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
1272
+ if (tools && typeof length === 'number' && Number.isSafeInteger(length) && length >= 0) {
1273
+ for (let index = 0; index < Math.min(length, MAX_STREAM_TOOL_CALLS); index += 1) {
1274
+ const descriptor = Object.getOwnPropertyDescriptor(tools, String(index));
1275
+ if (!descriptor || !('value' in descriptor)) {
1276
+ continue;
1277
+ }
1278
+ const tool = descriptor.value as ChatCompletionInputTool;
1279
+ if (
1280
+ isChatCompletionFunctionTool(tool) &&
1281
+ (isAutoParsableTool(tool) || tool.function.strict === true)
1282
+ ) {
1283
+ this.#hasAutoParseableTool = true;
1284
+ break;
1285
+ }
1286
+ }
1287
+ }
1288
+ this.#partialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
301
1289
  }
302
1290
 
303
1291
  /** The latest accumulated completion, or `undefined` before a chunk arrives or after finalization. */
@@ -341,6 +1329,7 @@ export class ChatCompletionStream<ParsedT = null>
341
1329
  }
342
1330
  this.#audioDoneChoiceIndexes = new Set();
343
1331
  this.#currentChatCompletionSnapshot = undefined;
1332
+ this.#partialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
344
1333
  }
345
1334
 
346
1335
  #getChoiceEventState(choice: ChatCompletionSnapshot.Choice): ChoiceEventState {
@@ -351,11 +1340,14 @@ export class ChatCompletionStream<ParsedT = null>
351
1340
 
352
1341
  state = {
353
1342
  content_done: false,
1343
+ content_parse_state: undefined,
354
1344
  refusal_done: false,
355
1345
  logprobs_content_done: false,
356
1346
  logprobs_refusal_done: false,
357
1347
  done_tool_calls: new Set(),
358
1348
  current_tool_call_index: null,
1349
+ tool_call_parse_states: new Map(),
1350
+ tool_call_identities: new Map(),
359
1351
  };
360
1352
  this.#choiceEventStates[choice.index] = state;
361
1353
  return state;
@@ -366,45 +1358,52 @@ export class ChatCompletionStream<ParsedT = null>
366
1358
  return;
367
1359
  }
368
1360
 
369
- const completion = this.#accumulateChatCompletion(chunk);
1361
+ const capturedChoiceFrames = new WeakMap<object, CapturedChoiceToolCallFrames>();
1362
+ const completion = this.#accumulateChatCompletion(chunk, capturedChoiceFrames);
370
1363
  this._emit('chunk', chunk, completion);
371
1364
 
372
1365
  for (const choice of chunk.choices) {
373
- const choiceSnapshot = completion.choices[choice.index]!;
1366
+ const capturedChoice = capturedChoiceFrames.get(choice);
1367
+ const choiceSnapshot = completion.choices[capturedChoice?.index ?? choice.index]!;
1368
+ const capturedToolCalls = capturedChoice?.tool_calls ?? [];
374
1369
  const { delta } = choice;
375
-
376
- if (
377
- delta?.content != null &&
378
- choiceSnapshot.message?.role === 'assistant' &&
379
- choiceSnapshot.message?.content
380
- ) {
381
- this._emit('content', delta.content, choiceSnapshot.message.content);
1370
+ const structuredResponse = isParseableResponseFormat(this.#params?.response_format);
1371
+ const boundedSnapshot = structuredResponse || this.#hasAutoParseableTool;
1372
+ const messageSnapshot = boundedSnapshot
1373
+ ? captureStructuredMessageSnapshot(choiceSnapshot)
1374
+ : choiceSnapshot.message;
1375
+ const refusal = boundedSnapshot
1376
+ ? captureStructuredJSONSnapshot(messageSnapshot, 'refusal')
1377
+ : messageSnapshot.refusal;
1378
+ const parseableContent = !refusal && structuredResponse;
1379
+ const messageContent = parseableContent
1380
+ ? captureStructuredJSONSnapshot(messageSnapshot, 'content')
1381
+ : messageSnapshot.content;
1382
+
1383
+ if (delta?.content != null && messageSnapshot.role === 'assistant' && messageContent) {
1384
+ this._emit('content', delta.content, messageContent);
382
1385
  this._emit('content.delta', {
383
1386
  delta: delta.content,
384
- snapshot: choiceSnapshot.message.content,
385
- parsed: choiceSnapshot.message.parsed,
1387
+ snapshot: messageContent,
1388
+ parsed: messageSnapshot.parsed,
386
1389
  });
387
1390
  }
388
1391
 
389
- if (
390
- delta?.refusal != null &&
391
- choiceSnapshot.message?.role === 'assistant' &&
392
- choiceSnapshot.message?.refusal
393
- ) {
1392
+ if (delta?.refusal != null && messageSnapshot.role === 'assistant' && refusal) {
394
1393
  this._emit('refusal.delta', {
395
1394
  delta: delta.refusal,
396
- snapshot: choiceSnapshot.message.refusal,
1395
+ snapshot: refusal,
397
1396
  });
398
1397
  }
399
1398
 
400
- if (choice.logprobs?.content != null && choiceSnapshot.message?.role === 'assistant') {
1399
+ if (choice.logprobs?.content != null && messageSnapshot.role === 'assistant') {
401
1400
  this._emit('logprobs.content.delta', {
402
1401
  content: choice.logprobs?.content,
403
1402
  snapshot: choiceSnapshot.logprobs?.content ?? [],
404
1403
  });
405
1404
  }
406
1405
 
407
- if (choice.logprobs?.refusal != null && choiceSnapshot.message?.role === 'assistant') {
1406
+ if (choice.logprobs?.refusal != null && messageSnapshot.role === 'assistant') {
408
1407
  this._emit('logprobs.refusal.delta', {
409
1408
  refusal: choice.logprobs?.refusal,
410
1409
  snapshot: choiceSnapshot.logprobs?.refusal ?? [],
@@ -421,7 +1420,7 @@ export class ChatCompletionStream<ParsedT = null>
421
1420
  }
422
1421
  }
423
1422
 
424
- for (const toolCall of delta?.tool_calls ?? []) {
1423
+ for (const toolCall of capturedToolCalls) {
425
1424
  if (state.current_tool_call_index !== toolCall.index) {
426
1425
  this.#emitContentDoneEvents(choiceSnapshot);
427
1426
 
@@ -434,19 +1433,30 @@ export class ChatCompletionStream<ParsedT = null>
434
1433
  state.current_tool_call_index = toolCall.index;
435
1434
  }
436
1435
 
437
- for (const toolCallDelta of delta?.tool_calls ?? []) {
438
- const toolCallSnapshot = choiceSnapshot.message.tool_calls?.[toolCallDelta.index];
1436
+ for (const toolCallDelta of capturedToolCalls) {
1437
+ const toolCallSnapshot = messageSnapshot.tool_calls?.[toolCallDelta.index];
439
1438
  if (!toolCallSnapshot?.type) {
440
1439
  continue;
441
1440
  }
442
1441
 
443
1442
  if (toolCallSnapshot.type === 'function') {
1443
+ const boundIdentity = state.tool_call_identities.get(toolCallDelta.index);
1444
+ let argumentsSnapshot: string;
1445
+ if (boundIdentity?.parseable) {
1446
+ const capturedArguments = captureStructuredJSONSnapshot(toolCallSnapshot.function, 'arguments');
1447
+ if (typeof capturedArguments !== 'string') {
1448
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1449
+ }
1450
+ argumentsSnapshot = capturedArguments;
1451
+ } else {
1452
+ argumentsSnapshot = toolCallSnapshot.function.arguments;
1453
+ }
444
1454
  this._emit('tool_calls.function.arguments.delta', {
445
1455
  name: toolCallSnapshot.function.name,
446
1456
  index: toolCallDelta.index,
447
- arguments: toolCallSnapshot.function.arguments,
1457
+ arguments: argumentsSnapshot,
448
1458
  parsed_arguments: toolCallSnapshot.function.parsed_arguments,
449
- arguments_delta: toolCallDelta.function?.arguments ?? '',
1459
+ arguments_delta: toolCallDelta.arguments_delta,
450
1460
  });
451
1461
  } else if (toolCallSnapshot.type !== 'custom') {
452
1462
  assertNever(toolCallSnapshot);
@@ -462,10 +1472,18 @@ export class ChatCompletionStream<ParsedT = null>
462
1472
  return;
463
1473
  }
464
1474
 
465
- const toolCallSnapshot = choiceSnapshot.message.tool_calls?.[toolCallIndex];
1475
+ const messageSnapshot = this.#hasAutoParseableTool
1476
+ ? captureStructuredMessageSnapshot(choiceSnapshot)
1477
+ : choiceSnapshot.message;
1478
+ const toolCallSnapshot = messageSnapshot.tool_calls?.[toolCallIndex];
466
1479
  if (!toolCallSnapshot) {
467
1480
  throw new Error('no tool call snapshot');
468
1481
  }
1482
+ const boundIdentity = state.tool_call_identities.get(toolCallIndex);
1483
+ if (boundIdentity) {
1484
+ assertBoundToolCallIdentity(toolCallSnapshot, boundIdentity);
1485
+ }
1486
+
469
1487
  if (!toolCallSnapshot.type) {
470
1488
  throw new Error('tool call snapshot missing `type`');
471
1489
  }
@@ -476,16 +1494,33 @@ export class ChatCompletionStream<ParsedT = null>
476
1494
  ) as ChatCompletionFunctionTool | undefined; // TS doesn't narrow based on isChatCompletionTool
477
1495
 
478
1496
  let parsedArguments: unknown = null;
1497
+ const parseable = isAutoParsableTool(inputTool) || inputTool?.function.strict === true;
1498
+ let argumentsSnapshot: string;
1499
+ if (parseable) {
1500
+ if (this.#currentChatCompletionSnapshot) {
1501
+ this.#validateStructuredSnapshots(this.#currentChatCompletionSnapshot);
1502
+ }
1503
+ const capturedArguments = captureStructuredJSONSnapshot(toolCallSnapshot.function, 'arguments');
1504
+ if (typeof capturedArguments !== 'string') {
1505
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1506
+ }
1507
+ argumentsSnapshot = capturedArguments;
1508
+ } else {
1509
+ argumentsSnapshot = toolCallSnapshot.function.arguments;
1510
+ }
479
1511
  if (isAutoParsableTool(inputTool)) {
480
- parsedArguments = inputTool.$parseRaw(toolCallSnapshot.function.arguments);
1512
+ parsedArguments = inputTool.$parseRaw(validateStructuredJSONSnapshot(argumentsSnapshot));
481
1513
  } else if (inputTool?.function.strict) {
482
- parsedArguments = JSON.parse(toolCallSnapshot.function.arguments);
1514
+ parsedArguments = parseResponseFormatContent(
1515
+ { type: 'json_schema', $parseRaw: undefined },
1516
+ validateStructuredJSONSnapshot(argumentsSnapshot),
1517
+ );
483
1518
  }
484
1519
 
485
1520
  this._emit('tool_calls.function.arguments.done', {
486
1521
  name: toolCallSnapshot.function.name,
487
1522
  index: toolCallIndex,
488
- arguments: toolCallSnapshot.function.arguments,
1523
+ arguments: argumentsSnapshot,
489
1524
  parsed_arguments: parsedArguments,
490
1525
  });
491
1526
  } else if (toolCallSnapshot.type !== 'custom') {
@@ -495,32 +1530,45 @@ export class ChatCompletionStream<ParsedT = null>
495
1530
 
496
1531
  #emitContentDoneEvents(choiceSnapshot: ChatCompletionSnapshot.Choice) {
497
1532
  const state = this.#getChoiceEventState(choiceSnapshot);
1533
+ const structuredResponse = isParseableResponseFormat(this.#params?.response_format);
1534
+ const boundedSnapshot = structuredResponse || this.#hasAutoParseableTool;
1535
+ const messageSnapshot = boundedSnapshot
1536
+ ? captureStructuredMessageSnapshot(choiceSnapshot)
1537
+ : choiceSnapshot.message;
1538
+ const refusal = boundedSnapshot
1539
+ ? captureStructuredJSONSnapshot(messageSnapshot, 'refusal')
1540
+ : messageSnapshot.refusal;
1541
+ const parseableContent = !refusal && structuredResponse;
1542
+ const content = parseableContent
1543
+ ? captureStructuredJSONSnapshot(messageSnapshot, 'content')
1544
+ : messageSnapshot.content;
498
1545
 
499
1546
  if (
500
- choiceSnapshot.message.content != null &&
501
- (choiceSnapshot.message.content !== '' ||
502
- (!choiceSnapshot.message.refusal &&
503
- !choiceSnapshot.message.tool_calls?.length &&
504
- !choiceSnapshot.message.function_call)) &&
1547
+ content != null &&
1548
+ (content !== '' ||
1549
+ (!refusal && !messageSnapshot.tool_calls?.length && !messageSnapshot.function_call)) &&
505
1550
  !state.content_done
506
1551
  ) {
1552
+ if (parseableContent && this.#currentChatCompletionSnapshot) {
1553
+ this.#validateStructuredSnapshots(this.#currentChatCompletionSnapshot);
1554
+ }
507
1555
  state.content_done = true;
508
1556
 
509
1557
  this._emit('content.done', {
510
- content: choiceSnapshot.message.content,
511
- parsed: choiceSnapshot.message.refusal
1558
+ content,
1559
+ parsed: refusal
512
1560
  ? null
513
1561
  : parseResponseFormatContent<ParsedT>(
514
1562
  this.#params?.response_format,
515
- choiceSnapshot.message.content,
1563
+ parseableContent ? validateStructuredJSONSnapshot(content) : content,
516
1564
  ),
517
1565
  });
518
1566
  }
519
1567
 
520
- if (choiceSnapshot.message.refusal && !state.refusal_done) {
1568
+ if (refusal && !state.refusal_done) {
521
1569
  state.refusal_done = true;
522
1570
 
523
- this._emit('refusal.done', { refusal: choiceSnapshot.message.refusal });
1571
+ this._emit('refusal.done', { refusal });
524
1572
  }
525
1573
 
526
1574
  if (choiceSnapshot.logprobs?.content && !state.logprobs_content_done) {
@@ -536,6 +1584,119 @@ export class ChatCompletionStream<ParsedT = null>
536
1584
  }
537
1585
  }
538
1586
 
1587
+ #validateStructuredSnapshots(
1588
+ snapshot: ChatCompletionSnapshot,
1589
+ ): WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot> {
1590
+ const finalJSONBudget: PartialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
1591
+ const parseableContent = isParseableResponseFormat(this.#params?.response_format);
1592
+ const validatedMessages = new WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot>();
1593
+ const choices = captureSnapshotArray<ChatCompletionSnapshot.Choice>(
1594
+ snapshot,
1595
+ 'choices',
1596
+ MAX_STREAM_CHOICES,
1597
+ 'choice',
1598
+ );
1599
+ if (!choices) {
1600
+ throw new OpenAIError('Chat completion stream contains an unsafe snapshot choice collection');
1601
+ }
1602
+ for (let choiceIndex = 0; choiceIndex < choices.length; choiceIndex += 1) {
1603
+ const choice = captureSnapshotArrayItem(choices, choiceIndex);
1604
+ if (!choice) {
1605
+ continue;
1606
+ }
1607
+ const message = captureStructuredMessageSnapshot(choice);
1608
+ const refusal = captureStructuredJSONSnapshot(message, 'refusal');
1609
+ const content = captureStructuredJSONSnapshot(message, 'content');
1610
+ const validatedTools = new Map<number, ValidatedToolCallSnapshot>();
1611
+ const toolCalls = captureSnapshotArray<ChatCompletionSnapshot.Choice.Message.ToolCall>(
1612
+ message,
1613
+ 'tool_calls',
1614
+ MAX_STREAM_TOOL_CALLS,
1615
+ 'tool-call',
1616
+ );
1617
+ validatedMessages.set(
1618
+ choice,
1619
+ Object.freeze({
1620
+ message,
1621
+ content,
1622
+ refusal,
1623
+ toolCallCollection: toolCalls,
1624
+ toolCalls: validatedTools,
1625
+ }),
1626
+ );
1627
+ const state = this.#choiceEventStates[choice.index];
1628
+ if (parseableContent && !refusal && typeof content === 'string') {
1629
+ validateStructuredJSONSnapshot(content, finalJSONBudget, this.#partialJSONParseBudget);
1630
+ }
1631
+ for (const [index, identity] of state?.tool_call_identities ?? []) {
1632
+ const toolCall = toolCalls && captureSnapshotArrayItem(toolCalls, index);
1633
+ if (!toolCall) {
1634
+ throw new OpenAIError('Chat completion stream contains a changed tool call identity');
1635
+ }
1636
+ assertBoundToolCallIdentity(toolCall, identity);
1637
+ }
1638
+ if (!this.#hasAutoParseableTool) {
1639
+ continue;
1640
+ }
1641
+ for (let toolCallIndex = 0; toolCallIndex < (toolCalls?.length ?? 0); toolCallIndex += 1) {
1642
+ const toolCall = captureSnapshotArrayItem(toolCalls!, toolCallIndex);
1643
+ if (!toolCall) {
1644
+ continue;
1645
+ }
1646
+ const identity = ownFunctionToolIdentity(toolCall);
1647
+ if (!identity) {
1648
+ const type = Object.getOwnPropertyDescriptor(toolCall, 'type');
1649
+ if (type && !('value' in type)) {
1650
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1651
+ }
1652
+ if (type?.value !== 'function') {
1653
+ continue;
1654
+ }
1655
+ const fn = Object.getOwnPropertyDescriptor(toolCall, 'function');
1656
+ if (fn && !('value' in fn)) {
1657
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1658
+ }
1659
+ if (fn && typeof fn.value === 'object' && fn.value !== null) {
1660
+ const name = Object.getOwnPropertyDescriptor(fn.value, 'name');
1661
+ if (name && !('value' in name)) {
1662
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1663
+ }
1664
+ }
1665
+ continue;
1666
+ }
1667
+ if (
1668
+ !shouldParseToolCall(this.#params, {
1669
+ type: identity.type,
1670
+ function: { name: identity.name },
1671
+ })
1672
+ ) {
1673
+ continue;
1674
+ }
1675
+ const descriptor = Object.getOwnPropertyDescriptor(toolCall, 'function');
1676
+ if (!descriptor || !('value' in descriptor)) {
1677
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1678
+ }
1679
+ const fn = descriptor.value as ChatCompletionSnapshot.Choice.Message.ToolCall.Function;
1680
+ const argumentsSnapshot = captureStructuredJSONSnapshot(fn, 'arguments');
1681
+ if (typeof argumentsSnapshot !== 'string') {
1682
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
1683
+ }
1684
+ validateStructuredJSONSnapshot(argumentsSnapshot, finalJSONBudget, this.#partialJSONParseBudget);
1685
+ validatedTools.set(
1686
+ toolCallIndex,
1687
+ Object.freeze({
1688
+ tool: toolCall,
1689
+ function: fn,
1690
+ type: identity.type,
1691
+ name: identity.name,
1692
+ arguments: argumentsSnapshot,
1693
+ }),
1694
+ );
1695
+ }
1696
+ }
1697
+ return validatedMessages;
1698
+ }
1699
+
539
1700
  #endRequest(): ParsedChatCompletion<ParsedT> {
540
1701
  if (this.ended) {
541
1702
  throw new OpenAIError(`stream has ended, this shouldn't happen`);
@@ -544,11 +1705,12 @@ export class ChatCompletionStream<ParsedT = null>
544
1705
  if (!snapshot) {
545
1706
  throw new OpenAIError(`request ended without sending any chunks`);
546
1707
  }
1708
+ const validatedMessages = this.#validateStructuredSnapshots(snapshot);
547
1709
  const audioDoneChoiceIndexes = this.#audioDoneChoiceIndexes;
548
1710
  this.#audioDoneChoiceIndexes = new Set();
549
1711
  this.#currentChatCompletionSnapshot = undefined;
550
1712
  this.#choiceEventStates = [];
551
- return finalizeChatCompletion(snapshot, this.#params, audioDoneChoiceIndexes);
1713
+ return finalizeChatCompletion(snapshot, this.#params, audioDoneChoiceIndexes, validatedMessages);
552
1714
  }
553
1715
 
554
1716
  protected override async _createChatCompletion(
@@ -557,12 +1719,35 @@ export class ChatCompletionStream<ParsedT = null>
557
1719
  options?: RequestOptions,
558
1720
  ): Promise<ParsedChatCompletion<ParsedT>> {
559
1721
  this._listenForAbort(options?.signal);
1722
+ const requestParams = { ...params, stream: true as const };
1723
+ this.#params = requestParams;
560
1724
  this.#beginRequest();
561
1725
 
562
- const stream = await client.chat.completions.create(
563
- { ...params, stream: true },
564
- { ...options, signal: this.controller.signal },
565
- );
1726
+ const parserParams = snapshotChatCompletionParserParams(requestParams);
1727
+ this.#params = parserParams;
1728
+ this.#hasAutoParseableTool =
1729
+ parserParams.tools?.some(
1730
+ (tool) =>
1731
+ isChatCompletionFunctionTool(tool) && (isAutoParsableTool(tool) || tool.function.strict === true),
1732
+ ) ?? false;
1733
+ const stopObserving =
1734
+ requestParams.tools || requestParams.response_format
1735
+ ? observeSerializedChatCompletionParserParams(requestParams, parserParams, (serialized) => {
1736
+ this.#params = serialized;
1737
+ this.#hasAutoParseableTool =
1738
+ serialized.tools?.some(
1739
+ (tool) =>
1740
+ isChatCompletionFunctionTool(tool) &&
1741
+ (isAutoParsableTool(tool) || tool.function.strict === true),
1742
+ ) ?? false;
1743
+ })
1744
+ : undefined;
1745
+ const stream = await client.chat.completions
1746
+ .create(requestParams, {
1747
+ ...options,
1748
+ signal: this.controller.signal,
1749
+ })
1750
+ .finally(stopObserving);
566
1751
  this._connected();
567
1752
  for await (const chunk of stream) {
568
1753
  this.#addChunk(chunk);
@@ -629,9 +1814,12 @@ export class ChatCompletionStream<ParsedT = null>
629
1814
  throw new OpenAIError(`request ended without sending any chunks`);
630
1815
  }
631
1816
 
632
- #accumulateChatCompletion(chunk: ChatCompletionChunk): ChatCompletionSnapshot {
1817
+ #accumulateChatCompletion(
1818
+ chunk: ChatCompletionChunk,
1819
+ capturedChoiceFrames: WeakMap<object, CapturedChoiceToolCallFrames>,
1820
+ ): ChatCompletionSnapshot {
633
1821
  let snapshot = this.#currentChatCompletionSnapshot;
634
- const { choices, ...rest } = chunk;
1822
+ const { choices, obfuscation: _obfuscation, ...rest } = chunk;
635
1823
  if (!snapshot) {
636
1824
  const newSnapshot: ChatCompletionSnapshot = {
637
1825
  ...rest,
@@ -651,17 +1839,23 @@ export class ChatCompletionStream<ParsedT = null>
651
1839
  ? Math.min(requestedChoiceCount, MAX_STREAM_CHOICES)
652
1840
  : MAX_STREAM_CHOICES;
653
1841
 
654
- for (const { delta, finish_reason, index, logprobs = null, ...other } of chunk.choices) {
1842
+ for (const chunkChoice of chunk.choices) {
1843
+ const { delta, finish_reason, index, logprobs = null, ...other } = chunkChoice;
1844
+ const capturedToolCalls: CapturedToolCallDeltaFrame[] = [];
1845
+ capturedChoiceFrames.set(chunkChoice, Object.freeze({ index, tool_calls: capturedToolCalls }));
655
1846
  if (!Number.isSafeInteger(index) || index < 0 || index >= maxChoices) {
656
1847
  throw new OpenAIError(`Chat completion stream contains an invalid choice index: ${index}`);
657
1848
  }
658
1849
 
659
1850
  let choice = snapshot.choices[index];
660
1851
  if (!choice) {
661
- const newChoice = { finish_reason, index, message: {}, logprobs, ...other };
1852
+ const newChoice = { finish_reason, index, message: {}, logprobs: null, ...other };
662
1853
  snapshot.choices[index] = newChoice;
663
1854
  choice = newChoice;
664
1855
  }
1856
+ if (isParseableResponseFormat(this.#params?.response_format) || this.#hasAutoParseableTool) {
1857
+ captureStructuredJSONSnapshot(captureStructuredMessageSnapshot(choice), 'refusal');
1858
+ }
665
1859
 
666
1860
  if (logprobs) {
667
1861
  if (choice.logprobs) {
@@ -680,6 +1874,14 @@ export class ChatCompletionStream<ParsedT = null>
680
1874
  }
681
1875
  } else {
682
1876
  choice.logprobs = { ...logprobs };
1877
+
1878
+ if (logprobs.content) {
1879
+ choice.logprobs.content = [...logprobs.content];
1880
+ }
1881
+
1882
+ if (logprobs.refusal) {
1883
+ choice.logprobs.refusal = [...logprobs.refusal];
1884
+ }
683
1885
  }
684
1886
  }
685
1887
 
@@ -700,13 +1902,17 @@ export class ChatCompletionStream<ParsedT = null>
700
1902
  assignOwnProperties(choice, other);
701
1903
 
702
1904
  if (!delta) {
1905
+ Object.freeze(capturedToolCalls);
703
1906
  continue;
704
1907
  } // Shouldn't happen; just in case.
705
1908
 
706
1909
  this.#audioDoneChoiceIndexes.delete(index);
707
- const { audio, content, refusal, function_call, role, tool_calls, ...rest } = delta as typeof delta & {
708
- audio?: Partial<ChatCompletionAudio> | null;
709
- };
1910
+ const { audio, content, refusal, function_call, role, ...capturedDeltaFields } =
1911
+ delta as typeof delta & {
1912
+ audio?: Partial<ChatCompletionAudio> | null;
1913
+ };
1914
+ const { tool_calls: capturedToolCallDelta, ...rest } = capturedDeltaFields;
1915
+ const tool_calls = hasOwn(capturedDeltaFields, 'tool_calls') ? capturedToolCallDelta : delta.tool_calls;
710
1916
  assertIsEmpty(rest);
711
1917
  assignOwnProperties(choice.message, rest);
712
1918
  if (
@@ -760,11 +1966,26 @@ export class ChatCompletionStream<ParsedT = null>
760
1966
  }
761
1967
  }
762
1968
  if (content != null) {
763
- choice.message.content = (choice.message.content || '') + content;
764
-
765
1969
  if (!choice.message.refusal && isParseableResponseFormat(this.#params?.response_format)) {
766
- // The partial parser does not accept whitespace-only input.
767
- choice.message.parsed = choice.message.content.trim() ? partialParse(choice.message.content) : null;
1970
+ const eventState = this.#getChoiceEventState(choice);
1971
+ const parseState = (eventState.content_parse_state ??= createPartialJSONParseState());
1972
+ const shouldParse = recordPartialJSONFragment(parseState, this.#partialJSONParseBudget, content);
1973
+ choice.message.content = (captureStructuredJSONSnapshot(choice.message, 'content') || '') + content;
1974
+
1975
+ // The partial parser does not accept whitespace-only input. Once output
1976
+ // grows, coalesce prefix reparses while preserving every raw snapshot.
1977
+ if (!parseState.has_non_whitespace) {
1978
+ choice.message.parsed = null;
1979
+ } else if (shouldParse && reservePartialJSONParse(parseState, this.#partialJSONParseBudget)) {
1980
+ this.#validateStructuredSnapshots(snapshot);
1981
+ choice.message.parsed = parseStructuredStreamingJSON(
1982
+ validateStructuredJSONSnapshot(choice.message.content),
1983
+ );
1984
+ } else if (content.length > 0) {
1985
+ choice.message.parsed = null;
1986
+ }
1987
+ } else {
1988
+ choice.message.content = (choice.message.content || '') + content;
768
1989
  }
769
1990
  }
770
1991
 
@@ -774,12 +1995,27 @@ export class ChatCompletionStream<ParsedT = null>
774
1995
  // once every delta for them has been accumulated.
775
1996
  const toolCallSnapshots = (choice.message.tool_calls ??= []) as PartialToolCallSnapshot[];
776
1997
 
777
- for (const { index, id, type, function: fn, custom, ...rest } of tool_calls) {
1998
+ for (const toolCallDelta of tool_calls) {
1999
+ const { index, id, type, function: fn, custom, ...rest } = toolCallDelta;
778
2000
  if (!Number.isSafeInteger(index) || index < 0 || index >= MAX_STREAM_TOOL_CALLS) {
779
2001
  throw new OpenAIError(`Chat completion stream contains an invalid tool call index: ${index}`);
780
2002
  }
2003
+ let argumentsDelta = '';
781
2004
 
782
2005
  const tool_call = (toolCallSnapshots[index] ??= {});
2006
+ const functionName = fn?.name;
2007
+ const eventState = this.#hasAutoParseableTool ? this.#getChoiceEventState(choice) : undefined;
2008
+ let boundIdentity = eventState?.tool_call_identities.get(index);
2009
+ if (boundIdentity) {
2010
+ assertBoundToolCallIdentity(tool_call, boundIdentity);
2011
+ if (
2012
+ (type !== undefined && type !== boundIdentity.type) ||
2013
+ (functionName !== undefined && functionName !== boundIdentity.name)
2014
+ ) {
2015
+ throw new OpenAIError('Chat completion stream contains a changed tool call identity');
2016
+ }
2017
+ }
2018
+
783
2019
  assignOwnProperties(tool_call, rest);
784
2020
  if (id) {
785
2021
  tool_call.id = id;
@@ -797,20 +2033,80 @@ export class ChatCompletionStream<ParsedT = null>
797
2033
  }
798
2034
  }
799
2035
  if (fn) {
800
- const functionSnapshot = (tool_call.function ??= { name: fn.name ?? '', arguments: '' });
801
- if (fn.name) {
802
- functionSnapshot.name = fn.name;
2036
+ const functionSnapshot = (tool_call.function ??= { name: functionName ?? '', arguments: '' });
2037
+ if (functionName) {
2038
+ functionSnapshot.name = functionName;
803
2039
  }
804
- if (fn.arguments) {
805
- functionSnapshot.arguments += fn.arguments;
2040
+ if (eventState && !boundIdentity) {
2041
+ const identity = ownFunctionToolIdentity(tool_call);
2042
+ const configuredTool =
2043
+ identity &&
2044
+ this.#params?.tools?.find(
2045
+ (tool) => isChatCompletionFunctionTool(tool) && tool.function.name === identity.name,
2046
+ );
2047
+ if (identity) {
2048
+ boundIdentity = {
2049
+ ...identity,
2050
+ parseable:
2051
+ configuredTool !== undefined &&
2052
+ shouldParseToolCall(this.#params, {
2053
+ type: identity.type,
2054
+ function: { name: identity.name },
2055
+ }),
2056
+ };
2057
+ eventState.tool_call_identities.set(index, boundIdentity);
2058
+ if (!boundIdentity.parseable) {
2059
+ const provisionalState = eventState.tool_call_parse_states.get(index);
2060
+ if (provisionalState) {
2061
+ this.#partialJSONParseBudget.bytes -= provisionalState.bytes;
2062
+ this.#partialJSONParseBudget.fragments -= provisionalState.fragments;
2063
+ this.#partialJSONParseBudget.work -= provisionalState.work;
2064
+ eventState.tool_call_parse_states.delete(index);
2065
+ }
2066
+ }
2067
+ }
2068
+ }
2069
+ const argumentFragment = fn.arguments;
2070
+ if (argumentFragment != null) {
2071
+ argumentsDelta = argumentFragment;
2072
+ if (eventState && boundIdentity?.parseable !== false) {
2073
+ let parseState = eventState.tool_call_parse_states.get(index);
2074
+ if (!parseState) {
2075
+ parseState = createPartialJSONParseState();
2076
+ eventState.tool_call_parse_states.set(index, parseState);
2077
+ }
806
2078
 
807
- if (shouldParseToolCall(this.#params, tool_call)) {
808
- functionSnapshot.parsed_arguments = partialParse(functionSnapshot.arguments);
2079
+ const shouldParse = recordPartialJSONFragment(
2080
+ parseState,
2081
+ this.#partialJSONParseBudget,
2082
+ argumentFragment,
2083
+ );
2084
+ const previousArguments = captureStructuredJSONSnapshot(functionSnapshot, 'arguments');
2085
+ if (typeof previousArguments !== 'string') {
2086
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
2087
+ }
2088
+ functionSnapshot.arguments = previousArguments + argumentFragment;
2089
+ if (
2090
+ shouldParse &&
2091
+ boundIdentity?.parseable === true &&
2092
+ reservePartialJSONParse(parseState, this.#partialJSONParseBudget)
2093
+ ) {
2094
+ this.#validateStructuredSnapshots(snapshot);
2095
+ functionSnapshot.parsed_arguments = parseStructuredStreamingJSON(
2096
+ validateStructuredJSONSnapshot(functionSnapshot.arguments),
2097
+ );
2098
+ } else if (argumentFragment.length > 0 && hasOwn(functionSnapshot, 'parsed_arguments')) {
2099
+ functionSnapshot.parsed_arguments = undefined;
2100
+ }
2101
+ } else {
2102
+ functionSnapshot.arguments += argumentFragment;
809
2103
  }
810
2104
  }
811
2105
  }
2106
+ capturedToolCalls.push(Object.freeze({ index, arguments_delta: argumentsDelta }));
812
2107
  }
813
2108
  }
2109
+ Object.freeze(capturedToolCalls);
814
2110
  }
815
2111
  return snapshot;
816
2112
  }
@@ -838,13 +2134,41 @@ function finalizeChatCompletion<ParsedT>(
838
2134
  snapshot: ChatCompletionSnapshot,
839
2135
  params: ChatCompletionCreateParams | null,
840
2136
  audioDoneChoiceIndexes: ReadonlySet<number>,
2137
+ validatedMessages: WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot>,
841
2138
  ): ParsedChatCompletion<ParsedT> {
842
2139
  const { id, choices, created, model, system_fingerprint, ...rest } = snapshot;
843
2140
  const completion: ChatCompletion = {
844
2141
  ...rest,
845
2142
  id,
846
- choices: choices.map(
847
- ({ message, finish_reason, index, logprobs, ...choiceRest }): ChatCompletion.Choice => {
2143
+ choices: mapCapturedSnapshotArray(
2144
+ choices,
2145
+ MAX_STREAM_CHOICES,
2146
+ 'choice',
2147
+ (choice): ChatCompletion.Choice => {
2148
+ const validated = validatedMessages.get(choice);
2149
+ if (!validated) {
2150
+ throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
2151
+ }
2152
+ const stableChoice = new Proxy(choice, {
2153
+ get(target, property, receiver) {
2154
+ return property === 'message' ? validated.message : Reflect.get(target, property, receiver);
2155
+ },
2156
+ });
2157
+ const { message: sourceMessage, finish_reason, index, logprobs, ...choiceRest } = stableChoice;
2158
+ const message = new Proxy(sourceMessage, {
2159
+ get(target, property, receiver) {
2160
+ if (property === 'content') {
2161
+ return validated.content;
2162
+ }
2163
+ if (property === 'refusal') {
2164
+ return validated.refusal;
2165
+ }
2166
+ if (property === 'tool_calls') {
2167
+ return validated.toolCallCollection;
2168
+ }
2169
+ return Reflect.get(target, property, receiver);
2170
+ },
2171
+ });
848
2172
  const { content = null, function_call, tool_calls, audio, ...messageRest } = message;
849
2173
  // Audio streams can end with an expires_at-only chunk after the
850
2174
  // generated audio, without a separate finish_reason.
@@ -897,47 +2221,91 @@ function finalizeChatCompletion<ParsedT>(
897
2221
  role,
898
2222
  content,
899
2223
  refusal: message.refusal ?? null,
900
- tool_calls: tool_calls.map((tool_call, i): ChatCompletionMessageToolCall => {
901
- if (tool_call.type == null) {
902
- throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].type\n${str(snapshot)}`);
903
- }
2224
+ tool_calls: mapCapturedSnapshotArray(
2225
+ tool_calls,
2226
+ MAX_STREAM_TOOL_CALLS,
2227
+ 'tool-call',
2228
+ (tool_call, i): ChatCompletionMessageToolCall => {
2229
+ const captured = validated.toolCalls.get(i);
2230
+ if (!captured) {
2231
+ const identity = ownFunctionToolIdentity(tool_call);
2232
+ if (
2233
+ identity &&
2234
+ shouldParseToolCall(params, {
2235
+ type: identity.type,
2236
+ function: { name: identity.name },
2237
+ })
2238
+ ) {
2239
+ throw new OpenAIError(
2240
+ 'Chat completion stream contains an unsafe structured JSON snapshot',
2241
+ );
2242
+ }
2243
+ }
2244
+ if (captured && captured.tool !== tool_call) {
2245
+ throw new OpenAIError('Chat completion stream contains a changed tool call identity');
2246
+ }
2247
+ const stableFunction =
2248
+ captured &&
2249
+ new Proxy(captured.function, {
2250
+ get(target, property, receiver) {
2251
+ if (property === 'arguments') {
2252
+ return captured.arguments;
2253
+ }
2254
+ if (property === 'name') {
2255
+ return captured.name;
2256
+ }
2257
+ return Reflect.get(target, property, receiver);
2258
+ },
2259
+ });
2260
+ const stableTool =
2261
+ captured && stableFunction
2262
+ ? new Proxy(tool_call, {
2263
+ get(target, property, receiver) {
2264
+ if (property === 'type') {
2265
+ return captured.type;
2266
+ }
2267
+ if (property === 'function') {
2268
+ return stableFunction;
2269
+ }
2270
+ return Reflect.get(target, property, receiver);
2271
+ },
2272
+ })
2273
+ : tool_call;
2274
+ if (stableTool.type == null) {
2275
+ throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].type`);
2276
+ }
904
2277
 
905
- if (tool_call.type === 'custom') {
906
- const { custom, type, id, ...toolRest } = tool_call;
907
- const { input = '', name, ...customRest } = custom || {};
2278
+ if (stableTool.type === 'custom') {
2279
+ const { custom, type, id, ...toolRest } = stableTool;
2280
+ const { input = '', name, ...customRest } = custom || {};
2281
+ if (name == null) {
2282
+ throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].custom.name`);
2283
+ }
2284
+ return {
2285
+ ...toolRest,
2286
+ id: id || `call_${uuid4()}`,
2287
+ type,
2288
+ custom: { ...customRest, name, input },
2289
+ };
2290
+ }
2291
+
2292
+ const { function: fn, type, id, ...toolRest } = stableTool;
2293
+ const { arguments: args, name, ...fnRest } = fn || {};
908
2294
  if (name == null) {
909
- throw new OpenAIError(
910
- `missing choices[${index}].tool_calls[${i}].custom.name\n${str(snapshot)}`,
911
- );
2295
+ throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].function.name`);
2296
+ }
2297
+ if (args == null) {
2298
+ throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].function.arguments`);
912
2299
  }
2300
+
913
2301
  return {
914
2302
  ...toolRest,
915
2303
  id: id || `call_${uuid4()}`,
916
2304
  type,
917
- custom: { ...customRest, name, input },
2305
+ function: { ...fnRest, name, arguments: args },
918
2306
  };
919
- }
920
-
921
- const { function: fn, type, id, ...toolRest } = tool_call;
922
- const { arguments: args, name, ...fnRest } = fn || {};
923
- if (name == null) {
924
- throw new OpenAIError(
925
- `missing choices[${index}].tool_calls[${i}].function.name\n${str(snapshot)}`,
926
- );
927
- }
928
- if (args == null) {
929
- throw new OpenAIError(
930
- `missing choices[${index}].tool_calls[${i}].function.arguments\n${str(snapshot)}`,
931
- );
932
- }
933
-
934
- return {
935
- ...toolRest,
936
- id: id || `call_${uuid4()}`,
937
- type,
938
- function: { ...fnRest, name, arguments: args },
939
- };
940
- }),
2307
+ },
2308
+ ),
941
2309
  },
942
2310
  };
943
2311
  }
@@ -965,10 +2333,6 @@ function isCompleteAudio(
965
2333
  return audio?.id != null && audio.data != null && audio.transcript != null && audio.expires_at != null;
966
2334
  }
967
2335
 
968
- function str(x: unknown) {
969
- return JSON.stringify(x);
970
- }
971
-
972
2336
  /**
973
2337
  * The chat completion accumulated from every streamed chunk received so far.
974
2338
  * Fields within each choice can remain incomplete until generation finishes.