@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,95 +1,150 @@
1
- import type { ApiError } from "@google/genai";
1
+ import type { ApiError } from '@google/genai';
2
2
  import {
3
- type Content, FinishReason, FunctionCallingConfigMode, type FunctionDeclaration, type GenerateContentConfig, type GenerateContentParameters,
3
+ type Content,
4
+ FinishReason,
5
+ FunctionCallingConfigMode,
6
+ type FunctionDeclaration,
7
+ type GenerateContentConfig,
8
+ type GenerateContentParameters,
4
9
  type GenerateContentResponseUsageMetadata,
5
- HarmBlockThreshold, HarmCategory, Modality, type Part,
10
+ HarmBlockThreshold,
11
+ HarmCategory,
12
+ Modality,
13
+ type Part,
6
14
  ProminentPeople,
7
- type SafetySetting, type ThinkingConfig,
15
+ type SafetySetting,
16
+ type ThinkingConfig,
8
17
  ThinkingLevel,
9
- type Tool
10
- } from "@google/genai";
18
+ type Tool,
19
+ } from '@google/genai';
11
20
  import {
12
- type AIModel, type Completion, type CompletionChunkObject, type CompletionResult, type ExecutionOptions,
21
+ type AIModel,
22
+ type Completion,
23
+ type CompletionChunkObject,
24
+ type CompletionResult,
25
+ type ExecutionOptions,
13
26
  type ExecutionTokenUsage,
14
27
  getConversationMeta,
15
28
  getGeminiModelVersion,
16
29
  incrementConversationTurn,
17
30
  isGeminiModelVersionGte,
18
- type JSONObject, LlumiverseError, type LlumiverseErrorContext, ModelType, type PromptOptions, PromptRole,
19
- type PromptSegment, readStreamAsBase64, type StatelessExecutionOptions,
31
+ type JSONObject,
32
+ LlumiverseError,
33
+ type LlumiverseErrorContext,
34
+ ModelType,
35
+ type PromptOptions,
36
+ PromptRole,
37
+ type PromptSegment,
38
+ readStreamAsBase64,
39
+ type StatelessExecutionOptions,
20
40
  stripBase64ImagesFromConversation,
21
41
  stripHeartbeatsFromConversation,
22
- type ToolDefinition, type ToolUse,
42
+ type ToolDefinition,
43
+ type ToolUse,
23
44
  truncateLargeTextInConversation,
24
45
  unwrapConversationArray,
25
- type VertexAIGeminiOptions
26
- } from "@llumiverse/core";
27
- import { asyncMap } from "@llumiverse/core/async";
28
- import type { GenerateContentPrompt, VertexAIDriver } from "../index.js";
29
- import type { ModelDefinition } from "../models.js";
46
+ type VertexAIGeminiOptions,
47
+ } from '@llumiverse/core';
48
+ import { asyncMap } from '@llumiverse/core/async';
49
+ import { truncateBinaryForDebug } from '../../shared/debug-prompt.js';
50
+ import type { GenerateContentPrompt, VertexAIDriver } from '../index.js';
51
+ import type { ModelDefinition } from '../models.js';
52
+
53
+ type GoogleApiErrorLike = Pick<ApiError, 'status' | 'message'>;
30
54
 
31
55
  function supportsStructuredOutput(options: PromptOptions): boolean {
32
56
  // Gemini 1.0 Ultra does not support JSON output, 1.0 Pro does.
33
- return !!options.result_schema && !options.model.includes("ultra");
57
+ return !!options.result_schema && !options.model.includes('ultra');
34
58
  }
35
59
 
36
60
  const geminiSafetySettings: SafetySetting[] = [
37
61
  {
38
62
  category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
39
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
63
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
40
64
  },
41
65
  {
42
66
  category: HarmCategory.HARM_CATEGORY_HARASSMENT,
43
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
67
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
44
68
  },
45
69
  {
46
70
  category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
47
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
71
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
48
72
  },
49
73
  {
50
74
  category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
51
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
75
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
52
76
  },
53
77
  {
54
78
  category: HarmCategory.HARM_CATEGORY_UNSPECIFIED,
55
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
79
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
56
80
  },
57
81
  {
58
82
  category: HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY,
59
- threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH
60
- }
83
+ threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
84
+ },
61
85
  ];
62
86
 
87
+ function formatGeminiContentForDebug(content: Content): Content {
88
+ return {
89
+ ...content,
90
+ parts: content.parts?.map((part) => {
91
+ if (!part.inlineData?.data) {
92
+ return part;
93
+ }
94
+ return {
95
+ ...part,
96
+ inlineData: {
97
+ ...part.inlineData,
98
+ data: truncateBinaryForDebug(part.inlineData.data),
99
+ },
100
+ } satisfies Part;
101
+ }),
102
+ };
103
+ }
104
+
105
+ export function formatGeminiDebugPrompt(prompt: GenerateContentPrompt): GenerateContentPrompt {
106
+ return {
107
+ ...prompt,
108
+ contents: prompt.contents.map(formatGeminiContentForDebug),
109
+ system: prompt.system ? formatGeminiContentForDebug(prompt.system) : undefined,
110
+ };
111
+ }
112
+
63
113
  // We do the mapping here rather than in common to avoid bringing the SDK into the common package.
64
- function getProminentPeopleOption(prominentPeople?: "PROMINENT_PEOPLE_UNSPECIFIED" | "ALLOW_PROMINENT_PEOPLE" | "BLOCK_PROMINENT_PEOPLE") {
114
+ function getProminentPeopleOption(
115
+ prominentPeople?: 'PROMINENT_PEOPLE_UNSPECIFIED' | 'ALLOW_PROMINENT_PEOPLE' | 'BLOCK_PROMINENT_PEOPLE',
116
+ ) {
65
117
  switch (prominentPeople) {
66
- case "ALLOW_PROMINENT_PEOPLE":
118
+ case 'ALLOW_PROMINENT_PEOPLE':
67
119
  return ProminentPeople.ALLOW_PROMINENT_PEOPLE;
68
- case "BLOCK_PROMINENT_PEOPLE":
120
+ case 'BLOCK_PROMINENT_PEOPLE':
69
121
  return ProminentPeople.BLOCK_PROMINENT_PEOPLE;
70
- case "PROMINENT_PEOPLE_UNSPECIFIED":
122
+ case 'PROMINENT_PEOPLE_UNSPECIFIED':
71
123
  return ProminentPeople.PROMINENT_PEOPLE_UNSPECIFIED;
72
124
  default:
73
125
  return undefined;
74
126
  }
75
127
  }
76
128
 
77
- function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentPrompt): GenerateContentParameters {
129
+ export function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentPrompt): GenerateContentParameters {
78
130
  const model_options = options.model_options as VertexAIGeminiOptions | undefined;
79
131
  const tools = getToolDefinitions(options.tools);
80
132
 
81
133
  // When no tools are provided but conversation contains functionCall/functionResponse parts
82
134
  // (e.g. checkpoint summary calls), convert them to text to avoid API errors.
83
135
  // Use a local variable to avoid mutating the caller's conversation object.
84
- let payloadContents = prompt.contents;
136
+ let payloadContents = mergeConsecutiveRole(prompt.contents);
85
137
  if (!tools && payloadContents) {
86
- const hasToolParts = payloadContents.some(c =>
87
- c.parts?.some(p => p.functionCall || p.functionResponse)
88
- );
138
+ const hasToolParts = payloadContents.some((c) => c.parts?.some((p) => p.functionCall || p.functionResponse));
89
139
  if (hasToolParts) {
90
140
  payloadContents = convertGeminiFunctionPartsToText(payloadContents);
91
141
  }
92
142
  }
143
+ // Drop functionResponse parts whose functionCall was lost (e.g. compaction),
144
+ // which would otherwise trip Gemini's functionCall/functionResponse pairing.
145
+ if (payloadContents) {
146
+ payloadContents = fixOrphanedToolResults(payloadContents);
147
+ }
93
148
 
94
149
  const useStructuredOutput = supportsStructuredOutput(options) && !tools;
95
150
 
@@ -112,21 +167,23 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
112
167
  prominentPeople: getProminentPeopleOption(model_options?.prominent_people),
113
168
  outputMimeType: model_options?.output_mime_type,
114
169
  outputCompressionQuality: model_options?.output_compression_quality,
115
- }
116
- }
170
+ },
171
+ };
117
172
 
118
173
  const config: GenerateContentConfig = {
119
174
  systemInstruction: prompt.system,
120
175
  safetySettings: geminiSafetySettings,
121
176
  tools: tools ? [tools] : undefined,
122
- toolConfig: tools ? {
123
- functionCallingConfig: {
124
- mode: FunctionCallingConfigMode.AUTO,
125
- }
126
- } : undefined,
177
+ toolConfig: tools
178
+ ? {
179
+ functionCallingConfig: {
180
+ mode: FunctionCallingConfigMode.AUTO,
181
+ },
182
+ }
183
+ : undefined,
127
184
  candidateCount: 1,
128
185
  //JSON/Structured output
129
- responseMimeType: useStructuredOutput ? "application/json" : undefined,
186
+ responseMimeType: useStructuredOutput ? 'application/json' : undefined,
130
187
  responseJsonSchema: useStructuredOutput ? options.result_schema : undefined,
131
188
  //Model options
132
189
  temperature: model_options?.temperature,
@@ -139,12 +196,12 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
139
196
  seed: model_options?.seed,
140
197
  thinkingConfig: geminiThinkingConfig(options),
141
198
  labels: options.labels,
142
- }
199
+ };
143
200
 
144
201
  return {
145
202
  model: options.model,
146
203
  contents: payloadContents,
147
- config: options.model.toLowerCase().includes("image") ? configNanoBanana : config,
204
+ config: options.model.toLowerCase().includes('image') ? configNanoBanana : config,
148
205
  };
149
206
  }
150
207
 
@@ -159,16 +216,16 @@ function extractCompletionResults(content: Content): CompletionResult[] {
159
216
  for (const part of parts) {
160
217
  if (part.text) {
161
218
  results.push({
162
- type: "text",
163
- value: part.text
219
+ type: 'text',
220
+ value: part.text,
164
221
  });
165
222
  } else if (part.inlineData) {
166
- const base64ImageBytes: string = part.inlineData.data ?? "";
167
- const mimeType = part.inlineData.mimeType ?? "image/png";
223
+ const base64ImageBytes: string = part.inlineData.data ?? '';
224
+ const mimeType = part.inlineData.mimeType ?? 'image/png';
168
225
  const imageUrl = `data:${mimeType};base64,${base64ImageBytes}`;
169
226
  results.push({
170
- type: "image",
171
- value: imageUrl
227
+ type: 'image',
228
+ value: imageUrl,
172
229
  });
173
230
  }
174
231
  }
@@ -200,8 +257,8 @@ function collectToolUseParts(content: Content): ToolUse[] | undefined {
200
257
  export function mergeConsecutiveRole(contents: Content[] | undefined): Content[] {
201
258
  if (!contents || contents.length === 0) return [];
202
259
 
203
- const needsMerging = contents.some((content, i) =>
204
- i < contents.length - 1 && content.role === contents[i + 1].role
260
+ const needsMerging = contents.some(
261
+ (content, i) => i < contents.length - 1 && content.role === contents[i + 1].role,
205
262
  );
206
263
  // If no merging needed, return original array
207
264
  if (!needsMerging) {
@@ -226,19 +283,61 @@ export function mergeConsecutiveRole(contents: Content[] | undefined): Content[]
226
283
  return result;
227
284
  }
228
285
 
286
+ /**
287
+ * Drop functionResponse parts whose name has no matching functionCall in the
288
+ * immediately-preceding `model` content. Gemini pairs a functionResponse to its
289
+ * functionCall by name; a response left dangling after its call was dropped
290
+ * (e.g. by conversation compaction/trimming, or an unmergeable parallel batch)
291
+ * causes the API to reject the request. Mirrors the same guard added to the
292
+ * Claude, Bedrock, and OpenAI drivers.
293
+ *
294
+ * Run after mergeConsecutiveRole so parallel responses that were split across
295
+ * user contents are first recombined under the model turn that issued the calls,
296
+ * and are therefore not mistaken for orphans.
297
+ */
298
+ export function fixOrphanedToolResults(contents: Content[]): Content[] {
299
+ if (contents.length === 0) return contents;
300
+ const result: Content[] = [];
301
+ for (let i = 0; i < contents.length; i++) {
302
+ const content = contents[i];
303
+ if (content.role !== 'user' || !content.parts) {
304
+ result.push(content);
305
+ continue;
306
+ }
307
+ const hasFunctionResponse = content.parts.some((part) => part.functionResponse);
308
+ if (!hasFunctionResponse) {
309
+ result.push(content);
310
+ continue;
311
+ }
312
+ const prev = contents[i - 1];
313
+ const allowedNames = new Set<string>();
314
+ if (prev && prev.role === 'model' && prev.parts) {
315
+ for (const part of prev.parts) {
316
+ if (part.functionCall?.name) allowedNames.add(part.functionCall.name);
317
+ }
318
+ }
319
+ const filtered = content.parts.filter((part) =>
320
+ part.functionResponse ? allowedNames.has(part.functionResponse.name ?? '') : true,
321
+ );
322
+ // Drop the content if every part was an orphaned functionResponse.
323
+ if (filtered.length === 0) continue;
324
+ result.push(filtered.length === content.parts.length ? content : { ...content, parts: filtered });
325
+ }
326
+ return result;
327
+ }
328
+
229
329
  const supportedFinishReasons: FinishReason[] = [
230
330
  FinishReason.MAX_TOKENS,
231
331
  FinishReason.STOP,
232
332
  FinishReason.FINISH_REASON_UNSPECIFIED,
233
- ]
333
+ ];
234
334
 
235
335
  // Finish reasons that indicate tool call issues but should be recovered gracefully
236
336
  // instead of throwing an error. The tool_use is still extracted and returned
237
337
  // so the workflow can generate a proper toolError response.
238
338
  const recoverableToolCallReasons = [
239
339
  'UNEXPECTED_TOOL_CALL', // Model called an undeclared tool
240
- ]
241
-
340
+ ];
242
341
 
243
342
  function geminiThinkingBudget(option: StatelessExecutionOptions) {
244
343
  const model_options = option.model_options as VertexAIGeminiOptions | undefined;
@@ -252,7 +351,7 @@ function geminiThinkingBudget(option: StatelessExecutionOptions) {
252
351
  // Set minimum thinking level by default.
253
352
  // Docs: https://ai.google.dev/gemini-api/docs/thinking#set-budget
254
353
  if (getGeminiModelVersion(option.model) === '2.5') {
255
- if (option.model.includes("pro")) {
354
+ if (option.model.includes('pro')) {
256
355
  return 128;
257
356
  }
258
357
  return 0;
@@ -260,37 +359,40 @@ function geminiThinkingBudget(option: StatelessExecutionOptions) {
260
359
  return undefined;
261
360
  }
262
361
 
263
- function geminiThinkingLevelForEffort(model: string, effort: VertexAIGeminiOptions["effort"]): ThinkingLevel | undefined {
264
- if (model.includes("gemini-3-pro-image")) {
362
+ function geminiThinkingLevelForEffort(
363
+ model: string,
364
+ effort: VertexAIGeminiOptions['effort'],
365
+ ): ThinkingLevel | undefined {
366
+ if (model.includes('gemini-3-pro-image')) {
265
367
  return ThinkingLevel.HIGH;
266
368
  }
267
- if (model.includes("gemini-3.1-flash-image")) {
268
- return effort === "low" ? ThinkingLevel.MINIMAL : ThinkingLevel.HIGH;
369
+ if (model.includes('gemini-3.1-flash-image')) {
370
+ return effort === 'low' ? ThinkingLevel.MINIMAL : ThinkingLevel.HIGH;
269
371
  }
270
372
  switch (effort) {
271
- case "low":
373
+ case 'low':
272
374
  return ThinkingLevel.LOW;
273
- case "medium":
375
+ case 'medium':
274
376
  return ThinkingLevel.MEDIUM;
275
- case "high":
377
+ case 'high':
276
378
  return ThinkingLevel.HIGH;
277
379
  default:
278
380
  return undefined;
279
381
  }
280
382
  }
281
383
 
282
- function geminiBudgetForEffort(model: string, effort: NonNullable<VertexAIGeminiOptions["effort"]>): number {
283
- const isFlashLite = model.includes("flash-lite");
284
- const isFlash = model.includes("flash") && !isFlashLite;
285
- const isPro = model.includes("pro");
384
+ function geminiBudgetForEffort(model: string, effort: NonNullable<VertexAIGeminiOptions['effort']>): number {
385
+ const isFlashLite = model.includes('flash-lite');
386
+ const isFlash = model.includes('flash') && !isFlashLite;
387
+ const isPro = model.includes('pro');
286
388
 
287
- if (effort === "low") {
389
+ if (effort === 'low') {
288
390
  if (isPro) return 128;
289
391
  if (isFlashLite) return 512;
290
392
  if (isFlash) return 1;
291
393
  return 1024;
292
394
  }
293
- if (effort === "medium") {
395
+ if (effort === 'medium') {
294
396
  return 8192;
295
397
  }
296
398
  if (isPro) return 32768;
@@ -329,21 +431,20 @@ function geminiThinkingConfig(option: StatelessExecutionOptions): ThinkingConfig
329
431
  if (isGeminiModelVersionGte(option.model, '3.0')) {
330
432
  return {
331
433
  includeThoughts: include_thoughts,
332
- thinkingLevel: option.model.includes("gemini-3-pro-image") ? ThinkingLevel.HIGH : ThinkingLevel.LOW
434
+ thinkingLevel: option.model.includes('gemini-3-pro-image') ? ThinkingLevel.HIGH : ThinkingLevel.LOW,
333
435
  };
334
436
  }
335
437
  if (isGeminiModelVersionGte(option.model, '2.5')) {
336
438
  const thinking_budget_tokens = geminiThinkingBudget(option) ?? 0;
337
439
  return {
338
440
  includeThoughts: include_thoughts,
339
- thinkingBudget: thinking_budget_tokens
441
+ thinkingBudget: thinking_budget_tokens,
340
442
  };
341
443
  }
342
444
  }
343
445
 
344
446
  export class GeminiModelDefinition implements ModelDefinition<GenerateContentPrompt> {
345
-
346
- model: AIModel
447
+ model: AIModel;
347
448
 
348
449
  constructor(modelId: string) {
349
450
  this.model = {
@@ -351,18 +452,22 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
351
452
  name: modelId,
352
453
  provider: 'vertexai',
353
454
  type: ModelType.Text,
354
- can_stream: true
455
+ can_stream: true,
355
456
  } satisfies AIModel;
356
457
  }
357
458
 
358
- async createPrompt(_driver: VertexAIDriver, segments: PromptSegment[], options: ExecutionOptions): Promise<GenerateContentPrompt> {
359
- const splits = options.model.split("/");
459
+ async createPrompt(
460
+ _driver: VertexAIDriver,
461
+ segments: PromptSegment[],
462
+ options: ExecutionOptions,
463
+ ): Promise<GenerateContentPrompt> {
464
+ const splits = options.model.split('/');
360
465
  const modelName = splits[splits.length - 1];
361
466
  options = { ...options, model: modelName };
362
467
 
363
468
  const schema = options.result_schema;
364
469
  let contents: Content[] = [];
365
- let system: Content | undefined = { role: "user", parts: [] }; // Single content block for system messages
470
+ let system: Content | undefined = { role: 'user', parts: [] }; // Single content block for system messages
366
471
 
367
472
  const safety: Content[] = [];
368
473
 
@@ -371,17 +476,19 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
371
476
  if (msg.role === PromptRole.system) {
372
477
  // Text only for system messages
373
478
  if (msg.files && msg.files.length > 0) {
374
- throw new Error("Gemini does not support files/images etc. in system messages. Only text content is allowed.");
479
+ throw new Error(
480
+ 'Gemini does not support files/images etc. in system messages. Only text content is allowed.',
481
+ );
375
482
  }
376
483
 
377
484
  if (msg.content) {
378
485
  system.parts?.push({
379
- text: msg.content
486
+ text: msg.content,
380
487
  });
381
488
  }
382
489
  } else if (msg.role === PromptRole.tool) {
383
490
  if (!msg.tool_use_id) {
384
- throw new Error("Tool response missing tool_use_id");
491
+ throw new Error('Tool response missing tool_use_id');
385
492
  }
386
493
  // Build functionResponse part with optional thought_signature for Gemini thinking models
387
494
  const functionResponsePart: Part = {
@@ -394,9 +501,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
394
501
  };
395
502
  contents.push({
396
503
  role: 'user',
397
- parts: [functionResponsePart]
504
+ parts: [functionResponsePart],
398
505
  });
399
- } else { // PromptRole.user, PromptRole.assistant, PromptRole.safety
506
+ } else {
507
+ // PromptRole.user, PromptRole.assistant, PromptRole.safety
400
508
  const parts: Part[] = [];
401
509
  // Text content handling
402
510
  if (msg.content) {
@@ -408,15 +516,16 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
408
516
  // File content handling
409
517
  if (msg.files) {
410
518
  for (const f of msg.files) {
411
- const fileUrl = await f.getURL();
412
- const isGsUrl = fileUrl.startsWith('gs://') || fileUrl.startsWith('https://storage.googleapis.com/');
519
+ const fileUri = await f.getURI();
520
+ const isGsUri =
521
+ fileUri.startsWith('gs://') || fileUri.startsWith('https://storage.googleapis.com/');
413
522
 
414
- if (isGsUrl) {
523
+ if (isGsUri) {
415
524
  parts.push({
416
525
  fileData: {
417
- fileUri: fileUrl,
418
- mimeType: f.mime_type
419
- }
526
+ fileUri,
527
+ mimeType: f.mime_type,
528
+ },
420
529
  });
421
530
  } else {
422
531
  // Inline data handling
@@ -425,8 +534,8 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
425
534
  parts.push({
426
535
  inlineData: {
427
536
  data,
428
- mimeType: f.mime_type
429
- }
537
+ mimeType: f.mime_type,
538
+ },
430
539
  });
431
540
  }
432
541
  }
@@ -453,15 +562,17 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
453
562
  if (supportsStructuredOutput(options) && !options.tools) {
454
563
  // Gemini structured output is unnecessarily sparse. Adding encouragement to fill the fields.
455
564
  // Putting JSON in prompt is not recommended by Google, when using structured output.
456
- system.parts?.push({ text: "Fill all appropriate fields in the JSON output." });
565
+ system.parts?.push({ text: 'Fill all appropriate fields in the JSON output.' });
457
566
  } else {
458
567
  // Fallback to putting the schema in the system instructions, if not using structured output.
459
568
  if (options.tools) {
460
569
  system.parts?.push({
461
- text: "When not calling tools, the output must be a JSON object using the following JSON Schema:\n" + JSON.stringify(schema)
570
+ text: `When not calling tools, the output must be a JSON object using the following JSON Schema:\n${JSON.stringify(schema)}`,
462
571
  });
463
572
  } else {
464
- system.parts?.push({ text: "The output must be a JSON object using the following JSON Schema:\n" + JSON.stringify(schema) });
573
+ system.parts?.push({
574
+ text: `The output must be a JSON object using the following JSON Schema:\n${JSON.stringify(schema)}`,
575
+ });
465
576
  }
466
577
  }
467
578
  }
@@ -482,8 +593,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
482
593
  return { contents, system };
483
594
  }
484
595
 
485
- usageMetadataToTokenUsage(driver: VertexAIDriver, usageMetadata: GenerateContentResponseUsageMetadata | undefined): ExecutionTokenUsage {
486
- if (!usageMetadata || !usageMetadata.totalTokenCount) {
596
+ usageMetadataToTokenUsage(
597
+ driver: VertexAIDriver,
598
+ usageMetadata: GenerateContentResponseUsageMetadata | undefined,
599
+ ): ExecutionTokenUsage {
600
+ if (!usageMetadata?.totalTokenCount) {
487
601
  return {};
488
602
  }
489
603
  const tokenUsage: ExecutionTokenUsage = {
@@ -494,9 +608,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
494
608
  };
495
609
 
496
610
  //Output/Response side
497
- tokenUsage.result = (usageMetadata.candidatesTokenCount ?? 0)
498
- + (usageMetadata.thoughtsTokenCount ?? 0)
499
- + (usageMetadata.toolUsePromptTokenCount ?? 0);
611
+ tokenUsage.result =
612
+ (usageMetadata.candidatesTokenCount ?? 0) +
613
+ (usageMetadata.thoughtsTokenCount ?? 0) +
614
+ (usageMetadata.toolUsePromptTokenCount ?? 0);
500
615
 
501
616
  if ((tokenUsage.total ?? 0) !== (tokenUsage.prompt ?? 0) + tokenUsage.result) {
502
617
  // Token-accounting mismatch: warn-level diagnostic (the call still
@@ -505,7 +620,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
505
620
  // log aggregators — see the recoverable-tool-call sites below.
506
621
  driver.logger.warn(
507
622
  { total: tokenUsage.total, prompt: tokenUsage.prompt, result: tokenUsage.result },
508
- "[VertexAI] Gemini token usage mismatch: total does not equal prompt + result",
623
+ '[VertexAI] Gemini token usage mismatch: total does not equal prompt + result',
509
624
  );
510
625
  }
511
626
 
@@ -516,10 +631,14 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
516
631
  return tokenUsage;
517
632
  }
518
633
 
519
- async requestTextCompletion(driver: VertexAIDriver, prompt: GenerateContentPrompt, options: ExecutionOptions): Promise<Completion> {
520
- const splits = options.model.split("/");
521
- let region: string | undefined = undefined;
522
- if (splits[0] === "locations" && splits.length >= 2) {
634
+ async requestTextCompletion(
635
+ driver: VertexAIDriver,
636
+ prompt: GenerateContentPrompt,
637
+ options: ExecutionOptions,
638
+ ): Promise<Completion> {
639
+ const splits = options.model.split('/');
640
+ let region: string | undefined;
641
+ if (splits[0] === 'locations' && splits.length >= 2) {
523
642
  region = splits[1];
524
643
  }
525
644
  const modelName = splits[splits.length - 1];
@@ -538,12 +657,12 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
538
657
  prompt.contents = conversation;
539
658
 
540
659
  // TODO: Remove hack, use global endpoint manually if needed.
541
- if (options.model.includes("gemini-2.5-flash-image")) {
542
- region = "global"; // Gemini Flash Image only available in global region, this is for nano-banana model
660
+ if (options.model.includes('gemini-2.5-flash-image')) {
661
+ region = 'global'; // Gemini Flash Image only available in global region, this is for nano-banana model
543
662
  }
544
663
 
545
664
  const model_options = options.model_options as VertexAIGeminiOptions | undefined;
546
- const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
665
+ const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
547
666
 
548
667
  const payload = getGeminiPayload(options, prompt);
549
668
  const response = await client.models.generateContent(payload);
@@ -551,22 +670,33 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
551
670
  const token_usage: ExecutionTokenUsage = this.usageMetadataToTokenUsage(driver, response.usageMetadata);
552
671
 
553
672
  let tool_use: ToolUse[] | undefined;
554
- let finish_reason: string | undefined, result: any;
555
- const candidate = response.candidates && response.candidates[0];
673
+ let finish_reason: string | undefined, result: CompletionResult[] | undefined;
674
+ const candidate = response.candidates?.[0];
556
675
  if (candidate) {
557
676
  switch (candidate.finishReason) {
558
- case FinishReason.MAX_TOKENS: finish_reason = "length"; break;
559
- case FinishReason.STOP: finish_reason = "stop"; break;
560
- default: finish_reason = candidate.finishReason;
677
+ case FinishReason.MAX_TOKENS:
678
+ finish_reason = 'length';
679
+ break;
680
+ case FinishReason.STOP:
681
+ finish_reason = 'stop';
682
+ break;
683
+ default:
684
+ finish_reason = candidate.finishReason;
561
685
  }
562
686
  const content = candidate.content;
563
687
 
564
688
  // Check for unsupported finish reasons, but allow recoverable tool call issues
565
689
  const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
566
- if (candidate.finishReason && !supportedFinishReasons.includes(candidate.finishReason) && !isRecoverableToolCall) {
567
- throw new Error(`Unsupported finish reason: ${candidate.finishReason}, `
568
- + `finish message: ${candidate.finishMessage}, `
569
- + `content: ${JSON.stringify(content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`);
690
+ if (
691
+ candidate.finishReason &&
692
+ !supportedFinishReasons.includes(candidate.finishReason) &&
693
+ !isRecoverableToolCall
694
+ ) {
695
+ throw new Error(
696
+ `Unsupported finish reason: ${candidate.finishReason}, ` +
697
+ `finish message: ${candidate.finishMessage}, ` +
698
+ `content: ${JSON.stringify(content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`,
699
+ );
570
700
  }
571
701
 
572
702
  if (content) {
@@ -580,7 +710,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
580
710
  if (isRecoverableToolCall && tool_use && tool_use.length > 0) {
581
711
  driver.logger.warn(
582
712
  `[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
583
- `Model tried to call undeclared tool(s): ${tool_use.map(t => t.tool_name).join(', ')}`,
713
+ `Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
584
714
  );
585
715
  }
586
716
 
@@ -589,10 +719,8 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
589
719
  }
590
720
  }
591
721
 
592
-
593
-
594
722
  if (tool_use) {
595
- finish_reason = "tool_use";
723
+ finish_reason = 'tool_use';
596
724
  }
597
725
 
598
726
  // Increment turn counter for deferred stripping
@@ -603,7 +731,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
603
731
  const stripOptions = {
604
732
  keepForTurns: options.stripImagesAfterTurns ?? Infinity,
605
733
  currentTurn,
606
- textMaxTokens: options.stripTextMaxTokens
734
+ textMaxTokens: options.stripTextMaxTokens,
607
735
  };
608
736
  let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
609
737
 
@@ -620,19 +748,23 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
620
748
  const finalConversation = storeSystemInConversation(processedConversation, prompt.system);
621
749
 
622
750
  return {
623
- result: result && result.length > 0 ? result : [{ type: "text" as const, value: '' }],
751
+ result: result && result.length > 0 ? result : [{ type: 'text' as const, value: '' }],
624
752
  token_usage: token_usage,
625
753
  finish_reason: finish_reason,
626
754
  original_response: options.include_original_response ? response : undefined,
627
755
  conversation: finalConversation,
628
- tool_use
756
+ tool_use,
629
757
  } satisfies Completion;
630
758
  }
631
759
 
632
- async requestTextCompletionStream(driver: VertexAIDriver, prompt: GenerateContentPrompt, options: ExecutionOptions): Promise<AsyncIterable<CompletionChunkObject>> {
633
- const splits = options.model.split("/");
634
- let region: string | undefined = undefined;
635
- if (splits[0] === "locations" && splits.length >= 2) {
760
+ async requestTextCompletionStream(
761
+ driver: VertexAIDriver,
762
+ prompt: GenerateContentPrompt,
763
+ options: ExecutionOptions,
764
+ ): Promise<AsyncIterable<CompletionChunkObject>> {
765
+ const splits = options.model.split('/');
766
+ let region: string | undefined;
767
+ if (splits[0] === 'locations' && splits.length >= 2) {
636
768
  region = splits[1];
637
769
  }
638
770
  const modelName = splits[splits.length - 1];
@@ -651,12 +783,12 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
651
783
  const conversation = updateConversation(options.conversation, prompt.contents);
652
784
  prompt.contents = conversation;
653
785
 
654
- if (options.model.includes("gemini-2.5-flash-image")) {
655
- region = "global"; // Gemini Flash Image only available in global region, this is for nano-banana model
786
+ if (options.model.includes('gemini-2.5-flash-image')) {
787
+ region = 'global'; // Gemini Flash Image only available in global region, this is for nano-banana model
656
788
  }
657
789
 
658
790
  const model_options = options.model_options as VertexAIGeminiOptions | undefined;
659
- const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false);
791
+ const client = driver.getGoogleGenAIClient(region, model_options?.flex ?? false, options.httpTimeout);
660
792
 
661
793
  const payload = getGeminiPayload(options, prompt);
662
794
  const response = await client.models.generateContentStream(payload);
@@ -668,23 +800,34 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
668
800
  let tool_use: ToolUse[] | undefined;
669
801
  let finish_reason: string | undefined;
670
802
  switch (candidate.finishReason) {
671
- case FinishReason.MAX_TOKENS: finish_reason = "length"; break;
672
- case FinishReason.STOP: finish_reason = "stop"; break;
673
- default: finish_reason = candidate.finishReason;
803
+ case FinishReason.MAX_TOKENS:
804
+ finish_reason = 'length';
805
+ break;
806
+ case FinishReason.STOP:
807
+ finish_reason = 'stop';
808
+ break;
809
+ default:
810
+ finish_reason = candidate.finishReason;
674
811
  }
675
812
  // Check for unsupported finish reasons, but allow recoverable tool call issues
676
813
  const isRecoverableToolCall = recoverableToolCallReasons.includes(candidate.finishReason as string);
677
- if (candidate.finishReason && !supportedFinishReasons.includes(candidate.finishReason) && !isRecoverableToolCall) {
678
- throw new Error(`Unsupported finish reason: ${candidate.finishReason}, `
679
- + `finish message: ${candidate.finishMessage}, `
680
- + `content: ${JSON.stringify(candidate.content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`);
814
+ if (
815
+ candidate.finishReason &&
816
+ !supportedFinishReasons.includes(candidate.finishReason) &&
817
+ !isRecoverableToolCall
818
+ ) {
819
+ throw new Error(
820
+ `Unsupported finish reason: ${candidate.finishReason}, ` +
821
+ `finish message: ${candidate.finishMessage}, ` +
822
+ `content: ${JSON.stringify(candidate.content, null, 2)}, safety: ${JSON.stringify(candidate.safetyRatings, null, 2)}`,
823
+ );
681
824
  }
682
825
  if (candidate.content?.role === 'model') {
683
826
  // Collect all parts in order (text and images)
684
827
  const combinedResults = extractCompletionResults(candidate.content);
685
828
  tool_use = collectToolUseParts(candidate.content);
686
829
  if (tool_use) {
687
- finish_reason = "tool_use";
830
+ finish_reason = 'tool_use';
688
831
  // Log warning for recoverable tool call issues — see the
689
832
  // matching site in `requestTextCompletion` above for why
690
833
  // we route through the driver's logger instead of
@@ -692,7 +835,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
692
835
  if (isRecoverableToolCall) {
693
836
  driver.logger.warn(
694
837
  `[Gemini] Recoverable tool call issue (${candidate.finishReason}): ` +
695
- `Model tried to call undeclared tool(s): ${tool_use.map(t => t.tool_name).join(', ')}`,
838
+ `Model tried to call undeclared tool(s): ${tool_use.map((t) => t.tool_name).join(', ')}`,
696
839
  );
697
840
  }
698
841
  }
@@ -707,8 +850,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
707
850
  }
708
851
  //No normal output, returning block reason if it exists.
709
852
  return {
710
- result: item.promptFeedback?.blockReasonMessage ? [{ type: "text" as const, value: item.promptFeedback.blockReasonMessage }] : [],
711
- finish_reason: item.promptFeedback?.blockReason ?? "",
853
+ result: item.promptFeedback?.blockReasonMessage
854
+ ? [{ type: 'text' as const, value: item.promptFeedback.blockReasonMessage }]
855
+ : [],
856
+ finish_reason: item.promptFeedback?.blockReason ?? '',
712
857
  token_usage: token_usage,
713
858
  };
714
859
  });
@@ -718,11 +863,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
718
863
 
719
864
  /**
720
865
  * Format Google API errors into LlumiverseError with proper status codes and retryability.
721
- *
866
+ *
722
867
  * Google API errors follow AIP-193 standard:
723
868
  * - ApiError.status: HTTP status code
724
869
  * - ApiError.message: Error message
725
- *
870
+ *
726
871
  * Common error codes:
727
872
  * - 400 (INVALID_ARGUMENT): Invalid request parameters
728
873
  * - 401 (UNAUTHENTICATED): Authentication required
@@ -732,15 +877,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
732
877
  * - 500 (INTERNAL): Internal server error
733
878
  * - 503 (UNAVAILABLE): Service temporarily unavailable
734
879
  * - 504 (DEADLINE_EXCEEDED): Request timeout
735
- *
880
+ *
736
881
  * @see https://google.aip.dev/193
737
882
  * @see https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/api-errors
738
883
  */
739
- formatLlumiverseError(
740
- _driver: VertexAIDriver,
741
- error: unknown,
742
- context: LlumiverseErrorContext
743
- ): LlumiverseError {
884
+ formatLlumiverseError(_driver: VertexAIDriver, error: unknown, context: LlumiverseErrorContext): LlumiverseError {
744
885
  // Check if it's a Google API error with status code
745
886
  const isApiError = this.isGoogleApiError(error);
746
887
 
@@ -750,11 +891,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
750
891
  throw error;
751
892
  }
752
893
 
753
- const apiError = error as ApiError;
894
+ const apiError = error;
754
895
  const httpStatusCode = apiError.status;
755
896
 
756
897
  // Extract error message
757
- const message = apiError.message || String(error);
898
+ const message = apiError.message;
758
899
 
759
900
  // Build user-facing message with status code
760
901
  let userMessage = message;
@@ -765,7 +906,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
765
906
  }
766
907
 
767
908
  // Determine retryability based on Google error codes
768
- const retryable = this.isGeminiErrorRetryable(httpStatusCode);
909
+ const retryable = this.isGeminiErrorRetryable(httpStatusCode, message);
769
910
 
770
911
  // Extract error name/type from message if present
771
912
  const errorName = this.extractErrorName(message);
@@ -776,34 +917,36 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
776
917
  context,
777
918
  error,
778
919
  httpStatusCode,
779
- errorName
920
+ errorName,
780
921
  );
781
922
  }
782
923
 
783
924
  /**
784
925
  * Type guard to check if error is a Google API error.
785
926
  */
786
- private isGoogleApiError(error: unknown): error is ApiError {
927
+ private isGoogleApiError(error: unknown): error is GoogleApiErrorLike {
787
928
  return (
788
929
  error !== null &&
789
930
  typeof error === 'object' &&
790
931
  'status' in error &&
791
- typeof (error as any).status === 'number' &&
792
- 'message' in error
932
+ typeof (error as { status?: unknown }).status === 'number' &&
933
+ 'message' in error &&
934
+ typeof (error as { message?: unknown }).message === 'string'
793
935
  );
794
936
  }
795
937
 
796
938
  /**
797
939
  * Determine if a Google API error is retryable based on HTTP status code.
798
- *
940
+ *
799
941
  * Retryable errors (per Google AIP-194):
800
942
  * - 408 (REQUEST_TIMEOUT): Request timeout
943
+ * - 499 (CANCELLED / Client Closed Request): Transport cancellation
801
944
  * - 429 (RESOURCE_EXHAUSTED): Rate limit exceeded, quota exhausted
802
945
  * - 500 (INTERNAL): Internal server error
803
946
  * - 502 (BAD_GATEWAY): Bad gateway
804
947
  * - 503 (UNAVAILABLE): Service temporarily unavailable
805
948
  * - 504 (DEADLINE_EXCEEDED): Gateway timeout
806
- *
949
+ *
807
950
  * Non-retryable errors:
808
951
  * - 400 (INVALID_ARGUMENT): Invalid request parameters
809
952
  * - 401 (UNAUTHENTICATED): Authentication required
@@ -811,19 +954,48 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
811
954
  * - 404 (NOT_FOUND): Resource not found
812
955
  * - 409 (CONFLICT): Resource conflict
813
956
  * - Other 4xx client errors
814
- *
957
+ *
958
+ * Exception: certain 400s from Vertex AI's inline URL fetcher (used when
959
+ * passing a file by URL to multimodal models) surface as INVALID_ARGUMENT
960
+ * but are actually transient throttling/rate-limit signals on the
961
+ * fetcher, not a bad request. Detect those by message substring and
962
+ * treat them as retryable.
963
+ *
815
964
  * @param httpStatusCode - The HTTP status code from the API error
965
+ * @param message - The error message (used to detect transient 400 sub-cases)
816
966
  * @returns True if retryable, false if not retryable, undefined if unknown
817
967
  */
818
- private isGeminiErrorRetryable(httpStatusCode: number): boolean | undefined {
968
+ private isGeminiErrorRetryable(httpStatusCode: number, message?: string): boolean | undefined {
969
+ if (message && this.isNonRetryableAuthError(message)) return false;
970
+
819
971
  // Retryable status codes
820
972
  if (httpStatusCode === 408) return true; // Request timeout
821
973
  if (httpStatusCode === 429) return true; // Rate limit/quota
974
+ if (httpStatusCode === 499) return true; // Client closed / operation cancelled
822
975
  if (httpStatusCode === 502) return true; // Bad gateway
823
976
  if (httpStatusCode === 503) return true; // Service unavailable
824
977
  if (httpStatusCode === 504) return true; // Gateway timeout
825
978
  if (httpStatusCode >= 500 && httpStatusCode < 600) return true; // Other 5xx server errors
826
979
 
980
+ // Vertex AI URL fetcher transient throttling, surfaced as 400 INVALID_ARGUMENT
981
+ // but really a Google-side rate limit on the inline-content fetcher.
982
+ if (httpStatusCode === 400 && message) {
983
+ if (
984
+ message.includes('URL_REJECTED-REJECTED_CLIENT_THROTTLED') ||
985
+ message.includes('URL_REJECTED-REJECTED_RATE_LIMITED')
986
+ ) {
987
+ return true;
988
+ }
989
+ }
990
+
991
+ // A transport-level abort/cancel (request-timeout / dropped connection, sometimes reported
992
+ // as 499 client-closed) or a deadline-exceeded is transient and should be retried,
993
+ // even though it carries a 4xx status. Honor it before the 4xx -> non-retryable rule.
994
+ if (message) {
995
+ const lower = message.toLowerCase();
996
+ if (lower.includes('aborted') || lower.includes('cancelled') || lower.includes('deadline')) return true;
997
+ }
998
+
827
999
  // Non-retryable 4xx client errors
828
1000
  if (httpStatusCode >= 400 && httpStatusCode < 500) return false;
829
1001
 
@@ -831,6 +1003,11 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
831
1003
  return undefined;
832
1004
  }
833
1005
 
1006
+ private isNonRetryableAuthError(message: string): boolean {
1007
+ const lowerMessage = message.toLowerCase();
1008
+ return lowerMessage.includes('invalid_grant') || lowerMessage.includes("credential's issuer");
1009
+ }
1010
+
834
1011
  /**
835
1012
  * Extract error type name from error message.
836
1013
  * Google errors often include the error type in the message.
@@ -839,9 +1016,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
839
1016
  private extractErrorName(message: string): string | undefined {
840
1017
  // Common Google error patterns
841
1018
  const patterns = [
842
- /^([A-Z_]+):/, // "ERROR_NAME: message"
1019
+ /^Error code ([a-zA-Z0-9_-]+):/, // "Error code invalid_grant: message"
1020
+ /^([A-Z_]+):/, // "ERROR_NAME: message"
843
1021
  /\[([A-Z_]+)\]/, // "[ERROR_NAME] message"
844
- /^(\w+Error):/, // "ErrorTypeError: message"
1022
+ /^(\w+Error):/, // "ErrorTypeError: message"
845
1023
  ];
846
1024
 
847
1025
  for (const pattern of patterns) {
@@ -853,31 +1031,30 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
853
1031
 
854
1032
  return undefined;
855
1033
  }
856
-
857
1034
  }
858
1035
 
859
-
860
1036
  /**
861
1037
  * Converts functionCall and functionResponse parts to text parts in Gemini Content[].
862
1038
  * Preserves tool call information while removing structured parts that require
863
1039
  * tools/toolConfig to be defined in the API request.
864
1040
  */
865
1041
  export function convertGeminiFunctionPartsToText(contents: Content[]): Content[] {
866
- return contents.map(content => {
1042
+ return contents.map((content) => {
867
1043
  if (!content.parts) return content;
868
- const hasFunctionParts = content.parts.some(p => p.functionCall || p.functionResponse);
1044
+ const hasFunctionParts = content.parts.some((p) => p.functionCall || p.functionResponse);
869
1045
  if (!hasFunctionParts) return content;
870
1046
 
871
- const newParts = content.parts.map(part => {
1047
+ const newParts = content.parts.map((part) => {
872
1048
  if (part.functionCall) {
873
1049
  const argsStr = part.functionCall.args ? JSON.stringify(part.functionCall.args) : '';
874
- const truncated = argsStr.length > 500 ? argsStr.substring(0, 500) + '...' : argsStr;
1050
+ const truncated = argsStr.length > 500 ? `${argsStr.substring(0, 500)}...` : argsStr;
875
1051
  return { text: `[Tool call: ${part.functionCall.name}(${truncated})]` };
876
1052
  }
877
1053
  if (part.functionResponse) {
878
1054
  const respStr = part.functionResponse.response
879
- ? JSON.stringify(part.functionResponse.response) : 'No response';
880
- const truncated = respStr.length > 500 ? respStr.substring(0, 500) + '...' : respStr;
1055
+ ? JSON.stringify(part.functionResponse.response)
1056
+ : 'No response';
1057
+ const truncated = respStr.length > 500 ? `${respStr.substring(0, 500)}...` : respStr;
881
1058
  return { text: `[Tool result for ${part.functionResponse.name}: ${truncated}]` };
882
1059
  }
883
1060
  return part;
@@ -894,7 +1071,7 @@ function getToolDefinitions(tools: ToolDefinition[] | undefined | null): Tool |
894
1071
  // For multiple tools, we have multiple functions in one tool.
895
1072
  return {
896
1073
  functionDeclarations: tools.map(getToolFunction),
897
- }
1074
+ };
898
1075
  }
899
1076
 
900
1077
  function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
@@ -917,7 +1094,7 @@ function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
917
1094
  function updateConversation(conversation: unknown, prompt: Content[]): Content[] {
918
1095
  // Unwrap array if wrapped, otherwise treat as array
919
1096
  const unwrapped = unwrapConversationArray<Content>(conversation);
920
- const convArray = unwrapped ?? (conversation as Content[] || []);
1097
+ const convArray = unwrapped ?? ((conversation as Content[]) || []);
921
1098
  return convArray.concat(prompt);
922
1099
  }
923
1100
 
@@ -946,7 +1123,7 @@ function extractSystemFromConversation(conversation: unknown): Content | undefin
946
1123
  function storeSystemInConversation(conversation: unknown, system: Content | undefined): unknown {
947
1124
  if (!system) return conversation;
948
1125
  if (typeof conversation === 'object' && conversation !== null) {
949
- return { ...conversation as object, [SYSTEM_KEY]: system };
1126
+ return { ...(conversation as object), [SYSTEM_KEY]: system };
950
1127
  }
951
1128
  return conversation;
952
1129
  }
@@ -964,10 +1141,10 @@ function storeSystemInConversation(conversation: unknown, system: Content | unde
964
1141
  */
965
1142
  function formatFunctionResponse(response: string): JSONObject {
966
1143
  response = response.trim();
967
- if (response.startsWith("{") && response.endsWith("}")) {
1144
+ if (response.startsWith('{') && response.endsWith('}')) {
968
1145
  try {
969
1146
  return JSON.parse(response);
970
- } catch (e) {
1147
+ } catch {
971
1148
  return { output: response };
972
1149
  }
973
1150
  } else {