@llumiverse/drivers 1.3.0 → 1.4.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 (409) hide show
  1. package/lib/{types/adobe → adobe}/firefly.d.ts +2 -1
  2. package/lib/adobe/firefly.d.ts.map +1 -0
  3. package/lib/adobe/firefly.js +120 -0
  4. package/lib/adobe/firefly.js.map +1 -0
  5. package/lib/{types/anthropic → anthropic}/index.d.ts +4 -2
  6. package/lib/anthropic/index.d.ts.map +1 -0
  7. package/lib/{esm/anthropic → anthropic}/index.js +19 -8
  8. package/lib/anthropic/index.js.map +1 -0
  9. package/lib/{types/azure → azure}/azure_foundry.d.ts +9 -7
  10. package/lib/azure/azure_foundry.d.ts.map +1 -0
  11. package/lib/{esm/azure → azure}/azure_foundry.js +138 -124
  12. package/lib/azure/azure_foundry.js.map +1 -0
  13. package/lib/{types/bedrock → bedrock}/converse.d.ts +2 -2
  14. package/lib/bedrock/converse.d.ts.map +1 -0
  15. package/lib/{esm/bedrock → bedrock}/converse.js +86 -79
  16. package/lib/bedrock/converse.js.map +1 -0
  17. package/lib/bedrock/embeddings.d.ts +4 -0
  18. package/lib/bedrock/embeddings.d.ts.map +1 -0
  19. package/lib/bedrock/embeddings.js +359 -0
  20. package/lib/bedrock/embeddings.js.map +1 -0
  21. package/lib/{types/bedrock → bedrock}/index.d.ts +33 -9
  22. package/lib/bedrock/index.d.ts.map +1 -0
  23. package/lib/{esm/bedrock → bedrock}/index.js +599 -324
  24. package/lib/bedrock/index.js.map +1 -0
  25. package/lib/{types/bedrock → bedrock}/nova-image-payload.d.ts +5 -5
  26. package/lib/bedrock/nova-image-payload.d.ts.map +1 -0
  27. package/lib/{esm/bedrock → bedrock}/nova-image-payload.js +31 -36
  28. package/lib/bedrock/nova-image-payload.js.map +1 -0
  29. package/lib/{types/bedrock → bedrock}/payloads.d.ts +1 -1
  30. package/lib/bedrock/payloads.d.ts.map +1 -0
  31. package/lib/{cjs/bedrock → bedrock}/payloads.js.map +1 -1
  32. package/lib/{types/bedrock → bedrock}/s3.d.ts +3 -2
  33. package/lib/bedrock/s3.d.ts.map +1 -0
  34. package/lib/{esm/bedrock → bedrock}/s3.js +27 -23
  35. package/lib/bedrock/s3.js.map +1 -0
  36. package/lib/{types/bedrock → bedrock}/twelvelabs.d.ts +8 -9
  37. package/lib/bedrock/twelvelabs.d.ts.map +1 -0
  38. package/lib/{esm/bedrock → bedrock}/twelvelabs.js +17 -21
  39. package/lib/bedrock/twelvelabs.js.map +1 -0
  40. package/lib/{types/groq → groq}/index.d.ts +6 -4
  41. package/lib/groq/index.d.ts.map +1 -0
  42. package/lib/{esm/groq → groq}/index.js +72 -39
  43. package/lib/groq/index.js.map +1 -0
  44. package/lib/{types/huggingface_ie.d.ts → huggingface_ie.d.ts} +4 -3
  45. package/lib/huggingface_ie.d.ts.map +1 -0
  46. package/lib/{esm/huggingface_ie.js → huggingface_ie.js} +32 -31
  47. package/lib/huggingface_ie.js.map +1 -0
  48. package/lib/index.d.ts +16 -0
  49. package/lib/index.d.ts.map +1 -0
  50. package/lib/index.js +16 -0
  51. package/lib/index.js.map +1 -0
  52. package/lib/{types/mistral → mistral}/index.d.ts +6 -5
  53. package/lib/mistral/index.d.ts.map +1 -0
  54. package/lib/{esm/mistral → mistral}/index.js +66 -38
  55. package/lib/mistral/index.js.map +1 -0
  56. package/lib/{types/mistral → mistral}/types.d.ts +1 -0
  57. package/lib/mistral/types.d.ts.map +1 -0
  58. package/lib/mistral/types.js.map +1 -0
  59. package/lib/{types/openai → openai}/azure_openai.d.ts +5 -4
  60. package/lib/openai/azure_openai.d.ts.map +1 -0
  61. package/lib/{esm/openai → openai}/azure_openai.js +17 -14
  62. package/lib/openai/azure_openai.js.map +1 -0
  63. package/lib/openai/drivers.d.ts +4 -0
  64. package/lib/openai/drivers.d.ts.map +1 -0
  65. package/lib/openai/drivers.js +9 -0
  66. package/lib/openai/drivers.js.map +1 -0
  67. package/lib/{types/openai → openai}/index.d.ts +16 -5
  68. package/lib/openai/index.d.ts.map +1 -0
  69. package/lib/{esm/openai → openai}/index.js +242 -141
  70. package/lib/openai/index.js.map +1 -0
  71. package/lib/{types/openai → openai}/openai.d.ts +3 -3
  72. package/lib/openai/openai.d.ts.map +1 -0
  73. package/lib/openai/openai.js +15 -0
  74. package/lib/openai/openai.js.map +1 -0
  75. package/lib/{types/openai → openai}/openai_compatible.d.ts +3 -3
  76. package/lib/openai/openai_compatible.d.ts.map +1 -0
  77. package/lib/{esm/openai → openai}/openai_compatible.js +14 -11
  78. package/lib/openai/openai_compatible.js.map +1 -0
  79. package/lib/{types/openai → openai}/openai_format.d.ts +3 -2
  80. package/lib/openai/openai_format.d.ts.map +1 -0
  81. package/lib/{esm/openai → openai}/openai_format.js +66 -24
  82. package/lib/openai/openai_format.js.map +1 -0
  83. package/lib/{types/replicate.d.ts → replicate.d.ts} +3 -2
  84. package/lib/replicate.d.ts.map +1 -0
  85. package/lib/{esm/replicate.js → replicate.js} +59 -49
  86. package/lib/replicate.js.map +1 -0
  87. package/lib/{types/shared → shared}/claude-messages.d.ts +25 -1
  88. package/lib/shared/claude-messages.d.ts.map +1 -0
  89. package/lib/{esm/shared → shared}/claude-messages.js +212 -59
  90. package/lib/shared/claude-messages.js.map +1 -0
  91. package/lib/{types/shared → shared}/claude-thinking.d.ts +1 -1
  92. package/lib/shared/claude-thinking.d.ts.map +1 -0
  93. package/lib/{esm/shared → shared}/claude-thinking.js +5 -7
  94. package/lib/shared/claude-thinking.js.map +1 -0
  95. package/lib/shared/debug-prompt.d.ts +5 -0
  96. package/lib/shared/debug-prompt.d.ts.map +1 -0
  97. package/lib/shared/debug-prompt.js +25 -0
  98. package/lib/shared/debug-prompt.js.map +1 -0
  99. package/lib/{types/test-driver → test-driver}/TestErrorCompletionStream.d.ts +1 -1
  100. package/lib/test-driver/TestErrorCompletionStream.d.ts.map +1 -0
  101. package/lib/test-driver/TestErrorCompletionStream.js +16 -0
  102. package/lib/test-driver/TestErrorCompletionStream.js.map +1 -0
  103. package/lib/{types/test-driver → test-driver}/TestValidationErrorCompletionStream.d.ts +1 -1
  104. package/lib/test-driver/TestValidationErrorCompletionStream.d.ts.map +1 -0
  105. package/lib/{esm/test-driver → test-driver}/TestValidationErrorCompletionStream.js +4 -4
  106. package/lib/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
  107. package/lib/{types/test-driver → test-driver}/index.d.ts +3 -3
  108. package/lib/test-driver/index.d.ts.map +1 -0
  109. package/lib/{esm/test-driver → test-driver}/index.js +20 -20
  110. package/lib/test-driver/index.js.map +1 -0
  111. package/lib/{types/test-driver → test-driver}/utils.d.ts +1 -1
  112. package/lib/test-driver/utils.d.ts.map +1 -0
  113. package/lib/{esm/test-driver → test-driver}/utils.js +4 -4
  114. package/lib/test-driver/utils.js.map +1 -0
  115. package/lib/togetherai/index.d.ts +15 -0
  116. package/lib/togetherai/index.d.ts.map +1 -0
  117. package/lib/togetherai/index.js +54 -0
  118. package/lib/togetherai/index.js.map +1 -0
  119. package/lib/{types/togetherai → togetherai}/interfaces.d.ts +1 -1
  120. package/lib/togetherai/interfaces.d.ts.map +1 -0
  121. package/lib/{cjs/watsonx → togetherai}/interfaces.js.map +1 -1
  122. package/lib/vertexai/debug.d.ts +2 -0
  123. package/lib/vertexai/debug.d.ts.map +1 -0
  124. package/lib/{esm/vertexai → vertexai}/debug.js +1 -1
  125. package/lib/vertexai/debug.js.map +1 -0
  126. package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts +9 -0
  127. package/lib/vertexai/embeddings/embed-legacy-multimodal.d.ts.map +1 -0
  128. package/lib/vertexai/embeddings/embed-legacy-multimodal.js +141 -0
  129. package/lib/vertexai/embeddings/embed-legacy-multimodal.js.map +1 -0
  130. package/lib/vertexai/embeddings/embed.d.ts +13 -0
  131. package/lib/vertexai/embeddings/embed.d.ts.map +1 -0
  132. package/lib/vertexai/embeddings/embed.js +162 -0
  133. package/lib/vertexai/embeddings/embed.js.map +1 -0
  134. package/lib/vertexai/embeddings/source-utils.d.ts +9 -0
  135. package/lib/vertexai/embeddings/source-utils.d.ts.map +1 -0
  136. package/lib/vertexai/embeddings/source-utils.js +18 -0
  137. package/lib/vertexai/embeddings/source-utils.js.map +1 -0
  138. package/lib/{types/vertexai → vertexai}/index.d.ts +31 -19
  139. package/lib/vertexai/index.d.ts.map +1 -0
  140. package/lib/{esm/vertexai → vertexai}/index.js +221 -159
  141. package/lib/vertexai/index.js.map +1 -0
  142. package/lib/{types/vertexai → vertexai}/models/claude.d.ts +4 -4
  143. package/lib/vertexai/models/claude.d.ts.map +1 -0
  144. package/lib/{esm/vertexai → vertexai}/models/claude.js +17 -20
  145. package/lib/vertexai/models/claude.js.map +1 -0
  146. package/lib/{types/vertexai → vertexai}/models/gemini.d.ts +28 -4
  147. package/lib/vertexai/models/gemini.d.ts.map +1 -0
  148. package/lib/{esm/vertexai → vertexai}/models/gemini.js +238 -116
  149. package/lib/vertexai/models/gemini.js.map +1 -0
  150. package/lib/{types/vertexai → vertexai}/models/imagen.d.ts +12 -11
  151. package/lib/vertexai/models/imagen.d.ts.map +1 -0
  152. package/lib/{esm/vertexai → vertexai}/models/imagen.js +74 -47
  153. package/lib/vertexai/models/imagen.js.map +1 -0
  154. package/lib/{types/vertexai → vertexai}/models/llama.d.ts +4 -4
  155. package/lib/vertexai/models/llama.d.ts.map +1 -0
  156. package/lib/{esm/vertexai → vertexai}/models/llama.js +37 -33
  157. package/lib/vertexai/models/llama.js.map +1 -0
  158. package/lib/vertexai/models.d.ts +21 -0
  159. package/lib/vertexai/models.d.ts.map +1 -0
  160. package/lib/{esm/vertexai → vertexai}/models.js +12 -9
  161. package/lib/vertexai/models.js.map +1 -0
  162. package/lib/{types/watsonx → watsonx}/index.d.ts +4 -3
  163. package/lib/watsonx/index.d.ts.map +1 -0
  164. package/lib/{esm/watsonx → watsonx}/index.js +69 -41
  165. package/lib/watsonx/index.js.map +1 -0
  166. package/lib/watsonx/interfaces.d.ts.map +1 -0
  167. package/lib/{esm/watsonx → watsonx}/interfaces.js.map +1 -1
  168. package/lib/{types/xai → xai}/index.d.ts +4 -4
  169. package/lib/xai/index.d.ts.map +1 -0
  170. package/lib/{esm/xai → xai}/index.js +22 -18
  171. package/lib/xai/index.js.map +1 -0
  172. package/package.json +87 -36
  173. package/src/adobe/firefly.ts +60 -43
  174. package/src/anthropic/index.ts +37 -20
  175. package/src/azure/azure_foundry.ts +210 -165
  176. package/src/bedrock/converse.ts +124 -101
  177. package/src/bedrock/embeddings.test.ts +508 -0
  178. package/src/bedrock/embeddings.ts +517 -0
  179. package/src/bedrock/error-handling.test.ts +62 -14
  180. package/src/bedrock/index.ts +832 -466
  181. package/src/bedrock/nova-image-payload.ts +98 -91
  182. package/src/bedrock/payloads.ts +9 -9
  183. package/src/bedrock/s3.test.ts +39 -0
  184. package/src/bedrock/s3.ts +57 -38
  185. package/src/bedrock/streaming-tool-use.test.ts +86 -69
  186. package/src/bedrock/twelvelabs.ts +35 -32
  187. package/src/groq/index.ts +143 -60
  188. package/src/http-timeout-wiring.test.ts +263 -0
  189. package/src/huggingface_ie.ts +45 -61
  190. package/src/index.ts +15 -16
  191. package/src/mistral/index.ts +97 -48
  192. package/src/mistral/types.ts +13 -12
  193. package/src/openai/azure_openai.ts +35 -27
  194. package/src/openai/drivers.ts +9 -0
  195. package/src/openai/error-handling.test.ts +308 -113
  196. package/src/openai/index.ts +378 -248
  197. package/src/openai/openai.ts +6 -13
  198. package/src/openai/openai_compatible.ts +35 -25
  199. package/src/openai/openai_format.ts +90 -40
  200. package/src/replicate.ts +108 -86
  201. package/src/shared/claude-messages.test.ts +37 -0
  202. package/src/shared/claude-messages.ts +303 -97
  203. package/src/shared/claude-thinking.ts +6 -12
  204. package/src/shared/debug-prompt.ts +28 -0
  205. package/src/test-driver/TestErrorCompletionStream.ts +9 -10
  206. package/src/test-driver/TestValidationErrorCompletionStream.ts +9 -10
  207. package/src/test-driver/index.ts +43 -27
  208. package/src/test-driver/utils.ts +8 -8
  209. package/src/togetherai/index.test.ts +32 -0
  210. package/src/togetherai/index.ts +64 -128
  211. package/src/togetherai/interfaces.ts +5 -5
  212. package/src/vertexai/debug.ts +3 -3
  213. package/src/vertexai/embeddings/embed-legacy-multimodal.ts +211 -0
  214. package/src/vertexai/embeddings/embed.ts +206 -0
  215. package/src/vertexai/embeddings/embeddings.test.ts +497 -0
  216. package/src/vertexai/embeddings/source-utils.test.ts +54 -0
  217. package/src/vertexai/embeddings/source-utils.ts +29 -0
  218. package/src/vertexai/index.ts +331 -244
  219. package/src/vertexai/models/claude-error-handling.test.ts +83 -36
  220. package/src/vertexai/models/claude-streaming-spacing.test.ts +82 -75
  221. package/src/vertexai/models/claude.ts +55 -40
  222. package/src/vertexai/models/gemini-conversation-mutation.test.ts +65 -23
  223. package/src/vertexai/models/gemini-error-handling.test.ts +126 -10
  224. package/src/vertexai/models/gemini-orphaned-tool-result.test.ts +118 -0
  225. package/src/vertexai/models/gemini.ts +349 -172
  226. package/src/vertexai/models/imagen.ts +164 -100
  227. package/src/vertexai/models/llama.ts +70 -48
  228. package/src/vertexai/models.ts +36 -16
  229. package/src/watsonx/index.ts +101 -65
  230. package/src/watsonx/interfaces.ts +10 -15
  231. package/src/xai/index.ts +30 -30
  232. package/lib/cjs/adobe/firefly.js +0 -120
  233. package/lib/cjs/adobe/firefly.js.map +0 -1
  234. package/lib/cjs/anthropic/index.js +0 -64
  235. package/lib/cjs/anthropic/index.js.map +0 -1
  236. package/lib/cjs/azure/azure_foundry.js +0 -432
  237. package/lib/cjs/azure/azure_foundry.js.map +0 -1
  238. package/lib/cjs/bedrock/converse.js +0 -359
  239. package/lib/cjs/bedrock/converse.js.map +0 -1
  240. package/lib/cjs/bedrock/index.js +0 -1508
  241. package/lib/cjs/bedrock/index.js.map +0 -1
  242. package/lib/cjs/bedrock/nova-image-payload.js +0 -207
  243. package/lib/cjs/bedrock/nova-image-payload.js.map +0 -1
  244. package/lib/cjs/bedrock/payloads.js +0 -3
  245. package/lib/cjs/bedrock/s3.js +0 -107
  246. package/lib/cjs/bedrock/s3.js.map +0 -1
  247. package/lib/cjs/bedrock/twelvelabs.js +0 -87
  248. package/lib/cjs/bedrock/twelvelabs.js.map +0 -1
  249. package/lib/cjs/groq/index.js +0 -328
  250. package/lib/cjs/groq/index.js.map +0 -1
  251. package/lib/cjs/huggingface_ie.js +0 -201
  252. package/lib/cjs/huggingface_ie.js.map +0 -1
  253. package/lib/cjs/index.js +0 -32
  254. package/lib/cjs/index.js.map +0 -1
  255. package/lib/cjs/mistral/index.js +0 -176
  256. package/lib/cjs/mistral/index.js.map +0 -1
  257. package/lib/cjs/mistral/types.js +0 -83
  258. package/lib/cjs/mistral/types.js.map +0 -1
  259. package/lib/cjs/openai/azure_openai.js +0 -72
  260. package/lib/cjs/openai/azure_openai.js.map +0 -1
  261. package/lib/cjs/openai/index.js +0 -1135
  262. package/lib/cjs/openai/index.js.map +0 -1
  263. package/lib/cjs/openai/openai.js +0 -21
  264. package/lib/cjs/openai/openai.js.map +0 -1
  265. package/lib/cjs/openai/openai_compatible.js +0 -63
  266. package/lib/cjs/openai/openai_compatible.js.map +0 -1
  267. package/lib/cjs/openai/openai_format.js +0 -131
  268. package/lib/cjs/openai/openai_format.js.map +0 -1
  269. package/lib/cjs/package.json +0 -3
  270. package/lib/cjs/replicate.js +0 -275
  271. package/lib/cjs/replicate.js.map +0 -1
  272. package/lib/cjs/shared/claude-messages.js +0 -737
  273. package/lib/cjs/shared/claude-messages.js.map +0 -1
  274. package/lib/cjs/shared/claude-thinking.js +0 -60
  275. package/lib/cjs/shared/claude-thinking.js.map +0 -1
  276. package/lib/cjs/test-driver/TestErrorCompletionStream.js +0 -20
  277. package/lib/cjs/test-driver/TestErrorCompletionStream.js.map +0 -1
  278. package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js +0 -24
  279. package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
  280. package/lib/cjs/test-driver/index.js +0 -109
  281. package/lib/cjs/test-driver/index.js.map +0 -1
  282. package/lib/cjs/test-driver/utils.js +0 -30
  283. package/lib/cjs/test-driver/utils.js.map +0 -1
  284. package/lib/cjs/togetherai/index.js +0 -126
  285. package/lib/cjs/togetherai/index.js.map +0 -1
  286. package/lib/cjs/togetherai/interfaces.js +0 -3
  287. package/lib/cjs/togetherai/interfaces.js.map +0 -1
  288. package/lib/cjs/vertexai/debug.js +0 -12
  289. package/lib/cjs/vertexai/debug.js.map +0 -1
  290. package/lib/cjs/vertexai/embeddings/embeddings-image.js +0 -27
  291. package/lib/cjs/vertexai/embeddings/embeddings-image.js.map +0 -1
  292. package/lib/cjs/vertexai/embeddings/embeddings-text.js +0 -23
  293. package/lib/cjs/vertexai/embeddings/embeddings-text.js.map +0 -1
  294. package/lib/cjs/vertexai/index.js +0 -644
  295. package/lib/cjs/vertexai/index.js.map +0 -1
  296. package/lib/cjs/vertexai/models/claude.js +0 -72
  297. package/lib/cjs/vertexai/models/claude.js.map +0 -1
  298. package/lib/cjs/vertexai/models/gemini.js +0 -864
  299. package/lib/cjs/vertexai/models/gemini.js.map +0 -1
  300. package/lib/cjs/vertexai/models/imagen.js +0 -303
  301. package/lib/cjs/vertexai/models/imagen.js.map +0 -1
  302. package/lib/cjs/vertexai/models/llama.js +0 -183
  303. package/lib/cjs/vertexai/models/llama.js.map +0 -1
  304. package/lib/cjs/vertexai/models.js +0 -35
  305. package/lib/cjs/vertexai/models.js.map +0 -1
  306. package/lib/cjs/watsonx/index.js +0 -161
  307. package/lib/cjs/watsonx/index.js.map +0 -1
  308. package/lib/cjs/watsonx/interfaces.js +0 -3
  309. package/lib/cjs/xai/index.js +0 -65
  310. package/lib/cjs/xai/index.js.map +0 -1
  311. package/lib/esm/adobe/firefly.js +0 -116
  312. package/lib/esm/adobe/firefly.js.map +0 -1
  313. package/lib/esm/anthropic/index.js.map +0 -1
  314. package/lib/esm/azure/azure_foundry.js.map +0 -1
  315. package/lib/esm/bedrock/converse.js.map +0 -1
  316. package/lib/esm/bedrock/index.js.map +0 -1
  317. package/lib/esm/bedrock/nova-image-payload.js.map +0 -1
  318. package/lib/esm/bedrock/payloads.js.map +0 -1
  319. package/lib/esm/bedrock/s3.js.map +0 -1
  320. package/lib/esm/bedrock/twelvelabs.js.map +0 -1
  321. package/lib/esm/groq/index.js.map +0 -1
  322. package/lib/esm/huggingface_ie.js.map +0 -1
  323. package/lib/esm/index.js +0 -16
  324. package/lib/esm/index.js.map +0 -1
  325. package/lib/esm/mistral/index.js.map +0 -1
  326. package/lib/esm/mistral/types.js.map +0 -1
  327. package/lib/esm/openai/azure_openai.js.map +0 -1
  328. package/lib/esm/openai/index.js.map +0 -1
  329. package/lib/esm/openai/openai.js +0 -14
  330. package/lib/esm/openai/openai.js.map +0 -1
  331. package/lib/esm/openai/openai_compatible.js.map +0 -1
  332. package/lib/esm/openai/openai_format.js.map +0 -1
  333. package/lib/esm/replicate.js.map +0 -1
  334. package/lib/esm/shared/claude-messages.js.map +0 -1
  335. package/lib/esm/shared/claude-thinking.js.map +0 -1
  336. package/lib/esm/test-driver/TestErrorCompletionStream.js +0 -16
  337. package/lib/esm/test-driver/TestErrorCompletionStream.js.map +0 -1
  338. package/lib/esm/test-driver/TestValidationErrorCompletionStream.js.map +0 -1
  339. package/lib/esm/test-driver/index.js.map +0 -1
  340. package/lib/esm/test-driver/utils.js.map +0 -1
  341. package/lib/esm/togetherai/index.js +0 -122
  342. package/lib/esm/togetherai/index.js.map +0 -1
  343. package/lib/esm/togetherai/interfaces.js.map +0 -1
  344. package/lib/esm/vertexai/debug.js.map +0 -1
  345. package/lib/esm/vertexai/embeddings/embeddings-image.js +0 -24
  346. package/lib/esm/vertexai/embeddings/embeddings-image.js.map +0 -1
  347. package/lib/esm/vertexai/embeddings/embeddings-text.js +0 -20
  348. package/lib/esm/vertexai/embeddings/embeddings-text.js.map +0 -1
  349. package/lib/esm/vertexai/index.js.map +0 -1
  350. package/lib/esm/vertexai/models/claude.js.map +0 -1
  351. package/lib/esm/vertexai/models/gemini.js.map +0 -1
  352. package/lib/esm/vertexai/models/imagen.js.map +0 -1
  353. package/lib/esm/vertexai/models/llama.js.map +0 -1
  354. package/lib/esm/vertexai/models.js.map +0 -1
  355. package/lib/esm/watsonx/index.js.map +0 -1
  356. package/lib/esm/xai/index.js.map +0 -1
  357. package/lib/types/adobe/firefly.d.ts.map +0 -1
  358. package/lib/types/anthropic/index.d.ts.map +0 -1
  359. package/lib/types/azure/azure_foundry.d.ts.map +0 -1
  360. package/lib/types/bedrock/converse.d.ts.map +0 -1
  361. package/lib/types/bedrock/index.d.ts.map +0 -1
  362. package/lib/types/bedrock/nova-image-payload.d.ts.map +0 -1
  363. package/lib/types/bedrock/payloads.d.ts.map +0 -1
  364. package/lib/types/bedrock/s3.d.ts.map +0 -1
  365. package/lib/types/bedrock/twelvelabs.d.ts.map +0 -1
  366. package/lib/types/groq/index.d.ts.map +0 -1
  367. package/lib/types/huggingface_ie.d.ts.map +0 -1
  368. package/lib/types/index.d.ts +0 -16
  369. package/lib/types/index.d.ts.map +0 -1
  370. package/lib/types/mistral/index.d.ts.map +0 -1
  371. package/lib/types/mistral/types.d.ts.map +0 -1
  372. package/lib/types/openai/azure_openai.d.ts.map +0 -1
  373. package/lib/types/openai/index.d.ts.map +0 -1
  374. package/lib/types/openai/openai.d.ts.map +0 -1
  375. package/lib/types/openai/openai_compatible.d.ts.map +0 -1
  376. package/lib/types/openai/openai_format.d.ts.map +0 -1
  377. package/lib/types/replicate.d.ts.map +0 -1
  378. package/lib/types/shared/claude-messages.d.ts.map +0 -1
  379. package/lib/types/shared/claude-thinking.d.ts.map +0 -1
  380. package/lib/types/test-driver/TestErrorCompletionStream.d.ts.map +0 -1
  381. package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts.map +0 -1
  382. package/lib/types/test-driver/index.d.ts.map +0 -1
  383. package/lib/types/test-driver/utils.d.ts.map +0 -1
  384. package/lib/types/togetherai/index.d.ts +0 -23
  385. package/lib/types/togetherai/index.d.ts.map +0 -1
  386. package/lib/types/togetherai/interfaces.d.ts.map +0 -1
  387. package/lib/types/vertexai/debug.d.ts +0 -2
  388. package/lib/types/vertexai/debug.d.ts.map +0 -1
  389. package/lib/types/vertexai/embeddings/embeddings-image.d.ts +0 -11
  390. package/lib/types/vertexai/embeddings/embeddings-image.d.ts.map +0 -1
  391. package/lib/types/vertexai/embeddings/embeddings-text.d.ts +0 -10
  392. package/lib/types/vertexai/embeddings/embeddings-text.d.ts.map +0 -1
  393. package/lib/types/vertexai/index.d.ts.map +0 -1
  394. package/lib/types/vertexai/models/claude.d.ts.map +0 -1
  395. package/lib/types/vertexai/models/gemini.d.ts.map +0 -1
  396. package/lib/types/vertexai/models/imagen.d.ts.map +0 -1
  397. package/lib/types/vertexai/models/llama.d.ts.map +0 -1
  398. package/lib/types/vertexai/models.d.ts +0 -20
  399. package/lib/types/vertexai/models.d.ts.map +0 -1
  400. package/lib/types/watsonx/index.d.ts.map +0 -1
  401. package/lib/types/watsonx/interfaces.d.ts.map +0 -1
  402. package/lib/types/xai/index.d.ts.map +0 -1
  403. package/src/vertexai/embeddings/embeddings-image.ts +0 -52
  404. package/src/vertexai/embeddings/embeddings-text.ts +0 -54
  405. /package/lib/{esm/bedrock → bedrock}/payloads.js +0 -0
  406. /package/lib/{esm/mistral → mistral}/types.js +0 -0
  407. /package/lib/{esm/togetherai → togetherai}/interfaces.js +0 -0
  408. /package/lib/{types/watsonx → watsonx}/interfaces.d.ts +0 -0
  409. /package/lib/{esm/watsonx → watsonx}/interfaces.js +0 -0
@@ -1,39 +1,44 @@
1
1
  import {
2
- AbstractDriver,
3
- AIModel,
4
- Completion,
5
- CompletionChunkObject,
6
- CompletionResult,
7
- DataSource,
8
- DriverOptions,
9
- EmbeddingsOptions,
10
- EmbeddingsResult,
11
- ExecutionOptions,
12
- ExecutionTokenUsage,
2
+ type AIModel,
3
+ type Completion,
4
+ type CompletionChunkObject,
5
+ type CompletionResult,
6
+ type DataSource,
7
+ type DriverOptions,
8
+ type EmbeddingResultItem,
9
+ type EmbeddingsOptions,
10
+ type EmbeddingsResult,
11
+ type ExecutionOptions,
12
+ type ExecutionTokenUsage,
13
13
  getConversationMeta,
14
14
  getModelCapabilities,
15
15
  incrementConversationTurn,
16
- JSONSchema,
16
+ type JSONSchema,
17
17
  LlumiverseError,
18
- LlumiverseErrorContext,
19
- modelModalitiesToArray,
18
+ type LlumiverseErrorContext,
20
19
  ModelType,
21
- OpenAiDalleOptions,
22
- OpenAiGptImageOptions,
23
- Providers,
20
+ modelModalitiesToArray,
21
+ normalizeEmbeddingsOptions,
22
+ OPENAI_DEFAULT_EMBEDDING_MODEL,
23
+ type OpenAiDalleOptions,
24
+ type OpenAiGptImageOptions,
25
+ type Providers,
24
26
  stripBase64ImagesFromConversation,
25
27
  stripHeartbeatsFromConversation,
26
28
  supportsToolUse,
27
- ToolDefinition,
28
- ToolUse,
29
- TrainingJob,
29
+ type TextFallbackOptions,
30
+ type ToolDefinition,
31
+ type ToolUse,
32
+ type TrainingJob,
30
33
  TrainingJobStatus,
31
- TrainingOptions,
32
- TrainingPromptOptions,
34
+ type TrainingOptions,
35
+ type TrainingPromptOptions,
33
36
  truncateLargeTextInConversation,
34
37
  unwrapConversationArray,
35
- } from "@llumiverse/core";
36
- import OpenAI, { AzureOpenAI } from "openai";
38
+ } from '@llumiverse/core';
39
+ import { AbstractDriver } from '@llumiverse/core/driver';
40
+ import type OpenAI from 'openai';
41
+ import type { AzureOpenAI } from 'openai';
37
42
  import {
38
43
  APIConnectionError,
39
44
  APIConnectionTimeoutError,
@@ -50,23 +55,49 @@ import {
50
55
  RateLimitError,
51
56
  UnprocessableEntityError,
52
57
  } from 'openai/error';
53
- import { formatOpenAILikeMultimodalPrompt } from "./openai_format.js";
58
+ import { formatOpenAILikeMultimodalPrompt } from './openai_format.js';
54
59
 
55
60
  // Response API types
56
61
  type ResponseInputItem = OpenAI.Responses.ResponseInputItem;
57
62
  type EasyInputMessage = OpenAI.Responses.EasyInputMessage;
63
+ type OpenAIRequestOptions = Partial<TextFallbackOptions> & {
64
+ image_detail?: 'low' | 'high' | 'auto';
65
+ effort?: string;
66
+ reasoning_effort?: string;
67
+ };
68
+ type OpenAIErrorWithStatus = Error & { status?: unknown };
69
+ type OpenAIUsageWithProviderDetails = OpenAI.Responses.ResponseUsage & {
70
+ cached_tokens?: number | null;
71
+ prompt_tokens_details?: {
72
+ cached_tokens?: number | null;
73
+ } | null;
74
+ };
75
+ type MutableRoleItem = { role: 'user' | 'developer' | 'system' | 'assistant' };
76
+ type MutableInputImagePart = { type: string; detail?: string };
77
+ type OpenAIFunctionItem = ResponseInputItem & {
78
+ type?: string;
79
+ name?: string;
80
+ arguments?: string;
81
+ output?: string;
82
+ };
58
83
 
59
84
  // Helper function to convert string to CompletionResult[]
60
85
  function textToCompletionResult(text: string): CompletionResult[] {
61
- return text ? [{ type: "text", value: text }] : [];
86
+ return text ? [{ type: 'text', value: text }] : [];
87
+ }
88
+
89
+ function hasNumericStatus(error: unknown): boolean {
90
+ return error instanceof Error && typeof (error as OpenAIErrorWithStatus).status === 'number';
62
91
  }
63
92
 
64
93
  function isOpenAIReasoningModel(model: string): boolean {
65
94
  const normalized = model.toLowerCase();
66
- return normalized.includes("o1")
67
- || normalized.includes("o3")
68
- || normalized.includes("o4")
69
- || normalized.includes("gpt-5");
95
+ return (
96
+ normalized.includes('o1') ||
97
+ normalized.includes('o3') ||
98
+ normalized.includes('o4') ||
99
+ normalized.includes('gpt-5')
100
+ );
70
101
  }
71
102
 
72
103
  function isGpt5ProModel(model: string): boolean {
@@ -74,33 +105,35 @@ function isGpt5ProModel(model: string): boolean {
74
105
  return /^gpt-5(?:\.\d+)?-pro/.test(modelName);
75
106
  }
76
107
 
77
- function openAIReasoningEffort(model: string, effort: string | undefined): "low" | "medium" | "high" | undefined {
108
+ function openAIReasoningEffort(model: string, effort: string | undefined): 'low' | 'medium' | 'high' | undefined {
78
109
  if (!effort || !isOpenAIReasoningModel(model)) {
79
110
  return undefined;
80
111
  }
81
112
  if (isGpt5ProModel(model)) {
82
- return "high";
113
+ return 'high';
83
114
  }
84
- return effort === "low" || effort === "medium" || effort === "high" ? effort : undefined;
115
+ return effort === 'low' || effort === 'medium' || effort === 'high' ? effort : undefined;
85
116
  }
86
117
 
87
118
  //TODO: Do we need a list?, replace with if statements and modernize?
88
119
  const supportFineTunning = new Set([
89
- "gpt-3.5-turbo-1106",
90
- "gpt-3.5-turbo-0613",
91
- "babbage-002",
92
- "davinci-002",
93
- "gpt-4-0613"
120
+ 'gpt-3.5-turbo-1106',
121
+ 'gpt-3.5-turbo-0613',
122
+ 'babbage-002',
123
+ 'davinci-002',
124
+ 'gpt-4-0613',
94
125
  ]);
95
126
 
96
- export interface BaseOpenAIDriverOptions extends DriverOptions {
97
- }
127
+ export interface BaseOpenAIDriverOptions extends DriverOptions {}
98
128
 
99
- export abstract class BaseOpenAIDriver extends AbstractDriver<
100
- BaseOpenAIDriverOptions,
101
- ResponseInputItem[]
102
- > {
103
- abstract provider: Providers.openai | Providers.azure_openai | Providers.xai | Providers.azure_foundry | Providers.openai_compatible;
129
+ export abstract class BaseOpenAIDriver extends AbstractDriver<BaseOpenAIDriverOptions, ResponseInputItem[]> {
130
+ abstract provider:
131
+ | Providers.openai
132
+ | Providers.azure_openai
133
+ | Providers.xai
134
+ | Providers.azure_foundry
135
+ | Providers.togetherai
136
+ | Providers.openai_compatible;
104
137
  abstract service: OpenAI | AzureOpenAI;
105
138
 
106
139
  constructor(opts: BaseOpenAIDriverOptions) {
@@ -108,10 +141,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
108
141
  this.formatPrompt = formatOpenAILikeMultimodalPrompt;
109
142
  }
110
143
 
111
- extractDataFromResponse(
112
- _options: ExecutionOptions,
113
- result: OpenAI.Responses.Response
114
- ): Completion {
144
+ extractDataFromResponse(_options: ExecutionOptions, result: OpenAI.Responses.Response): Completion {
115
145
  const tokenInfo = mapUsage(result.usage);
116
146
 
117
147
  const tools = collectTools(result.output);
@@ -119,8 +149,8 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
119
149
  const allResults = extractCompletionResults(result.output);
120
150
 
121
151
  if (allResults.length === 0 && !tools) {
122
- this.logger.error({ result }, "[OpenAI] Response is not valid");
123
- throw new Error("Response is not valid: no data");
152
+ this.logger.error({ result }, '[OpenAI] Response is not valid');
153
+ throw new Error('Response is not valid: no data');
124
154
  }
125
155
 
126
156
  return {
@@ -131,17 +161,22 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
131
161
  };
132
162
  }
133
163
 
134
- async requestTextCompletionStream(prompt: ResponseInputItem[], options: ExecutionOptions): Promise<AsyncIterable<CompletionChunkObject>> {
135
- if (options.model_options?._option_id !== undefined &&
136
- options.model_options?._option_id !== "openai-text" &&
137
- options.model_options?._option_id !== "openai-thinking" &&
138
- options.model_options?._option_id !== "text-fallback") {
139
- this.logger.debug({ options: options.model_options }, "Unexpected option id");
164
+ async requestTextCompletionStream(
165
+ prompt: ResponseInputItem[],
166
+ options: ExecutionOptions,
167
+ ): Promise<AsyncIterable<CompletionChunkObject>> {
168
+ if (
169
+ options.model_options?._option_id !== undefined &&
170
+ options.model_options?._option_id !== 'openai-text' &&
171
+ options.model_options?._option_id !== 'openai-thinking' &&
172
+ options.model_options?._option_id !== 'text-fallback'
173
+ ) {
174
+ this.logger.debug({ options: options.model_options }, 'Unexpected option id');
140
175
  }
141
176
 
142
177
  // Include conversation history (same as non-streaming)
143
178
  // Fix orphaned function_call items (can occur when agent is stopped mid-tool-execution)
144
- let conversation = fixOrphanedToolUse(updateConversation(options.conversation, prompt));
179
+ let conversation = fixOrphanedToolResults(fixOrphanedToolUse(updateConversation(options.conversation, prompt)));
145
180
 
146
181
  const toolDefs = getToolDefinitions(options.tools);
147
182
  const useTools: boolean = toolDefs ? supportsToolUse(options.model, this.provider, true) : false;
@@ -154,17 +189,16 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
154
189
 
155
190
  convertRoles(prompt, options.model);
156
191
 
157
- const model_options = options.model_options as any;
158
- insert_image_detail(prompt, model_options?.image_detail ?? "auto");
192
+ const model_options = options.model_options as OpenAIRequestOptions | undefined;
193
+ insert_image_detail(prompt, model_options?.image_detail ?? 'auto');
159
194
 
160
- let parsedSchema: JSONSchema | undefined = undefined;
195
+ let parsedSchema: JSONSchema | undefined;
161
196
  let strictMode = false;
162
- if (options.result_schema && supportsSchema(options.model)) {
197
+ if (options.result_schema && supportsSchema(options.model, this.provider)) {
163
198
  try {
164
199
  parsedSchema = openAISchemaFormat(options.result_schema);
165
200
  strictMode = true;
166
- }
167
- catch (e) {
201
+ } catch {
168
202
  parsedSchema = limitedSchemaFormat(options.result_schema);
169
203
  strictMode = false;
170
204
  }
@@ -183,36 +217,41 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
183
217
  top_p: isReasoningModel ? undefined : model_options?.top_p,
184
218
  max_output_tokens: model_options?.max_tokens,
185
219
  tools: useTools ? toolDefs : undefined,
186
- text: parsedSchema ? {
187
- format: {
188
- type: "json_schema",
189
- name: "format_output",
190
- schema: parsedSchema,
191
- strict: strictMode,
192
- }
193
- } : undefined,
220
+ text: parsedSchema
221
+ ? {
222
+ format: {
223
+ type: 'json_schema',
224
+ name: 'format_output',
225
+ schema: parsedSchema,
226
+ strict: strictMode,
227
+ },
228
+ }
229
+ : undefined,
194
230
  });
195
231
 
196
232
  return mapResponseStream(stream);
197
233
  }
198
234
 
199
235
  async requestTextCompletion(prompt: ResponseInputItem[], options: ExecutionOptions): Promise<Completion> {
200
- if (options.model_options?._option_id !== undefined &&
201
- options.model_options?._option_id !== "openai-text" &&
202
- options.model_options?._option_id !== "openai-thinking") {
203
- this.logger.debug({ options: options.model_options }, "Unexpected option id");
236
+ if (
237
+ options.model_options?._option_id !== undefined &&
238
+ options.model_options?._option_id !== 'openai-text' &&
239
+ options.model_options?._option_id !== 'openai-thinking' &&
240
+ options.model_options?._option_id !== 'text-fallback'
241
+ ) {
242
+ this.logger.debug({ options: options.model_options }, 'Unexpected option id');
204
243
  }
205
244
 
206
245
  convertRoles(prompt, options.model);
207
246
 
208
- const model_options = options.model_options as any;
209
- insert_image_detail(prompt, model_options?.image_detail ?? "auto");
247
+ const model_options = options.model_options as OpenAIRequestOptions | undefined;
248
+ insert_image_detail(prompt, model_options?.image_detail ?? 'auto');
210
249
 
211
250
  const toolDefs = getToolDefinitions(options.tools);
212
251
  const useTools: boolean = toolDefs ? supportsToolUse(options.model, this.provider) : false;
213
252
 
214
253
  // Fix orphaned function_call items (can occur when agent is stopped mid-tool-execution)
215
- let conversation = fixOrphanedToolUse(updateConversation(options.conversation, prompt));
254
+ let conversation = fixOrphanedToolResults(fixOrphanedToolUse(updateConversation(options.conversation, prompt)));
216
255
 
217
256
  // When no tools are provided but conversation contains function_call/function_call_output
218
257
  // items (e.g. checkpoint summary calls), convert them to text to avoid API errors
@@ -220,14 +259,13 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
220
259
  conversation = convertOpenAIFunctionItemsToText(conversation);
221
260
  }
222
261
 
223
- let parsedSchema: JSONSchema | undefined = undefined;
262
+ let parsedSchema: JSONSchema | undefined;
224
263
  let strictMode = false;
225
- if (options.result_schema && supportsSchema(options.model)) {
264
+ if (options.result_schema && supportsSchema(options.model, this.provider)) {
226
265
  try {
227
266
  parsedSchema = openAISchemaFormat(options.result_schema);
228
267
  strictMode = true;
229
- }
230
- catch (e) {
268
+ } catch {
231
269
  parsedSchema = limitedSchemaFormat(options.result_schema);
232
270
  strictMode = false;
233
271
  }
@@ -246,14 +284,16 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
246
284
  top_p: isReasoningModel ? undefined : model_options?.top_p,
247
285
  max_output_tokens: model_options?.max_tokens, //TODO: use max_tokens for older models, currently relying on OpenAI to handle it
248
286
  tools: useTools ? toolDefs : undefined,
249
- text: parsedSchema ? {
250
- format: {
251
- type: "json_schema",
252
- name: "format_output",
253
- schema: parsedSchema,
254
- strict: strictMode,
255
- }
256
- } : undefined,
287
+ text: parsedSchema
288
+ ? {
289
+ format: {
290
+ type: 'json_schema',
291
+ name: 'format_output',
292
+ schema: parsedSchema,
293
+ strict: strictMode,
294
+ },
295
+ }
296
+ : undefined,
257
297
  });
258
298
 
259
299
  const completion = this.extractDataFromResponse(options, res);
@@ -271,7 +311,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
271
311
  const stripOptions = {
272
312
  keepForTurns: options.stripImagesAfterTurns ?? Infinity,
273
313
  currentTurn,
274
- textMaxTokens: options.stripTextMaxTokens
314
+ textMaxTokens: options.stripTextMaxTokens,
275
315
  };
276
316
  let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
277
317
 
@@ -291,14 +331,15 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
291
331
 
292
332
  protected canStream(_options: ExecutionOptions): Promise<boolean> {
293
333
  // Image generation models don't support streaming
294
- if (_options.model.includes("dall-e")
295
- || _options.model.includes("gpt-image")
296
- || _options.model.includes("chatgpt-image")) {
334
+ if (
335
+ _options.model.includes('dall-e') ||
336
+ _options.model.includes('gpt-image') ||
337
+ _options.model.includes('chatgpt-image')
338
+ ) {
297
339
  return Promise.resolve(false);
298
340
  }
299
341
 
300
- if (_options.model.includes("o1")
301
- && !(_options.model.includes("mini") || _options.model.includes("preview"))) {
342
+ if (_options.model.includes('o1') && !(_options.model.includes('mini') || _options.model.includes('preview'))) {
302
343
  //o1 full does not support streaming
303
344
  //TODO: Update when OpenAI adds support for streaming, last check 16/02/2025
304
345
  return Promise.resolve(false);
@@ -314,7 +355,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
314
355
  prompt: ResponseInputItem[],
315
356
  result: unknown[],
316
357
  toolUse: unknown[] | undefined,
317
- options: ExecutionOptions
358
+ options: ExecutionOptions,
318
359
  ): ResponseInputItem[] | undefined {
319
360
  // Build assistant message from accumulated CompletionResult[]
320
361
  const completionResults = result as CompletionResult[];
@@ -327,6 +368,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
327
368
  // Add assistant message as EasyInputMessage
328
369
  if (textContent) {
329
370
  const assistantMessage: EasyInputMessage = {
371
+ type: 'message',
330
372
  role: 'assistant',
331
373
  content: textContent,
332
374
  };
@@ -335,7 +377,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
335
377
 
336
378
  // Add function calls as separate items (Response API format)
337
379
  if (toolUse && toolUse.length > 0) {
338
- const functionCalls: OpenAI.Responses.ResponseFunctionToolCall[] = (toolUse as ToolUse[]).map(t => ({
380
+ const functionCalls: OpenAI.Responses.ResponseFunctionToolCall[] = (toolUse as ToolUse[]).map((t) => ({
339
381
  type: 'function_call' as const,
340
382
  call_id: t.id,
341
383
  name: t.tool_name,
@@ -352,7 +394,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
352
394
  const stripOptions = {
353
395
  keepForTurns: options.stripImagesAfterTurns ?? Infinity,
354
396
  currentTurn,
355
- textMaxTokens: options.stripTextMaxTokens
397
+ textMaxTokens: options.stripTextMaxTokens,
356
398
  };
357
399
  let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
358
400
  processedConversation = truncateLargeTextInConversation(processedConversation, stripOptions);
@@ -365,11 +407,11 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
365
407
  }
366
408
 
367
409
  createTrainingPrompt(options: TrainingPromptOptions): Promise<string> {
368
- if (options.model.includes("gpt")) {
410
+ if (options.model.includes('gpt')) {
369
411
  return super.createTrainingPrompt(options);
370
412
  } else {
371
413
  // babbage, davinci not yet implemented
372
- throw new Error("Unsupported model for training: " + options.model);
414
+ throw new Error(`Unsupported model for training: ${options.model}`);
373
415
  }
374
416
  }
375
417
 
@@ -377,14 +419,14 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
377
419
  const url = await dataset.getURL();
378
420
  const file = await this.service.files.create({
379
421
  file: await fetch(url),
380
- purpose: "fine-tune",
422
+ purpose: 'fine-tune',
381
423
  });
382
424
 
383
425
  const job = await this.service.fineTuning.jobs.create({
384
426
  training_file: file.id,
385
427
  model: options.model,
386
- hyperparameters: options.params
387
- })
428
+ hyperparameters: options.params,
429
+ });
388
430
 
389
431
  return jobInfo(job);
390
432
  }
@@ -405,7 +447,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
405
447
  try {
406
448
  await this.service.models.list();
407
449
  return true;
408
- } catch (error) {
450
+ } catch {
409
451
  return false;
410
452
  }
411
453
  }
@@ -423,9 +465,21 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
423
465
 
424
466
  //Some of these use the completions API instead of the chat completions API.
425
467
  //Others are for non-text input modalities. Therefore common to both.
426
- const wordBlacklist = ["embed", "whisper", "transcribe", "audio", "moderation", "tts",
427
- "realtime", "babbage", "davinci", "codex", "o1-pro", "computer-use", "sora"];
428
-
468
+ const wordBlacklist = [
469
+ 'embed',
470
+ 'whisper',
471
+ 'transcribe',
472
+ 'audio',
473
+ 'moderation',
474
+ 'tts',
475
+ 'realtime',
476
+ 'babbage',
477
+ 'davinci',
478
+ 'codex',
479
+ 'o1-pro',
480
+ 'computer-use',
481
+ 'sora',
482
+ ];
429
483
 
430
484
  //OpenAI has very little information, filtering based on name.
431
485
  result = result.filter((m) => {
@@ -433,61 +487,84 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
433
487
  });
434
488
 
435
489
  const models = filter ? result.filter(filter) : result;
436
- const aiModels = models.map((m) => {
437
- const modelCapability = getModelCapabilities(m.id, "openai");
438
- let owner = m.owned_by;
439
- if (owner == "system") {
440
- owner = "openai";
441
- }
442
-
443
- // Determine model type based on capabilities
444
- let modelType = ModelType.Text;
445
- if (m.id.includes("dall-e") || m.id.includes("gpt-image")) {
446
- modelType = ModelType.Image;
447
- }
490
+ const aiModels = models
491
+ .map((m) => {
492
+ const modelCapability = getModelCapabilities(m.id, this.provider);
493
+ let owner = m.owned_by;
494
+ if (owner === 'system') {
495
+ owner = 'openai';
496
+ }
448
497
 
498
+ // Determine model type based on capabilities
499
+ let modelType = ModelType.Text;
500
+ if (m.id.includes('dall-e') || m.id.includes('gpt-image')) {
501
+ modelType = ModelType.Image;
502
+ }
449
503
 
450
- return {
451
- id: m.id,
452
- name: m.id,
453
- provider: this.provider,
454
- owner: owner,
455
- type: modelType,
456
- input_modalities: modelModalitiesToArray(modelCapability.input),
457
- output_modalities: modelModalitiesToArray(modelCapability.output),
458
- tool_support: modelCapability.tool_support,
459
- } satisfies AIModel<string>;
460
- }).sort((a, b) => a.id.localeCompare(b.id));
504
+ return {
505
+ id: m.id,
506
+ name: m.id,
507
+ provider: this.provider,
508
+ owner: owner,
509
+ type: modelType,
510
+ input_modalities: modelModalitiesToArray(modelCapability.input),
511
+ output_modalities: modelModalitiesToArray(modelCapability.output),
512
+ tool_support: modelCapability.tool_support,
513
+ } satisfies AIModel<string>;
514
+ })
515
+ .sort((a, b) => a.id.localeCompare(b.id));
461
516
 
462
517
  return aiModels;
463
518
  }
464
519
 
520
+ async generateEmbeddings(options: EmbeddingsOptions): Promise<EmbeddingsResult> {
521
+ const normalized = normalizeEmbeddingsOptions(options);
522
+ const model = normalized.model ?? OPENAI_DEFAULT_EMBEDDING_MODEL;
465
523
 
466
- async generateEmbeddings({ text, image, model = "text-embedding-3-small" }: EmbeddingsOptions): Promise<EmbeddingsResult> {
467
-
468
- if (image) {
469
- throw new Error("Image embeddings not supported by OpenAI");
470
- }
524
+ const texts: string[] = normalized.inputs.map((input) => {
525
+ if (input.type !== 'text') {
526
+ throw new Error(
527
+ `Provider 'openai' does not support '${input.type}' embeddings; only 'text' is supported.`,
528
+ );
529
+ }
530
+ return input.text;
531
+ });
471
532
 
472
- if (!text) {
473
- throw new Error("No text provided");
474
- }
533
+ try {
534
+ const res = await this.service.embeddings.create({
535
+ input: texts,
536
+ model,
537
+ ...(normalized.dimensions ? { dimensions: normalized.dimensions } : {}),
538
+ encoding_format: 'float',
539
+ });
475
540
 
476
- const res = await this.service.embeddings.create({
477
- input: text,
478
- model: model,
479
- });
541
+ // OpenAI does not guarantee data is returned in the same order as the input,
542
+ // but does return a stable `index` per item. Sort by index to align with inputs.
543
+ const ordered = [...res.data].sort((a, b) => a.index - b.index);
544
+ const items = ordered.map((entry): EmbeddingResultItem => {
545
+ if (!entry.embedding || entry.embedding.length === 0) {
546
+ throw new Error(`OpenAI embedding empty for input index ${entry.index}`);
547
+ }
548
+ return { outputs: [{ values: entry.embedding, modality: 'text' }] };
549
+ });
480
550
 
481
- const embeddings = res.data[0].embedding;
551
+ const usage = res.usage
552
+ ? { input_tokens: res.usage.prompt_tokens, input_text_tokens: res.usage.prompt_tokens }
553
+ : undefined;
482
554
 
483
- if (!embeddings || embeddings.length === 0) {
484
- throw new Error("No embedding found");
555
+ return { model, results: items, usage };
556
+ } catch (error) {
557
+ if (LlumiverseError.isLlumiverseError(error)) throw error;
558
+ if (error instanceof Error && !hasNumericStatus(error)) throw error;
559
+ throw this.formatLlumiverseError(error, {
560
+ provider: this.provider,
561
+ model,
562
+ operation: 'execute',
563
+ });
485
564
  }
486
-
487
- return { values: embeddings, model } satisfies EmbeddingsResult;
488
565
  }
489
566
 
490
- imageModels = ["dall-e", "gpt-image", "chatgpt-image"];
567
+ imageModels = ['dall-e', 'gpt-image', 'chatgpt-image'];
491
568
 
492
569
  /**
493
570
  * Determine if a model is specifically an image generation model (not conversational image model)
@@ -495,7 +572,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
495
572
  isImageModel(model: string): boolean {
496
573
  // DALL-E models are standalone image generation
497
574
  // gpt-image models can generate images in conversations, not standalone
498
- return this.imageModels.some(imageModel => model.includes(imageModel));
575
+ return this.imageModels.some((imageModel) => model.includes(imageModel));
499
576
  }
500
577
 
501
578
  /**
@@ -508,15 +585,15 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
508
585
  const model_options = options.model_options as OpenAiDalleOptions | OpenAiGptImageOptions | undefined;
509
586
 
510
587
  // Extract prompt text from ResponseInputItem[]
511
- let promptText = "";
588
+ let promptText = '';
512
589
  for (const item of prompt) {
513
590
  if ('content' in item && typeof item.content === 'string') {
514
- promptText += item.content + "\\n";
591
+ promptText += `${item.content}\\n`;
515
592
  } else if ('content' in item && Array.isArray(item.content)) {
516
593
  // Extract text from content array
517
594
  for (const part of item.content) {
518
595
  if ('type' in part && part.type === 'input_text' && 'text' in part) {
519
- promptText += part.text + "\\n";
596
+ promptText += `${part.text}\\n`;
520
597
  }
521
598
  }
522
599
  }
@@ -527,17 +604,17 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
527
604
  const generateParams: OpenAI.Images.ImageGenerateParamsNonStreaming = {
528
605
  model: options.model,
529
606
  prompt: promptText,
530
- size: model_options?.size || "1024x1024",
607
+ size: model_options?.size || '1024x1024',
531
608
  };
532
609
 
533
610
  // Add DALL-E specific options
534
- if (options.model.includes("dall-e") || model_options?._option_id === "openai-dalle") {
611
+ if (options.model.includes('dall-e') || model_options?._option_id === 'openai-dalle') {
535
612
  const dalleOptions = model_options as OpenAiDalleOptions | undefined;
536
613
  generateParams.n = dalleOptions?.n || 1;
537
- generateParams.response_format = dalleOptions?.response_format || "b64_json";
614
+ generateParams.response_format = dalleOptions?.response_format || 'b64_json';
538
615
 
539
- if (options.model.includes("dall-e-3")) {
540
- generateParams.quality = dalleOptions?.image_quality || "standard";
616
+ if (options.model.includes('dall-e-3')) {
617
+ generateParams.quality = dalleOptions?.image_quality || 'standard';
541
618
  if (dalleOptions?.style) {
542
619
  generateParams.style = dalleOptions.style;
543
620
  }
@@ -567,31 +644,35 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
567
644
  }
568
645
 
569
646
  results.push({
570
- type: "image",
571
- value: imageValue
647
+ type: 'image',
648
+ value: imageValue,
572
649
  });
573
650
  }
574
651
  }
575
652
 
576
653
  return {
577
- result: results
654
+ result: results,
578
655
  };
579
-
580
- } catch (error: any) {
656
+ } catch (error: unknown) {
581
657
  this.logger.error({ error }, `[${this.provider}] Image generation failed`);
658
+ const generationError = error instanceof Error ? error : new Error(String(error));
659
+ const errorCode =
660
+ (error as { code?: unknown })?.code === 'content_policy_violation'
661
+ ? 'content_policy_violation'
662
+ : 'validation_error';
582
663
  return {
583
664
  result: [],
584
665
  error: {
585
- message: error.message,
586
- code: error.code || 'GENERATION_FAILED'
587
- }
666
+ message: generationError.message,
667
+ code: errorCode,
668
+ },
588
669
  };
589
670
  }
590
671
  }
591
672
 
592
673
  /**
593
674
  * Format OpenAI API errors into LlumiverseError with proper status codes and retryability.
594
- *
675
+ *
595
676
  * OpenAI API errors have a specific structure:
596
677
  * - APIError.status: HTTP status code (400, 401, 403, 404, 409, 422, 429, 500+)
597
678
  * - APIError.error: Error object with type, message, param, code
@@ -599,7 +680,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
599
680
  * - APIError.code: Error code (e.g., 'invalid_api_key', 'rate_limit_exceeded')
600
681
  * - APIError.param: Parameter that caused the error (optional)
601
682
  * - APIError.type: Error type (optional)
602
- *
683
+ *
603
684
  * Common error types:
604
685
  * - BadRequestError (400): Invalid request parameters
605
686
  * - AuthenticationError (401): Invalid API key
@@ -613,20 +694,17 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
613
694
  * - APIConnectionTimeoutError: Request timeout (no status code)
614
695
  * - LengthFinishReasonError: Response truncated due to length
615
696
  * - ContentFilterFinishReasonError: Content filtered
616
- *
697
+ *
617
698
  * This implementation works for:
618
699
  * - OpenAI API
619
700
  * - Azure OpenAI
620
701
  * - xAI (uses OpenAI-compatible API)
621
702
  * - Azure Foundry (OpenAI-compatible)
622
703
  * - Other OpenAI-compatible APIs
623
- *
704
+ *
624
705
  * @see https://platform.openai.com/docs/guides/error-codes
625
706
  */
626
- public formatLlumiverseError(
627
- error: unknown,
628
- context: LlumiverseErrorContext
629
- ): LlumiverseError {
707
+ public formatLlumiverseError(error: unknown, context: LlumiverseErrorContext): LlumiverseError {
630
708
  // Check if it's an OpenAI API error
631
709
  const isOpenAIError = this.isOpenAIApiError(error);
632
710
 
@@ -681,7 +759,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
681
759
  context,
682
760
  error,
683
761
  httpStatusCode,
684
- errorName
762
+ errorName,
685
763
  );
686
764
  }
687
765
 
@@ -690,22 +768,20 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
690
768
  */
691
769
  private isOpenAIApiError(error: unknown): error is APIError | OpenAIError {
692
770
  return (
693
- error !== null &&
694
- typeof error === 'object' &&
695
- (error instanceof APIError || error instanceof OpenAIError)
771
+ error !== null && typeof error === 'object' && (error instanceof APIError || error instanceof OpenAIError)
696
772
  );
697
773
  }
698
774
 
699
775
  /**
700
776
  * Determine if an OpenAI API error is retryable.
701
- *
777
+ *
702
778
  * Retryable errors:
703
779
  * - RateLimitError (429): Rate limit exceeded, retry with backoff
704
780
  * - InternalServerError (500+): Server-side errors
705
781
  * - APIConnectionTimeoutError: Request timeout
706
782
  * - Error codes: 'timeout', 'server_error', 'service_unavailable'
707
783
  * - Status codes: 408, 429, 502, 503, 504, 529, 5xx
708
- *
784
+ *
709
785
  * Non-retryable errors:
710
786
  * - BadRequestError (400): Invalid request parameters
711
787
  * - AuthenticationError (401): Invalid API key
@@ -717,7 +793,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
717
793
  * - ContentFilterFinishReasonError: Content filtered
718
794
  * - Error codes: 'invalid_api_key', 'invalid_request_error', 'model_not_found'
719
795
  * - Other 4xx client errors
720
- *
796
+ *
721
797
  * @param error - The error object
722
798
  * @param httpStatusCode - The HTTP status code if available
723
799
  * @param errorCode - The error code if available
@@ -728,7 +804,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
728
804
  error: unknown,
729
805
  httpStatusCode: number | undefined,
730
806
  errorCode: string | null | undefined,
731
- errorType: string | undefined
807
+ errorType: string | undefined,
732
808
  ): boolean | undefined {
733
809
  // Check specific OpenAI error types by class
734
810
  if (error instanceof RateLimitError) return true;
@@ -759,7 +835,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
759
835
  if (errorCode === 'model_not_found') return false;
760
836
  if (errorCode === 'insufficient_quota') return false;
761
837
  if (errorCode === 'invalid_model') return false;
762
- if (errorCode.includes('invalid_')) return false;
838
+ if (typeof errorCode === 'string' && errorCode.includes('invalid_')) return false;
763
839
  }
764
840
 
765
841
  // Check error type
@@ -787,10 +863,8 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
787
863
  // Unknown error type - let consumer decide retry strategy
788
864
  return undefined;
789
865
  }
790
-
791
866
  }
792
867
 
793
-
794
868
  function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
795
869
  //validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`.
796
870
  const jobStatus = job.status;
@@ -800,7 +874,9 @@ function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
800
874
  status = TrainingJobStatus.succeeded;
801
875
  } else if (jobStatus === 'failed') {
802
876
  status = TrainingJobStatus.failed;
803
- details = job.error ? `${job.error.code} - ${job.error.message} ${job.error.param ? " [" + job.error.param + "]" : ""}` : "error";
877
+ details = job.error
878
+ ? `${job.error.code} - ${job.error.message} ${job.error.param ? ` [${job.error.param}]` : ''}`
879
+ : 'error';
804
880
  } else if (jobStatus === 'cancelled') {
805
881
  status = TrainingJobStatus.cancelled;
806
882
  } else {
@@ -811,20 +887,22 @@ function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
811
887
  id: job.id,
812
888
  model: job.fine_tuned_model || undefined,
813
889
  status,
814
- details
815
- }
890
+ details,
891
+ };
816
892
  }
817
893
 
818
- function mapUsage(usage?: OpenAI.Responses.ResponseUsage | null): ExecutionTokenUsage | undefined {
894
+ function mapUsage(usage?: OpenAIUsageWithProviderDetails | null): ExecutionTokenUsage | undefined {
819
895
  if (!usage) {
820
896
  return undefined;
821
897
  }
898
+ const cachedTokens =
899
+ usage.input_tokens_details?.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens;
822
900
  return {
823
901
  prompt: usage.input_tokens,
824
902
  result: usage.output_tokens,
825
903
  total: usage.total_tokens,
826
- prompt_cached: usage.input_tokens_details?.cached_tokens ?? undefined,
827
- prompt_new: usage.input_tokens - (usage.input_tokens_details?.cached_tokens ?? 0),
904
+ prompt_cached: cachedTokens ?? undefined,
905
+ prompt_new: usage.input_tokens - (cachedTokens ?? 0),
828
906
  };
829
907
  }
830
908
 
@@ -833,7 +911,7 @@ function completionResultsToText(completionResults: CompletionResult[] | undefin
833
911
  return '';
834
912
  }
835
913
  return completionResults
836
- .map(r => {
914
+ .map((r) => {
837
915
  switch (r.type) {
838
916
  case 'text':
839
917
  return r.value;
@@ -842,8 +920,10 @@ function completionResultsToText(completionResults: CompletionResult[] | undefin
842
920
  case 'image':
843
921
  // Skip images in conversation - they're in the result
844
922
  return '';
845
- default:
846
- return String((r as any).value || '');
923
+ default: {
924
+ const _exhaustive: never = r;
925
+ return String(_exhaustive);
926
+ }
847
927
  }
848
928
  })
849
929
  .join('');
@@ -856,6 +936,7 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
856
936
  // Add assistant text message if present
857
937
  if (textContent) {
858
938
  const assistantMessage: EasyInputMessage = {
939
+ type: 'message',
859
940
  role: 'assistant',
860
941
  content: textContent,
861
942
  };
@@ -869,9 +950,7 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
869
950
  type: 'function_call',
870
951
  call_id: t.id,
871
952
  name: t.tool_name,
872
- arguments: typeof t.tool_input === 'string'
873
- ? t.tool_input
874
- : JSON.stringify(t.tool_input ?? {}),
953
+ arguments: typeof t.tool_input === 'string' ? t.tool_input : JSON.stringify(t.tool_input ?? {}),
875
954
  };
876
955
  result.push(functionCall);
877
956
  }
@@ -880,11 +959,15 @@ function createAssistantMessageFromCompletion(completion: Completion): ResponseI
880
959
  return result;
881
960
  }
882
961
 
883
- export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>): AsyncIterable<CompletionChunkObject> {
884
- const toolCallMetadata = new Map<string, { syntheticId: string, callId?: string, name?: string }>();
962
+ export function mapResponseStream(
963
+ stream: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>,
964
+ ): AsyncIterable<CompletionChunkObject> {
965
+ const toolCallMetadata = new Map<string, { syntheticId: string; callId?: string; name?: string }>();
885
966
 
886
967
  return {
887
968
  async *[Symbol.asyncIterator]() {
969
+ let hasTextDeltas = false;
970
+ let refusalText = '';
888
971
  for await (const event of stream) {
889
972
  if (event.type === 'response.output_item.added' && event.item.type === 'function_call') {
890
973
  const syntheticId = `tool_${event.output_index}`;
@@ -896,11 +979,11 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
896
979
  if (event.item.call_id) {
897
980
  toolCallMetadata.set(event.item.call_id, metadata);
898
981
  }
899
- const toolUse: ToolUse & { _actual_id?: string } = {
982
+ const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
900
983
  id: syntheticId,
901
984
  _actual_id: callId,
902
985
  tool_name: event.item.name,
903
- tool_input: '' as any,
986
+ tool_input: '',
904
987
  };
905
988
  yield {
906
989
  result: [],
@@ -910,11 +993,11 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
910
993
  const metadata = toolCallMetadata.get(event.item_id);
911
994
  const syntheticId = metadata?.syntheticId ?? `tool_${event.output_index}`;
912
995
  const callId = metadata?.callId ?? event.item_id;
913
- const toolUse: ToolUse & { _actual_id?: string } = {
996
+ const toolUse: ToolUse<unknown> & { _actual_id?: string } = {
914
997
  id: syntheticId,
915
998
  _actual_id: callId,
916
999
  tool_name: metadata?.name ?? '',
917
- tool_input: event.delta as any,
1000
+ tool_input: event.delta,
918
1001
  };
919
1002
  yield {
920
1003
  result: [],
@@ -933,13 +1016,33 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
933
1016
  toolCallMetadata.set(event.item_id, { syntheticId, callId: metadata?.callId, name: tool_name });
934
1017
  }
935
1018
  } else if (event.type === 'response.output_text.delta') {
1019
+ hasTextDeltas = true;
936
1020
  yield {
937
1021
  result: textToCompletionResult(event.delta),
938
1022
  } satisfies CompletionChunkObject;
939
- }
940
- // Note: We don't emit response.output_text.done because the text was already
941
- // streamed via delta events. Emitting it again would duplicate the content.
942
- else if (event.type === 'response.completed' || event.type === 'response.incomplete' || event.type === 'response.failed') {
1023
+ } else if (event.type === 'response.output_text.done') {
1024
+ // Fallback: some models (e.g. gpt-5 with json_schema structured output) buffer
1025
+ // the entire output and never emit delta events only the done event with the
1026
+ // full text. Emit it here only when no deltas arrived to avoid duplication.
1027
+ if (!hasTextDeltas && event.text) {
1028
+ yield {
1029
+ result: textToCompletionResult(event.text),
1030
+ } satisfies CompletionChunkObject;
1031
+ }
1032
+ } else if (event.type === 'response.refusal.delta') {
1033
+ refusalText += event.delta;
1034
+ } else if (event.type === 'response.refusal.done') {
1035
+ throw new Error(`[OpenAI] Model refused: ${event.refusal || refusalText}`);
1036
+ } else if ((event as { type: string }).type === 'response.error') {
1037
+ const errEvent = event as unknown as { message: string; code?: string | null };
1038
+ throw new Error(
1039
+ `[OpenAI Responses API] ${errEvent.message}${errEvent.code ? ` (${errEvent.code})` : ''}`,
1040
+ );
1041
+ } else if (
1042
+ event.type === 'response.completed' ||
1043
+ event.type === 'response.incomplete' ||
1044
+ event.type === 'response.failed'
1045
+ ) {
943
1046
  const finalTools = collectTools(event.response.output);
944
1047
  yield {
945
1048
  result: [],
@@ -948,12 +1051,12 @@ export function mapResponseStream(stream: AsyncIterable<OpenAI.Responses.Respons
948
1051
  } satisfies CompletionChunkObject;
949
1052
  }
950
1053
  }
951
- }
1054
+ },
952
1055
  };
953
1056
  }
954
1057
 
955
1058
  function insert_image_detail(items: ResponseInputItem[], detail_level: string): ResponseInputItem[] {
956
- if (detail_level === "auto" || detail_level === "low" || detail_level === "high") {
1059
+ if (detail_level === 'auto' || detail_level === 'low' || detail_level === 'high') {
957
1060
  for (const item of items) {
958
1061
  // Check if it's an EasyInputMessage or Message with content array
959
1062
  if ('role' in item && 'content' in item && item.role !== 'assistant') {
@@ -961,7 +1064,7 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
961
1064
  if (Array.isArray(content)) {
962
1065
  for (const part of content) {
963
1066
  if (typeof part === 'object' && part.type === 'input_image') {
964
- (part as any).detail = detail_level;
1067
+ (part as MutableInputImagePart).detail = detail_level;
965
1068
  }
966
1069
  }
967
1070
  }
@@ -973,19 +1076,19 @@ function insert_image_detail(items: ResponseInputItem[], detail_level: string):
973
1076
 
974
1077
  function convertRoles(items: ResponseInputItem[], model: string): ResponseInputItem[] {
975
1078
  //New openai models use developer role instead of system
976
- if (model.includes("o1") || model.includes("o3")) {
977
- if (model.includes("o1-mini") || model.includes("o1-preview")) {
1079
+ if (model.includes('o1') || model.includes('o3')) {
1080
+ if (model.includes('o1-mini') || model.includes('o1-preview')) {
978
1081
  //o1-mini and o1-preview support neither system nor developer
979
1082
  for (const item of items) {
980
1083
  if ('role' in item && (item as EasyInputMessage).role === 'system') {
981
- (item as any).role = 'user';
1084
+ (item as MutableRoleItem).role = 'user';
982
1085
  }
983
1086
  }
984
1087
  } else {
985
1088
  //Models newer than o1 use developer role
986
1089
  for (const item of items) {
987
1090
  if ('role' in item && (item as EasyInputMessage).role === 'system') {
988
- (item as any).role = 'developer';
1091
+ (item as MutableRoleItem).role = 'developer';
989
1092
  }
990
1093
  }
991
1094
  }
@@ -995,12 +1098,12 @@ function convertRoles(items: ResponseInputItem[], model: string): ResponseInputI
995
1098
 
996
1099
  //Structured output support is typically aligned with tool use support
997
1100
  //Not true for realtime models, which do not support structured output, but do support tool use.
998
- function supportsSchema(model: string): boolean {
999
- const realtimeModel = model.includes("realtime");
1101
+ function supportsSchema(model: string, provider: string | Providers): boolean {
1102
+ const realtimeModel = model.includes('realtime');
1000
1103
  if (realtimeModel) {
1001
1104
  return false;
1002
1105
  }
1003
- return supportsToolUse(model, "openai");
1106
+ return supportsToolUse(model, provider);
1004
1107
  }
1005
1108
 
1006
1109
  /**
@@ -1009,17 +1112,17 @@ function supportsSchema(model: string): boolean {
1009
1112
  * tools to be defined in the API request.
1010
1113
  */
1011
1114
  export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): ResponseInputItem[] {
1012
- const hasFunctionItems = items.some(item => {
1013
- const type = (item as any).type;
1115
+ const hasFunctionItems = items.some((item) => {
1116
+ const type = (item as OpenAIFunctionItem).type;
1014
1117
  return type === 'function_call' || type === 'function_call_output';
1015
1118
  });
1016
1119
  if (!hasFunctionItems) return items;
1017
1120
 
1018
- return items.map(item => {
1019
- const typed = item as any;
1121
+ return items.map((item) => {
1122
+ const typed = item as OpenAIFunctionItem;
1020
1123
  if (typed.type === 'function_call') {
1021
1124
  const argsStr = typed.arguments || '';
1022
- const truncated = argsStr.length > 500 ? argsStr.substring(0, 500) + '...' : argsStr;
1125
+ const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
1023
1126
  return {
1024
1127
  role: 'assistant' as const,
1025
1128
  content: `[Tool call: ${typed.name}(${truncated})]`,
@@ -1027,7 +1130,7 @@ export function convertOpenAIFunctionItemsToText(items: ResponseInputItem[]): Re
1027
1130
  }
1028
1131
  if (typed.type === 'function_call_output') {
1029
1132
  const output = typed.output || 'No output';
1030
- const truncated = output.length > 500 ? output.substring(0, 500) + '...' : output;
1133
+ const truncated = output.length > 500 ? `${output.substring(0, 500)}...` : output;
1031
1134
  return {
1032
1135
  role: 'user' as const,
1033
1136
  content: `[Tool result: ${truncated}]`,
@@ -1041,15 +1144,14 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): OpenAI.
1041
1144
  return tools ? tools.map(getToolDefinition) : undefined;
1042
1145
  }
1043
1146
  function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTool {
1044
- let parsedSchema: JSONSchema | undefined = undefined;
1147
+ let parsedSchema: JSONSchema | undefined;
1045
1148
  let strictMode = false;
1046
1149
  if (toolDef.input_schema) {
1047
1150
  try {
1048
1151
  //TODO: type assertion here is not safe, does not work with satisfies
1049
1152
  parsedSchema = openAISchemaFormat(toolDef.input_schema as JSONSchema);
1050
1153
  strictMode = true;
1051
- }
1052
- catch (e) {
1154
+ } catch {
1053
1155
  //TODO: type assertion here is not safe, does not work with satisfies
1054
1156
  parsedSchema = limitedSchemaFormat(toolDef.input_schema as JSONSchema);
1055
1157
  strictMode = false;
@@ -1057,7 +1159,7 @@ function getToolDefinition(toolDef: ToolDefinition): OpenAI.Responses.FunctionTo
1057
1159
  }
1058
1160
 
1059
1161
  return {
1060
- type: "function",
1162
+ type: 'function',
1061
1163
  name: toolDef.name,
1062
1164
  description: toolDef.description,
1063
1165
  parameters: parsedSchema ?? null,
@@ -1069,7 +1171,7 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
1069
1171
  if (!items) {
1070
1172
  // Unwrap array if wrapped, otherwise treat as array
1071
1173
  const unwrapped = unwrapConversationArray<ResponseInputItem>(conversation);
1072
- return unwrapped ?? (conversation as ResponseInputItem[] || []);
1174
+ return unwrapped ?? ((conversation as ResponseInputItem[]) || []);
1073
1175
  }
1074
1176
  if (!conversation) {
1075
1177
  return items;
@@ -1080,12 +1182,12 @@ function updateConversation(conversation: unknown, items: ResponseInputItem[]):
1080
1182
  return [...convArray, ...items];
1081
1183
  }
1082
1184
 
1083
- export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse[] | undefined {
1185
+ export function collectTools(output?: OpenAI.Responses.ResponseOutputItem[]): ToolUse<unknown>[] | undefined {
1084
1186
  if (!output) {
1085
1187
  return undefined;
1086
1188
  }
1087
1189
 
1088
- const tools: ToolUse[] = [];
1190
+ const tools: ToolUse<unknown>[] = [];
1089
1191
  for (const item of output) {
1090
1192
  if (item.type === 'function_call') {
1091
1193
  const id = item.call_id || item.id;
@@ -1118,8 +1220,8 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
1118
1220
  for (const part of item.content) {
1119
1221
  if (part.type === 'output_text' && part.text) {
1120
1222
  results.push({
1121
- type: "text",
1122
- value: part.text
1223
+ type: 'text',
1224
+ value: part.text,
1123
1225
  });
1124
1226
  }
1125
1227
  }
@@ -1127,12 +1229,10 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
1127
1229
  // GPT-image models return base64 encoded images in result field
1128
1230
  const base64Data = item.result;
1129
1231
  // Format as data URL for consistency with other image outputs
1130
- const imageUrl = base64Data.startsWith('data:')
1131
- ? base64Data
1132
- : `data:image/png;base64,${base64Data}`;
1232
+ const imageUrl = base64Data.startsWith('data:') ? base64Data : `data:image/png;base64,${base64Data}`;
1133
1233
  results.push({
1134
- type: "image",
1135
- value: imageUrl
1234
+ type: 'image',
1235
+ value: imageUrl,
1136
1236
  });
1137
1237
  }
1138
1238
  }
@@ -1141,7 +1241,7 @@ function extractCompletionResults(output?: OpenAI.Responses.ResponseOutputItem[]
1141
1241
 
1142
1242
  //For strict mode false
1143
1243
  function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
1144
- const formattedSchema = { ...schema };
1244
+ const formattedSchema: JSONSchema = { ...schema };
1145
1245
 
1146
1246
  // Defaults not supported
1147
1247
  delete formattedSchema.default;
@@ -1182,16 +1282,16 @@ function limitedSchemaFormat(schema: JSONSchema): JSONSchema {
1182
1282
  //For strict mode true
1183
1283
  function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema {
1184
1284
  if (nesting > 5) {
1185
- throw new Error("OpenAI schema nesting too deep");
1285
+ throw new Error('OpenAI schema nesting too deep');
1186
1286
  }
1187
1287
 
1188
- const formattedSchema = { ...schema };
1288
+ const formattedSchema: JSONSchema = { ...schema };
1189
1289
 
1190
1290
  // Defaults not supported
1191
1291
  delete formattedSchema.default;
1192
1292
 
1193
1293
  // Additional properties not supported, required to be set.
1194
- if (formattedSchema?.type === "object") {
1294
+ if (formattedSchema?.type === 'object') {
1195
1295
  formattedSchema.additionalProperties = false;
1196
1296
  }
1197
1297
 
@@ -1220,18 +1320,24 @@ function openAISchemaFormat(schema: JSONSchema, nesting: number = 0): JSONSchema
1220
1320
  }
1221
1321
  }
1222
1322
  }
1223
- if (formattedSchema?.type === 'object' && (!formattedSchema?.properties || Object.keys(formattedSchema?.properties ?? {}).length == 0)) {
1323
+ if (
1324
+ formattedSchema?.type === 'object' &&
1325
+ (!formattedSchema?.properties || Object.keys(formattedSchema?.properties ?? {}).length === 0)
1326
+ ) {
1224
1327
  //If no properties are defined, then additionalProperties: true was set or the object would be empty.
1225
1328
  //OpenAI does not support this on structured output/ strict mode.
1226
- throw new Error("OpenAI does not support empty objects or objects with additionalProperties set to true");
1329
+ throw new Error('OpenAI does not support empty objects or objects with additionalProperties set to true');
1227
1330
  }
1228
1331
 
1229
- return formattedSchema
1332
+ return formattedSchema;
1230
1333
  }
1231
1334
 
1232
- function responseFinishReason(response: OpenAI.Responses.Response, tools?: ToolUse[] | undefined): string | undefined {
1335
+ function responseFinishReason(
1336
+ response: OpenAI.Responses.Response,
1337
+ tools?: ToolUse<unknown>[] | undefined,
1338
+ ): string | undefined {
1233
1339
  if (tools && tools.length > 0) {
1234
- return "tool_use";
1340
+ return 'tool_use';
1235
1341
  }
1236
1342
  if (response.status === 'incomplete') {
1237
1343
  if (response.incomplete_details?.reason === 'max_output_tokens') {
@@ -1310,7 +1416,31 @@ export function fixOrphanedToolUse(items: ResponseInputItem[]): ResponseInputIte
1310
1416
  return result;
1311
1417
  }
1312
1418
 
1313
- function safeJsonParse(value: unknown): any {
1419
+ /**
1420
+ * Drop function_call_output items whose call_id has no matching function_call
1421
+ * item anywhere in the input. Mirror of {@link fixOrphanedToolUse}: that
1422
+ * synthesizes outputs for calls left unanswered (e.g. a cancelled run); this
1423
+ * removes outputs left dangling after their function_call was dropped (e.g. by
1424
+ * conversation compaction/trimming). The OpenAI Responses API requires every
1425
+ * function_call_output to reference a prior function_call's call_id.
1426
+ */
1427
+ export function fixOrphanedToolResults(items: ResponseInputItem[]): ResponseInputItem[] {
1428
+ if (items.length === 0) return items;
1429
+ const callIds = new Set<string>();
1430
+ for (const item of items) {
1431
+ if ('type' in item && item.type === 'function_call') {
1432
+ callIds.add(item.call_id);
1433
+ }
1434
+ }
1435
+ return items.filter((item) => {
1436
+ if ('type' in item && item.type === 'function_call_output') {
1437
+ return callIds.has(item.call_id);
1438
+ }
1439
+ return true;
1440
+ });
1441
+ }
1442
+
1443
+ function safeJsonParse(value: unknown): unknown {
1314
1444
  if (typeof value !== 'string') {
1315
1445
  return value;
1316
1446
  }