@juspay/neurolink 9.63.1 → 9.65.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 (358) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +18 -17
  3. package/dist/adapters/providerImageAdapter.js +29 -1
  4. package/dist/adapters/replicate/auth.d.ts +19 -0
  5. package/dist/adapters/replicate/auth.js +32 -0
  6. package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
  7. package/dist/adapters/replicate/predictionLifecycle.js +283 -0
  8. package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
  9. package/dist/adapters/video/klingVideoHandler.js +305 -0
  10. package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
  11. package/dist/adapters/video/replicateVideoHandler.js +157 -0
  12. package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
  13. package/dist/adapters/video/runwayVideoHandler.js +316 -0
  14. package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
  15. package/dist/adapters/video/vertexVideoHandler.js +42 -11
  16. package/dist/autoresearch/runner.js +8 -2
  17. package/dist/avatar/index.d.ts +13 -0
  18. package/dist/avatar/index.js +13 -0
  19. package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
  20. package/dist/avatar/providers/DIDAvatar.js +501 -0
  21. package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
  22. package/dist/avatar/providers/HeyGenAvatar.js +337 -0
  23. package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
  24. package/dist/avatar/providers/ReplicateAvatar.js +267 -0
  25. package/dist/browser/neurolink.min.js +573 -554
  26. package/dist/cli/commands/mcp.js +29 -0
  27. package/dist/cli/commands/proxy.js +24 -5
  28. package/dist/cli/factories/commandFactory.d.ts +25 -1
  29. package/dist/cli/factories/commandFactory.js +341 -63
  30. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  31. package/dist/cli/loop/optionsSchema.js +12 -0
  32. package/dist/constants/contextWindows.js +101 -0
  33. package/dist/constants/enums.d.ts +273 -2
  34. package/dist/constants/enums.js +290 -1
  35. package/dist/constants/videoErrors.d.ts +4 -0
  36. package/dist/constants/videoErrors.js +4 -0
  37. package/dist/core/baseProvider.d.ts +23 -3
  38. package/dist/core/baseProvider.js +217 -11
  39. package/dist/core/constants.d.ts +11 -0
  40. package/dist/core/constants.js +69 -1
  41. package/dist/core/modules/MessageBuilder.js +20 -0
  42. package/dist/core/redisConversationMemoryManager.js +6 -0
  43. package/dist/evaluation/index.d.ts +2 -0
  44. package/dist/evaluation/index.js +4 -0
  45. package/dist/factories/providerFactory.js +7 -1
  46. package/dist/factories/providerRegistry.js +203 -2
  47. package/dist/features/ppt/contentPlanner.js +42 -14
  48. package/dist/index.d.ts +9 -1
  49. package/dist/index.js +16 -1
  50. package/dist/lib/adapters/providerImageAdapter.js +29 -1
  51. package/dist/lib/adapters/replicate/auth.d.ts +19 -0
  52. package/dist/lib/adapters/replicate/auth.js +33 -0
  53. package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
  54. package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
  55. package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
  56. package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
  57. package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
  58. package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
  59. package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
  60. package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
  61. package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
  62. package/dist/lib/adapters/video/vertexVideoHandler.js +42 -11
  63. package/dist/lib/autoresearch/runner.js +8 -2
  64. package/dist/lib/avatar/index.d.ts +13 -0
  65. package/dist/lib/avatar/index.js +14 -0
  66. package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
  67. package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
  68. package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
  69. package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
  70. package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
  71. package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
  72. package/dist/lib/constants/contextWindows.js +101 -0
  73. package/dist/lib/constants/enums.d.ts +273 -2
  74. package/dist/lib/constants/enums.js +290 -1
  75. package/dist/lib/constants/videoErrors.d.ts +4 -0
  76. package/dist/lib/constants/videoErrors.js +4 -0
  77. package/dist/lib/core/baseProvider.d.ts +23 -3
  78. package/dist/lib/core/baseProvider.js +217 -11
  79. package/dist/lib/core/constants.d.ts +11 -0
  80. package/dist/lib/core/constants.js +69 -1
  81. package/dist/lib/core/modules/MessageBuilder.js +20 -0
  82. package/dist/lib/core/redisConversationMemoryManager.js +6 -0
  83. package/dist/lib/evaluation/index.d.ts +2 -0
  84. package/dist/lib/evaluation/index.js +4 -0
  85. package/dist/lib/factories/providerFactory.js +7 -1
  86. package/dist/lib/factories/providerRegistry.js +203 -2
  87. package/dist/lib/features/ppt/contentPlanner.js +42 -14
  88. package/dist/lib/index.d.ts +9 -1
  89. package/dist/lib/index.js +16 -1
  90. package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
  91. package/dist/lib/memory/hippocampusInitializer.js +32 -2
  92. package/dist/lib/middleware/builtin/lifecycle.js +52 -51
  93. package/dist/lib/music/index.d.ts +13 -0
  94. package/dist/lib/music/index.js +14 -0
  95. package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
  96. package/dist/lib/music/providers/BeatovenMusic.js +334 -0
  97. package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
  98. package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
  99. package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
  100. package/dist/lib/music/providers/LyriaMusic.js +173 -0
  101. package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
  102. package/dist/lib/music/providers/ReplicateMusic.js +262 -0
  103. package/dist/lib/neurolink.d.ts +30 -0
  104. package/dist/lib/neurolink.js +342 -49
  105. package/dist/lib/providers/amazonBedrock.d.ts +10 -0
  106. package/dist/lib/providers/amazonBedrock.js +94 -39
  107. package/dist/lib/providers/anthropic.js +55 -7
  108. package/dist/lib/providers/anthropicBaseProvider.js +1 -1
  109. package/dist/lib/providers/azureOpenai.js +66 -17
  110. package/dist/lib/providers/cloudflare.d.ts +35 -0
  111. package/dist/lib/providers/cloudflare.js +174 -0
  112. package/dist/lib/providers/cohere.d.ts +52 -0
  113. package/dist/lib/providers/cohere.js +253 -0
  114. package/dist/lib/providers/deepseek.js +72 -17
  115. package/dist/lib/providers/fireworks.d.ts +33 -0
  116. package/dist/lib/providers/fireworks.js +164 -0
  117. package/dist/lib/providers/googleAiStudio.d.ts +11 -3
  118. package/dist/lib/providers/googleAiStudio.js +336 -344
  119. package/dist/lib/providers/googleNativeGemini3.d.ts +107 -2
  120. package/dist/lib/providers/googleNativeGemini3.js +381 -25
  121. package/dist/lib/providers/googleVertex.d.ts +116 -129
  122. package/dist/lib/providers/googleVertex.js +3002 -1988
  123. package/dist/lib/providers/groq.d.ts +33 -0
  124. package/dist/lib/providers/groq.js +181 -0
  125. package/dist/lib/providers/huggingFace.js +9 -8
  126. package/dist/lib/providers/ideogram.d.ts +34 -0
  127. package/dist/lib/providers/ideogram.js +184 -0
  128. package/dist/lib/providers/index.d.ts +13 -0
  129. package/dist/lib/providers/index.js +13 -0
  130. package/dist/lib/providers/jina.d.ts +59 -0
  131. package/dist/lib/providers/jina.js +218 -0
  132. package/dist/lib/providers/llamaCpp.js +14 -46
  133. package/dist/lib/providers/lmStudio.js +14 -47
  134. package/dist/lib/providers/mistral.js +7 -7
  135. package/dist/lib/providers/nvidiaNim.js +160 -19
  136. package/dist/lib/providers/ollama.js +7 -7
  137. package/dist/lib/providers/openAI.d.ts +22 -1
  138. package/dist/lib/providers/openAI.js +181 -0
  139. package/dist/lib/providers/openRouter.js +38 -22
  140. package/dist/lib/providers/openaiCompatible.js +9 -8
  141. package/dist/lib/providers/perplexity.d.ts +33 -0
  142. package/dist/lib/providers/perplexity.js +179 -0
  143. package/dist/lib/providers/recraft.d.ts +34 -0
  144. package/dist/lib/providers/recraft.js +197 -0
  145. package/dist/lib/providers/replicate.d.ts +75 -0
  146. package/dist/lib/providers/replicate.js +403 -0
  147. package/dist/lib/providers/stability.d.ts +37 -0
  148. package/dist/lib/providers/stability.js +191 -0
  149. package/dist/lib/providers/togetherAi.d.ts +33 -0
  150. package/dist/lib/providers/togetherAi.js +176 -0
  151. package/dist/lib/providers/voyage.d.ts +47 -0
  152. package/dist/lib/providers/voyage.js +177 -0
  153. package/dist/lib/providers/xai.d.ts +33 -0
  154. package/dist/lib/providers/xai.js +172 -0
  155. package/dist/lib/telemetry/index.d.ts +1 -1
  156. package/dist/lib/telemetry/index.js +1 -1
  157. package/dist/lib/telemetry/tracers.d.ts +19 -0
  158. package/dist/lib/telemetry/tracers.js +19 -0
  159. package/dist/lib/telemetry/withSpan.d.ts +35 -0
  160. package/dist/lib/telemetry/withSpan.js +103 -0
  161. package/dist/lib/types/aliases.d.ts +14 -0
  162. package/dist/lib/types/avatar.d.ts +143 -0
  163. package/dist/lib/types/avatar.js +20 -0
  164. package/dist/lib/types/cli.d.ts +6 -0
  165. package/dist/lib/types/common.d.ts +0 -3
  166. package/dist/lib/types/conversation.d.ts +10 -3
  167. package/dist/lib/types/generate.d.ts +76 -5
  168. package/dist/lib/types/index.d.ts +6 -0
  169. package/dist/lib/types/index.js +8 -0
  170. package/dist/lib/types/memory.d.ts +96 -0
  171. package/dist/lib/types/memory.js +23 -0
  172. package/dist/lib/types/middleware.d.ts +27 -0
  173. package/dist/lib/types/multimodal.d.ts +35 -2
  174. package/dist/lib/types/music.d.ts +165 -0
  175. package/dist/lib/types/music.js +21 -0
  176. package/dist/lib/types/providers.d.ts +284 -3
  177. package/dist/lib/types/replicate.d.ts +67 -0
  178. package/dist/lib/types/replicate.js +10 -0
  179. package/dist/lib/types/safeFetch.d.ts +15 -0
  180. package/dist/lib/types/safeFetch.js +7 -0
  181. package/dist/lib/types/stream.d.ts +8 -1
  182. package/dist/lib/types/tools.d.ts +13 -0
  183. package/dist/lib/types/video.d.ts +89 -0
  184. package/dist/lib/types/video.js +15 -0
  185. package/dist/lib/utils/avatarProcessor.d.ts +68 -0
  186. package/dist/lib/utils/avatarProcessor.js +172 -0
  187. package/dist/lib/utils/cloneOptions.d.ts +36 -0
  188. package/dist/lib/utils/cloneOptions.js +62 -0
  189. package/dist/lib/utils/lifecycleCallbacks.d.ts +56 -0
  190. package/dist/lib/utils/lifecycleCallbacks.js +100 -0
  191. package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
  192. package/dist/lib/utils/lifecycleTimeout.js +39 -0
  193. package/dist/lib/utils/logSanitize.d.ts +49 -0
  194. package/dist/lib/utils/logSanitize.js +170 -0
  195. package/dist/lib/utils/loggingFetch.d.ts +29 -0
  196. package/dist/lib/utils/loggingFetch.js +60 -0
  197. package/dist/lib/utils/messageBuilder.d.ts +10 -0
  198. package/dist/lib/utils/messageBuilder.js +83 -30
  199. package/dist/lib/utils/modelChoices.js +236 -3
  200. package/dist/lib/utils/modelDetection.d.ts +11 -0
  201. package/dist/lib/utils/modelDetection.js +27 -0
  202. package/dist/lib/utils/musicProcessor.d.ts +67 -0
  203. package/dist/lib/utils/musicProcessor.js +189 -0
  204. package/dist/lib/utils/optionsConversion.js +3 -2
  205. package/dist/lib/utils/parameterValidation.js +14 -4
  206. package/dist/lib/utils/pricing.js +193 -0
  207. package/dist/lib/utils/providerConfig.d.ts +55 -0
  208. package/dist/lib/utils/providerConfig.js +224 -0
  209. package/dist/lib/utils/providerHealth.js +7 -7
  210. package/dist/lib/utils/safeFetch.d.ts +26 -0
  211. package/dist/lib/utils/safeFetch.js +83 -0
  212. package/dist/lib/utils/schemaConversion.d.ts +1 -1
  213. package/dist/lib/utils/schemaConversion.js +59 -4
  214. package/dist/lib/utils/sizeGuard.d.ts +34 -0
  215. package/dist/lib/utils/sizeGuard.js +45 -0
  216. package/dist/lib/utils/ssrfGuard.d.ts +52 -0
  217. package/dist/lib/utils/ssrfGuard.js +411 -0
  218. package/dist/lib/utils/tokenLimits.js +23 -32
  219. package/dist/lib/utils/videoProcessor.d.ts +60 -0
  220. package/dist/lib/utils/videoProcessor.js +201 -0
  221. package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
  222. package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
  223. package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
  224. package/dist/memory/hippocampusInitializer.d.ts +2 -2
  225. package/dist/memory/hippocampusInitializer.js +32 -2
  226. package/dist/middleware/builtin/lifecycle.js +52 -51
  227. package/dist/music/index.d.ts +13 -0
  228. package/dist/music/index.js +13 -0
  229. package/dist/music/providers/BeatovenMusic.d.ts +31 -0
  230. package/dist/music/providers/BeatovenMusic.js +333 -0
  231. package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
  232. package/dist/music/providers/ElevenLabsMusic.js +168 -0
  233. package/dist/music/providers/LyriaMusic.d.ts +29 -0
  234. package/dist/music/providers/LyriaMusic.js +172 -0
  235. package/dist/music/providers/ReplicateMusic.d.ts +31 -0
  236. package/dist/music/providers/ReplicateMusic.js +261 -0
  237. package/dist/neurolink.d.ts +30 -0
  238. package/dist/neurolink.js +342 -49
  239. package/dist/providers/amazonBedrock.d.ts +10 -0
  240. package/dist/providers/amazonBedrock.js +94 -39
  241. package/dist/providers/anthropic.js +55 -7
  242. package/dist/providers/anthropicBaseProvider.js +1 -1
  243. package/dist/providers/azureOpenai.js +66 -17
  244. package/dist/providers/cloudflare.d.ts +35 -0
  245. package/dist/providers/cloudflare.js +173 -0
  246. package/dist/providers/cohere.d.ts +52 -0
  247. package/dist/providers/cohere.js +252 -0
  248. package/dist/providers/deepseek.js +72 -17
  249. package/dist/providers/fireworks.d.ts +33 -0
  250. package/dist/providers/fireworks.js +163 -0
  251. package/dist/providers/googleAiStudio.d.ts +11 -3
  252. package/dist/providers/googleAiStudio.js +335 -344
  253. package/dist/providers/googleNativeGemini3.d.ts +107 -2
  254. package/dist/providers/googleNativeGemini3.js +381 -25
  255. package/dist/providers/googleVertex.d.ts +116 -129
  256. package/dist/providers/googleVertex.js +3000 -1987
  257. package/dist/providers/groq.d.ts +33 -0
  258. package/dist/providers/groq.js +180 -0
  259. package/dist/providers/huggingFace.js +9 -8
  260. package/dist/providers/ideogram.d.ts +34 -0
  261. package/dist/providers/ideogram.js +183 -0
  262. package/dist/providers/index.d.ts +13 -0
  263. package/dist/providers/index.js +13 -0
  264. package/dist/providers/jina.d.ts +59 -0
  265. package/dist/providers/jina.js +217 -0
  266. package/dist/providers/llamaCpp.js +14 -46
  267. package/dist/providers/lmStudio.js +14 -47
  268. package/dist/providers/mistral.js +7 -7
  269. package/dist/providers/nvidiaNim.js +160 -19
  270. package/dist/providers/ollama.js +7 -7
  271. package/dist/providers/openAI.d.ts +22 -1
  272. package/dist/providers/openAI.js +181 -0
  273. package/dist/providers/openRouter.js +38 -22
  274. package/dist/providers/openaiCompatible.js +9 -8
  275. package/dist/providers/perplexity.d.ts +33 -0
  276. package/dist/providers/perplexity.js +178 -0
  277. package/dist/providers/recraft.d.ts +34 -0
  278. package/dist/providers/recraft.js +196 -0
  279. package/dist/providers/replicate.d.ts +75 -0
  280. package/dist/providers/replicate.js +402 -0
  281. package/dist/providers/stability.d.ts +37 -0
  282. package/dist/providers/stability.js +190 -0
  283. package/dist/providers/togetherAi.d.ts +33 -0
  284. package/dist/providers/togetherAi.js +175 -0
  285. package/dist/providers/voyage.d.ts +47 -0
  286. package/dist/providers/voyage.js +176 -0
  287. package/dist/providers/xai.d.ts +33 -0
  288. package/dist/providers/xai.js +171 -0
  289. package/dist/telemetry/index.d.ts +1 -1
  290. package/dist/telemetry/index.js +1 -1
  291. package/dist/telemetry/tracers.d.ts +19 -0
  292. package/dist/telemetry/tracers.js +19 -0
  293. package/dist/telemetry/withSpan.d.ts +35 -0
  294. package/dist/telemetry/withSpan.js +103 -0
  295. package/dist/types/aliases.d.ts +14 -0
  296. package/dist/types/avatar.d.ts +143 -0
  297. package/dist/types/avatar.js +19 -0
  298. package/dist/types/cli.d.ts +6 -0
  299. package/dist/types/common.d.ts +0 -3
  300. package/dist/types/conversation.d.ts +10 -3
  301. package/dist/types/generate.d.ts +76 -5
  302. package/dist/types/index.d.ts +6 -0
  303. package/dist/types/index.js +8 -0
  304. package/dist/types/memory.d.ts +96 -0
  305. package/dist/types/memory.js +22 -0
  306. package/dist/types/middleware.d.ts +27 -0
  307. package/dist/types/multimodal.d.ts +35 -2
  308. package/dist/types/music.d.ts +165 -0
  309. package/dist/types/music.js +20 -0
  310. package/dist/types/providers.d.ts +284 -3
  311. package/dist/types/replicate.d.ts +67 -0
  312. package/dist/types/replicate.js +9 -0
  313. package/dist/types/safeFetch.d.ts +15 -0
  314. package/dist/types/safeFetch.js +6 -0
  315. package/dist/types/stream.d.ts +8 -1
  316. package/dist/types/tools.d.ts +13 -0
  317. package/dist/types/video.d.ts +89 -0
  318. package/dist/types/video.js +14 -0
  319. package/dist/utils/avatarProcessor.d.ts +68 -0
  320. package/dist/utils/avatarProcessor.js +171 -0
  321. package/dist/utils/cloneOptions.d.ts +36 -0
  322. package/dist/utils/cloneOptions.js +61 -0
  323. package/dist/utils/lifecycleCallbacks.d.ts +56 -0
  324. package/dist/utils/lifecycleCallbacks.js +99 -0
  325. package/dist/utils/lifecycleTimeout.d.ts +25 -0
  326. package/dist/utils/lifecycleTimeout.js +38 -0
  327. package/dist/utils/logSanitize.d.ts +49 -0
  328. package/dist/utils/logSanitize.js +169 -0
  329. package/dist/utils/loggingFetch.d.ts +29 -0
  330. package/dist/utils/loggingFetch.js +59 -0
  331. package/dist/utils/messageBuilder.d.ts +10 -0
  332. package/dist/utils/messageBuilder.js +83 -30
  333. package/dist/utils/modelChoices.js +236 -3
  334. package/dist/utils/modelDetection.d.ts +11 -0
  335. package/dist/utils/modelDetection.js +27 -0
  336. package/dist/utils/musicProcessor.d.ts +67 -0
  337. package/dist/utils/musicProcessor.js +188 -0
  338. package/dist/utils/optionsConversion.js +3 -2
  339. package/dist/utils/parameterValidation.js +14 -4
  340. package/dist/utils/pricing.js +193 -0
  341. package/dist/utils/providerConfig.d.ts +55 -0
  342. package/dist/utils/providerConfig.js +224 -0
  343. package/dist/utils/providerHealth.js +7 -7
  344. package/dist/utils/safeFetch.d.ts +26 -0
  345. package/dist/utils/safeFetch.js +82 -0
  346. package/dist/utils/schemaConversion.d.ts +1 -1
  347. package/dist/utils/schemaConversion.js +59 -4
  348. package/dist/utils/sizeGuard.d.ts +34 -0
  349. package/dist/utils/sizeGuard.js +44 -0
  350. package/dist/utils/ssrfGuard.d.ts +52 -0
  351. package/dist/utils/ssrfGuard.js +410 -0
  352. package/dist/utils/tokenLimits.js +23 -32
  353. package/dist/utils/videoProcessor.d.ts +60 -0
  354. package/dist/utils/videoProcessor.js +200 -0
  355. package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
  356. package/dist/voice/providers/FishAudioTTS.js +182 -0
  357. package/dist/workflow/core/ensembleExecutor.js +26 -9
  358. package/package.json +42 -8
@@ -1,22 +1,16 @@
1
- import { createGoogleGenerativeAI } from "@ai-sdk/google";
2
- import { embed, embedMany, stepCountIs, streamText, } from "ai";
1
+ import {} from "ai";
3
2
  import { ErrorCategory, ErrorSeverity, GoogleAIModels, } from "../constants/enums.js";
4
3
  import { BaseProvider } from "../core/baseProvider.js";
5
- import { DEFAULT_MAX_STEPS } from "../core/constants.js";
6
- import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
7
- import { markStreamProviderEmittedGenerationEnd, } from "../neurolink.js";
8
- import { SpanStatusCode } from "@opentelemetry/api";
9
- import { ATTR, tracers, withClientSpan } from "../telemetry/index.js";
10
- import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
4
+ import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
5
+ import { processUnifiedFilesArray } from "../utils/messageBuilder.js";
6
+ import { ATTR, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../telemetry/index.js";
7
+ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
11
8
  import { ERROR_CODES, NeuroLinkError } from "../utils/errorHandling.js";
12
9
  import { logger } from "../utils/logger.js";
13
- import { isGemini3Model } from "../utils/modelDetection.js";
14
10
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
15
11
  import { estimateTokens } from "../utils/tokenEstimation.js";
16
- import { resolveToolChoice } from "../utils/toolChoice.js";
17
- import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
18
- import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps, createTextChannel, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
19
- import { toAnalyticsStreamResult } from "./providerTypeUtils.js";
12
+ import { buildGeminiResponseSchema, buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps, createTextChannel, buildUserPartsWithMultimodal, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, prependConversationMessages, pushModelResponseToHistory, } from "./googleNativeGemini3.js";
13
+ import { createProxyFetch } from "../proxy/proxyFetch.js";
20
14
  // Google AI Live API types now imported from ../types/providerSpecific.js
21
15
  // Import proper types for multimodal message handling
22
16
  // Create Google GenAI client
@@ -34,7 +28,13 @@ async function createGoogleGenAIClient(apiKey) {
34
28
  });
35
29
  }
36
30
  const Ctor = ctor;
37
- return new Ctor({ apiKey });
31
+ // Include httpOptions with proxy fetch for corporate network support
32
+ return new Ctor({
33
+ apiKey,
34
+ httpOptions: {
35
+ fetch: createProxyFetch(),
36
+ },
37
+ });
38
38
  }
39
39
  /**
40
40
  * Google AI Studio provider implementation using BaseProvider
@@ -88,12 +88,18 @@ export class GoogleAIStudioProvider extends BaseProvider {
88
88
  return process.env.GOOGLE_AI_MODEL || GoogleAIModels.GEMINI_2_5_FLASH;
89
89
  }
90
90
  /**
91
- * 🔧 PHASE 2: Return AI SDK model instance for tool calling
91
+ * AI SDK model instance no longer used.
92
+ * All models are routed through native @google/genai SDK directly.
92
93
  */
93
94
  getAISDKModel() {
94
- const apiKey = this.getApiKey();
95
- const google = createGoogleGenerativeAI({ apiKey });
96
- return google(this.modelName);
95
+ throw new NeuroLinkError({
96
+ code: ERROR_CODES.INVALID_CONFIGURATION,
97
+ message: "GoogleAIStudioProvider no longer uses @ai-sdk/google. All models use native @google/genai SDK.",
98
+ category: ErrorCategory.CONFIGURATION,
99
+ severity: ErrorSeverity.CRITICAL,
100
+ retriable: false,
101
+ context: { provider: this.providerName, model: this.modelName },
102
+ });
97
103
  }
98
104
  formatProviderError(error) {
99
105
  if (error instanceof TimeoutError) {
@@ -103,12 +109,53 @@ export class GoogleAIStudioProvider extends BaseProvider {
103
109
  const message = typeof errorRecord?.message === "string"
104
110
  ? errorRecord.message
105
111
  : "Unknown error";
106
- if (message.includes("API_KEY_INVALID")) {
112
+ const statusCode = typeof errorRecord?.status === "number"
113
+ ? errorRecord.status
114
+ : typeof errorRecord?.statusCode === "number"
115
+ ? errorRecord.statusCode
116
+ : undefined;
117
+ // Authentication errors
118
+ if (message.includes("API_KEY_INVALID") ||
119
+ message.includes("Invalid API key") ||
120
+ statusCode === 401) {
107
121
  return new AuthenticationError("Invalid Google AI API key. Please check your GOOGLE_AI_API_KEY environment variable.", this.providerName);
108
122
  }
109
- if (message.includes("RATE_LIMIT_EXCEEDED")) {
123
+ // Rate limit errors
124
+ if (message.includes("RATE_LIMIT_EXCEEDED") ||
125
+ message.includes("rate limit") ||
126
+ message.includes("429") ||
127
+ statusCode === 429) {
110
128
  return new RateLimitError("Google AI rate limit exceeded. Please try again later.", this.providerName);
111
129
  }
130
+ // Model not found errors — gate on a 404 status when available; fall
131
+ // back to literal phrase matching only when we have no status code at
132
+ // all. Avoids misclassifying permission/validation errors that happen
133
+ // to mention model resource paths (e.g. "...models/foo permission...").
134
+ if (statusCode === 404 ||
135
+ (statusCode === undefined &&
136
+ (message.includes("model not found") ||
137
+ message.includes("Model not found")))) {
138
+ return new InvalidModelError(`Model '${this.modelName}' not found. Please check the model name and ensure it is available.`, this.providerName);
139
+ }
140
+ // Network connectivity errors
141
+ if (message.includes("ECONNRESET") ||
142
+ message.includes("ENOTFOUND") ||
143
+ message.includes("ETIMEDOUT") ||
144
+ message.includes("ECONNREFUSED") ||
145
+ message.includes("network") ||
146
+ message.includes("connection")) {
147
+ return new NetworkError(`Connection error: ${message}`, this.providerName);
148
+ }
149
+ // Server errors (5xx)
150
+ if (message.includes("500") ||
151
+ message.includes("502") ||
152
+ message.includes("503") ||
153
+ message.includes("504") ||
154
+ message.includes("server error") ||
155
+ message.includes("Internal Server Error") ||
156
+ (statusCode && statusCode >= 500 && statusCode < 600)) {
157
+ return new ProviderError(`Google AI server error: ${message}. Please try again later.`, this.providerName);
158
+ }
112
159
  return new ProviderError(`Google AI error: ${message}`, this.providerName);
113
160
  }
114
161
  /**
@@ -388,202 +435,53 @@ export class GoogleAIStudioProvider extends BaseProvider {
388
435
  }
389
436
  // executeGenerate removed - BaseProvider handles all generation with tools
390
437
  async executeStream(options, analysisSchema) {
391
- // Check if this is a Gemini 3 model with tools - use native SDK for thought_signature
392
- const gemini3CheckModelName = options.model || this.modelName;
393
- // Structured output (analysisSchema, JSON format, or schema) is incompatible with tools on Gemini.
394
- // Compute once and reuse in both the native Gemini 3 gate and the streamText fallback path.
395
- const wantsStructuredOutput = analysisSchema || options.output?.format === "json" || options.schema;
396
- // Check for tools from options AND from SDK (MCP tools)
397
- // Need to check early if we should route to native SDK
398
- const gemini3CheckShouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
399
- const tools = options.tools || {};
400
- const hasTools = gemini3CheckShouldUseTools && Object.keys(tools).length > 0;
401
- if (isGemini3Model(gemini3CheckModelName) && hasTools) {
402
- // Merge SDK tools into options for native SDK path
403
- let mergedOptions = {
404
- ...options,
405
- tools: tools,
406
- };
407
- // Check for tools + JSON schema conflict (Gemini limitation)
408
- const wantsJsonOutput = options.output?.format === "json" || options.schema;
409
- if (wantsJsonOutput &&
410
- mergedOptions.tools &&
411
- Object.keys(mergedOptions.tools).length > 0 &&
412
- !mergedOptions.disableTools) {
413
- logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
414
- mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
415
- }
416
- // Only route to native path if tools are still active after conflict check
417
- const hasActiveTools = !mergedOptions.disableTools &&
418
- mergedOptions.tools &&
419
- Object.keys(mergedOptions.tools).length > 0;
420
- if (hasActiveTools) {
421
- logger.info("[GoogleAIStudio] Routing Gemini 3 to native SDK for tool calling", {
422
- model: gemini3CheckModelName,
423
- totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
424
- });
425
- return this.executeNativeGemini3Stream(mergedOptions);
426
- }
427
- // Fall through to standard stream path using merged options (tools disabled for schema)
428
- options = mergedOptions;
429
- }
438
+ const modelName = options.model || this.modelName;
430
439
  // Phase 1: if audio input present, bridge to Gemini Live (Studio) using @google/genai
431
440
  if (options.input?.audio) {
432
441
  return await this.executeAudioStreamViaGeminiLive(options);
433
442
  }
434
- this.validateStreamOptions(options);
435
- const startTime = Date.now();
436
- const model = await this.getAISDKModelWithMiddleware(options);
437
- const timeout = this.getTimeout(options);
438
- const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
439
- try {
440
- // Get tools consistently with generate method (include user-provided RAG tools)
441
- // wantsStructuredOutput already computed before the Gemini 3 native-routing gate
442
- if (wantsStructuredOutput &&
443
- !options.disableTools &&
444
- this.supportsTools()) {
445
- logger.warn("[GoogleAIStudio] Structured output active — disabling tools (Gemini limitation).");
446
- }
447
- const shouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
448
- const filteredTools = shouldUseTools
449
- ? (options.tools ?? {})
450
- : {};
451
- // Sanitize tool schemas for Gemini proto compatibility (converts anyOf/oneOf unions to string)
452
- let tools;
453
- if (Object.keys(filteredTools).length > 0) {
454
- const sanitized = sanitizeToolsForGemini(filteredTools);
455
- if (sanitized.dropped.length > 0) {
456
- logger.warn(`[GoogleAIStudio] Dropped ${sanitized.dropped.length} incompatible tool(s): ${sanitized.dropped.join(", ")}`);
457
- }
458
- tools =
459
- Object.keys(sanitized.tools).length > 0 ? sanitized.tools : undefined;
460
- }
461
- else {
462
- tools = undefined;
463
- }
464
- // Build message array from options with multimodal support
465
- // Using protected helper from BaseProvider to eliminate code duplication
466
- const messages = await this.buildMessagesForStream(options);
467
- const collectedToolCalls = [];
468
- const collectedToolResults = [];
469
- // Reviewer follow-up: capture upstream provider errors via onError
470
- // so the post-stream NoOutput sentinel carries the real cause.
471
- let capturedProviderError;
472
- const result = await streamText({
473
- model,
474
- messages: messages,
475
- temperature: options.temperature,
476
- maxOutputTokens: options.maxTokens, // No default limit - unlimited unless specified
477
- tools,
478
- stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
479
- toolChoice: resolveToolChoice(options, tools, shouldUseTools),
480
- abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
481
- experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
482
- experimental_repairToolCall: this.getToolCallRepairFn(options),
483
- onError: (event) => {
484
- capturedProviderError = event.error;
485
- logger.error("GoogleAiStudio: Stream error", {
486
- error: event.error instanceof Error
487
- ? event.error.message
488
- : String(event.error),
489
- });
490
- },
491
- // Gemini 3: use thinkingLevel via providerOptions
492
- // Gemini 2.5: use thinkingBudget via providerOptions
493
- ...(options.thinkingConfig?.enabled && {
494
- providerOptions: {
495
- google: {
496
- thinkingConfig: {
497
- ...(options.thinkingConfig.thinkingLevel && {
498
- thinkingLevel: options.thinkingConfig.thinkingLevel,
499
- }),
500
- ...(options.thinkingConfig.budgetTokens &&
501
- !options.thinkingConfig.thinkingLevel && {
502
- thinkingBudget: options.thinkingConfig.budgetTokens,
503
- }),
504
- includeThoughts: true,
505
- },
506
- },
507
- },
508
- }),
509
- onStepFinish: ({ toolCalls, toolResults }) => {
510
- for (const toolCall of toolCalls) {
511
- collectedToolCalls.push({
512
- toolCallId: toolCall.toolCallId,
513
- toolName: toolCall.toolName,
514
- args: toolCall.args ??
515
- toolCall.input ??
516
- toolCall
517
- .parameters ??
518
- {},
519
- });
520
- }
521
- for (const toolResult of toolResults) {
522
- const rawToolResult = toolResult;
523
- collectedToolResults.push({
524
- toolName: toolResult.toolName,
525
- status: rawToolResult.error ? "failure" : "success",
526
- output: (rawToolResult.output ??
527
- rawToolResult.result) ??
528
- undefined,
529
- error: rawToolResult.error,
530
- id: rawToolResult.toolCallId ?? toolResult.toolName,
531
- });
532
- }
533
- // Emit tool:end for each completed tool result so Pipeline B
534
- // captures telemetry for AI-SDK-driven tool calls (gap S2).
535
- emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResults);
536
- this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
537
- logger.warn("[GoogleAiStudioProvider] Failed to store tool executions", {
538
- provider: this.providerName,
539
- error: error instanceof Error ? error.message : String(error),
540
- });
541
- });
542
- },
543
- });
544
- // Defer timeout cleanup until the stream completes or errors.
545
- // Guard against NoOutputGeneratedError becoming an unhandled rejection.
546
- Promise.resolve(result.text)
547
- .catch((err) => {
548
- logger.debug("Stream text promise rejected (expected for empty streams)", {
549
- error: err instanceof Error ? err.message : String(err),
550
- });
551
- })
552
- .finally(() => timeoutController?.cleanup());
553
- // Transform string stream to content object stream using BaseProvider method
554
- const transformedStream = this.createTextStream(result, () => capturedProviderError);
555
- // Create analytics promise that resolves after stream completion
556
- const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, toAnalyticsStreamResult(result), Date.now() - startTime, {
557
- requestId: `google-ai-stream-${Date.now()}`,
558
- streamingMode: true,
559
- });
560
- return {
561
- stream: transformedStream,
562
- provider: this.providerName,
563
- model: this.modelName,
564
- ...(shouldUseTools && {
565
- toolCalls: collectedToolCalls,
566
- toolResults: collectedToolResults,
567
- }),
568
- analytics: analyticsPromise,
569
- metadata: {
570
- startTime,
571
- streamId: `google-ai-${Date.now()}`,
572
- },
573
- };
443
+ // Structured output (analysisSchema, JSON format, or schema) is incompatible with tools on Gemini.
444
+ const wantsStructuredOutput = analysisSchema || options.output?.format === "json" || options.schema;
445
+ // Tool filter (a0269210): trust options.tools — caller (BaseProvider.stream)
446
+ // already merged MCP/built-in tools with user tools and applied any
447
+ // enabledToolNames filter. Re-attaching getAllTools() here would clobber
448
+ // that filter and re-introduce filtered-out tools.
449
+ const shouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
450
+ const optionTools = options.tools || {};
451
+ // Merge into options for native SDK path
452
+ let mergedOptions = {
453
+ ...options,
454
+ tools: optionTools,
455
+ };
456
+ // Check for tools + JSON schema conflict (Gemini limitation)
457
+ const wantsJsonOutput = options.output?.format === "json" || options.schema;
458
+ if (wantsJsonOutput &&
459
+ mergedOptions.tools &&
460
+ Object.keys(mergedOptions.tools).length > 0 &&
461
+ !mergedOptions.disableTools) {
462
+ logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
463
+ mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
574
464
  }
575
- catch (error) {
576
- timeoutController?.cleanup();
577
- throw this.handleProviderError(error);
465
+ const hasActiveTools = shouldUseTools &&
466
+ !mergedOptions.disableTools &&
467
+ mergedOptions.tools &&
468
+ Object.keys(mergedOptions.tools).length > 0;
469
+ if (hasActiveTools) {
470
+ logger.info("[GoogleAIStudio] Routing to native @google/genai SDK for tool calling", {
471
+ model: modelName,
472
+ totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
473
+ });
578
474
  }
475
+ // Route ALL models through native @google/genai SDK (no more @ai-sdk/google dependency)
476
+ return this.executeNativeGemini3Stream(mergedOptions);
579
477
  }
580
478
  /**
581
- * Execute stream using native @google/genai SDK for Gemini 3 models
582
- * This bypasses @ai-sdk/google to properly handle thought_signature
479
+ * Execute stream using native @google/genai SDK
480
+ * Uses @google/genai directly for all Gemini models (2.0, 2.5, 3.x)
583
481
  */
584
482
  async executeNativeGemini3Stream(options) {
585
483
  const modelName = options.model || this.modelName;
586
- return withClientSpan({
484
+ return withClientStreamSpan({
587
485
  name: "neurolink.provider.stream",
588
486
  tracer: tracers.provider,
589
487
  attributes: {
@@ -603,23 +501,54 @@ export class GoogleAIStudioProvider extends BaseProvider {
603
501
  model: modelName,
604
502
  hasTools: !!options.tools && Object.keys(options.tools).length > 0,
605
503
  });
606
- // Build contents from input
607
- const currentContents = [{ role: "user", parts: [{ text: options.input.text }] }];
504
+ // Build contents from input. Prepend prior conversation turns so
505
+ // multi-turn callers (memory, loop REPL, agent flows) actually
506
+ // carry context — the previous build started fresh from the
507
+ // current user input only, which silently dropped history.
508
+ //
509
+ // `buildUserPartsWithMultimodal` is the shared helper that also
510
+ // attaches `input.images` and `input.pdfFiles` as `inlineData`
511
+ // parts. The previous AI Studio path pushed only `{ text }` and
512
+ // silently dropped both, which is why the model legitimately
513
+ // reported "no image attached" on multimodal calls.
514
+ const currentContents = [];
515
+ prependConversationMessages(currentContents, options.conversationMessages);
516
+ const userParts = await buildUserPartsWithMultimodal(options.input, options.input.text, "[GoogleAIStudio:stream]");
517
+ currentContents.push({
518
+ role: "user",
519
+ parts: userParts,
520
+ });
608
521
  // Convert tools
609
522
  let toolsConfig;
610
523
  let executeMap = new Map();
524
+ let originalNameMap = new Map();
611
525
  if (options.tools &&
612
526
  Object.keys(options.tools).length > 0 &&
613
527
  !options.disableTools) {
614
528
  const result = buildNativeToolDeclarations(options.tools);
615
529
  toolsConfig = result.toolsConfig;
616
530
  executeMap = result.executeMap;
531
+ originalNameMap = result.originalNameMap;
617
532
  logger.debug("[GoogleAIStudio] Converted tools for native SDK", {
618
533
  toolCount: toolsConfig[0].functionDeclarations.length,
619
534
  toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
620
535
  });
621
536
  }
622
- const config = buildNativeConfig(options, toolsConfig);
537
+ // Native JSON / schema enforcement: when no tools are being sent
538
+ // (the AI Studio orchestrator above already force-disables tools
539
+ // whenever JSON/schema output is requested), enforce the response
540
+ // shape natively via responseMimeType / responseSchema. Without
541
+ // this, JSON output was best-effort prompting only.
542
+ const wantsNativeJson = !toolsConfig &&
543
+ (options.output?.format === "json" || !!options.schema);
544
+ const nativeResponseSchema = wantsNativeJson && options.schema
545
+ ? buildGeminiResponseSchema(options.schema)
546
+ : undefined;
547
+ const config = buildNativeConfig({
548
+ ...options,
549
+ wantsJsonOutput: wantsNativeJson,
550
+ responseSchema: nativeResponseSchema,
551
+ }, toolsConfig);
623
552
  const maxSteps = computeMaxSteps(options.maxSteps);
624
553
  // Compose abort signal from user signal + timeout
625
554
  const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
@@ -698,7 +627,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
698
627
  logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
699
628
  // Add model response with ALL parts (including thoughtSignature) to history
700
629
  pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
701
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
630
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal, originalNameMap });
702
631
  // Add function responses to history — the @google/genai SDK
703
632
  // only accepts "user" and "model" as valid roles in contents.
704
633
  // Function/tool responses must use role: "user" (matching the
@@ -742,68 +671,9 @@ export class GoogleAIStudioProvider extends BaseProvider {
742
671
  requestDuration: responseTime,
743
672
  timestamp: new Date().toISOString(),
744
673
  });
745
- // Emit generation:end so Pipeline B (Langfuse) creates a GENERATION
746
- // observation. The native @google/genai stream path bypasses the Vercel
747
- // AI SDK so experimental_telemetry is never injected; we emit manually.
748
- const nativeStreamEmitter = this.neurolink?.getEventEmitter();
749
- if (nativeStreamEmitter) {
750
- // Curator P2-4 dedup: flag the per-stream context attached
751
- // to options so the orchestration skips its own emit.
752
- markStreamProviderEmittedGenerationEnd(options);
753
- nativeStreamEmitter.emit("generation:end", {
754
- provider: this.providerName,
755
- responseTime,
756
- timestamp: Date.now(),
757
- result: {
758
- content: "",
759
- usage: {
760
- input: totalInputTokens,
761
- output: totalOutputTokens,
762
- total: totalInputTokens + totalOutputTokens,
763
- },
764
- model: modelName,
765
- provider: this.providerName,
766
- finishReason: hitStepLimitWithoutFinalAnswer
767
- ? "max_steps"
768
- : "stop",
769
- },
770
- success: true,
771
- });
772
- }
773
674
  channel.close();
774
675
  }
775
676
  catch (err) {
776
- // Propagate error to OTel span so traces show ERROR status
777
- span.recordException(err instanceof Error ? err : new Error(String(err)));
778
- span.setStatus({
779
- code: SpanStatusCode.ERROR,
780
- message: err instanceof Error ? err.message : String(err),
781
- });
782
- // Emit failure generation:end so Pipeline B records the failed stream
783
- const errorEmitter = this.neurolink?.getEventEmitter();
784
- if (errorEmitter) {
785
- // Curator P2-4 dedup: flag the per-stream context attached
786
- // to options so the orchestration skips its own emit.
787
- markStreamProviderEmittedGenerationEnd(options);
788
- errorEmitter.emit("generation:end", {
789
- provider: this.providerName,
790
- responseTime: Date.now() - startTime,
791
- timestamp: Date.now(),
792
- result: {
793
- content: "",
794
- usage: {
795
- input: totalInputTokens,
796
- output: totalOutputTokens,
797
- total: totalInputTokens + totalOutputTokens,
798
- },
799
- model: modelName,
800
- provider: this.providerName,
801
- finishReason: "error",
802
- },
803
- success: false,
804
- error: err instanceof Error ? err.message : String(err),
805
- });
806
- }
807
677
  channel.error(err);
808
678
  analyticsReject(err);
809
679
  }
@@ -826,7 +696,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
826
696
  finally {
827
697
  // Timeout controller cleanup is managed inside the background loop
828
698
  }
829
- });
699
+ }, (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
830
700
  }
831
701
  /**
832
702
  * Execute generate using native @google/genai SDK for Gemini 3 models
@@ -858,10 +728,24 @@ export class GoogleAIStudioProvider extends BaseProvider {
858
728
  // Prefer input.text over prompt — processCSVFilesForNativeSDK enriches
859
729
  // input.text with inlined CSV data, so using prompt first would discard it.
860
730
  const promptText = options.input?.text || options.prompt || "";
861
- const currentContents = [{ role: "user", parts: [{ text: promptText }] }];
862
- // Convert tools (merge SDK tools with options.tools)
731
+ // Prepend prior conversation turns so multi-turn generate calls
732
+ // see history; otherwise the native generate path silently drops
733
+ // every turn before the current prompt.
734
+ //
735
+ // `buildUserPartsWithMultimodal` also attaches inline image / PDF
736
+ // parts. Without it the request body was text-only and the model
737
+ // legitimately reported "no image / PDF attached".
738
+ const currentContents = [];
739
+ prependConversationMessages(currentContents, options.conversationMessages);
740
+ const userParts = await buildUserPartsWithMultimodal(options.input, promptText, "[GoogleAIStudio:generate]");
741
+ currentContents.push({
742
+ role: "user",
743
+ parts: userParts,
744
+ });
745
+ // Convert tools (a0269210: trust options.tools — already merged + filtered upstream)
863
746
  let toolsConfig;
864
747
  let executeMap = new Map();
748
+ let originalNameMap = new Map();
865
749
  const shouldUseTools = !options.disableTools;
866
750
  if (shouldUseTools) {
867
751
  const tools = options.tools || {};
@@ -869,13 +753,26 @@ export class GoogleAIStudioProvider extends BaseProvider {
869
753
  const result = buildNativeToolDeclarations(tools);
870
754
  toolsConfig = result.toolsConfig;
871
755
  executeMap = result.executeMap;
756
+ originalNameMap = result.originalNameMap;
872
757
  logger.debug("[GoogleAIStudio] Converted tools for native SDK generate", {
873
758
  toolCount: toolsConfig[0].functionDeclarations.length,
874
759
  toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
875
760
  });
876
761
  }
877
762
  }
878
- const config = buildNativeConfig(options, toolsConfig);
763
+ // Native JSON / schema enforcement (generate path). Mirrors the
764
+ // stream block above; only set when no tools are being sent
765
+ // because Gemini cannot combine function calling with JSON mime.
766
+ const wantsNativeJson = !toolsConfig &&
767
+ (options.output?.format === "json" || !!options.schema);
768
+ const nativeResponseSchema = wantsNativeJson && options.schema
769
+ ? buildGeminiResponseSchema(options.schema)
770
+ : undefined;
771
+ const config = buildNativeConfig({
772
+ ...options,
773
+ wantsJsonOutput: wantsNativeJson,
774
+ responseSchema: nativeResponseSchema,
775
+ }, toolsConfig);
879
776
  const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
880
777
  const maxSteps = computeMaxSteps(options.maxSteps);
881
778
  let finalText = "";
@@ -925,7 +822,11 @@ export class GoogleAIStudioProvider extends BaseProvider {
925
822
  // Add model response with ALL parts (including thoughtSignature) to history
926
823
  // This is critical for Gemini 3 - it requires thought signatures in subsequent turns
927
824
  pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
928
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
825
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, {
826
+ toolExecutions,
827
+ abortSignal: composedSignal,
828
+ originalNameMap,
829
+ });
929
830
  // Add function responses to history — the @google/genai SDK
930
831
  // only accepts "user" and "model" as valid roles in contents.
931
832
  // Function/tool responses must use role: "user" (matching the
@@ -946,31 +847,11 @@ export class GoogleAIStudioProvider extends BaseProvider {
946
847
  span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
947
848
  span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
948
849
  span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= maxSteps ? "max_steps" : "stop");
949
- // Emit generation:end so Pipeline B (Langfuse) creates a GENERATION
950
- // observation. The native @google/genai path bypasses the Vercel AI SDK
951
- // so experimental_telemetry is never injected; we emit the event manually.
952
- const nativeGenerateEmitter = this.neurolink?.getEventEmitter();
953
- if (nativeGenerateEmitter) {
954
- nativeGenerateEmitter.emit("generation:end", {
955
- provider: this.providerName,
956
- responseTime,
957
- timestamp: Date.now(),
958
- result: {
959
- content: finalText,
960
- usage: {
961
- input: totalInputTokens,
962
- output: totalOutputTokens,
963
- total: totalInputTokens + totalOutputTokens,
964
- },
965
- model: modelName,
966
- provider: this.providerName,
967
- finishReason: step >= maxSteps ? "max_steps" : "stop",
968
- },
969
- success: true,
970
- });
971
- }
972
- // Build EnhancedGenerateResult
973
- return {
850
+ // Build EnhancedGenerateResult and route through enhanceResult so
851
+ // analytics / evaluation / tracing stay attached. The native AI
852
+ // Studio generate path bypasses BaseProvider.generate(), so
853
+ // skipping enhanceResult would silently drop those features.
854
+ const baseResult = {
974
855
  content: finalText,
975
856
  provider: this.providerName,
976
857
  model: modelName,
@@ -984,6 +865,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
984
865
  toolExecutions: toolExecutions,
985
866
  enhancedWithTools: allToolCalls.length > 0,
986
867
  };
868
+ return this.enhanceResult(baseResult, options, startTime);
987
869
  }
988
870
  finally {
989
871
  timeoutController?.cleanup();
@@ -999,40 +881,147 @@ export class GoogleAIStudioProvider extends BaseProvider {
999
881
  ? { prompt: optionsOrPrompt }
1000
882
  : optionsOrPrompt;
1001
883
  const modelName = options.model || this.modelName;
1002
- // Check if we should use native SDK for Gemini 3 with tools
1003
- const shouldUseTools = !options.disableTools && this.supportsTools();
1004
- const hasTools = shouldUseTools && options.tools && Object.keys(options.tools).length > 0;
1005
- if (isGemini3Model(modelName) && hasTools) {
1006
- // Merge SDK tools into options for native SDK path
1007
- let mergedOptions = {
1008
- ...options,
1009
- tools: options.tools,
1010
- };
1011
- // Check for tools + JSON schema conflict (Gemini limitation)
1012
- const wantsJsonOutput = options.output?.format === "json" || options.schema;
1013
- if (wantsJsonOutput &&
1014
- mergedOptions.tools &&
1015
- Object.keys(mergedOptions.tools).length > 0 &&
1016
- !mergedOptions.disableTools) {
1017
- logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
1018
- mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
884
+ // Image-generation models reject function-calling. Route them to
885
+ // executeImageGeneration without merging tools. This must happen
886
+ // BEFORE getToolsForStream to avoid leaking registered (MCP / built-in)
887
+ // tools into the image API request, which trips
888
+ // "Function calling is not enabled for this model".
889
+ // startsWith (not includes) so a hypothetical text model whose ID
890
+ // contains an image-model string as a substring isn't silently routed
891
+ // to executeImageGeneration and stripped of tool support.
892
+ const isImageModel = IMAGE_GENERATION_MODELS.some((m) => modelName.toLowerCase().startsWith(m.toLowerCase()));
893
+ if (isImageModel) {
894
+ logger.info("[GoogleAIStudio] Routing image generation model to executeImageGeneration", { model: modelName });
895
+ return this.executeImageGeneration(options);
896
+ }
897
+ // TTS direct-synthesis mode: synthesise the input text directly (no LLM
898
+ // call). BaseProvider.runGenerateInActiveContext does the same dispatch
899
+ // replicated here because AI Studio's override bypasses that path.
900
+ if (options.tts?.enabled && !options.tts?.useAiResponse) {
901
+ logger.info("[GoogleAIStudio] Routing TTS direct-synthesis to handleDirectTTSSynthesis", { model: modelName });
902
+ return this.handleDirectTTSSynthesis(options, Date.now());
903
+ }
904
+ // Process the unified `input.files` array before routing to the
905
+ // native SDK. BaseProvider.generate() runs this preprocessing via
906
+ // buildMultimodalMessagesArray, but AI Studio's override skips it,
907
+ // which would otherwise drop text-file content (and the
908
+ // mimetype-hint contract) on the floor. Mutates options.input.text /
909
+ // options.input.images / options.input.pdfFiles in place.
910
+ if (options.input?.files && options.input.files.length > 0) {
911
+ try {
912
+ await processUnifiedFilesArray(options, 100 * 1024 * 1024, this.providerName);
1019
913
  }
1020
- // Only route to native path if tools are still active after conflict check
1021
- const hasActiveTools = !mergedOptions.disableTools &&
1022
- mergedOptions.tools &&
1023
- Object.keys(mergedOptions.tools).length > 0;
1024
- if (hasActiveTools) {
1025
- logger.info("[GoogleAIStudio] Routing Gemini 3 generate to native SDK for tool calling", {
1026
- model: modelName,
1027
- totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
1028
- });
1029
- return this.executeNativeGemini3Generate(mergedOptions);
914
+ catch (fileError) {
915
+ logger.warn(`[GoogleAIStudio] processUnifiedFilesArray threw, continuing without file content: ${fileError instanceof Error ? fileError.message : String(fileError)}`);
1030
916
  }
1031
- // Fall through to standard generate path using merged options (tools disabled for schema)
1032
- return super.generate(mergedOptions);
1033
917
  }
1034
- // Fall back to BaseProvider implementation
1035
- return super.generate(options);
918
+ // Merge registered (built-in / MCP) tools with caller-supplied tools.
919
+ // AI Studio's generate() bypasses BaseProvider.generate(), so the
920
+ // ToolsManager-driven merge that normally injects sdk.registerTool()
921
+ // entries never runs here. Without this call, registered tools never
922
+ // reach the native function-calling path.
923
+ const baseTools = !options.disableTools
924
+ ? await this.getToolsForStream(options)
925
+ : {};
926
+ let mergedOptions = {
927
+ ...options,
928
+ tools: baseTools,
929
+ };
930
+ // Check for tools + JSON schema conflict (Gemini limitation)
931
+ const wantsJsonOutput = options.output?.format === "json" || options.schema;
932
+ if (wantsJsonOutput &&
933
+ mergedOptions.tools &&
934
+ Object.keys(mergedOptions.tools).length > 0 &&
935
+ !mergedOptions.disableTools) {
936
+ logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
937
+ mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
938
+ }
939
+ const hasActiveTools = !mergedOptions.disableTools &&
940
+ mergedOptions.tools &&
941
+ Object.keys(mergedOptions.tools).length > 0;
942
+ if (hasActiveTools) {
943
+ logger.info("[GoogleAIStudio] Routing generate to native @google/genai SDK for tool calling", {
944
+ model: modelName,
945
+ totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
946
+ });
947
+ }
948
+ // Route ALL models through native @google/genai SDK (no more @ai-sdk/google dependency).
949
+ // Emit Pipeline B `generation:end` so the observability listener
950
+ // creates a `model.generation` span — AI Studio's native path bypasses
951
+ // the AI SDK + experimental_telemetry plumbing the same way Vertex's
952
+ // does, so the event has to be emitted manually.
953
+ const generateStartTime = Date.now();
954
+ const inputPrompt = mergedOptions.input?.text ||
955
+ mergedOptions.prompt ||
956
+ "";
957
+ try {
958
+ // Wrap in `neurolink.executeGeneration` so the observability span
959
+ // chain (Test: Generate Span Chain) sees a third inner span on the
960
+ // native @google/genai path — Pipeline A providers get this from
961
+ // GenerationHandler.executeGeneration; the native path bypasses
962
+ // GenerationHandler so we add the span here.
963
+ let result = await withSpan({
964
+ name: "neurolink.executeGeneration",
965
+ tracer: tracers.provider,
966
+ attributes: {
967
+ [ATTR.GEN_AI_SYSTEM]: this.providerName,
968
+ [ATTR.GEN_AI_MODEL]: modelName,
969
+ "neurolink.path": "native.google-genai",
970
+ },
971
+ }, async () => this.executeNativeGemini3Generate(mergedOptions));
972
+ // Pipe through TTS-of-AI-response when caller asks for it. No-op when
973
+ // tts is disabled or useAiResponse is false.
974
+ result = await this.synthesizeAIResponseIfNeeded(result, options);
975
+ this.emitPipelineBGenerationEvent(modelName, result, generateStartTime, true, undefined, inputPrompt);
976
+ return result;
977
+ }
978
+ catch (error) {
979
+ this.emitPipelineBGenerationEvent(modelName, null, generateStartTime, false, error, inputPrompt);
980
+ throw error;
981
+ }
982
+ }
983
+ /**
984
+ * Emit `generation:end` so the Pipeline B observability listener creates
985
+ * a `model.generation` span for native Google AI Studio generate calls.
986
+ * Without this hand-off the native path silently disappears from
987
+ * Pipeline B exporters (Langfuse, custom OTEL collectors).
988
+ */
989
+ emitPipelineBGenerationEvent(modelName, result, startTime, success, error, prompt) {
990
+ const emitter = this.neurolink?.getEventEmitter();
991
+ if (!emitter) {
992
+ return;
993
+ }
994
+ const usage = result?.usage && typeof result.usage === "object"
995
+ ? result.usage
996
+ : { input: 0, output: 0, total: 0 };
997
+ // Mark on the result so the SDK-level runStandardGenerateRequest knows
998
+ // this provider already emitted `generation:end` itself and skips its
999
+ // own duplicate emission. Without this flag the public event listener
1000
+ // (and the observability test) would see two events per generate call.
1001
+ if (result && typeof result === "object") {
1002
+ result._generationEndEmitted =
1003
+ true;
1004
+ }
1005
+ emitter.emit("generation:end", {
1006
+ provider: this.providerName,
1007
+ responseTime: Date.now() - startTime,
1008
+ timestamp: Date.now(),
1009
+ // The Pipeline B listener reads `data.prompt` to populate the
1010
+ // `input` span attribute. Without this, the Observability Spans
1011
+ // test fails with "input capture not working".
1012
+ prompt: prompt || "",
1013
+ result: {
1014
+ content: result?.content || "",
1015
+ usage,
1016
+ model: modelName,
1017
+ provider: this.providerName,
1018
+ finishReason: success ? "stop" : "error",
1019
+ },
1020
+ success,
1021
+ ...(error
1022
+ ? { error: error instanceof Error ? error.message : String(error) }
1023
+ : {}),
1024
+ });
1036
1025
  }
1037
1026
  // ===================
1038
1027
  // HELPER METHODS
@@ -1240,18 +1229,21 @@ export class GoogleAIStudioProvider extends BaseProvider {
1240
1229
  });
1241
1230
  try {
1242
1231
  const apiKey = this.getApiKey();
1243
- const google = createGoogleGenerativeAI({ apiKey });
1244
- const embeddingModel = google.textEmbeddingModel(embeddingModelName);
1245
- const result = await embed({
1246
- model: embeddingModel,
1247
- value: text,
1232
+ const client = await createGoogleGenAIClient(apiKey);
1233
+ const result = await client.models.embedContent({
1234
+ model: embeddingModelName,
1235
+ contents: [text],
1248
1236
  });
1237
+ const embedding = result.embeddings?.[0]?.values;
1238
+ if (!embedding) {
1239
+ throw new ProviderError("No embedding returned from Google AI", this.providerName);
1240
+ }
1249
1241
  logger.debug("Embedding generated successfully", {
1250
1242
  provider: this.providerName,
1251
1243
  model: embeddingModelName,
1252
- embeddingDimension: result.embedding.length,
1244
+ embeddingDimension: embedding.length,
1253
1245
  });
1254
- return result.embedding;
1246
+ return embedding;
1255
1247
  }
1256
1248
  catch (error) {
1257
1249
  logger.error("Embedding generation failed", {
@@ -1277,19 +1269,19 @@ export class GoogleAIStudioProvider extends BaseProvider {
1277
1269
  });
1278
1270
  try {
1279
1271
  const apiKey = this.getApiKey();
1280
- const google = createGoogleGenerativeAI({ apiKey });
1281
- const embeddingModel = google.textEmbeddingModel(embeddingModelName);
1282
- const result = await embedMany({
1283
- model: embeddingModel,
1284
- values: texts,
1272
+ const client = await createGoogleGenAIClient(apiKey);
1273
+ const result = await client.models.embedContent({
1274
+ model: embeddingModelName,
1275
+ contents: texts,
1285
1276
  });
1277
+ const embeddings = (result.embeddings || []).map((e) => e.values || []);
1286
1278
  logger.debug("Batch embeddings generated successfully", {
1287
1279
  provider: this.providerName,
1288
1280
  model: embeddingModelName,
1289
- count: result.embeddings.length,
1290
- embeddingDimension: result.embeddings[0]?.length,
1281
+ count: embeddings.length,
1282
+ embeddingDimension: embeddings[0]?.length,
1291
1283
  });
1292
- return result.embeddings;
1284
+ return embeddings;
1293
1285
  }
1294
1286
  catch (error) {
1295
1287
  logger.error("Batch embedding generation failed", {