@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,15 @@
1
- import { createGoogleGenerativeAI } from "@ai-sdk/google";
2
- import { embed, embedMany, stepCountIs, streamText, } from "ai";
3
1
  import { ErrorCategory, ErrorSeverity, GoogleAIModels, } from "../constants/enums.js";
4
2
  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";
3
+ import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
4
+ import { processUnifiedFilesArray } from "../utils/messageBuilder.js";
5
+ import { ATTR, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../telemetry/index.js";
6
+ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
11
7
  import { ERROR_CODES, NeuroLinkError } from "../utils/errorHandling.js";
12
8
  import { logger } from "../utils/logger.js";
13
- import { isGemini3Model } from "../utils/modelDetection.js";
14
9
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
15
10
  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";
11
+ import { buildGeminiResponseSchema, buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps, createTextChannel, buildUserPartsWithMultimodal, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, prependConversationMessages, pushModelResponseToHistory, } from "./googleNativeGemini3.js";
12
+ import { createProxyFetch } from "../proxy/proxyFetch.js";
20
13
  // Google AI Live API types now imported from ../types/providerSpecific.js
21
14
  // Import proper types for multimodal message handling
22
15
  // Create Google GenAI client
@@ -34,7 +27,13 @@ async function createGoogleGenAIClient(apiKey) {
34
27
  });
35
28
  }
36
29
  const Ctor = ctor;
37
- return new Ctor({ apiKey });
30
+ // Include httpOptions with proxy fetch for corporate network support
31
+ return new Ctor({
32
+ apiKey,
33
+ httpOptions: {
34
+ fetch: createProxyFetch(),
35
+ },
36
+ });
38
37
  }
39
38
  /**
40
39
  * Google AI Studio provider implementation using BaseProvider
@@ -88,12 +87,18 @@ export class GoogleAIStudioProvider extends BaseProvider {
88
87
  return process.env.GOOGLE_AI_MODEL || GoogleAIModels.GEMINI_2_5_FLASH;
89
88
  }
90
89
  /**
91
- * 🔧 PHASE 2: Return AI SDK model instance for tool calling
90
+ * AI SDK model instance no longer used.
91
+ * All models are routed through native @google/genai SDK directly.
92
92
  */
93
93
  getAISDKModel() {
94
- const apiKey = this.getApiKey();
95
- const google = createGoogleGenerativeAI({ apiKey });
96
- return google(this.modelName);
94
+ throw new NeuroLinkError({
95
+ code: ERROR_CODES.INVALID_CONFIGURATION,
96
+ message: "GoogleAIStudioProvider no longer uses @ai-sdk/google. All models use native @google/genai SDK.",
97
+ category: ErrorCategory.CONFIGURATION,
98
+ severity: ErrorSeverity.CRITICAL,
99
+ retriable: false,
100
+ context: { provider: this.providerName, model: this.modelName },
101
+ });
97
102
  }
98
103
  formatProviderError(error) {
99
104
  if (error instanceof TimeoutError) {
@@ -103,12 +108,53 @@ export class GoogleAIStudioProvider extends BaseProvider {
103
108
  const message = typeof errorRecord?.message === "string"
104
109
  ? errorRecord.message
105
110
  : "Unknown error";
106
- if (message.includes("API_KEY_INVALID")) {
111
+ const statusCode = typeof errorRecord?.status === "number"
112
+ ? errorRecord.status
113
+ : typeof errorRecord?.statusCode === "number"
114
+ ? errorRecord.statusCode
115
+ : undefined;
116
+ // Authentication errors
117
+ if (message.includes("API_KEY_INVALID") ||
118
+ message.includes("Invalid API key") ||
119
+ statusCode === 401) {
107
120
  return new AuthenticationError("Invalid Google AI API key. Please check your GOOGLE_AI_API_KEY environment variable.", this.providerName);
108
121
  }
109
- if (message.includes("RATE_LIMIT_EXCEEDED")) {
122
+ // Rate limit errors
123
+ if (message.includes("RATE_LIMIT_EXCEEDED") ||
124
+ message.includes("rate limit") ||
125
+ message.includes("429") ||
126
+ statusCode === 429) {
110
127
  return new RateLimitError("Google AI rate limit exceeded. Please try again later.", this.providerName);
111
128
  }
129
+ // Model not found errors — gate on a 404 status when available; fall
130
+ // back to literal phrase matching only when we have no status code at
131
+ // all. Avoids misclassifying permission/validation errors that happen
132
+ // to mention model resource paths (e.g. "...models/foo permission...").
133
+ if (statusCode === 404 ||
134
+ (statusCode === undefined &&
135
+ (message.includes("model not found") ||
136
+ message.includes("Model not found")))) {
137
+ return new InvalidModelError(`Model '${this.modelName}' not found. Please check the model name and ensure it is available.`, this.providerName);
138
+ }
139
+ // Network connectivity errors
140
+ if (message.includes("ECONNRESET") ||
141
+ message.includes("ENOTFOUND") ||
142
+ message.includes("ETIMEDOUT") ||
143
+ message.includes("ECONNREFUSED") ||
144
+ message.includes("network") ||
145
+ message.includes("connection")) {
146
+ return new NetworkError(`Connection error: ${message}`, this.providerName);
147
+ }
148
+ // Server errors (5xx)
149
+ if (message.includes("500") ||
150
+ message.includes("502") ||
151
+ message.includes("503") ||
152
+ message.includes("504") ||
153
+ message.includes("server error") ||
154
+ message.includes("Internal Server Error") ||
155
+ (statusCode && statusCode >= 500 && statusCode < 600)) {
156
+ return new ProviderError(`Google AI server error: ${message}. Please try again later.`, this.providerName);
157
+ }
112
158
  return new ProviderError(`Google AI error: ${message}`, this.providerName);
113
159
  }
114
160
  /**
@@ -388,202 +434,53 @@ export class GoogleAIStudioProvider extends BaseProvider {
388
434
  }
389
435
  // executeGenerate removed - BaseProvider handles all generation with tools
390
436
  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
- }
437
+ const modelName = options.model || this.modelName;
430
438
  // Phase 1: if audio input present, bridge to Gemini Live (Studio) using @google/genai
431
439
  if (options.input?.audio) {
432
440
  return await this.executeAudioStreamViaGeminiLive(options);
433
441
  }
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
- };
442
+ // Structured output (analysisSchema, JSON format, or schema) is incompatible with tools on Gemini.
443
+ const wantsStructuredOutput = analysisSchema || options.output?.format === "json" || options.schema;
444
+ // Tool filter (a0269210): trust options.tools — caller (BaseProvider.stream)
445
+ // already merged MCP/built-in tools with user tools and applied any
446
+ // enabledToolNames filter. Re-attaching getAllTools() here would clobber
447
+ // that filter and re-introduce filtered-out tools.
448
+ const shouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
449
+ const optionTools = options.tools || {};
450
+ // Merge into options for native SDK path
451
+ let mergedOptions = {
452
+ ...options,
453
+ tools: optionTools,
454
+ };
455
+ // Check for tools + JSON schema conflict (Gemini limitation)
456
+ const wantsJsonOutput = options.output?.format === "json" || options.schema;
457
+ if (wantsJsonOutput &&
458
+ mergedOptions.tools &&
459
+ Object.keys(mergedOptions.tools).length > 0 &&
460
+ !mergedOptions.disableTools) {
461
+ logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
462
+ mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
574
463
  }
575
- catch (error) {
576
- timeoutController?.cleanup();
577
- throw this.handleProviderError(error);
464
+ const hasActiveTools = shouldUseTools &&
465
+ !mergedOptions.disableTools &&
466
+ mergedOptions.tools &&
467
+ Object.keys(mergedOptions.tools).length > 0;
468
+ if (hasActiveTools) {
469
+ logger.info("[GoogleAIStudio] Routing to native @google/genai SDK for tool calling", {
470
+ model: modelName,
471
+ totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
472
+ });
578
473
  }
474
+ // Route ALL models through native @google/genai SDK (no more @ai-sdk/google dependency)
475
+ return this.executeNativeGemini3Stream(mergedOptions);
579
476
  }
580
477
  /**
581
- * Execute stream using native @google/genai SDK for Gemini 3 models
582
- * This bypasses @ai-sdk/google to properly handle thought_signature
478
+ * Execute stream using native @google/genai SDK
479
+ * Uses @google/genai directly for all Gemini models (2.0, 2.5, 3.x)
583
480
  */
584
481
  async executeNativeGemini3Stream(options) {
585
482
  const modelName = options.model || this.modelName;
586
- return withClientSpan({
483
+ return withClientStreamSpan({
587
484
  name: "neurolink.provider.stream",
588
485
  tracer: tracers.provider,
589
486
  attributes: {
@@ -603,23 +500,54 @@ export class GoogleAIStudioProvider extends BaseProvider {
603
500
  model: modelName,
604
501
  hasTools: !!options.tools && Object.keys(options.tools).length > 0,
605
502
  });
606
- // Build contents from input
607
- const currentContents = [{ role: "user", parts: [{ text: options.input.text }] }];
503
+ // Build contents from input. Prepend prior conversation turns so
504
+ // multi-turn callers (memory, loop REPL, agent flows) actually
505
+ // carry context — the previous build started fresh from the
506
+ // current user input only, which silently dropped history.
507
+ //
508
+ // `buildUserPartsWithMultimodal` is the shared helper that also
509
+ // attaches `input.images` and `input.pdfFiles` as `inlineData`
510
+ // parts. The previous AI Studio path pushed only `{ text }` and
511
+ // silently dropped both, which is why the model legitimately
512
+ // reported "no image attached" on multimodal calls.
513
+ const currentContents = [];
514
+ prependConversationMessages(currentContents, options.conversationMessages);
515
+ const userParts = await buildUserPartsWithMultimodal(options.input, options.input.text, "[GoogleAIStudio:stream]");
516
+ currentContents.push({
517
+ role: "user",
518
+ parts: userParts,
519
+ });
608
520
  // Convert tools
609
521
  let toolsConfig;
610
522
  let executeMap = new Map();
523
+ let originalNameMap = new Map();
611
524
  if (options.tools &&
612
525
  Object.keys(options.tools).length > 0 &&
613
526
  !options.disableTools) {
614
527
  const result = buildNativeToolDeclarations(options.tools);
615
528
  toolsConfig = result.toolsConfig;
616
529
  executeMap = result.executeMap;
530
+ originalNameMap = result.originalNameMap;
617
531
  logger.debug("[GoogleAIStudio] Converted tools for native SDK", {
618
532
  toolCount: toolsConfig[0].functionDeclarations.length,
619
533
  toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
620
534
  });
621
535
  }
622
- const config = buildNativeConfig(options, toolsConfig);
536
+ // Native JSON / schema enforcement: when no tools are being sent
537
+ // (the AI Studio orchestrator above already force-disables tools
538
+ // whenever JSON/schema output is requested), enforce the response
539
+ // shape natively via responseMimeType / responseSchema. Without
540
+ // this, JSON output was best-effort prompting only.
541
+ const wantsNativeJson = !toolsConfig &&
542
+ (options.output?.format === "json" || !!options.schema);
543
+ const nativeResponseSchema = wantsNativeJson && options.schema
544
+ ? buildGeminiResponseSchema(options.schema)
545
+ : undefined;
546
+ const config = buildNativeConfig({
547
+ ...options,
548
+ wantsJsonOutput: wantsNativeJson,
549
+ responseSchema: nativeResponseSchema,
550
+ }, toolsConfig);
623
551
  const maxSteps = computeMaxSteps(options.maxSteps);
624
552
  // Compose abort signal from user signal + timeout
625
553
  const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
@@ -698,7 +626,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
698
626
  logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
699
627
  // Add model response with ALL parts (including thoughtSignature) to history
700
628
  pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
701
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
629
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal, originalNameMap });
702
630
  // Add function responses to history — the @google/genai SDK
703
631
  // only accepts "user" and "model" as valid roles in contents.
704
632
  // Function/tool responses must use role: "user" (matching the
@@ -742,68 +670,9 @@ export class GoogleAIStudioProvider extends BaseProvider {
742
670
  requestDuration: responseTime,
743
671
  timestamp: new Date().toISOString(),
744
672
  });
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
673
  channel.close();
774
674
  }
775
675
  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
676
  channel.error(err);
808
677
  analyticsReject(err);
809
678
  }
@@ -826,7 +695,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
826
695
  finally {
827
696
  // Timeout controller cleanup is managed inside the background loop
828
697
  }
829
- });
698
+ }, (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
830
699
  }
831
700
  /**
832
701
  * Execute generate using native @google/genai SDK for Gemini 3 models
@@ -858,10 +727,24 @@ export class GoogleAIStudioProvider extends BaseProvider {
858
727
  // Prefer input.text over prompt — processCSVFilesForNativeSDK enriches
859
728
  // input.text with inlined CSV data, so using prompt first would discard it.
860
729
  const promptText = options.input?.text || options.prompt || "";
861
- const currentContents = [{ role: "user", parts: [{ text: promptText }] }];
862
- // Convert tools (merge SDK tools with options.tools)
730
+ // Prepend prior conversation turns so multi-turn generate calls
731
+ // see history; otherwise the native generate path silently drops
732
+ // every turn before the current prompt.
733
+ //
734
+ // `buildUserPartsWithMultimodal` also attaches inline image / PDF
735
+ // parts. Without it the request body was text-only and the model
736
+ // legitimately reported "no image / PDF attached".
737
+ const currentContents = [];
738
+ prependConversationMessages(currentContents, options.conversationMessages);
739
+ const userParts = await buildUserPartsWithMultimodal(options.input, promptText, "[GoogleAIStudio:generate]");
740
+ currentContents.push({
741
+ role: "user",
742
+ parts: userParts,
743
+ });
744
+ // Convert tools (a0269210: trust options.tools — already merged + filtered upstream)
863
745
  let toolsConfig;
864
746
  let executeMap = new Map();
747
+ let originalNameMap = new Map();
865
748
  const shouldUseTools = !options.disableTools;
866
749
  if (shouldUseTools) {
867
750
  const tools = options.tools || {};
@@ -869,13 +752,26 @@ export class GoogleAIStudioProvider extends BaseProvider {
869
752
  const result = buildNativeToolDeclarations(tools);
870
753
  toolsConfig = result.toolsConfig;
871
754
  executeMap = result.executeMap;
755
+ originalNameMap = result.originalNameMap;
872
756
  logger.debug("[GoogleAIStudio] Converted tools for native SDK generate", {
873
757
  toolCount: toolsConfig[0].functionDeclarations.length,
874
758
  toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
875
759
  });
876
760
  }
877
761
  }
878
- const config = buildNativeConfig(options, toolsConfig);
762
+ // Native JSON / schema enforcement (generate path). Mirrors the
763
+ // stream block above; only set when no tools are being sent
764
+ // because Gemini cannot combine function calling with JSON mime.
765
+ const wantsNativeJson = !toolsConfig &&
766
+ (options.output?.format === "json" || !!options.schema);
767
+ const nativeResponseSchema = wantsNativeJson && options.schema
768
+ ? buildGeminiResponseSchema(options.schema)
769
+ : undefined;
770
+ const config = buildNativeConfig({
771
+ ...options,
772
+ wantsJsonOutput: wantsNativeJson,
773
+ responseSchema: nativeResponseSchema,
774
+ }, toolsConfig);
879
775
  const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
880
776
  const maxSteps = computeMaxSteps(options.maxSteps);
881
777
  let finalText = "";
@@ -925,7 +821,11 @@ export class GoogleAIStudioProvider extends BaseProvider {
925
821
  // Add model response with ALL parts (including thoughtSignature) to history
926
822
  // This is critical for Gemini 3 - it requires thought signatures in subsequent turns
927
823
  pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
928
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
824
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, {
825
+ toolExecutions,
826
+ abortSignal: composedSignal,
827
+ originalNameMap,
828
+ });
929
829
  // Add function responses to history — the @google/genai SDK
930
830
  // only accepts "user" and "model" as valid roles in contents.
931
831
  // Function/tool responses must use role: "user" (matching the
@@ -946,31 +846,11 @@ export class GoogleAIStudioProvider extends BaseProvider {
946
846
  span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
947
847
  span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
948
848
  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 {
849
+ // Build EnhancedGenerateResult and route through enhanceResult so
850
+ // analytics / evaluation / tracing stay attached. The native AI
851
+ // Studio generate path bypasses BaseProvider.generate(), so
852
+ // skipping enhanceResult would silently drop those features.
853
+ const baseResult = {
974
854
  content: finalText,
975
855
  provider: this.providerName,
976
856
  model: modelName,
@@ -984,6 +864,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
984
864
  toolExecutions: toolExecutions,
985
865
  enhancedWithTools: allToolCalls.length > 0,
986
866
  };
867
+ return this.enhanceResult(baseResult, options, startTime);
987
868
  }
988
869
  finally {
989
870
  timeoutController?.cleanup();
@@ -999,40 +880,147 @@ export class GoogleAIStudioProvider extends BaseProvider {
999
880
  ? { prompt: optionsOrPrompt }
1000
881
  : optionsOrPrompt;
1001
882
  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: {} };
883
+ // Image-generation models reject function-calling. Route them to
884
+ // executeImageGeneration without merging tools. This must happen
885
+ // BEFORE getToolsForStream to avoid leaking registered (MCP / built-in)
886
+ // tools into the image API request, which trips
887
+ // "Function calling is not enabled for this model".
888
+ // startsWith (not includes) so a hypothetical text model whose ID
889
+ // contains an image-model string as a substring isn't silently routed
890
+ // to executeImageGeneration and stripped of tool support.
891
+ const isImageModel = IMAGE_GENERATION_MODELS.some((m) => modelName.toLowerCase().startsWith(m.toLowerCase()));
892
+ if (isImageModel) {
893
+ logger.info("[GoogleAIStudio] Routing image generation model to executeImageGeneration", { model: modelName });
894
+ return this.executeImageGeneration(options);
895
+ }
896
+ // TTS direct-synthesis mode: synthesise the input text directly (no LLM
897
+ // call). BaseProvider.runGenerateInActiveContext does the same dispatch
898
+ // replicated here because AI Studio's override bypasses that path.
899
+ if (options.tts?.enabled && !options.tts?.useAiResponse) {
900
+ logger.info("[GoogleAIStudio] Routing TTS direct-synthesis to handleDirectTTSSynthesis", { model: modelName });
901
+ return this.handleDirectTTSSynthesis(options, Date.now());
902
+ }
903
+ // Process the unified `input.files` array before routing to the
904
+ // native SDK. BaseProvider.generate() runs this preprocessing via
905
+ // buildMultimodalMessagesArray, but AI Studio's override skips it,
906
+ // which would otherwise drop text-file content (and the
907
+ // mimetype-hint contract) on the floor. Mutates options.input.text /
908
+ // options.input.images / options.input.pdfFiles in place.
909
+ if (options.input?.files && options.input.files.length > 0) {
910
+ try {
911
+ await processUnifiedFilesArray(options, 100 * 1024 * 1024, this.providerName);
1019
912
  }
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);
913
+ catch (fileError) {
914
+ logger.warn(`[GoogleAIStudio] processUnifiedFilesArray threw, continuing without file content: ${fileError instanceof Error ? fileError.message : String(fileError)}`);
1030
915
  }
1031
- // Fall through to standard generate path using merged options (tools disabled for schema)
1032
- return super.generate(mergedOptions);
1033
916
  }
1034
- // Fall back to BaseProvider implementation
1035
- return super.generate(options);
917
+ // Merge registered (built-in / MCP) tools with caller-supplied tools.
918
+ // AI Studio's generate() bypasses BaseProvider.generate(), so the
919
+ // ToolsManager-driven merge that normally injects sdk.registerTool()
920
+ // entries never runs here. Without this call, registered tools never
921
+ // reach the native function-calling path.
922
+ const baseTools = !options.disableTools
923
+ ? await this.getToolsForStream(options)
924
+ : {};
925
+ let mergedOptions = {
926
+ ...options,
927
+ tools: baseTools,
928
+ };
929
+ // Check for tools + JSON schema conflict (Gemini limitation)
930
+ const wantsJsonOutput = options.output?.format === "json" || options.schema;
931
+ if (wantsJsonOutput &&
932
+ mergedOptions.tools &&
933
+ Object.keys(mergedOptions.tools).length > 0 &&
934
+ !mergedOptions.disableTools) {
935
+ logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
936
+ mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
937
+ }
938
+ const hasActiveTools = !mergedOptions.disableTools &&
939
+ mergedOptions.tools &&
940
+ Object.keys(mergedOptions.tools).length > 0;
941
+ if (hasActiveTools) {
942
+ logger.info("[GoogleAIStudio] Routing generate to native @google/genai SDK for tool calling", {
943
+ model: modelName,
944
+ totalToolCount: Object.keys(mergedOptions.tools ?? {}).length,
945
+ });
946
+ }
947
+ // Route ALL models through native @google/genai SDK (no more @ai-sdk/google dependency).
948
+ // Emit Pipeline B `generation:end` so the observability listener
949
+ // creates a `model.generation` span — AI Studio's native path bypasses
950
+ // the AI SDK + experimental_telemetry plumbing the same way Vertex's
951
+ // does, so the event has to be emitted manually.
952
+ const generateStartTime = Date.now();
953
+ const inputPrompt = mergedOptions.input?.text ||
954
+ mergedOptions.prompt ||
955
+ "";
956
+ try {
957
+ // Wrap in `neurolink.executeGeneration` so the observability span
958
+ // chain (Test: Generate Span Chain) sees a third inner span on the
959
+ // native @google/genai path — Pipeline A providers get this from
960
+ // GenerationHandler.executeGeneration; the native path bypasses
961
+ // GenerationHandler so we add the span here.
962
+ let result = await withSpan({
963
+ name: "neurolink.executeGeneration",
964
+ tracer: tracers.provider,
965
+ attributes: {
966
+ [ATTR.GEN_AI_SYSTEM]: this.providerName,
967
+ [ATTR.GEN_AI_MODEL]: modelName,
968
+ "neurolink.path": "native.google-genai",
969
+ },
970
+ }, async () => this.executeNativeGemini3Generate(mergedOptions));
971
+ // Pipe through TTS-of-AI-response when caller asks for it. No-op when
972
+ // tts is disabled or useAiResponse is false.
973
+ result = await this.synthesizeAIResponseIfNeeded(result, options);
974
+ this.emitPipelineBGenerationEvent(modelName, result, generateStartTime, true, undefined, inputPrompt);
975
+ return result;
976
+ }
977
+ catch (error) {
978
+ this.emitPipelineBGenerationEvent(modelName, null, generateStartTime, false, error, inputPrompt);
979
+ throw error;
980
+ }
981
+ }
982
+ /**
983
+ * Emit `generation:end` so the Pipeline B observability listener creates
984
+ * a `model.generation` span for native Google AI Studio generate calls.
985
+ * Without this hand-off the native path silently disappears from
986
+ * Pipeline B exporters (Langfuse, custom OTEL collectors).
987
+ */
988
+ emitPipelineBGenerationEvent(modelName, result, startTime, success, error, prompt) {
989
+ const emitter = this.neurolink?.getEventEmitter();
990
+ if (!emitter) {
991
+ return;
992
+ }
993
+ const usage = result?.usage && typeof result.usage === "object"
994
+ ? result.usage
995
+ : { input: 0, output: 0, total: 0 };
996
+ // Mark on the result so the SDK-level runStandardGenerateRequest knows
997
+ // this provider already emitted `generation:end` itself and skips its
998
+ // own duplicate emission. Without this flag the public event listener
999
+ // (and the observability test) would see two events per generate call.
1000
+ if (result && typeof result === "object") {
1001
+ result._generationEndEmitted =
1002
+ true;
1003
+ }
1004
+ emitter.emit("generation:end", {
1005
+ provider: this.providerName,
1006
+ responseTime: Date.now() - startTime,
1007
+ timestamp: Date.now(),
1008
+ // The Pipeline B listener reads `data.prompt` to populate the
1009
+ // `input` span attribute. Without this, the Observability Spans
1010
+ // test fails with "input capture not working".
1011
+ prompt: prompt || "",
1012
+ result: {
1013
+ content: result?.content || "",
1014
+ usage,
1015
+ model: modelName,
1016
+ provider: this.providerName,
1017
+ finishReason: success ? "stop" : "error",
1018
+ },
1019
+ success,
1020
+ ...(error
1021
+ ? { error: error instanceof Error ? error.message : String(error) }
1022
+ : {}),
1023
+ });
1036
1024
  }
1037
1025
  // ===================
1038
1026
  // HELPER METHODS
@@ -1240,18 +1228,21 @@ export class GoogleAIStudioProvider extends BaseProvider {
1240
1228
  });
1241
1229
  try {
1242
1230
  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,
1231
+ const client = await createGoogleGenAIClient(apiKey);
1232
+ const result = await client.models.embedContent({
1233
+ model: embeddingModelName,
1234
+ contents: [text],
1248
1235
  });
1236
+ const embedding = result.embeddings?.[0]?.values;
1237
+ if (!embedding) {
1238
+ throw new ProviderError("No embedding returned from Google AI", this.providerName);
1239
+ }
1249
1240
  logger.debug("Embedding generated successfully", {
1250
1241
  provider: this.providerName,
1251
1242
  model: embeddingModelName,
1252
- embeddingDimension: result.embedding.length,
1243
+ embeddingDimension: embedding.length,
1253
1244
  });
1254
- return result.embedding;
1245
+ return embedding;
1255
1246
  }
1256
1247
  catch (error) {
1257
1248
  logger.error("Embedding generation failed", {
@@ -1277,19 +1268,19 @@ export class GoogleAIStudioProvider extends BaseProvider {
1277
1268
  });
1278
1269
  try {
1279
1270
  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,
1271
+ const client = await createGoogleGenAIClient(apiKey);
1272
+ const result = await client.models.embedContent({
1273
+ model: embeddingModelName,
1274
+ contents: texts,
1285
1275
  });
1276
+ const embeddings = (result.embeddings || []).map((e) => e.values || []);
1286
1277
  logger.debug("Batch embeddings generated successfully", {
1287
1278
  provider: this.providerName,
1288
1279
  model: embeddingModelName,
1289
- count: result.embeddings.length,
1290
- embeddingDimension: result.embeddings[0]?.length,
1280
+ count: embeddings.length,
1281
+ embeddingDimension: embeddings[0]?.length,
1291
1282
  });
1292
- return result.embeddings;
1283
+ return embeddings;
1293
1284
  }
1294
1285
  catch (error) {
1295
1286
  logger.error("Batch embedding generation failed", {