@llumiverse/drivers 1.3.0 → 1.4.0-dev.20260629.090753Z

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,42 +1,46 @@
1
1
  import {
2
- type AIModel, type Completion, type CompletionChunkObject, type ExecutionOptions, type LlumiverseError,
2
+ type AIModel,
3
+ type Completion,
4
+ type CompletionChunkObject,
5
+ type ExecutionOptions,
6
+ type LlumiverseError,
3
7
  type LlumiverseErrorContext,
4
8
  ModelType,
5
9
  type PromptSegment,
6
10
  type VertexAIClaudeOptions,
7
- } from "@llumiverse/core";
8
- import type { ClaudePrompt } from "../../shared/claude-messages.js";
11
+ } from '@llumiverse/core';
12
+ import type { ClaudePrompt } from '../../shared/claude-messages.js';
9
13
  import {
10
14
  executeClaudeCompletion,
11
15
  formatAnthropicLlumiverseError,
12
16
  formatClaudePrompt,
13
17
  isClaudeErrorRetryable,
14
18
  streamClaudeCompletion,
15
- } from "../../shared/claude-messages.js";
19
+ } from '../../shared/claude-messages.js';
16
20
 
17
-
18
- import type { VertexAIDriver } from "../index.js";
19
- import type { ModelDefinition } from "../models.js";
21
+ import type { VertexAIDriver } from '../index.js';
22
+ import type { ModelDefinition } from '../models.js';
20
23
 
21
24
  export const ANTHROPIC_REGIONS: Record<string, string> = {
22
- us: "us-east5",
23
- europe: "europe-west1",
24
- global: "global",
25
- }
25
+ us: 'us-east5',
26
+ europe: 'europe-west1',
27
+ global: 'global',
28
+ };
26
29
 
27
- export const NON_GLOBAL_ANTHROPIC_MODELS = [
28
- "claude-3-5",
29
- "claude-3",
30
- ];
30
+ export const NON_GLOBAL_ANTHROPIC_MODELS = ['claude-3-5', 'claude-3'];
31
31
 
32
32
  /**
33
33
  * Parse a VertexAI model path (e.g. "locations/us-east5/claude-3-5-sonnet") into
34
34
  * its region and model name components.
35
35
  */
36
- function resolveVertexAIModelPath(options: ExecutionOptions): { modelName: string; region: string | undefined; options: ExecutionOptions } {
37
- const splits = options.model.split("/");
36
+ function resolveVertexAIModelPath(options: ExecutionOptions): {
37
+ modelName: string;
38
+ region: string | undefined;
39
+ options: ExecutionOptions;
40
+ } {
41
+ const splits = options.model.split('/');
38
42
  let region: string | undefined;
39
- if (splits[0] === "locations" && splits.length >= 2) {
43
+ if (splits[0] === 'locations' && splits.length >= 2) {
40
44
  region = splits[1];
41
45
  }
42
46
  const modelName = splits[splits.length - 1];
@@ -44,8 +48,7 @@ function resolveVertexAIModelPath(options: ExecutionOptions): { modelName: strin
44
48
  }
45
49
 
46
50
  export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
47
-
48
- model: AIModel
51
+ model: AIModel;
49
52
 
50
53
  constructor(modelId: string) {
51
54
  this.model = {
@@ -57,30 +60,46 @@ export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
57
60
  } satisfies AIModel;
58
61
  }
59
62
 
60
- async createPrompt(_driver: VertexAIDriver, segments: PromptSegment[], options: ExecutionOptions): Promise<ClaudePrompt> {
61
- return formatClaudePrompt(segments, options);
63
+ async createPrompt(
64
+ _driver: VertexAIDriver,
65
+ segments: PromptSegment[],
66
+ options: ExecutionOptions,
67
+ ): Promise<ClaudePrompt> {
68
+ return formatClaudePrompt(segments, options, _driver.logger);
62
69
  }
63
70
 
64
- async requestTextCompletion(driver: VertexAIDriver, prompt: ClaudePrompt, options: ExecutionOptions): Promise<Completion> {
71
+ async requestTextCompletion(
72
+ driver: VertexAIDriver,
73
+ prompt: ClaudePrompt,
74
+ options: ExecutionOptions,
75
+ ): Promise<Completion> {
65
76
  const { region, options: resolvedOptions } = resolveVertexAIModelPath(options);
66
- const client = await driver.getAnthropicClient(region);
77
+ const client = await driver.getAnthropicClient(region, resolvedOptions.httpTimeout);
67
78
  const model_options = resolvedOptions.model_options as VertexAIClaudeOptions | undefined;
68
- if (model_options?._option_id !== undefined &&
69
- model_options?._option_id !== "vertexai-claude" &&
70
- model_options?._option_id !== "text-fallback") {
71
- driver.logger.debug({ options: resolvedOptions.model_options }, "Unexpected option id");
79
+ if (
80
+ model_options?._option_id !== undefined &&
81
+ model_options?._option_id !== 'vertexai-claude' &&
82
+ model_options?._option_id !== 'text-fallback'
83
+ ) {
84
+ driver.logger.debug({ options: resolvedOptions.model_options }, 'Unexpected option id');
72
85
  }
73
86
  return executeClaudeCompletion(client, prompt, resolvedOptions);
74
87
  }
75
88
 
76
- async requestTextCompletionStream(driver: VertexAIDriver, prompt: ClaudePrompt, options: ExecutionOptions): Promise<AsyncIterable<CompletionChunkObject>> {
89
+ async requestTextCompletionStream(
90
+ driver: VertexAIDriver,
91
+ prompt: ClaudePrompt,
92
+ options: ExecutionOptions,
93
+ ): Promise<AsyncIterable<CompletionChunkObject>> {
77
94
  const { region, options: resolvedOptions } = resolveVertexAIModelPath(options);
78
- const client = await driver.getAnthropicClient(region);
95
+ const client = await driver.getAnthropicClient(region, resolvedOptions.httpTimeout);
79
96
  const model_options = resolvedOptions.model_options as VertexAIClaudeOptions | undefined;
80
- if (model_options?._option_id !== undefined &&
81
- model_options?._option_id !== "vertexai-claude" &&
82
- model_options?._option_id !== "text-fallback") {
83
- driver.logger.debug({ options: resolvedOptions.model_options }, "Unexpected option id");
97
+ if (
98
+ model_options?._option_id !== undefined &&
99
+ model_options?._option_id !== 'vertexai-claude' &&
100
+ model_options?._option_id !== 'text-fallback'
101
+ ) {
102
+ driver.logger.debug({ options: resolvedOptions.model_options }, 'Unexpected option id');
84
103
  }
85
104
  return streamClaudeCompletion(client, prompt, resolvedOptions);
86
105
  }
@@ -88,16 +107,12 @@ export class ClaudeModelDefinition implements ModelDefinition<ClaudePrompt> {
88
107
  isClaudeErrorRetryable(
89
108
  error: unknown,
90
109
  httpStatusCode: number | undefined,
91
- errorType: string | undefined
110
+ errorType: string | undefined,
92
111
  ): boolean | undefined {
93
112
  return isClaudeErrorRetryable(error, httpStatusCode, errorType);
94
113
  }
95
114
 
96
- formatLlumiverseError(
97
- _driver: VertexAIDriver,
98
- error: unknown,
99
- context: LlumiverseErrorContext
100
- ): LlumiverseError {
115
+ formatLlumiverseError(_driver: VertexAIDriver, error: unknown, context: LlumiverseErrorContext): LlumiverseError {
101
116
  return formatAnthropicLlumiverseError(error, context);
102
117
  }
103
118
  }
@@ -13,10 +13,10 @@
13
13
  * Fix: use a local `payloadContents` variable so the caller's conversation is never mutated.
14
14
  */
15
15
 
16
- import { ExecutionOptions } from '@llumiverse/core';
17
16
  import { FinishReason } from '@google/genai';
18
- import { describe, expect, it } from 'vitest';
19
- import { VertexAIDriver } from '../index.js';
17
+ import { type DataSource, type ExecutionOptions, PromptRole, type PromptSegment } from '@llumiverse/core';
18
+ import { describe, expect, it, vi } from 'vitest';
19
+ import type { GenerateContentPrompt, VertexAIDriver } from '../index.js';
20
20
  import { convertGeminiFunctionPartsToText, GeminiModelDefinition } from './gemini.js';
21
21
 
22
22
  // ---------------------------------------------------------------------------
@@ -35,8 +35,8 @@ describe('convertGeminiFunctionPartsToText', () => {
35
35
  parts: [{ functionResponse: { name: 'plan', response: { output: 'done' } } }],
36
36
  },
37
37
  ];
38
- const originalItemRefs = original.map(c => c);
39
- const originalPartRefs = original.map(c => c.parts[0]);
38
+ const originalItemRefs = original.map((c) => c);
39
+ const originalPartRefs = original.map((c) => c.parts[0]);
40
40
 
41
41
  const result = convertGeminiFunctionPartsToText(original);
42
42
 
@@ -64,7 +64,7 @@ describe('convertGeminiFunctionPartsToText', () => {
64
64
 
65
65
  const result = convertGeminiFunctionPartsToText(contents);
66
66
 
67
- expect(result[0].parts![0]).toEqual({
67
+ expect(result[0].parts?.[0]).toEqual({
68
68
  text: '[Tool call: get_weather({"location":"Paris"})]',
69
69
  });
70
70
  });
@@ -79,7 +79,7 @@ describe('convertGeminiFunctionPartsToText', () => {
79
79
 
80
80
  const result = convertGeminiFunctionPartsToText(contents);
81
81
 
82
- expect(result[0].parts![0]).toEqual({
82
+ expect(result[0].parts?.[0]).toEqual({
83
83
  text: '[Tool result for get_weather: {"temperature":"15°C"}]',
84
84
  });
85
85
  });
@@ -90,7 +90,7 @@ describe('convertGeminiFunctionPartsToText', () => {
90
90
 
91
91
  const result = convertGeminiFunctionPartsToText(contents);
92
92
 
93
- expect(result[0].parts![0]).toBe(textPart);
93
+ expect(result[0].parts?.[0]).toBe(textPart);
94
94
  });
95
95
  });
96
96
 
@@ -105,7 +105,10 @@ function makeContentsWithFunctionParts() {
105
105
  ];
106
106
  }
107
107
 
108
- function makeDriver(overrides: { generateContent?: () => Promise<any>; generateContentStream?: () => Promise<AsyncIterable<any>> }) {
108
+ function makeDriver(overrides: {
109
+ generateContent?: () => Promise<unknown>;
110
+ generateContentStream?: () => Promise<AsyncIterable<unknown>>;
111
+ }) {
109
112
  return {
110
113
  logger: { warn: () => {}, info: () => {}, error: () => {} },
111
114
  getGoogleGenAIClient: () => ({
@@ -119,30 +122,64 @@ function makeDriver(overrides: { generateContent?: () => Promise<any>; generateC
119
122
 
120
123
  const mockNonStreamingResponse = {
121
124
  usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
122
- candidates: [{
123
- finishReason: FinishReason.STOP,
124
- content: { role: 'model', parts: [{ text: 'Summary.' }] },
125
- safetyRatings: [],
126
- }],
125
+ candidates: [
126
+ {
127
+ finishReason: FinishReason.STOP,
128
+ content: { role: 'model', parts: [{ text: 'Summary.' }] },
129
+ safetyRatings: [],
130
+ },
131
+ ],
127
132
  };
128
133
 
129
134
  const mockStreamingChunk = {
130
135
  usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
131
- candidates: [{
132
- finishReason: FinishReason.STOP,
133
- content: { role: 'model', parts: [{ text: 'Summary.' }] },
134
- safetyRatings: [],
135
- }],
136
+ candidates: [
137
+ {
138
+ finishReason: FinishReason.STOP,
139
+ content: { role: 'model', parts: [{ text: 'Summary.' }] },
140
+ safetyRatings: [],
141
+ },
142
+ ],
136
143
  };
137
144
 
138
145
  describe('GeminiModelDefinition - no conversation mutation', () => {
146
+ it('createPrompt uses DataSource URIs directly for Gemini file data', async () => {
147
+ const modelDef = new GeminiModelDefinition('gemini-2.0-flash');
148
+ const file: DataSource = {
149
+ name: 'doc.pdf',
150
+ mime_type: 'application/pdf',
151
+ getURI: vi.fn().mockResolvedValue('gs://test-bucket/doc.pdf'),
152
+ getURL: vi.fn().mockResolvedValue('https://signed.example/doc.pdf'),
153
+ getStream: vi.fn().mockResolvedValue(new ReadableStream()),
154
+ };
155
+ const segments: PromptSegment[] = [
156
+ {
157
+ role: PromptRole.user,
158
+ files: [file],
159
+ } as PromptSegment,
160
+ ];
161
+ const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash' };
162
+
163
+ const prompt = await modelDef.createPrompt({} as VertexAIDriver, segments, options);
164
+
165
+ expect(file.getURI).toHaveBeenCalledTimes(1);
166
+ expect(file.getURL).not.toHaveBeenCalled();
167
+ expect(file.getStream).not.toHaveBeenCalled();
168
+ expect(prompt.contents[0].parts?.[0]).toEqual({
169
+ fileData: {
170
+ fileUri: 'gs://test-bucket/doc.pdf',
171
+ mimeType: 'application/pdf',
172
+ },
173
+ });
174
+ });
175
+
139
176
  it('requestTextCompletion: does not mutate prompt.contents when tools=[] and conversation has function parts', async () => {
140
177
  const modelDef = new GeminiModelDefinition('gemini-2.0-flash');
141
178
  const originalContents = makeContentsWithFunctionParts();
142
179
  const contentsSnapshot = JSON.stringify(originalContents);
143
180
 
144
181
  const driver = makeDriver({ generateContent: async () => mockNonStreamingResponse });
145
- const prompt = { contents: originalContents, system: undefined } as any;
182
+ const prompt = { contents: originalContents, system: undefined } as unknown as GenerateContentPrompt;
146
183
  const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash', tools: [] };
147
184
 
148
185
  await modelDef.requestTextCompletion(driver, prompt, options);
@@ -158,14 +195,19 @@ describe('GeminiModelDefinition - no conversation mutation', () => {
158
195
  const contentsSnapshot = JSON.stringify(originalContents);
159
196
 
160
197
  const driver = makeDriver({
161
- generateContentStream: async () => (async function* () { yield mockStreamingChunk; })(),
198
+ generateContentStream: async () =>
199
+ (async function* () {
200
+ yield mockStreamingChunk;
201
+ })(),
162
202
  });
163
- const prompt = { contents: originalContents, system: undefined } as any;
203
+ const prompt = { contents: originalContents, system: undefined } as unknown as GenerateContentPrompt;
164
204
  const options: ExecutionOptions = { model: 'publishers/google/models/gemini-2.0-flash', tools: [] };
165
205
 
166
206
  const stream = await modelDef.requestTextCompletionStream(driver, prompt, options);
167
207
  // Drain the stream to trigger all processing
168
- for await (const _chunk of stream) { /* noop */ }
208
+ for await (const _chunk of stream) {
209
+ /* noop */
210
+ }
169
211
 
170
212
  expect(JSON.stringify(originalContents)).toBe(contentsSnapshot);
171
213
  expect(originalContents[0].parts[0]).toHaveProperty('functionCall');
@@ -1,8 +1,13 @@
1
1
  import { LlumiverseError } from '@llumiverse/core';
2
2
  import { beforeEach, describe, expect, it } from 'vitest';
3
+ import { exposePrivate, getProp } from '../../../test/__helpers__/test-utils.js';
3
4
  import { VertexAIDriver } from '../index.js';
4
5
  import { GeminiModelDefinition } from './gemini.js';
5
6
 
7
+ type GeminiModelInternals = {
8
+ isGeminiErrorRetryable: (httpStatusCode: number, message?: string) => boolean | undefined;
9
+ };
10
+
6
11
  describe('GeminiModelDefinition Error Handling', () => {
7
12
  let driver: VertexAIDriver;
8
13
  let modelDef: GeminiModelDefinition;
@@ -88,6 +93,33 @@ describe('GeminiModelDefinition Error Handling', () => {
88
93
  expect(error.name).toBe('NOT_FOUND');
89
94
  });
90
95
 
96
+ it('should mark a client-closed/aborted request (499) as retryable despite the 4xx status', () => {
97
+ const error = modelDef.formatLlumiverseError(
98
+ driver,
99
+ { status: 499, message: 'This operation was aborted' },
100
+ { provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
101
+ );
102
+ expect(error.retryable).toBe(true);
103
+ });
104
+
105
+ it('should mark a client-closed/cancelled request (499) as retryable despite the 4xx status', () => {
106
+ const error = modelDef.formatLlumiverseError(
107
+ driver,
108
+ { status: 499, message: 'Client Closed Request: The operation was cancelled.' },
109
+ { provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
110
+ );
111
+ expect(error.retryable).toBe(true);
112
+ });
113
+
114
+ it('should mark DEADLINE_EXCEEDED as retryable even under a 4xx status', () => {
115
+ const error = modelDef.formatLlumiverseError(
116
+ driver,
117
+ { status: 400, message: 'DEADLINE_EXCEEDED: Deadline expired before operation could complete' },
118
+ { provider: 'vertexai', model: 'gemini-2.0-flash', operation: 'execute' },
119
+ );
120
+ expect(error.retryable).toBe(true);
121
+ });
122
+
91
123
  it('should handle RESOURCE_EXHAUSTED error (429) as retryable', () => {
92
124
  const googleError = {
93
125
  status: 429,
@@ -202,7 +234,7 @@ describe('GeminiModelDefinition Error Handling', () => {
202
234
  });
203
235
 
204
236
  expect(error.originalError).toBe(googleError);
205
- expect((error.originalError as any).status).toBe(429);
237
+ expect(getProp<number>(error.originalError, 'status')).toBe(429);
206
238
  });
207
239
 
208
240
  it('should throw for non-Google API errors', () => {
@@ -247,6 +279,29 @@ describe('GeminiModelDefinition Error Handling', () => {
247
279
  expect(error.name).toBe('ValidationError');
248
280
  });
249
281
 
282
+ it('should mark URL_REJECTED-REJECTED_CLIENT_THROTTLED 400s as retryable', () => {
283
+ // Reproduces a real Vertex AI failure: passing an audio file by URL
284
+ // (sys:AnalyzeAudio) sometimes 400s with INVALID_ARGUMENT whose
285
+ // message contains URL_REJECTED-REJECTED_CLIENT_THROTTLED — that's
286
+ // really a transient throttle on Google's URL fetcher.
287
+ const googleError = {
288
+ status: 400,
289
+ message:
290
+ 'Cannot fetch content from the provided URL. Please ensure the URL is valid ' +
291
+ 'and accessible by Vertex AI. Vertex AI respects robots.txt rules, so confirm ' +
292
+ 'the URL is allowed to be crawled. Status: URL_REJECTED-REJECTED_CLIENT_THROTTLED',
293
+ };
294
+
295
+ const error = modelDef.formatLlumiverseError(driver, googleError, {
296
+ provider: 'vertexai',
297
+ model: 'gemini-2.5-flash',
298
+ operation: 'execute',
299
+ });
300
+
301
+ expect(error.code).toBe(400);
302
+ expect(error.retryable).toBe(true);
303
+ });
304
+
250
305
  it('should handle errors without extractable name', () => {
251
306
  const googleError = {
252
307
  status: 500,
@@ -263,14 +318,44 @@ describe('GeminiModelDefinition Error Handling', () => {
263
318
  expect(error.name).toBe('LlumiverseError');
264
319
  expect(error.code).toBe(500);
265
320
  });
321
+
322
+ it('should treat invalid_grant credential issuer failures as non-retryable', () => {
323
+ const authError = new Error("Error code invalid_grant: Error connecting to the given credential's issuer.");
324
+
325
+ const error = driver.formatLlumiverseError(authError, {
326
+ provider: 'vertexai',
327
+ model: 'gemini-2.5-flash',
328
+ operation: 'execute',
329
+ });
330
+
331
+ expect(error.retryable).toBe(false);
332
+ expect(error.code).toBeUndefined();
333
+ });
334
+
335
+ it('should preserve invalid_grant from Google API errors', () => {
336
+ const googleError = {
337
+ status: 400,
338
+ message: "Error code invalid_grant: Error connecting to the given credential's issuer.",
339
+ };
340
+
341
+ const error = modelDef.formatLlumiverseError(driver, googleError, {
342
+ provider: 'vertexai',
343
+ model: 'locations/global/publishers/google/models/gemini-2.5-flash',
344
+ operation: 'execute',
345
+ });
346
+
347
+ expect(error.retryable).toBe(false);
348
+ expect(error.code).toBe(400);
349
+ expect(error.name).toBe('invalid_grant');
350
+ });
266
351
  });
267
352
 
268
353
  describe('isGeminiErrorRetryable', () => {
269
354
  it('should classify retryable status codes correctly', () => {
270
- const retryableStatusCodes = [408, 429, 500, 502, 503, 504];
355
+ const retryableStatusCodes = [408, 429, 499, 500, 502, 503, 504];
271
356
 
272
357
  retryableStatusCodes.forEach((statusCode) => {
273
- const result = (modelDef as any).isGeminiErrorRetryable(statusCode);
358
+ const result = exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(statusCode);
274
359
  expect(result, `Status code ${statusCode} should be retryable`).toBe(true);
275
360
  });
276
361
  });
@@ -279,21 +364,52 @@ describe('GeminiModelDefinition Error Handling', () => {
279
364
  const nonRetryableStatusCodes = [400, 401, 403, 404, 409];
280
365
 
281
366
  nonRetryableStatusCodes.forEach((statusCode) => {
282
- const result = (modelDef as any).isGeminiErrorRetryable(statusCode);
367
+ const result = exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(statusCode);
283
368
  expect(result, `Status code ${statusCode} should not be retryable`).toBe(false);
284
369
  });
285
370
  });
286
371
 
287
372
  it('should classify other 5xx errors as retryable', () => {
288
- expect((modelDef as any).isGeminiErrorRetryable(501)).toBe(true);
289
- expect((modelDef as any).isGeminiErrorRetryable(505)).toBe(true);
290
- expect((modelDef as any).isGeminiErrorRetryable(599)).toBe(true);
373
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(501)).toBe(true);
374
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(505)).toBe(true);
375
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(599)).toBe(true);
291
376
  });
292
377
 
293
378
  it('should classify other 4xx errors as non-retryable', () => {
294
- expect((modelDef as any).isGeminiErrorRetryable(402)).toBe(false);
295
- expect((modelDef as any).isGeminiErrorRetryable(405)).toBe(false);
296
- expect((modelDef as any).isGeminiErrorRetryable(499)).toBe(false);
379
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(402)).toBe(false);
380
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(405)).toBe(false);
381
+ });
382
+
383
+ it('should treat 400 URL_REJECTED-REJECTED_CLIENT_THROTTLED as retryable', () => {
384
+ // Vertex AI surfaces inline-URL-fetcher throttling as 400 INVALID_ARGUMENT.
385
+ // The status is misleading — it's a transient Google-side throttle.
386
+ const message =
387
+ 'Cannot fetch content from the provided URL. ' + 'Status: URL_REJECTED-REJECTED_CLIENT_THROTTLED';
388
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(400, message)).toBe(true);
389
+ });
390
+
391
+ it('should treat 400 URL_REJECTED-REJECTED_RATE_LIMITED as retryable', () => {
392
+ const message =
393
+ 'Cannot fetch content from the provided URL. ' + 'Status: URL_REJECTED-REJECTED_RATE_LIMITED';
394
+ expect(exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(400, message)).toBe(true);
395
+ });
396
+
397
+ it('should still treat plain 400 INVALID_ARGUMENT as non-retryable', () => {
398
+ expect(
399
+ exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(
400
+ 400,
401
+ 'INVALID_ARGUMENT: bad input',
402
+ ),
403
+ ).toBe(false);
404
+ });
405
+
406
+ it('should treat invalid_grant as non-retryable before status-based retry rules', () => {
407
+ expect(
408
+ exposePrivate<GeminiModelInternals>(modelDef).isGeminiErrorRetryable(
409
+ 503,
410
+ "Error code invalid_grant: Error connecting to the given credential's issuer.",
411
+ ),
412
+ ).toBe(false);
297
413
  });
298
414
  });
299
415
 
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Unit tests for fixOrphanedToolResults (Gemini).
3
+ *
4
+ * Gemini pairs a `functionResponse` part to its `functionCall` part by name. A
5
+ * functionResponse left dangling after its functionCall was dropped — e.g. by
6
+ * conversation compaction trimming the model tool-call turn, or an unmergeable
7
+ * parallel batch — causes the Gemini/Vertex API to reject the request. This is
8
+ * the same class of bug fixed in the Claude, Bedrock, and OpenAI drivers.
9
+ */
10
+
11
+ import type { Content } from '@google/genai';
12
+ import type { ExecutionOptions } from '@llumiverse/core';
13
+ import { describe, expect, test } from 'vitest';
14
+ import { fixOrphanedToolResults, getGeminiPayload } from './gemini.js';
15
+
16
+ const OPTIONS_WITH_TOOLS = {
17
+ model: 'gemini-2.5-flash',
18
+ tools: [
19
+ { name: 'a', description: '', input_schema: { type: 'object', properties: {} } },
20
+ { name: 'b', description: '', input_schema: { type: 'object', properties: {} } },
21
+ ],
22
+ } as unknown as ExecutionOptions;
23
+
24
+ function functionResponseNames(contents: Content[]): string[] {
25
+ return contents.flatMap((content) =>
26
+ (content.parts ?? []).flatMap((part) => (part.functionResponse?.name ? [part.functionResponse.name] : [])),
27
+ );
28
+ }
29
+
30
+ describe('fixOrphanedToolResults - Gemini', () => {
31
+ test('returns empty array for empty input', () => {
32
+ expect(fixOrphanedToolResults([])).toEqual([]);
33
+ });
34
+
35
+ test('keeps a functionResponse that has a matching functionCall in the previous model content', () => {
36
+ const contents: Content[] = [
37
+ { role: 'model', parts: [{ functionCall: { name: 'search', args: {} } }] },
38
+ { role: 'user', parts: [{ functionResponse: { name: 'search', response: { ok: true } } }] },
39
+ ];
40
+ expect(fixOrphanedToolResults(contents)).toEqual(contents);
41
+ });
42
+
43
+ test('keeps all responses of a parallel batch when both calls are present', () => {
44
+ const contents: Content[] = [
45
+ {
46
+ role: 'model',
47
+ parts: [{ functionCall: { name: 'a', args: {} } }, { functionCall: { name: 'b', args: {} } }],
48
+ },
49
+ {
50
+ role: 'user',
51
+ parts: [
52
+ { functionResponse: { name: 'a', response: {} } },
53
+ { functionResponse: { name: 'b', response: {} } },
54
+ ],
55
+ },
56
+ ];
57
+ expect(fixOrphanedToolResults(contents)).toEqual(contents);
58
+ });
59
+
60
+ test('drops a functionResponse whose functionCall is absent from the previous content', () => {
61
+ const contents: Content[] = [
62
+ { role: 'model', parts: [{ functionCall: { name: 'a', args: {} } }] },
63
+ {
64
+ role: 'user',
65
+ parts: [
66
+ { functionResponse: { name: 'a', response: {} } },
67
+ { functionResponse: { name: 'gone', response: {} } },
68
+ ],
69
+ },
70
+ ];
71
+
72
+ const result = fixOrphanedToolResults(contents);
73
+ expect(result[1].parts).toHaveLength(1);
74
+ expect(result[1].parts?.[0].functionResponse?.name).toBe('a');
75
+ });
76
+
77
+ test('drops a content that becomes empty after removing orphaned responses', () => {
78
+ const contents: Content[] = [
79
+ { role: 'user', parts: [{ text: '[summary of prior work]' }] },
80
+ { role: 'user', parts: [{ functionResponse: { name: 'gone', response: {} } }] },
81
+ ];
82
+
83
+ const result = fixOrphanedToolResults(contents);
84
+ expect(result).toHaveLength(1);
85
+ expect(result[0].parts?.[0].text).toBe('[summary of prior work]');
86
+ });
87
+
88
+ test('preserves non-functionResponse parts while dropping the orphan', () => {
89
+ const contents: Content[] = [
90
+ { role: 'model', parts: [{ text: 'thinking' }] },
91
+ {
92
+ role: 'user',
93
+ parts: [{ functionResponse: { name: 'gone', response: {} } }, { text: 'continue please' }],
94
+ },
95
+ ];
96
+
97
+ const result = fixOrphanedToolResults(contents);
98
+ expect(result[1].parts).toHaveLength(1);
99
+ expect(result[1].parts?.[0].text).toBe('continue please');
100
+ });
101
+ });
102
+
103
+ describe('getGeminiPayload - orphaned tool results', () => {
104
+ test('retains split parallel functionResponses by merging consecutive user contents before cleanup', () => {
105
+ const contents: Content[] = [
106
+ {
107
+ role: 'model',
108
+ parts: [{ functionCall: { name: 'a', args: {} } }, { functionCall: { name: 'b', args: {} } }],
109
+ },
110
+ { role: 'user', parts: [{ functionResponse: { name: 'a', response: { ok: true } } }] },
111
+ { role: 'user', parts: [{ functionResponse: { name: 'b', response: { ok: true } } }] },
112
+ ];
113
+
114
+ const payload = getGeminiPayload(OPTIONS_WITH_TOOLS, { contents });
115
+
116
+ expect(functionResponseNames(payload.contents as Content[])).toEqual(['a', 'b']);
117
+ });
118
+ });