@juspay/neurolink 9.32.0 → 9.33.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 (475) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/auth/anthropicOAuth.js +1 -1
  3. package/dist/cli/commands/proxy.js +18 -5
  4. package/dist/client/aiSdkAdapter.js +1 -1
  5. package/dist/client/index.js +137 -501
  6. package/dist/core/factory.js +0 -1
  7. package/dist/core/redisConversationMemoryManager.js +1 -1
  8. package/dist/features/ppt/slideGenerator.js +0 -1
  9. package/dist/features/ppt/utils.js +0 -1
  10. package/dist/lib/neurolink.d.ts +10 -0
  11. package/dist/lib/neurolink.js +41 -7
  12. package/dist/lib/server/routes/claudeProxyRoutes.js +45 -9
  13. package/dist/lib/types/generateTypes.d.ts +16 -0
  14. package/dist/lib/types/streamTypes.d.ts +15 -0
  15. package/dist/mcp/elicitationProtocol.js +1 -1
  16. package/dist/mcp/servers/agent/directToolsServer.js +0 -1
  17. package/dist/neurolink.d.ts +10 -0
  18. package/dist/neurolink.js +41 -7
  19. package/dist/providers/azureOpenai.js +1 -1
  20. package/dist/providers/huggingFace.js +0 -1
  21. package/dist/providers/openaiCompatible.js +0 -1
  22. package/dist/sdk/toolRegistration.js +0 -1
  23. package/dist/server/openapi/generator.js +1 -1
  24. package/dist/server/routes/claudeProxyRoutes.js +45 -9
  25. package/dist/types/configTypes.js +0 -5
  26. package/dist/types/generateTypes.d.ts +16 -0
  27. package/dist/types/modelTypes.js +0 -1
  28. package/dist/types/streamTypes.d.ts +15 -0
  29. package/dist/types/tools.js +0 -1
  30. package/dist/types/typeAliases.js +0 -1
  31. package/dist/types/utilities.js +1 -1
  32. package/dist/types/workflowTypes.js +0 -1
  33. package/dist/utils/providerRetry.js +0 -1
  34. package/dist/utils/providerUtils.js +0 -1
  35. package/package.json +2 -2
  36. package/dist/client/adapters/providerImageAdapter.js +0 -588
  37. package/dist/client/adapters/tts/googleTTSHandler.js +0 -344
  38. package/dist/client/adapters/video/directorPipeline.js +0 -516
  39. package/dist/client/adapters/video/ffmpegAdapter.js +0 -206
  40. package/dist/client/adapters/video/frameExtractor.js +0 -143
  41. package/dist/client/adapters/video/vertexVideoHandler.js +0 -763
  42. package/dist/client/adapters/video/videoAnalyzer.js +0 -238
  43. package/dist/client/adapters/video/videoMerger.js +0 -171
  44. package/dist/client/agent/directTools.js +0 -840
  45. package/dist/client/auth/AuthProviderFactory.js +0 -111
  46. package/dist/client/auth/AuthProviderRegistry.js +0 -190
  47. package/dist/client/auth/RequestContext.js +0 -78
  48. package/dist/client/auth/accountPool.js +0 -178
  49. package/dist/client/auth/anthropicOAuth.js +0 -974
  50. package/dist/client/auth/authContext.js +0 -314
  51. package/dist/client/auth/errors.js +0 -39
  52. package/dist/client/auth/index.js +0 -61
  53. package/dist/client/auth/middleware/AuthMiddleware.js +0 -519
  54. package/dist/client/auth/middleware/rateLimitByUser.js +0 -554
  55. package/dist/client/auth/providers/BaseAuthProvider.js +0 -723
  56. package/dist/client/auth/providers/CognitoProvider.js +0 -304
  57. package/dist/client/auth/providers/KeycloakProvider.js +0 -393
  58. package/dist/client/auth/providers/auth0.js +0 -274
  59. package/dist/client/auth/providers/betterAuth.js +0 -182
  60. package/dist/client/auth/providers/clerk.js +0 -317
  61. package/dist/client/auth/providers/custom.js +0 -112
  62. package/dist/client/auth/providers/firebase.js +0 -226
  63. package/dist/client/auth/providers/jwt.js +0 -212
  64. package/dist/client/auth/providers/oauth2.js +0 -303
  65. package/dist/client/auth/providers/supabase.js +0 -259
  66. package/dist/client/auth/providers/workos.js +0 -284
  67. package/dist/client/auth/serverBridge.js +0 -25
  68. package/dist/client/auth/sessionManager.js +0 -437
  69. package/dist/client/auth/tokenStore.js +0 -799
  70. package/dist/client/client/aiSdkAdapter.js +0 -487
  71. package/dist/client/client/auth.js +0 -473
  72. package/dist/client/client/errors.js +0 -552
  73. package/dist/client/client/httpClient.js +0 -837
  74. package/dist/client/client/index.js +0 -172
  75. package/dist/client/client/interceptors.js +0 -601
  76. package/dist/client/client/sseClient.js +0 -545
  77. package/dist/client/client/streamingClient.js +0 -917
  78. package/dist/client/client/wsClient.js +0 -369
  79. package/dist/client/config/configManager.js +0 -303
  80. package/dist/client/config/conversationMemory.js +0 -86
  81. package/dist/client/config/taskClassificationConfig.js +0 -148
  82. package/dist/client/constants/contextWindows.js +0 -295
  83. package/dist/client/constants/enums.js +0 -853
  84. package/dist/client/constants/index.js +0 -207
  85. package/dist/client/constants/performance.js +0 -389
  86. package/dist/client/constants/retry.js +0 -266
  87. package/dist/client/constants/timeouts.js +0 -182
  88. package/dist/client/constants/tokens.js +0 -380
  89. package/dist/client/constants/videoErrors.js +0 -46
  90. package/dist/client/context/budgetChecker.js +0 -98
  91. package/dist/client/context/contextCompactor.js +0 -205
  92. package/dist/client/context/emergencyTruncation.js +0 -88
  93. package/dist/client/context/errorDetection.js +0 -171
  94. package/dist/client/context/errors.js +0 -21
  95. package/dist/client/context/fileTokenBudget.js +0 -127
  96. package/dist/client/context/prompts/summarizationPrompt.js +0 -117
  97. package/dist/client/context/stages/fileReadDeduplicator.js +0 -66
  98. package/dist/client/context/stages/slidingWindowTruncator.js +0 -190
  99. package/dist/client/context/stages/structuredSummarizer.js +0 -99
  100. package/dist/client/context/stages/toolOutputPruner.js +0 -52
  101. package/dist/client/context/summarizationEngine.js +0 -136
  102. package/dist/client/context/toolOutputLimits.js +0 -78
  103. package/dist/client/context/toolPairRepair.js +0 -66
  104. package/dist/client/core/analytics.js +0 -88
  105. package/dist/client/core/baseProvider.js +0 -1385
  106. package/dist/client/core/constants.js +0 -140
  107. package/dist/client/core/conversationMemoryFactory.js +0 -141
  108. package/dist/client/core/conversationMemoryInitializer.js +0 -128
  109. package/dist/client/core/conversationMemoryManager.js +0 -344
  110. package/dist/client/core/dynamicModels.js +0 -358
  111. package/dist/client/core/evaluation.js +0 -309
  112. package/dist/client/core/evaluationProviders.js +0 -248
  113. package/dist/client/core/factory.js +0 -412
  114. package/dist/client/core/infrastructure/baseError.js +0 -22
  115. package/dist/client/core/infrastructure/baseFactory.js +0 -54
  116. package/dist/client/core/infrastructure/baseRegistry.js +0 -53
  117. package/dist/client/core/infrastructure/index.js +0 -5
  118. package/dist/client/core/infrastructure/retry.js +0 -20
  119. package/dist/client/core/infrastructure/typedEventEmitter.js +0 -23
  120. package/dist/client/core/modelConfiguration.js +0 -851
  121. package/dist/client/core/modules/GenerationHandler.js +0 -588
  122. package/dist/client/core/modules/MessageBuilder.js +0 -273
  123. package/dist/client/core/modules/StreamHandler.js +0 -185
  124. package/dist/client/core/modules/TelemetryHandler.js +0 -203
  125. package/dist/client/core/modules/ToolsManager.js +0 -499
  126. package/dist/client/core/modules/Utilities.js +0 -331
  127. package/dist/client/core/redisConversationMemoryManager.js +0 -1435
  128. package/dist/client/core/streamAnalytics.js +0 -131
  129. package/dist/client/evaluation/contextBuilder.js +0 -134
  130. package/dist/client/evaluation/index.js +0 -61
  131. package/dist/client/evaluation/prompts.js +0 -73
  132. package/dist/client/evaluation/ragasEvaluator.js +0 -110
  133. package/dist/client/evaluation/retryManager.js +0 -78
  134. package/dist/client/evaluation/scoring.js +0 -61
  135. package/dist/client/factories/providerFactory.js +0 -166
  136. package/dist/client/factories/providerRegistry.js +0 -166
  137. package/dist/client/features/ppt/constants.js +0 -896
  138. package/dist/client/features/ppt/contentPlanner.js +0 -529
  139. package/dist/client/features/ppt/presentationOrchestrator.js +0 -236
  140. package/dist/client/features/ppt/slideGenerator.js +0 -532
  141. package/dist/client/features/ppt/slideRenderers.js +0 -2383
  142. package/dist/client/features/ppt/slideTypeInference.js +0 -405
  143. package/dist/client/features/ppt/types.js +0 -13
  144. package/dist/client/features/ppt/utils.js +0 -443
  145. package/dist/client/files/fileReferenceRegistry.js +0 -1543
  146. package/dist/client/files/fileTools.js +0 -450
  147. package/dist/client/files/streamingReader.js +0 -321
  148. package/dist/client/files/types.js +0 -23
  149. package/dist/client/hitl/hitlErrors.js +0 -54
  150. package/dist/client/hitl/hitlManager.js +0 -460
  151. package/dist/client/mcp/agentExposure.js +0 -356
  152. package/dist/client/mcp/auth/index.js +0 -11
  153. package/dist/client/mcp/auth/oauthClientProvider.js +0 -325
  154. package/dist/client/mcp/auth/tokenStorage.js +0 -134
  155. package/dist/client/mcp/batching/index.js +0 -10
  156. package/dist/client/mcp/batching/requestBatcher.js +0 -441
  157. package/dist/client/mcp/caching/index.js +0 -10
  158. package/dist/client/mcp/caching/toolCache.js +0 -433
  159. package/dist/client/mcp/elicitation/elicitationManager.js +0 -376
  160. package/dist/client/mcp/elicitation/index.js +0 -11
  161. package/dist/client/mcp/elicitation/types.js +0 -10
  162. package/dist/client/mcp/elicitationProtocol.js +0 -375
  163. package/dist/client/mcp/enhancedToolDiscovery.js +0 -481
  164. package/dist/client/mcp/externalServerManager.js +0 -1478
  165. package/dist/client/mcp/factory.js +0 -161
  166. package/dist/client/mcp/flexibleToolValidator.js +0 -161
  167. package/dist/client/mcp/httpRateLimiter.js +0 -391
  168. package/dist/client/mcp/httpRetryHandler.js +0 -178
  169. package/dist/client/mcp/index.js +0 -74
  170. package/dist/client/mcp/mcpCircuitBreaker.js +0 -427
  171. package/dist/client/mcp/mcpClientFactory.js +0 -708
  172. package/dist/client/mcp/mcpRegistryClient.js +0 -488
  173. package/dist/client/mcp/mcpServerBase.js +0 -373
  174. package/dist/client/mcp/multiServerManager.js +0 -579
  175. package/dist/client/mcp/registry.js +0 -158
  176. package/dist/client/mcp/routing/index.js +0 -10
  177. package/dist/client/mcp/routing/toolRouter.js +0 -416
  178. package/dist/client/mcp/serverCapabilities.js +0 -502
  179. package/dist/client/mcp/servers/agent/directToolsServer.js +0 -150
  180. package/dist/client/mcp/toolAnnotations.js +0 -239
  181. package/dist/client/mcp/toolConverter.js +0 -258
  182. package/dist/client/mcp/toolDiscoveryService.js +0 -798
  183. package/dist/client/mcp/toolIntegration.js +0 -334
  184. package/dist/client/mcp/toolRegistry.js +0 -729
  185. package/dist/client/memory/hippocampusInitializer.js +0 -19
  186. package/dist/client/memory/memoryRetrievalTools.js +0 -166
  187. package/dist/client/middleware/builtin/analytics.js +0 -132
  188. package/dist/client/middleware/builtin/autoEvaluation.js +0 -203
  189. package/dist/client/middleware/builtin/guardrails.js +0 -109
  190. package/dist/client/middleware/builtin/lifecycle.js +0 -168
  191. package/dist/client/middleware/factory.js +0 -327
  192. package/dist/client/middleware/registry.js +0 -295
  193. package/dist/client/middleware/utils/guardrailsUtils.js +0 -396
  194. package/dist/client/models/anthropicModels.js +0 -527
  195. package/dist/client/neurolink.js +0 -8233
  196. package/dist/client/observability/exporterRegistry.js +0 -413
  197. package/dist/client/observability/exporters/arizeExporter.js +0 -138
  198. package/dist/client/observability/exporters/baseExporter.js +0 -190
  199. package/dist/client/observability/exporters/braintrustExporter.js +0 -154
  200. package/dist/client/observability/exporters/datadogExporter.js +0 -196
  201. package/dist/client/observability/exporters/laminarExporter.js +0 -302
  202. package/dist/client/observability/exporters/langfuseExporter.js +0 -209
  203. package/dist/client/observability/exporters/langsmithExporter.js +0 -143
  204. package/dist/client/observability/exporters/otelExporter.js +0 -164
  205. package/dist/client/observability/exporters/posthogExporter.js +0 -287
  206. package/dist/client/observability/exporters/sentryExporter.js +0 -165
  207. package/dist/client/observability/index.js +0 -31
  208. package/dist/client/observability/metricsAggregator.js +0 -556
  209. package/dist/client/observability/otelBridge.js +0 -131
  210. package/dist/client/observability/retryPolicy.js +0 -383
  211. package/dist/client/observability/sampling/samplers.js +0 -216
  212. package/dist/client/observability/spanProcessor.js +0 -303
  213. package/dist/client/observability/tokenTracker.js +0 -413
  214. package/dist/client/observability/types/exporterTypes.js +0 -5
  215. package/dist/client/observability/types/index.js +0 -4
  216. package/dist/client/observability/types/spanTypes.js +0 -92
  217. package/dist/client/observability/utils/safeMetadata.js +0 -25
  218. package/dist/client/observability/utils/spanSerializer.js +0 -292
  219. package/dist/client/processors/archive/ArchiveProcessor.js +0 -1308
  220. package/dist/client/processors/base/BaseFileProcessor.js +0 -614
  221. package/dist/client/processors/base/types.js +0 -82
  222. package/dist/client/processors/config/fileTypes.js +0 -520
  223. package/dist/client/processors/config/index.js +0 -92
  224. package/dist/client/processors/config/languageMap.js +0 -410
  225. package/dist/client/processors/config/mimeTypes.js +0 -363
  226. package/dist/client/processors/config/sizeLimits.js +0 -258
  227. package/dist/client/processors/document/ExcelProcessor.js +0 -590
  228. package/dist/client/processors/document/OpenDocumentProcessor.js +0 -212
  229. package/dist/client/processors/document/PptxProcessor.js +0 -157
  230. package/dist/client/processors/document/RtfProcessor.js +0 -361
  231. package/dist/client/processors/document/WordProcessor.js +0 -353
  232. package/dist/client/processors/errors/FileErrorCode.js +0 -255
  233. package/dist/client/processors/errors/errorHelpers.js +0 -386
  234. package/dist/client/processors/errors/errorSerializer.js +0 -507
  235. package/dist/client/processors/errors/index.js +0 -49
  236. package/dist/client/processors/markup/SvgProcessor.js +0 -240
  237. package/dist/client/processors/media/AudioProcessor.js +0 -707
  238. package/dist/client/processors/media/VideoProcessor.js +0 -1045
  239. package/dist/client/providers/amazonBedrock.js +0 -1512
  240. package/dist/client/providers/amazonSagemaker.js +0 -162
  241. package/dist/client/providers/anthropic.js +0 -831
  242. package/dist/client/providers/azureOpenai.js +0 -143
  243. package/dist/client/providers/googleAiStudio.js +0 -1200
  244. package/dist/client/providers/googleNativeGemini3.js +0 -543
  245. package/dist/client/providers/googleVertex.js +0 -2936
  246. package/dist/client/providers/huggingFace.js +0 -315
  247. package/dist/client/providers/litellm.js +0 -488
  248. package/dist/client/providers/mistral.js +0 -157
  249. package/dist/client/providers/ollama.js +0 -1579
  250. package/dist/client/providers/openAI.js +0 -627
  251. package/dist/client/providers/openRouter.js +0 -543
  252. package/dist/client/providers/openaiCompatible.js +0 -290
  253. package/dist/client/providers/providerTypeUtils.js +0 -46
  254. package/dist/client/providers/sagemaker/adaptive-semaphore.js +0 -215
  255. package/dist/client/providers/sagemaker/client.js +0 -472
  256. package/dist/client/providers/sagemaker/config.js +0 -317
  257. package/dist/client/providers/sagemaker/detection.js +0 -606
  258. package/dist/client/providers/sagemaker/error-constants.js +0 -227
  259. package/dist/client/providers/sagemaker/errors.js +0 -299
  260. package/dist/client/providers/sagemaker/language-model.js +0 -775
  261. package/dist/client/providers/sagemaker/parsers.js +0 -634
  262. package/dist/client/providers/sagemaker/streaming.js +0 -331
  263. package/dist/client/providers/sagemaker/structured-parser.js +0 -625
  264. package/dist/client/proxy/accountQuota.js +0 -162
  265. package/dist/client/proxy/claudeFormat.js +0 -595
  266. package/dist/client/proxy/modelRouter.js +0 -29
  267. package/dist/client/proxy/oauthFetch.js +0 -367
  268. package/dist/client/proxy/proxyFetch.js +0 -586
  269. package/dist/client/proxy/requestLogger.js +0 -207
  270. package/dist/client/proxy/tokenRefresh.js +0 -124
  271. package/dist/client/proxy/usageStats.js +0 -74
  272. package/dist/client/proxy/utils/noProxyUtils.js +0 -149
  273. package/dist/client/rag/ChunkerFactory.js +0 -320
  274. package/dist/client/rag/ChunkerRegistry.js +0 -421
  275. package/dist/client/rag/chunkers/BaseChunker.js +0 -143
  276. package/dist/client/rag/chunkers/CharacterChunker.js +0 -28
  277. package/dist/client/rag/chunkers/HTMLChunker.js +0 -38
  278. package/dist/client/rag/chunkers/JSONChunker.js +0 -68
  279. package/dist/client/rag/chunkers/LaTeXChunker.js +0 -63
  280. package/dist/client/rag/chunkers/MarkdownChunker.js +0 -306
  281. package/dist/client/rag/chunkers/RecursiveChunker.js +0 -139
  282. package/dist/client/rag/chunkers/SemanticMarkdownChunker.js +0 -138
  283. package/dist/client/rag/chunkers/SentenceChunker.js +0 -66
  284. package/dist/client/rag/chunkers/TokenChunker.js +0 -61
  285. package/dist/client/rag/chunkers/index.js +0 -15
  286. package/dist/client/rag/chunking/characterChunker.js +0 -142
  287. package/dist/client/rag/chunking/chunkerRegistry.js +0 -194
  288. package/dist/client/rag/chunking/htmlChunker.js +0 -247
  289. package/dist/client/rag/chunking/index.js +0 -17
  290. package/dist/client/rag/chunking/jsonChunker.js +0 -281
  291. package/dist/client/rag/chunking/latexChunker.js +0 -251
  292. package/dist/client/rag/chunking/markdownChunker.js +0 -373
  293. package/dist/client/rag/chunking/recursiveChunker.js +0 -148
  294. package/dist/client/rag/chunking/semanticChunker.js +0 -306
  295. package/dist/client/rag/chunking/sentenceChunker.js +0 -230
  296. package/dist/client/rag/chunking/tokenChunker.js +0 -183
  297. package/dist/client/rag/document/MDocument.js +0 -392
  298. package/dist/client/rag/document/index.js +0 -5
  299. package/dist/client/rag/document/loaders.js +0 -500
  300. package/dist/client/rag/errors/RAGError.js +0 -274
  301. package/dist/client/rag/errors/index.js +0 -6
  302. package/dist/client/rag/graphRag/graphRAG.js +0 -401
  303. package/dist/client/rag/graphRag/index.js +0 -4
  304. package/dist/client/rag/index.js +0 -141
  305. package/dist/client/rag/metadata/MetadataExtractorFactory.js +0 -418
  306. package/dist/client/rag/metadata/MetadataExtractorRegistry.js +0 -362
  307. package/dist/client/rag/metadata/index.js +0 -9
  308. package/dist/client/rag/metadata/metadataExtractor.js +0 -280
  309. package/dist/client/rag/pipeline/RAGPipeline.js +0 -436
  310. package/dist/client/rag/pipeline/contextAssembly.js +0 -341
  311. package/dist/client/rag/pipeline/index.js +0 -5
  312. package/dist/client/rag/ragIntegration.js +0 -321
  313. package/dist/client/rag/reranker/RerankerFactory.js +0 -430
  314. package/dist/client/rag/reranker/RerankerRegistry.js +0 -402
  315. package/dist/client/rag/reranker/index.js +0 -9
  316. package/dist/client/rag/reranker/reranker.js +0 -277
  317. package/dist/client/rag/resilience/CircuitBreaker.js +0 -431
  318. package/dist/client/rag/resilience/RetryHandler.js +0 -304
  319. package/dist/client/rag/resilience/index.js +0 -7
  320. package/dist/client/rag/retrieval/hybridSearch.js +0 -335
  321. package/dist/client/rag/retrieval/index.js +0 -5
  322. package/dist/client/rag/retrieval/vectorQueryTool.js +0 -307
  323. package/dist/client/rag/types.js +0 -8
  324. package/dist/client/sdk/toolRegistration.js +0 -377
  325. package/dist/client/server/abstract/baseServerAdapter.js +0 -575
  326. package/dist/client/server/adapters/expressAdapter.js +0 -486
  327. package/dist/client/server/adapters/fastifyAdapter.js +0 -472
  328. package/dist/client/server/adapters/honoAdapter.js +0 -632
  329. package/dist/client/server/adapters/koaAdapter.js +0 -510
  330. package/dist/client/server/errors.js +0 -486
  331. package/dist/client/server/factory/serverAdapterFactory.js +0 -160
  332. package/dist/client/server/index.js +0 -108
  333. package/dist/client/server/middleware/abortSignal.js +0 -111
  334. package/dist/client/server/middleware/auth.js +0 -388
  335. package/dist/client/server/middleware/cache.js +0 -359
  336. package/dist/client/server/middleware/common.js +0 -281
  337. package/dist/client/server/middleware/deprecation.js +0 -190
  338. package/dist/client/server/middleware/mcpBodyAttachment.js +0 -63
  339. package/dist/client/server/middleware/rateLimit.js +0 -227
  340. package/dist/client/server/middleware/validation.js +0 -388
  341. package/dist/client/server/openapi/generator.js +0 -398
  342. package/dist/client/server/openapi/index.js +0 -36
  343. package/dist/client/server/openapi/schemas.js +0 -695
  344. package/dist/client/server/openapi/templates.js +0 -374
  345. package/dist/client/server/routes/agentRoutes.js +0 -189
  346. package/dist/client/server/routes/claudeProxyRoutes.js +0 -1600
  347. package/dist/client/server/routes/healthRoutes.js +0 -187
  348. package/dist/client/server/routes/index.js +0 -57
  349. package/dist/client/server/routes/mcpRoutes.js +0 -342
  350. package/dist/client/server/routes/memoryRoutes.js +0 -350
  351. package/dist/client/server/routes/openApiRoutes.js +0 -126
  352. package/dist/client/server/routes/toolRoutes.js +0 -199
  353. package/dist/client/server/streaming/dataStream.js +0 -486
  354. package/dist/client/server/streaming/index.js +0 -11
  355. package/dist/client/server/types.js +0 -67
  356. package/dist/client/server/utils/redaction.js +0 -334
  357. package/dist/client/server/utils/validation.js +0 -243
  358. package/dist/client/server/websocket/WebSocketHandler.js +0 -383
  359. package/dist/client/server/websocket/index.js +0 -4
  360. package/dist/client/services/server/ai/observability/instrumentation.js +0 -808
  361. package/dist/client/telemetry/attributes.js +0 -100
  362. package/dist/client/telemetry/index.js +0 -26
  363. package/dist/client/telemetry/telemetryService.js +0 -308
  364. package/dist/client/telemetry/tracers.js +0 -17
  365. package/dist/client/telemetry/withSpan.js +0 -34
  366. package/dist/client/types/actionTypes.js +0 -6
  367. package/dist/client/types/analytics.js +0 -5
  368. package/dist/client/types/authTypes.js +0 -9
  369. package/dist/client/types/circuitBreakerErrors.js +0 -34
  370. package/dist/client/types/cli.js +0 -21
  371. package/dist/client/types/clientTypes.js +0 -10
  372. package/dist/client/types/common.js +0 -51
  373. package/dist/client/types/configTypes.js +0 -49
  374. package/dist/client/types/content.js +0 -19
  375. package/dist/client/types/contextTypes.js +0 -400
  376. package/dist/client/types/conversation.js +0 -47
  377. package/dist/client/types/conversationMemoryInterface.js +0 -6
  378. package/dist/client/types/domainTypes.js +0 -5
  379. package/dist/client/types/errors.js +0 -167
  380. package/dist/client/types/evaluation.js +0 -5
  381. package/dist/client/types/evaluationProviders.js +0 -5
  382. package/dist/client/types/evaluationTypes.js +0 -1
  383. package/dist/client/types/externalMcp.js +0 -6
  384. package/dist/client/types/fileReferenceTypes.js +0 -8
  385. package/dist/client/types/fileTypes.js +0 -4
  386. package/dist/client/types/generateTypes.js +0 -1
  387. package/dist/client/types/guardrails.js +0 -1
  388. package/dist/client/types/hitlTypes.js +0 -8
  389. package/dist/client/types/index.js +0 -57
  390. package/dist/client/types/mcpTypes.js +0 -5
  391. package/dist/client/types/middlewareTypes.js +0 -1
  392. package/dist/client/types/modelTypes.js +0 -30
  393. package/dist/client/types/multimodal.js +0 -135
  394. package/dist/client/types/observability.js +0 -6
  395. package/dist/client/types/pptTypes.js +0 -82
  396. package/dist/client/types/providers.js +0 -111
  397. package/dist/client/types/proxyTypes.js +0 -16
  398. package/dist/client/types/ragTypes.js +0 -7
  399. package/dist/client/types/sdkTypes.js +0 -8
  400. package/dist/client/types/serviceTypes.js +0 -5
  401. package/dist/client/types/streamTypes.js +0 -1
  402. package/dist/client/types/subscriptionTypes.js +0 -9
  403. package/dist/client/types/taskClassificationTypes.js +0 -5
  404. package/dist/client/types/tools.js +0 -24
  405. package/dist/client/types/ttsTypes.js +0 -57
  406. package/dist/client/types/typeAliases.js +0 -48
  407. package/dist/client/types/utilities.js +0 -4
  408. package/dist/client/types/workflowTypes.js +0 -30
  409. package/dist/client/utils/async/withTimeout.js +0 -98
  410. package/dist/client/utils/asyncMutex.js +0 -60
  411. package/dist/client/utils/conversationMemory.js +0 -431
  412. package/dist/client/utils/csvProcessor.js +0 -846
  413. package/dist/client/utils/errorHandling.js +0 -936
  414. package/dist/client/utils/evaluationUtils.js +0 -131
  415. package/dist/client/utils/factoryProcessing.js +0 -589
  416. package/dist/client/utils/fileDetector.js +0 -2161
  417. package/dist/client/utils/imageCache.js +0 -376
  418. package/dist/client/utils/imageProcessor.js +0 -704
  419. package/dist/client/utils/logger.js +0 -491
  420. package/dist/client/utils/mcpDefaults.js +0 -134
  421. package/dist/client/utils/messageBuilder.js +0 -1653
  422. package/dist/client/utils/modelAliasResolver.js +0 -54
  423. package/dist/client/utils/modelDetection.js +0 -80
  424. package/dist/client/utils/modelRouter.js +0 -292
  425. package/dist/client/utils/multimodalOptionsBuilder.js +0 -65
  426. package/dist/client/utils/observabilityHelpers.js +0 -47
  427. package/dist/client/utils/parameterValidation.js +0 -966
  428. package/dist/client/utils/pdfProcessor.js +0 -410
  429. package/dist/client/utils/performance.js +0 -222
  430. package/dist/client/utils/pricing.js +0 -340
  431. package/dist/client/utils/promptRedaction.js +0 -62
  432. package/dist/client/utils/providerConfig.js +0 -1009
  433. package/dist/client/utils/providerHealth.js +0 -1237
  434. package/dist/client/utils/providerRetry.js +0 -112
  435. package/dist/client/utils/providerUtils.js +0 -434
  436. package/dist/client/utils/rateLimiter.js +0 -200
  437. package/dist/client/utils/redis.js +0 -368
  438. package/dist/client/utils/retryHandler.js +0 -269
  439. package/dist/client/utils/retryability.js +0 -22
  440. package/dist/client/utils/sanitizers/svg.js +0 -481
  441. package/dist/client/utils/schemaConversion.js +0 -255
  442. package/dist/client/utils/taskClassificationUtils.js +0 -149
  443. package/dist/client/utils/taskClassifier.js +0 -94
  444. package/dist/client/utils/thinkingConfig.js +0 -104
  445. package/dist/client/utils/timeout.js +0 -359
  446. package/dist/client/utils/tokenEstimation.js +0 -142
  447. package/dist/client/utils/tokenLimits.js +0 -125
  448. package/dist/client/utils/tokenUtils.js +0 -239
  449. package/dist/client/utils/toolUtils.js +0 -75
  450. package/dist/client/utils/transformationUtils.js +0 -554
  451. package/dist/client/utils/ttsProcessor.js +0 -286
  452. package/dist/client/utils/typeUtils.js +0 -97
  453. package/dist/client/utils/videoAnalysisProcessor.js +0 -67
  454. package/dist/client/workflow/config.js +0 -398
  455. package/dist/client/workflow/core/ensembleExecutor.js +0 -407
  456. package/dist/client/workflow/core/judgeScorer.js +0 -544
  457. package/dist/client/workflow/core/responseConditioner.js +0 -225
  458. package/dist/client/workflow/core/types/conditionerTypes.js +0 -7
  459. package/dist/client/workflow/core/types/ensembleTypes.js +0 -7
  460. package/dist/client/workflow/core/types/index.js +0 -7
  461. package/dist/client/workflow/core/types/judgeTypes.js +0 -7
  462. package/dist/client/workflow/core/types/layerTypes.js +0 -7
  463. package/dist/client/workflow/core/types/registryTypes.js +0 -7
  464. package/dist/client/workflow/core/workflowRegistry.js +0 -304
  465. package/dist/client/workflow/core/workflowRunner.js +0 -586
  466. package/dist/client/workflow/index.js +0 -50
  467. package/dist/client/workflow/types.js +0 -9
  468. package/dist/client/workflow/utils/types/index.js +0 -7
  469. package/dist/client/workflow/utils/workflowMetrics.js +0 -311
  470. package/dist/client/workflow/utils/workflowValidation.js +0 -420
  471. package/dist/client/workflow/workflows/adaptiveWorkflow.js +0 -366
  472. package/dist/client/workflow/workflows/consensusWorkflow.js +0 -192
  473. package/dist/client/workflow/workflows/fallbackWorkflow.js +0 -225
  474. package/dist/client/workflow/workflows/multiJudgeWorkflow.js +0 -351
  475. /package/dist/client/{client/reactHooks.js → reactHooks.js} +0 -0
@@ -1,2936 +0,0 @@
1
- import dns from "node:dns";
2
- import { createVertex, } from "@ai-sdk/google-vertex";
3
- import { createVertexAnthropic, } from "@ai-sdk/google-vertex/anthropic";
4
- import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
5
- import { embed, embedMany, Output, stepCountIs, streamText, } from "ai";
6
- import fs from "fs";
7
- import os from "os";
8
- import path from "path";
9
- import { ErrorCategory, ErrorSeverity, } from "../constants/enums.js";
10
- import { BaseProvider } from "../core/baseProvider.js";
11
- import { DEFAULT_MAX_STEPS, GLOBAL_LOCATION_MODELS, } from "../core/constants.js";
12
- import { ModelConfigurationManager } from "../core/modelConfiguration.js";
13
- import { createProxyFetch } from "../proxy/proxyFetch.js";
14
- import { ATTR, tracers, withClientSpan } from "../telemetry/index.js";
15
- import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/errors.js";
16
- import { ERROR_CODES, NeuroLinkError } from "../utils/errorHandling.js";
17
- import { FileDetector } from "../utils/fileDetector.js";
18
- import { logger } from "../utils/logger.js";
19
- import { isGemini3Model } from "../utils/modelDetection.js";
20
- import { calculateCost } from "../utils/pricing.js";
21
- import { createGoogleAuthConfig, createVertexProjectConfig, validateApiKey, } from "../utils/providerConfig.js";
22
- import { convertZodToJsonSchema, inlineJsonSchema, } from "../utils/schemaConversion.js";
23
- import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
24
- import { estimateTokens } from "../utils/tokenEstimation.js";
25
- import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps as computeMaxStepsShared, createTextChannel, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
26
- import { getModelId } from "./providerTypeUtils.js";
27
- // Import proper types for multimodal message handling
28
- // Keep-alive note: Node.js native fetch and undici (used by createProxyFetch)
29
- // handle HTTP keep-alive internally. The fetchWithRetry wrapper in proxyFetch.ts
30
- // provides retry protection for transient ECONNRESET/ETIMEDOUT errors.
31
- //
32
- // Auth isolation note: @ai-sdk/google-vertex resolves auth tokens (via
33
- // generateAuthToken → google-auth-library) BEFORE applying the user AbortSignal
34
- // to the main API call. Auth token refresh uses gaxios internally, not our
35
- // custom fetch, so it is inherently isolated from user cancellation signals.
36
- // The image generation path (getImageGenerationAccessToken) has an additional
37
- // explicit 15s timeout per attempt for direct REST API calls.
38
- /** Check whether an IP address belongs to a private, loopback, or link-local range. */
39
- function isPrivateOrLoopbackAddress(address) {
40
- const lower = address.toLowerCase();
41
- // IPv4 loopback, unspecified, and private ranges
42
- if (address.startsWith("127.") || address === "0.0.0.0") {
43
- return true;
44
- }
45
- if (address.startsWith("10.") || address.startsWith("192.168.")) {
46
- return true;
47
- }
48
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(address)) {
49
- return true;
50
- }
51
- // IPv6 loopback, link-local, unique-local
52
- if (address === "::1" ||
53
- lower.startsWith("fe80:") ||
54
- lower.startsWith("fc00:") ||
55
- lower.startsWith("fd00:")) {
56
- return true;
57
- }
58
- return false;
59
- }
60
- const MAX_IMAGE_DOWNLOAD_BYTES = 10 * 1024 * 1024; // 10 MB
61
- const streamTracer = trace.getTracer("neurolink.provider.vertex");
62
- // Enhanced Anthropic support with direct imports
63
- // Using the dual provider architecture from Vercel AI SDK
64
- const hasAnthropicSupport = () => {
65
- try {
66
- // Verify the anthropic module is available
67
- return typeof createVertexAnthropic === "function";
68
- }
69
- catch {
70
- return false;
71
- }
72
- };
73
- // Configuration helpers - now using consolidated utility
74
- const getVertexProjectId = () => {
75
- return validateApiKey(createVertexProjectConfig());
76
- };
77
- const getVertexLocation = () => {
78
- return (process.env.GOOGLE_CLOUD_LOCATION ||
79
- process.env.VERTEX_LOCATION ||
80
- process.env.GOOGLE_VERTEX_LOCATION ||
81
- "us-central1");
82
- };
83
- const getDefaultVertexModel = () => {
84
- // Use gemini-2.5-flash as default - latest and best price-performance model
85
- // Override with VERTEX_MODEL environment variable if needed
86
- return process.env.VERTEX_MODEL || "gemini-2.5-flash";
87
- };
88
- const hasGoogleCredentials = () => {
89
- return !!(process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK ||
90
- process.env.GOOGLE_APPLICATION_CREDENTIALS ||
91
- process.env.GOOGLE_SERVICE_ACCOUNT_KEY ||
92
- (process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
93
- process.env.GOOGLE_AUTH_PRIVATE_KEY));
94
- };
95
- // Module-level cache for runtime-created credentials file to avoid per-request writes
96
- let cachedCredentialsPath = null;
97
- // Enhanced Vertex settings creation with authentication fallback and proxy support
98
- const createVertexSettings = async (region) => {
99
- const location = region || getVertexLocation();
100
- const project = getVertexProjectId();
101
- const baseSettings = {
102
- project,
103
- location,
104
- fetch: createProxyFetch(),
105
- };
106
- // Special handling for global endpoint
107
- // Google's global endpoint uses aiplatform.googleapis.com (no region prefix)
108
- // instead of {region}-aiplatform.googleapis.com
109
- if (location === "global") {
110
- baseSettings.baseURL = `https://aiplatform.googleapis.com/v1/projects/${project}/locations/global/publishers/google`;
111
- logger.debug("[GoogleVertexProvider] Using global endpoint", {
112
- baseURL: baseSettings.baseURL,
113
- location,
114
- project,
115
- });
116
- }
117
- // šŸŽÆ OPTION 2: Create credentials file from environment variables at runtime
118
- // This solves the problem where GOOGLE_APPLICATION_CREDENTIALS exists in ZSHRC locally
119
- // but the file doesn't exist on production servers
120
- // First, try to create credentials file from individual environment variables
121
- const requiredEnvVarsForFile = {
122
- type: process.env.GOOGLE_AUTH_TYPE,
123
- project_id: process.env.GOOGLE_AUTH_BREEZE_PROJECT_ID,
124
- private_key: process.env.GOOGLE_AUTH_PRIVATE_KEY,
125
- client_email: process.env.GOOGLE_AUTH_CLIENT_EMAIL,
126
- client_id: process.env.GOOGLE_AUTH_CLIENT_ID,
127
- auth_uri: process.env.GOOGLE_AUTH_AUTH_URI,
128
- token_uri: process.env.GOOGLE_AUTH_TOKEN_URI,
129
- auth_provider_x509_cert_url: process.env.GOOGLE_AUTH_AUTH_PROVIDER_CERT_URL,
130
- client_x509_cert_url: process.env.GOOGLE_AUTH_CLIENT_CERT_URL,
131
- universe_domain: process.env.GOOGLE_AUTH_UNIVERSE_DOMAIN,
132
- };
133
- // If we have the essential fields, create a runtime credentials file (cached)
134
- if (requiredEnvVarsForFile.client_email &&
135
- requiredEnvVarsForFile.private_key) {
136
- // Return cached path if already written and still exists on disk
137
- if (cachedCredentialsPath && fs.existsSync(cachedCredentialsPath)) {
138
- process.env.GOOGLE_APPLICATION_CREDENTIALS = cachedCredentialsPath;
139
- return baseSettings;
140
- }
141
- try {
142
- // Build complete service account credentials object
143
- const serviceAccountCredentials = {
144
- type: requiredEnvVarsForFile.type || "service_account",
145
- project_id: requiredEnvVarsForFile.project_id || getVertexProjectId(),
146
- private_key: requiredEnvVarsForFile.private_key.replace(/\\n/g, "\n"),
147
- client_email: requiredEnvVarsForFile.client_email,
148
- client_id: requiredEnvVarsForFile.client_id || "",
149
- auth_uri: requiredEnvVarsForFile.auth_uri ||
150
- "https://accounts.google.com/o/oauth2/auth",
151
- token_uri: requiredEnvVarsForFile.token_uri ||
152
- "https://oauth2.googleapis.com/token",
153
- auth_provider_x509_cert_url: requiredEnvVarsForFile.auth_provider_x509_cert_url ||
154
- "https://www.googleapis.com/oauth2/v1/certs",
155
- client_x509_cert_url: requiredEnvVarsForFile.client_x509_cert_url || "",
156
- universe_domain: requiredEnvVarsForFile.universe_domain || "googleapis.com",
157
- };
158
- // Create temporary credentials file with restricted permissions
159
- const tmpDir = os.tmpdir();
160
- const credentialsFileName = `google-credentials-${Date.now()}-${Math.random().toString(36).substring(2, 11)}.json`;
161
- const credentialsFilePath = path.join(tmpDir, credentialsFileName);
162
- fs.writeFileSync(credentialsFilePath, JSON.stringify(serviceAccountCredentials, null, 2), { mode: 0o600 });
163
- cachedCredentialsPath = credentialsFilePath;
164
- // Register cleanup on process exit to remove the credentials file
165
- process.once("exit", () => {
166
- try {
167
- if (cachedCredentialsPath && fs.existsSync(cachedCredentialsPath)) {
168
- fs.unlinkSync(cachedCredentialsPath);
169
- }
170
- }
171
- catch {
172
- /* ignore cleanup errors */
173
- }
174
- });
175
- // Set the environment variable to point to our runtime-created file
176
- process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsFilePath;
177
- return baseSettings;
178
- }
179
- catch {
180
- // Silent error handling for runtime credentials file creation
181
- }
182
- }
183
- // šŸŽÆ OPTION 1: Check for principal account authentication (Accept any valid GOOGLE_APPLICATION_CREDENTIALS file (service account OR ADC))
184
- if (process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK) {
185
- const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK;
186
- // Check if the credentials file exists
187
- let fileExists;
188
- try {
189
- fileExists = fs.existsSync(credentialsPath);
190
- }
191
- catch {
192
- fileExists = false;
193
- }
194
- if (fileExists) {
195
- return baseSettings;
196
- }
197
- }
198
- else {
199
- if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
200
- const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
201
- // Check if the credentials file exists
202
- let fileExists;
203
- try {
204
- fileExists = fs.existsSync(credentialsPath);
205
- }
206
- catch {
207
- fileExists = false;
208
- }
209
- if (fileExists) {
210
- return baseSettings;
211
- }
212
- }
213
- }
214
- // Fallback to explicit credentials for development and production
215
- // Enhanced to check ALL required fields from the .env file configuration
216
- const requiredEnvVars = {
217
- type: process.env.GOOGLE_AUTH_TYPE,
218
- project_id: process.env.GOOGLE_AUTH_BREEZE_PROJECT_ID,
219
- private_key: process.env.GOOGLE_AUTH_PRIVATE_KEY,
220
- client_email: process.env.GOOGLE_AUTH_CLIENT_EMAIL,
221
- client_id: process.env.GOOGLE_AUTH_CLIENT_ID,
222
- auth_uri: process.env.GOOGLE_AUTH_AUTH_URI,
223
- token_uri: process.env.GOOGLE_AUTH_TOKEN_URI,
224
- auth_provider_x509_cert_url: process.env.GOOGLE_AUTH_AUTH_PROVIDER_CERT_URL,
225
- client_x509_cert_url: process.env.GOOGLE_AUTH_CLIENT_CERT_URL,
226
- universe_domain: process.env.GOOGLE_AUTH_UNIVERSE_DOMAIN,
227
- };
228
- // Check if we have the minimal required fields (client_email and private_key are essential)
229
- if (requiredEnvVars.client_email && requiredEnvVars.private_key) {
230
- logger.debug("Using explicit service account credentials authentication", {
231
- authMethod: "explicit_service_account_credentials",
232
- hasType: !!requiredEnvVars.type,
233
- hasProjectId: !!requiredEnvVars.project_id,
234
- hasClientEmail: !!requiredEnvVars.client_email,
235
- hasPrivateKey: !!requiredEnvVars.private_key,
236
- hasClientId: !!requiredEnvVars.client_id,
237
- hasAuthUri: !!requiredEnvVars.auth_uri,
238
- hasTokenUri: !!requiredEnvVars.token_uri,
239
- hasAuthProviderCertUrl: !!requiredEnvVars.auth_provider_x509_cert_url,
240
- hasClientCertUrl: !!requiredEnvVars.client_x509_cert_url,
241
- hasUniverseDomain: !!requiredEnvVars.universe_domain,
242
- credentialsCompleteness: "using_individual_env_vars_as_fallback",
243
- });
244
- // Build complete service account credentials object
245
- const serviceAccountCredentials = {
246
- type: requiredEnvVars.type || "service_account",
247
- project_id: requiredEnvVars.project_id || getVertexProjectId(),
248
- private_key: requiredEnvVars.private_key.replace(/\\n/g, "\n"),
249
- client_email: requiredEnvVars.client_email,
250
- client_id: requiredEnvVars.client_id || "",
251
- auth_uri: requiredEnvVars.auth_uri || "https://accounts.google.com/o/oauth2/auth",
252
- token_uri: requiredEnvVars.token_uri || "https://oauth2.googleapis.com/token",
253
- auth_provider_x509_cert_url: requiredEnvVars.auth_provider_x509_cert_url ||
254
- "https://www.googleapis.com/oauth2/v1/certs",
255
- client_x509_cert_url: requiredEnvVars.client_x509_cert_url || "",
256
- universe_domain: requiredEnvVars.universe_domain || "googleapis.com",
257
- };
258
- return {
259
- ...baseSettings,
260
- googleAuthOptions: {
261
- credentials: serviceAccountCredentials,
262
- },
263
- };
264
- }
265
- // Log comprehensive warning if no valid authentication is available
266
- logger.warn("No valid authentication found for Google Vertex AI", {
267
- authMethod: "none",
268
- authenticationAttempts: {
269
- principalAccountFile: {
270
- envVarSet: !!process.env.GOOGLE_APPLICATION_CREDENTIALS,
271
- filePath: process.env.GOOGLE_APPLICATION_CREDENTIALS || "NOT_SET",
272
- fileExists: false, // We already checked above
273
- },
274
- explicitCredentials: {
275
- hasClientEmail: !!requiredEnvVars.client_email,
276
- hasPrivateKey: !!requiredEnvVars.private_key,
277
- hasProjectId: !!requiredEnvVars.project_id,
278
- hasType: !!requiredEnvVars.type,
279
- missingFields: Object.entries(requiredEnvVars)
280
- .filter(([_key, value]) => !value)
281
- .map(([key]) => key),
282
- },
283
- },
284
- troubleshooting: [
285
- "1. Ensure GOOGLE_APPLICATION_CREDENTIALS points to an existing file, OR",
286
- "2. Set individual environment variables: GOOGLE_AUTH_CLIENT_EMAIL and GOOGLE_AUTH_PRIVATE_KEY",
287
- ],
288
- });
289
- return baseSettings;
290
- };
291
- // Create Anthropic-specific Vertex settings with the same authentication and proxy support
292
- const createVertexAnthropicSettings = async (region) => {
293
- // The @ai-sdk/google-vertex SDK constructs Anthropic URLs as:
294
- // https://{location}-aiplatform.googleapis.com/...
295
- // When location is "global", this creates "https://global-aiplatform.googleapis.com"
296
- // which is invalid. The correct global endpoint omits the region prefix entirely.
297
- // Since the SDK doesn't handle this, redirect "global" to "us-east5" for Anthropic.
298
- const anthropicRegion = !region || region === "global" ? "us-east5" : region;
299
- const baseVertexSettings = await createVertexSettings(anthropicRegion);
300
- // GoogleVertexAnthropicProviderSettings extends GoogleVertexProviderSettings
301
- // so we can use the same settings with proper typing
302
- return {
303
- project: baseVertexSettings.project,
304
- location: baseVertexSettings.location,
305
- fetch: baseVertexSettings.fetch,
306
- ...(baseVertexSettings.googleAuthOptions && {
307
- googleAuthOptions: baseVertexSettings.googleAuthOptions,
308
- }),
309
- };
310
- };
311
- // Helper function to determine if a model is an Anthropic model
312
- const isAnthropicModel = (modelName) => {
313
- return modelName.toLowerCase().includes("claude");
314
- };
315
- /**
316
- * Vertex Model Aliases
317
- *
318
- * Maps shorthand model names to their full versioned IDs required by the
319
- * Vertex AI API. This allows users to pass convenient names like
320
- * "claude-sonnet-4-5" instead of "claude-sonnet-4-5@20250929".
321
- *
322
- * Alias resolution runs at the very start of getModel() so that all
323
- * downstream code (isAnthropicModel, validateAnthropicModelName, etc.)
324
- * sees the canonical versioned name.
325
- *
326
- * To add a new model: simply add an entry mapping the shorthand to the
327
- * full versioned string. No other changes are needed.
328
- */
329
- export const VERTEX_MODEL_ALIASES = {
330
- // Claude 4.x shorthand aliases → versioned names
331
- "claude-sonnet-4-5": "claude-sonnet-4-5@20250929",
332
- "claude-opus-4-5": "claude-opus-4-5@20251124",
333
- "claude-haiku-4-5": "claude-haiku-4-5@20251001",
334
- "claude-sonnet-4": "claude-sonnet-4@20250514",
335
- "claude-opus-4": "claude-opus-4@20250514",
336
- "claude-opus-4-1": "claude-opus-4-1@20250805",
337
- // Claude 3.x shorthand aliases → versioned names
338
- "claude-3-7-sonnet": "claude-3-7-sonnet@20250219",
339
- "claude-3-5-sonnet": "claude-3-5-sonnet-20241022",
340
- "claude-3-5-haiku": "claude-3-5-haiku-20241022",
341
- "claude-3-opus": "claude-3-opus-20240229",
342
- "claude-3-sonnet": "claude-3-sonnet-20240229",
343
- "claude-3-haiku": "claude-3-haiku-20240307",
344
- // Gemini shorthand aliases
345
- "gemini-3-pro": "gemini-3.1-pro-preview",
346
- "gemini-3-flash": "gemini-3-flash-preview",
347
- };
348
- /**
349
- * Google Vertex AI Provider v2 - BaseProvider Implementation
350
- *
351
- * Features:
352
- * - Extends BaseProvider for shared functionality
353
- * - Preserves existing Google Cloud authentication
354
- * - Maintains Anthropic model support via dynamic imports
355
- * - Fresh model creation for each request
356
- * - Enhanced error handling with setup guidance
357
- * - Tool registration and context management
358
- *
359
- * @important Structured Output Limitation (Gemini Models Only)
360
- * Google Gemini models on Vertex AI cannot combine function calling (tools) with
361
- * structured output (JSON schema). When using schemas, you MUST set disableTools: true.
362
- *
363
- * Error without disableTools:
364
- * "Function calling with a response mime type: 'application/json' is unsupported"
365
- *
366
- * This limitation ONLY affects Gemini models. Anthropic Claude models via Vertex
367
- * AI do NOT have this limitation and support both tools + schemas simultaneously.
368
- *
369
- * @example Gemini models with schemas
370
- * ```typescript
371
- * const provider = new GoogleVertexProvider("gemini-2.5-flash");
372
- * const result = await provider.generate({
373
- * input: { text: "Analyze data" },
374
- * schema: MySchema,
375
- * output: { format: "json" },
376
- * disableTools: true // Required for Gemini models
377
- * });
378
- * ```
379
- *
380
- * @example Claude models (no limitation)
381
- * ```typescript
382
- * const provider = new GoogleVertexProvider("claude-3-5-sonnet-20241022");
383
- * const result = await provider.generate({
384
- * input: { text: "Analyze data" },
385
- * schema: MySchema,
386
- * output: { format: "json" }
387
- * // No disableTools needed - Claude supports both
388
- * });
389
- * ```
390
- *
391
- * @note Gemini 3 Pro Preview (November 2025) will support combining tools + schemas
392
- * @see https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models
393
- */
394
- export class GoogleVertexProvider extends BaseProvider {
395
- projectId;
396
- location;
397
- registeredTools = new Map();
398
- toolContext = {};
399
- // Memory-managed cache for model configuration lookups to avoid repeated calls
400
- // Uses WeakMap for automatic cleanup and bounded LRU for recently used models
401
- static modelConfigCache = new Map();
402
- static modelConfigCacheTime = 0;
403
- static CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
404
- static MAX_CACHE_SIZE = 50; // Prevent memory leaks by limiting cache size
405
- // Memory-managed cache for maxTokens handling decisions to optimize streaming performance
406
- static maxTokensCache = new Map();
407
- static maxTokensCacheTime = 0;
408
- constructor(modelName, _providerName, sdk, region) {
409
- super(modelName, "vertex", sdk);
410
- // Validate Google Cloud credentials - now using consolidated utility
411
- if (!hasGoogleCredentials()) {
412
- validateApiKey(createGoogleAuthConfig());
413
- }
414
- // Initialize Google Cloud configuration
415
- this.projectId = getVertexProjectId();
416
- this.location = region || getVertexLocation();
417
- logger.debug("[GoogleVertexProvider] Constructor initialized", {
418
- regionParam: region,
419
- resolvedLocation: this.location,
420
- projectId: this.projectId,
421
- });
422
- logger.debug("Google Vertex AI BaseProvider v2 initialized", {
423
- modelName: this.modelName,
424
- projectId: this.projectId,
425
- location: this.location,
426
- provider: this.providerName,
427
- });
428
- }
429
- getProviderName() {
430
- return "vertex";
431
- }
432
- getDefaultModel() {
433
- return getDefaultVertexModel();
434
- }
435
- /**
436
- * Get the default embedding model for Google Vertex
437
- * @returns The default Vertex AI embedding model name
438
- */
439
- getDefaultEmbeddingModel() {
440
- return (process.env.VERTEX_EMBEDDING_MODEL ||
441
- process.env.GOOGLE_EMBEDDING_MODEL ||
442
- "text-embedding-004");
443
- }
444
- /**
445
- * Returns the Vercel AI SDK model instance for Google Vertex
446
- * Creates fresh model instances for each request
447
- */
448
- async getAISDKModel() {
449
- const model = await this.getModel();
450
- return model;
451
- }
452
- /**
453
- * Resolve a raw model name through the alias map.
454
- * Used internally to normalize model names before any API calls.
455
- */
456
- resolveAlias(modelName) {
457
- return VERTEX_MODEL_ALIASES[modelName] ?? modelName;
458
- }
459
- /**
460
- * Initialize model creation tracking
461
- */
462
- initializeModelCreationLogging() {
463
- const modelCreationId = `vertex-model-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
464
- const modelCreationStartTime = Date.now();
465
- const modelCreationHrTimeStart = process.hrtime.bigint();
466
- // Resolve shorthand model aliases (e.g. "claude-sonnet-4-5" → "claude-sonnet-4-5@20250929")
467
- // before any downstream logic that depends on the versioned name.
468
- const rawModelName = this.modelName || getDefaultVertexModel();
469
- const modelName = VERTEX_MODEL_ALIASES[rawModelName] ?? rawModelName;
470
- return {
471
- modelCreationId,
472
- modelCreationStartTime,
473
- modelCreationHrTimeStart,
474
- modelName,
475
- };
476
- }
477
- /**
478
- * Check if model is Anthropic-based and attempt creation
479
- */
480
- async attemptAnthropicModelCreation(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart) {
481
- const isAnthropic = isAnthropicModel(modelName);
482
- if (!isAnthropic) {
483
- return null;
484
- }
485
- logger.debug("Creating Anthropic model using vertexAnthropic provider", {
486
- modelName,
487
- });
488
- if (!hasAnthropicSupport()) {
489
- logger.warn(`[GoogleVertexProvider] Anthropic support not available, falling back to Google model`);
490
- return null;
491
- }
492
- try {
493
- const anthropicModel = await this.createAnthropicModel(modelName);
494
- if (anthropicModel) {
495
- return anthropicModel;
496
- }
497
- // Anthropic model creation returned null, falling back to Google model
498
- }
499
- catch (error) {
500
- logger.error(`[GoogleVertexProvider] āŒ LOG_POINT_V006_ANTHROPIC_MODEL_ERROR`, {
501
- logPoint: "V006_ANTHROPIC_MODEL_ERROR",
502
- modelCreationId,
503
- timestamp: new Date().toISOString(),
504
- elapsedMs: Date.now() - modelCreationStartTime,
505
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
506
- modelName,
507
- error: error instanceof Error ? error.message : String(error),
508
- errorName: error instanceof Error ? error.name : "UnknownError",
509
- errorStack: error instanceof Error ? error.stack : undefined,
510
- fallbackToGoogle: true,
511
- message: "Anthropic model creation failed - falling back to Google model",
512
- });
513
- }
514
- // Fall back to regular model if Anthropic not available
515
- logger.warn(`Anthropic model ${modelName} requested but not available, falling back to Google model`);
516
- return null;
517
- }
518
- /**
519
- * Create Google Vertex model with comprehensive logging and error handling
520
- */
521
- async createGoogleVertexModel(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart) {
522
- logger.debug("Creating Google Vertex model", {
523
- modelName,
524
- project: this.projectId,
525
- location: this.location,
526
- });
527
- const vertexSettingsStartTime = process.hrtime.bigint();
528
- logger.debug(`[GoogleVertexProvider] āš™ļø LOG_POINT_V008_VERTEX_SETTINGS_START`, {
529
- logPoint: "V008_VERTEX_SETTINGS_START",
530
- modelCreationId,
531
- timestamp: new Date().toISOString(),
532
- elapsedMs: Date.now() - modelCreationStartTime,
533
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
534
- vertexSettingsStartTimeNs: vertexSettingsStartTime.toString(),
535
- // Network configuration analysis
536
- networkConfig: {
537
- projectId: this.projectId,
538
- location: this.location,
539
- expectedEndpoint: `https://${this.location}-aiplatform.googleapis.com`,
540
- httpProxy: process.env.HTTP_PROXY || process.env.http_proxy,
541
- httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy,
542
- noProxy: process.env.NO_PROXY || process.env.no_proxy,
543
- proxyConfigured: !!(process.env.HTTP_PROXY ||
544
- process.env.HTTPS_PROXY ||
545
- process.env.http_proxy ||
546
- process.env.https_proxy),
547
- },
548
- message: "Starting Vertex settings creation with network configuration analysis",
549
- });
550
- try {
551
- const vertexSettings = await createVertexSettings(this.location);
552
- const vertexSettingsEndTime = process.hrtime.bigint();
553
- const vertexSettingsDurationNs = vertexSettingsEndTime - vertexSettingsStartTime;
554
- logger.debug(`[GoogleVertexProvider] āœ… LOG_POINT_V009_VERTEX_SETTINGS_SUCCESS`, {
555
- logPoint: "V009_VERTEX_SETTINGS_SUCCESS",
556
- modelCreationId,
557
- timestamp: new Date().toISOString(),
558
- elapsedMs: Date.now() - modelCreationStartTime,
559
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
560
- vertexSettingsDurationNs: vertexSettingsDurationNs.toString(),
561
- vertexSettingsDurationMs: Number(vertexSettingsDurationNs) / 1000000,
562
- // Settings analysis
563
- vertexSettingsAnalysis: {
564
- hasSettings: !!vertexSettings,
565
- settingsType: typeof vertexSettings,
566
- settingsKeys: vertexSettings ? Object.keys(vertexSettings) : [],
567
- projectId: vertexSettings?.project,
568
- location: vertexSettings?.location,
569
- hasFetch: !!vertexSettings?.fetch,
570
- hasGoogleAuthOptions: !!vertexSettings?.googleAuthOptions,
571
- settingsSize: vertexSettings
572
- ? JSON.stringify(vertexSettings).length
573
- : 0,
574
- },
575
- message: "Vertex settings created successfully",
576
- });
577
- return await this.createVertexInstance(vertexSettings, modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart);
578
- }
579
- catch (error) {
580
- const vertexSettingsErrorTime = process.hrtime.bigint();
581
- const vertexSettingsDurationNs = vertexSettingsErrorTime - vertexSettingsStartTime;
582
- const totalErrorDurationNs = vertexSettingsErrorTime - modelCreationHrTimeStart;
583
- logger.error(`[GoogleVertexProvider] āŒ LOG_POINT_V014_VERTEX_SETTINGS_ERROR`, {
584
- logPoint: "V014_VERTEX_SETTINGS_ERROR",
585
- modelCreationId,
586
- timestamp: new Date().toISOString(),
587
- totalElapsedMs: Date.now() - modelCreationStartTime,
588
- totalElapsedNs: totalErrorDurationNs.toString(),
589
- totalErrorDurationMs: Number(totalErrorDurationNs) / 1000000,
590
- vertexSettingsDurationNs: vertexSettingsDurationNs.toString(),
591
- vertexSettingsDurationMs: Number(vertexSettingsDurationNs) / 1000000,
592
- // Comprehensive error analysis
593
- error: error instanceof Error ? error.message : String(error),
594
- errorName: error instanceof Error ? error.name : "UnknownError",
595
- errorStack: error instanceof Error ? error.stack : undefined,
596
- // Network diagnostic information
597
- networkDiagnostics: {
598
- errorCode: error?.code || "UNKNOWN",
599
- errorErrno: error?.errno || "UNKNOWN",
600
- errorAddress: error?.address || "UNKNOWN",
601
- errorPort: error?.port || "UNKNOWN",
602
- errorSyscall: error?.syscall || "UNKNOWN",
603
- errorHostname: error?.hostname || "UNKNOWN",
604
- isTimeoutError: error instanceof Error &&
605
- (error.message.includes("timeout") ||
606
- error.message.includes("ETIMEDOUT")),
607
- isNetworkError: error instanceof Error &&
608
- (error.message.includes("ENOTFOUND") ||
609
- error.message.includes("ECONNREFUSED") ||
610
- error.message.includes("ETIMEDOUT")),
611
- isAuthError: error instanceof Error &&
612
- (error.message.includes("PERMISSION_DENIED") ||
613
- error.message.includes("401") ||
614
- error.message.includes("403")),
615
- infrastructureIssue: error instanceof Error &&
616
- error.message.includes("ETIMEDOUT") &&
617
- error.message.includes("aiplatform.googleapis.com"),
618
- },
619
- // Environment at error time
620
- errorEnvironment: {
621
- httpProxy: process.env.HTTP_PROXY || process.env.http_proxy,
622
- httpsProxy: process.env.HTTPS_PROXY || process.env.https_proxy,
623
- googleAppCreds: process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK ||
624
- process.env.GOOGLE_APPLICATION_CREDENTIALS ||
625
- "NOT_SET",
626
- hasGoogleServiceKey: !!process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
627
- nodeVersion: process.version,
628
- memoryUsage: process.memoryUsage(),
629
- uptime: process.uptime(),
630
- },
631
- message: "Vertex settings creation failed - critical network/authentication error",
632
- });
633
- throw error;
634
- }
635
- }
636
- /**
637
- * Create Vertex AI instance and model with comprehensive logging
638
- */
639
- async createVertexInstance(vertexSettings, modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart) {
640
- const vertexInstanceStartTime = process.hrtime.bigint();
641
- logger.debug(`[GoogleVertexProvider] šŸ—ļø LOG_POINT_V010_VERTEX_INSTANCE_START`, {
642
- logPoint: "V010_VERTEX_INSTANCE_START",
643
- modelCreationId,
644
- timestamp: new Date().toISOString(),
645
- elapsedMs: Date.now() - modelCreationStartTime,
646
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
647
- vertexInstanceStartTimeNs: vertexInstanceStartTime.toString(),
648
- // Pre-creation network environment
649
- networkEnvironment: {
650
- dnsServers: (() => {
651
- try {
652
- return dns.getServers ? dns.getServers() : "NOT_AVAILABLE";
653
- }
654
- catch {
655
- return "NOT_AVAILABLE";
656
- }
657
- })(),
658
- networkInterfaces: (() => {
659
- try {
660
- return Object.keys(os.networkInterfaces());
661
- }
662
- catch {
663
- return [];
664
- }
665
- })(),
666
- hostname: (() => {
667
- try {
668
- return os.hostname();
669
- }
670
- catch {
671
- return "UNKNOWN";
672
- }
673
- })(),
674
- platform: (() => {
675
- try {
676
- return os.platform();
677
- }
678
- catch {
679
- return "UNKNOWN";
680
- }
681
- })(),
682
- release: (() => {
683
- try {
684
- return os.release();
685
- }
686
- catch {
687
- return "UNKNOWN";
688
- }
689
- })(),
690
- },
691
- message: "Creating Vertex AI instance",
692
- });
693
- const vertex = createVertex(vertexSettings);
694
- const vertexInstanceEndTime = process.hrtime.bigint();
695
- const vertexInstanceDurationNs = vertexInstanceEndTime - vertexInstanceStartTime;
696
- logger.debug(`[GoogleVertexProvider] āœ… LOG_POINT_V011_VERTEX_INSTANCE_SUCCESS`, {
697
- logPoint: "V011_VERTEX_INSTANCE_SUCCESS",
698
- modelCreationId,
699
- timestamp: new Date().toISOString(),
700
- elapsedMs: Date.now() - modelCreationStartTime,
701
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
702
- vertexInstanceDurationNs: vertexInstanceDurationNs.toString(),
703
- vertexInstanceDurationMs: Number(vertexInstanceDurationNs) / 1000000,
704
- hasVertexInstance: !!vertex,
705
- vertexInstanceType: typeof vertex,
706
- message: "Vertex AI instance created successfully",
707
- });
708
- const modelInstanceStartTime = process.hrtime.bigint();
709
- logger.debug(`[GoogleVertexProvider] šŸŽÆ LOG_POINT_V012_MODEL_INSTANCE_START`, {
710
- logPoint: "V012_MODEL_INSTANCE_START",
711
- modelCreationId,
712
- timestamp: new Date().toISOString(),
713
- elapsedMs: Date.now() - modelCreationStartTime,
714
- elapsedNs: (process.hrtime.bigint() - modelCreationHrTimeStart).toString(),
715
- modelInstanceStartTimeNs: modelInstanceStartTime.toString(),
716
- modelName,
717
- hasVertexInstance: !!vertex,
718
- message: "Creating model instance from Vertex AI instance",
719
- });
720
- const model = vertex(modelName);
721
- const modelInstanceEndTime = process.hrtime.bigint();
722
- const modelInstanceDurationNs = modelInstanceEndTime - modelInstanceStartTime;
723
- const totalModelCreationDurationNs = modelInstanceEndTime - modelCreationHrTimeStart;
724
- logger.info(`[GoogleVertexProvider] šŸ LOG_POINT_V013_MODEL_CREATION_COMPLETE`, {
725
- logPoint: "V013_MODEL_CREATION_COMPLETE",
726
- modelCreationId,
727
- timestamp: new Date().toISOString(),
728
- totalElapsedMs: Date.now() - modelCreationStartTime,
729
- totalElapsedNs: totalModelCreationDurationNs.toString(),
730
- totalDurationMs: Number(totalModelCreationDurationNs) / 1000000,
731
- modelInstanceDurationNs: modelInstanceDurationNs.toString(),
732
- modelInstanceDurationMs: Number(modelInstanceDurationNs) / 1000000,
733
- // Final model analysis
734
- finalModel: {
735
- hasModel: !!model,
736
- modelType: typeof model,
737
- modelName,
738
- isAnthropicModel: isAnthropicModel(modelName),
739
- projectId: this.projectId,
740
- location: this.location,
741
- },
742
- // Performance summary
743
- performanceSummary: {
744
- vertexSettingsDurationMs: Number(vertexInstanceDurationNs) / 1000000,
745
- vertexInstanceDurationMs: Number(vertexInstanceDurationNs) / 1000000,
746
- modelInstanceDurationMs: Number(modelInstanceDurationNs) / 1000000,
747
- totalDurationMs: Number(totalModelCreationDurationNs) / 1000000,
748
- },
749
- // Memory usage
750
- finalMemoryUsage: process.memoryUsage(),
751
- message: "Model creation completed successfully - ready for API calls",
752
- });
753
- return model;
754
- }
755
- /**
756
- * Gets the appropriate model instance (Google or Anthropic)
757
- * Uses dual provider architecture for proper model routing
758
- * Creates fresh instances for each request to ensure proper authentication
759
- */
760
- async getModel() {
761
- // Initialize logging and setup (alias resolution happens inside)
762
- const { modelCreationId, modelCreationStartTime, modelCreationHrTimeStart, modelName, } = this.initializeModelCreationLogging();
763
- // Check if this is an Anthropic model and attempt creation
764
- const anthropicModel = await this.attemptAnthropicModelCreation(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart);
765
- if (anthropicModel) {
766
- return anthropicModel;
767
- }
768
- // Fall back to Google Vertex model creation
769
- return await this.createGoogleVertexModel(modelName, modelCreationId, modelCreationStartTime, modelCreationHrTimeStart);
770
- }
771
- // executeGenerate removed - BaseProvider handles all generation with tools
772
- /**
773
- * Validate stream options
774
- */
775
- validateStreamOptionsOnly(options) {
776
- this.validateStreamOptions(options);
777
- }
778
- async executeStream(options, analysisSchema) {
779
- // Check if this is a Gemini 3 model with tools - use native SDK for thought_signature
780
- const gemini3CheckModelName = this.resolveAlias(options.model || this.modelName || getDefaultVertexModel());
781
- // Structured output (analysisSchema, JSON format, or schema) is incompatible with tools on Gemini.
782
- // Compute once and reuse in both the native Gemini 3 gate and the streamText fallback path.
783
- const wantsStructuredOutput = analysisSchema || options.output?.format === "json" || options.schema;
784
- // Check for tools from options AND from SDK (MCP tools)
785
- // Need to check early if we should route to native SDK
786
- const gemini3CheckShouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
787
- const optionTools = options.tools || {};
788
- const sdkTools = gemini3CheckShouldUseTools ? await this.getAllTools() : {};
789
- const combinedToolCount = Object.keys(optionTools).length + Object.keys(sdkTools).length;
790
- const hasTools = gemini3CheckShouldUseTools && combinedToolCount > 0;
791
- if (isGemini3Model(gemini3CheckModelName) && hasTools) {
792
- // Process CSV files before routing to native SDK (bypasses normal message builder)
793
- const processedOptions = await this.processCSVFilesForNativeSDK(options);
794
- // Merge SDK tools into options for native SDK path
795
- const mergedOptions = {
796
- ...processedOptions,
797
- tools: { ...sdkTools, ...optionTools },
798
- };
799
- logger.info("[GoogleVertex] Routing Gemini 3 to native SDK for tool calling", {
800
- model: gemini3CheckModelName,
801
- optionToolCount: Object.keys(optionTools).length,
802
- sdkToolCount: Object.keys(sdkTools).length,
803
- totalToolCount: combinedToolCount,
804
- });
805
- return this.executeNativeGemini3Stream(mergedOptions);
806
- }
807
- // Initialize stream execution tracking
808
- const functionTag = "GoogleVertexProvider.executeStream";
809
- let chunkCount = 0;
810
- // Setup timeout controller
811
- const timeout = this.getTimeout(options);
812
- const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
813
- try {
814
- // Validate stream options
815
- this.validateStreamOptionsOnly(options);
816
- // Build message array from options with multimodal support
817
- // Using protected helper from BaseProvider to eliminate code duplication
818
- const messages = await this.buildMessagesForStream(options);
819
- const model = await this.getAISDKModelWithMiddleware(options); // This is where network connection happens!
820
- // Get all available tools (direct + MCP + external + user-provided RAG tools) for streaming
821
- const shouldUseTools = !options.disableTools && this.supportsTools();
822
- const baseStreamTools = shouldUseTools ? await this.getAllTools() : {};
823
- const rawTools = shouldUseTools
824
- ? { ...baseStreamTools, ...(options.tools || {}) }
825
- : {};
826
- // Only sanitize for Gemini models (not Anthropic/Claude models routed through Vertex)
827
- const isAnthropic = isAnthropicModel(gemini3CheckModelName);
828
- let tools;
829
- if (Object.keys(rawTools).length > 0 && !isAnthropic) {
830
- const sanitized = sanitizeToolsForGemini(rawTools);
831
- if (sanitized.dropped.length > 0) {
832
- logger.warn(`[GoogleVertex] Dropped ${sanitized.dropped.length} incompatible tool(s): ${sanitized.dropped.join(", ")}`);
833
- }
834
- tools =
835
- Object.keys(sanitized.tools).length > 0 ? sanitized.tools : undefined;
836
- }
837
- else if (isAnthropic && Object.keys(rawTools).length > 0) {
838
- // Anthropic models don't need Gemini sanitization — pass tools through
839
- tools = rawTools;
840
- }
841
- else {
842
- tools = undefined;
843
- }
844
- logger.debug(`${functionTag}: Tools for streaming`, {
845
- shouldUseTools,
846
- baseToolCount: Object.keys(baseStreamTools).length,
847
- externalToolCount: Object.keys(options.tools || {}).length,
848
- toolCount: Object.keys(tools ?? {}).length,
849
- toolNames: Object.keys(tools ?? {}),
850
- });
851
- // Model-specific maxTokens handling
852
- const modelName = this.resolveAlias(options.model || this.modelName || getDefaultVertexModel());
853
- // Use cached model configuration to determine maxTokens handling for streaming performance
854
- // This avoids hardcoded model-specific logic and repeated config lookups
855
- const shouldSetMaxTokens = this.shouldSetMaxTokensCached(modelName);
856
- const maxTokens = shouldSetMaxTokens
857
- ? options.maxTokens // No default limit
858
- : undefined;
859
- // Build complete stream options with proper typing
860
- let streamOptions = {
861
- model: model,
862
- messages: messages,
863
- temperature: options.temperature,
864
- ...(maxTokens && { maxTokens }),
865
- maxRetries: 0, // NL11: Disable AI SDK's invisible internal retries; we handle retries with OTel instrumentation
866
- ...(shouldUseTools &&
867
- tools &&
868
- Object.keys(tools).length > 0 && {
869
- tools,
870
- toolChoice: "auto",
871
- stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
872
- }),
873
- abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
874
- experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
875
- // Gemini 3: use thinkingLevel via providerOptions (Vertex AI)
876
- // Gemini 2.5: use thinkingBudget via providerOptions
877
- ...(options.thinkingConfig?.enabled && {
878
- providerOptions: {
879
- vertex: {
880
- thinkingConfig: {
881
- ...(options.thinkingConfig.thinkingLevel && {
882
- thinkingLevel: options.thinkingConfig.thinkingLevel,
883
- }),
884
- ...(options.thinkingConfig.budgetTokens &&
885
- !options.thinkingConfig.thinkingLevel && {
886
- thinkingBudget: options.thinkingConfig.budgetTokens,
887
- }),
888
- includeThoughts: true,
889
- },
890
- },
891
- },
892
- }),
893
- onError: (event) => {
894
- const error = event.error;
895
- const errorMessage = error instanceof Error ? error.message : String(error);
896
- logger.error(`${functionTag}: Stream error`, {
897
- provider: this.providerName,
898
- modelName: this.modelName,
899
- error: errorMessage,
900
- chunkCount,
901
- });
902
- },
903
- onFinish: (event) => {
904
- logger.debug(`${functionTag}: Stream finished`, {
905
- finishReason: event.finishReason,
906
- totalChunks: chunkCount,
907
- });
908
- },
909
- onChunk: () => {
910
- chunkCount++;
911
- },
912
- onStepFinish: ({ toolCalls, toolResults }) => {
913
- logger.info("Tool execution completed", { toolResults, toolCalls });
914
- // Handle tool execution storage
915
- this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
916
- logger.warn("[GoogleVertexProvider] Failed to store tool executions", {
917
- provider: this.providerName,
918
- error: error instanceof Error ? error.message : String(error),
919
- });
920
- });
921
- },
922
- };
923
- if (analysisSchema) {
924
- try {
925
- // Gemini cannot use tools and JSON schema simultaneously
926
- if (!isAnthropic) {
927
- delete streamOptions.tools;
928
- delete streamOptions.toolChoice;
929
- delete streamOptions.stopWhen;
930
- }
931
- streamOptions = {
932
- ...streamOptions,
933
- experimental_output: Output.object({
934
- schema: analysisSchema,
935
- }),
936
- };
937
- }
938
- catch (error) {
939
- logger.warn("Schema application failed, continuing without schema", {
940
- error: String(error),
941
- });
942
- }
943
- }
944
- // Wrap streamText in an OTel span to capture provider-level latency and token usage
945
- const streamSpan = streamTracer.startSpan("neurolink.provider.streamText", {
946
- kind: SpanKind.CLIENT,
947
- attributes: {
948
- "gen_ai.system": "vertex",
949
- "gen_ai.request.model": getModelId(model, this.modelName || "unknown"),
950
- },
951
- });
952
- let result;
953
- try {
954
- result = streamText(streamOptions);
955
- }
956
- catch (err) {
957
- streamSpan.recordException(err instanceof Error ? err : new Error(String(err)));
958
- streamSpan.setStatus({
959
- code: SpanStatusCode.ERROR,
960
- message: err instanceof Error ? err.message : String(err),
961
- });
962
- streamSpan.end();
963
- throw err;
964
- }
965
- // Collect token usage and finish reason asynchronously when the stream completes,
966
- // then end the span. This avoids blocking the stream consumer.
967
- Promise.resolve(result.usage)
968
- .then((usage) => {
969
- streamSpan.setAttribute("gen_ai.usage.input_tokens", usage.inputTokens || 0);
970
- streamSpan.setAttribute("gen_ai.usage.output_tokens", usage.outputTokens || 0);
971
- const effectiveModel = options.model ||
972
- getModelId(model, this.modelName || getDefaultVertexModel());
973
- const cost = calculateCost(this.providerName, effectiveModel, {
974
- input: usage.inputTokens || 0,
975
- output: usage.outputTokens || 0,
976
- total: (usage.inputTokens || 0) + (usage.outputTokens || 0),
977
- });
978
- if (cost && cost > 0) {
979
- streamSpan.setAttribute("neurolink.cost", cost);
980
- }
981
- })
982
- .catch(() => {
983
- // Usage may not be available if the stream is aborted
984
- });
985
- Promise.resolve(result.finishReason)
986
- .then((reason) => {
987
- streamSpan.setAttribute("gen_ai.response.finish_reason", reason || "unknown");
988
- })
989
- .catch(() => {
990
- // Finish reason may not be available if the stream is aborted
991
- });
992
- Promise.resolve(result.text)
993
- .then(() => {
994
- streamSpan.end();
995
- })
996
- .catch((err) => {
997
- streamSpan.setStatus({
998
- code: SpanStatusCode.ERROR,
999
- message: err instanceof Error ? err.message : String(err),
1000
- });
1001
- streamSpan.end();
1002
- });
1003
- // Defer timeout cleanup until the stream completes or errors.
1004
- // Guard against NoOutputGeneratedError becoming an unhandled rejection.
1005
- Promise.resolve(result.text)
1006
- .catch((err) => {
1007
- logger.debug("Stream text promise rejected (expected for empty streams)", {
1008
- error: err instanceof Error ? err.message : String(err),
1009
- });
1010
- })
1011
- .finally(() => timeoutController?.cleanup());
1012
- // Transform string stream to content object stream using BaseProvider method
1013
- const transformedStream = this.createTextStream(result);
1014
- // Track tool calls and results for streaming
1015
- const toolCalls = [];
1016
- const toolResults = [];
1017
- return {
1018
- stream: transformedStream,
1019
- provider: this.providerName,
1020
- model: this.modelName,
1021
- ...(shouldUseTools && {
1022
- toolCalls,
1023
- toolResults,
1024
- }),
1025
- };
1026
- }
1027
- catch (error) {
1028
- timeoutController?.cleanup();
1029
- logger.error(`${functionTag}: Exception`, {
1030
- provider: this.providerName,
1031
- modelName: this.modelName,
1032
- error: String(error),
1033
- chunkCount,
1034
- });
1035
- throw this.handleProviderError(error);
1036
- }
1037
- }
1038
- /**
1039
- * Create @google/genai client configured for Vertex AI
1040
- */
1041
- async createVertexGenAIClient(regionOverride) {
1042
- const project = getVertexProjectId();
1043
- const location = regionOverride || this.location || getVertexLocation();
1044
- const mod = await import("@google/genai");
1045
- const ctor = mod.GoogleGenAI;
1046
- if (!ctor) {
1047
- throw new NeuroLinkError({
1048
- code: ERROR_CODES.INVALID_CONFIGURATION,
1049
- message: "@google/genai does not export GoogleGenAI",
1050
- category: ErrorCategory.CONFIGURATION,
1051
- severity: ErrorSeverity.CRITICAL,
1052
- retriable: false,
1053
- context: { module: "@google/genai", expectedExport: "GoogleGenAI" },
1054
- });
1055
- }
1056
- const Ctor = ctor;
1057
- // Use vertexai mode with project and location
1058
- return new Ctor({
1059
- vertexai: true,
1060
- project,
1061
- location,
1062
- });
1063
- }
1064
- // ── Shared helpers for native Gemini 3 SDK methods ──
1065
- /**
1066
- * Build multimodal content parts (user message) from input text, PDFs, and images.
1067
- * Shared by both stream and generate native Gemini 3 paths.
1068
- */
1069
- buildNativeContentParts(inputText, multimodalInput, logLabel) {
1070
- const userParts = [{ text: inputText }];
1071
- // Add PDF files as inlineData parts if present
1072
- if (multimodalInput?.pdfFiles && multimodalInput.pdfFiles.length > 0) {
1073
- logger.debug(`[GoogleVertex] Processing ${multimodalInput.pdfFiles.length} PDF file(s) for ${logLabel}`);
1074
- for (const pdfFile of multimodalInput.pdfFiles) {
1075
- let pdfBuffer;
1076
- if (typeof pdfFile === "string") {
1077
- if (fs.existsSync(pdfFile)) {
1078
- pdfBuffer = fs.readFileSync(pdfFile);
1079
- }
1080
- else {
1081
- pdfBuffer = Buffer.from(pdfFile, "base64");
1082
- }
1083
- }
1084
- else {
1085
- pdfBuffer = pdfFile;
1086
- }
1087
- const base64Data = pdfBuffer.toString("base64");
1088
- userParts.push({
1089
- inlineData: {
1090
- mimeType: "application/pdf",
1091
- data: base64Data,
1092
- },
1093
- });
1094
- }
1095
- }
1096
- // Add images as inlineData parts if present
1097
- if (multimodalInput?.images && multimodalInput.images.length > 0) {
1098
- logger.debug(`[GoogleVertex] Processing ${multimodalInput.images.length} image(s) for ${logLabel}`);
1099
- for (const image of multimodalInput.images) {
1100
- let imageBuffer;
1101
- let mimeType = "image/jpeg"; // Default
1102
- if (typeof image === "string") {
1103
- if (fs.existsSync(image)) {
1104
- imageBuffer = fs.readFileSync(image);
1105
- const ext = path.extname(image).toLowerCase();
1106
- if (ext === ".png") {
1107
- mimeType = "image/png";
1108
- }
1109
- else if (ext === ".gif") {
1110
- mimeType = "image/gif";
1111
- }
1112
- else if (ext === ".webp") {
1113
- mimeType = "image/webp";
1114
- }
1115
- }
1116
- else if (image.startsWith("data:")) {
1117
- const matches = image.match(/^data:([^;]+);base64,(.+)$/);
1118
- if (matches) {
1119
- mimeType = matches[1];
1120
- imageBuffer = Buffer.from(matches[2], "base64");
1121
- }
1122
- else {
1123
- continue; // Skip invalid data URL
1124
- }
1125
- }
1126
- else {
1127
- imageBuffer = Buffer.from(image, "base64");
1128
- }
1129
- }
1130
- else {
1131
- imageBuffer = image;
1132
- }
1133
- const base64Data = imageBuffer.toString("base64");
1134
- userParts.push({
1135
- inlineData: {
1136
- mimeType,
1137
- data: base64Data,
1138
- },
1139
- });
1140
- }
1141
- }
1142
- return [
1143
- {
1144
- role: "user",
1145
- parts: userParts,
1146
- },
1147
- ];
1148
- }
1149
- /**
1150
- * Convert conversationMessages from NeuroLink's ChatMessage format into
1151
- * the @google/genai contents format and prepend them before the current
1152
- * user message. This gives the native Gemini 3 path multi-turn context
1153
- * that was previously dropped (only the current prompt was sent).
1154
- */
1155
- prependConversationHistory(currentContents, conversationMessages) {
1156
- if (!conversationMessages || conversationMessages.length === 0) {
1157
- return currentContents;
1158
- }
1159
- const history = [];
1160
- for (const msg of conversationMessages) {
1161
- // @google/genai only accepts "user" and "model" roles in contents.
1162
- // Skip system messages (handled via config.systemInstruction).
1163
- // Map "assistant" → "model" (Gemini convention).
1164
- const role = msg.role === "assistant" ? "model" : msg.role;
1165
- if (role !== "user" && role !== "model") {
1166
- continue;
1167
- }
1168
- if (!msg.content || msg.content.trim().length === 0) {
1169
- continue;
1170
- }
1171
- history.push({ role, parts: [{ text: msg.content }] });
1172
- }
1173
- // Prepend history before current user message
1174
- return [...history, ...currentContents];
1175
- }
1176
- // ── Shared Gemini 3 helpers are now in ./googleNativeGemini3.ts ──
1177
- /**
1178
- * Execute stream using native @google/genai SDK for Gemini 3 models on Vertex AI
1179
- * This bypasses @ai-sdk/google-vertex to properly handle thought_signature
1180
- */
1181
- async executeNativeGemini3Stream(options) {
1182
- const modelName = this.resolveAlias(options.model || this.modelName || getDefaultVertexModel());
1183
- return withClientSpan({
1184
- name: "neurolink.provider.stream",
1185
- tracer: tracers.provider,
1186
- attributes: {
1187
- [ATTR.GEN_AI_SYSTEM]: "vertex",
1188
- [ATTR.GEN_AI_MODEL]: modelName,
1189
- [ATTR.GEN_AI_OPERATION]: "stream",
1190
- [ATTR.NL_PROVIDER]: this.providerName,
1191
- },
1192
- }, async (span) => {
1193
- const client = await this.createVertexGenAIClient(options.region);
1194
- const effectiveLocation = options.region || this.location || getVertexLocation();
1195
- logger.debug("[GoogleVertex] Using native @google/genai for Gemini 3", {
1196
- model: modelName,
1197
- hasTools: !!options.tools && Object.keys(options.tools).length > 0,
1198
- project: this.projectId,
1199
- location: effectiveLocation,
1200
- });
1201
- // Build contents from input with multimodal support
1202
- const multimodalInput = options.input;
1203
- const contents = this.buildNativeContentParts(options.input.text, multimodalInput, "native stream");
1204
- // Convert tools to native format
1205
- let hasToolsInput = options.tools &&
1206
- Object.keys(options.tools).length > 0 &&
1207
- !options.disableTools;
1208
- // Guard: Gemini cannot use tools + JSON schema simultaneously
1209
- const streamOptions = options;
1210
- const wantsJsonOutput = streamOptions.output?.format === "json" || streamOptions.schema;
1211
- if (wantsJsonOutput && hasToolsInput) {
1212
- logger.warn("[GoogleVertex] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
1213
- hasToolsInput = false;
1214
- }
1215
- let toolsConfig;
1216
- let executeMap = new Map();
1217
- if (hasToolsInput) {
1218
- const result = buildNativeToolDeclarations(options.tools);
1219
- toolsConfig = result.toolsConfig;
1220
- executeMap = result.executeMap;
1221
- logger.debug("[GoogleVertex] Converted tools for native SDK", {
1222
- toolCount: toolsConfig[0].functionDeclarations.length,
1223
- toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
1224
- });
1225
- }
1226
- // Build config — systemInstruction stays in config for Gemini 3.x.
1227
- // The @google/genai SDK maps config.systemInstruction to the HTTP-level
1228
- // system_instruction field, which is the correct mechanism for all
1229
- // Gemini 3.x models (including global endpoint). Older workaround
1230
- // that moved systemInstruction into user/model content messages caused
1231
- // "Please use a valid role: user, model" on Gemini 3.1+ preview models.
1232
- const config = buildNativeConfig(options, toolsConfig);
1233
- // Add JSON output format support for native SDK stream
1234
- if (streamOptions.output?.format === "json" || streamOptions.schema) {
1235
- config.responseMimeType = "application/json";
1236
- if (streamOptions.schema) {
1237
- const rawSchema = convertZodToJsonSchema(streamOptions.schema);
1238
- const inlinedSchema = inlineJsonSchema(rawSchema);
1239
- if (inlinedSchema.$schema) {
1240
- delete inlinedSchema.$schema;
1241
- }
1242
- config.responseSchema = inlinedSchema;
1243
- logger.debug("[GoogleVertex] Added responseSchema for JSON output (stream)", {
1244
- schemaKeys: Object.keys(inlinedSchema),
1245
- });
1246
- }
1247
- }
1248
- const startTime = Date.now();
1249
- const timeout = this.getTimeout(options);
1250
- const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
1251
- const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
1252
- const maxSteps = computeMaxStepsShared(options.maxSteps);
1253
- // Inject conversation history so the native path has multi-turn context
1254
- const currentContents = this.prependConversationHistory([...contents], options.conversationMessages);
1255
- // Create a push-based text channel so the caller receives tokens as
1256
- // they arrive from the network rather than after full buffering.
1257
- const channel = createTextChannel();
1258
- // Shared mutable state updated by the background agentic loop.
1259
- const allToolCalls = [];
1260
- // Shared metadata object mutated by the background loop so that
1261
- // responseTime and totalToolExecutions reflect final values.
1262
- const metadata = {
1263
- streamId: `native-vertex-${Date.now()}`,
1264
- startTime,
1265
- responseTime: 0,
1266
- totalToolExecutions: 0,
1267
- };
1268
- // analyticsResolvers lets the background loop settle the analytics
1269
- // promise once token counts are known (after the loop completes).
1270
- let analyticsResolve;
1271
- let analyticsReject;
1272
- const analyticsPromise = new Promise((res, rej) => {
1273
- analyticsResolve = res;
1274
- analyticsReject = rej;
1275
- });
1276
- // Run the agentic loop in the background without awaiting it here,
1277
- // so we can return the StreamResult (with channel.iterable) immediately.
1278
- const loopPromise = (async () => {
1279
- let lastStepText = "";
1280
- let totalInputTokens = 0;
1281
- let totalOutputTokens = 0;
1282
- let step = 0;
1283
- let completedWithFinalAnswer = false;
1284
- const failedTools = new Map();
1285
- try {
1286
- // Agentic loop for tool calling
1287
- while (step < maxSteps) {
1288
- if (composedSignal?.aborted) {
1289
- throw composedSignal.reason instanceof Error
1290
- ? composedSignal.reason
1291
- : new Error("Request aborted");
1292
- }
1293
- step++;
1294
- logger.debug(`[GoogleVertex] Native SDK step ${step}/${maxSteps}`);
1295
- try {
1296
- const rawStream = await client.models.generateContentStream({
1297
- model: modelName,
1298
- contents: currentContents,
1299
- config,
1300
- ...(composedSignal
1301
- ? { httpOptions: { signal: composedSignal } }
1302
- : {}),
1303
- });
1304
- // For every step, use incremental collection so text parts
1305
- // are pushed to the channel as they arrive. For intermediate
1306
- // steps (those that produce function calls) we still need the
1307
- // complete rawResponseParts for pushModelResponseToHistory,
1308
- // which collectStreamChunksIncremental provides at stream end.
1309
- const chunkResult = await collectStreamChunksIncremental(rawStream, channel);
1310
- totalInputTokens += chunkResult.inputTokens;
1311
- totalOutputTokens += chunkResult.outputTokens;
1312
- const stepText = extractTextFromParts(chunkResult.rawResponseParts);
1313
- // If no function calls, this was the final step — channel
1314
- // already received all text parts incrementally.
1315
- if (chunkResult.stepFunctionCalls.length === 0) {
1316
- completedWithFinalAnswer = true;
1317
- break;
1318
- }
1319
- lastStepText = stepText;
1320
- // Record tool call events on the span
1321
- for (const fc of chunkResult.stepFunctionCalls) {
1322
- span.addEvent("gen_ai.tool_call", {
1323
- "tool.name": fc.name,
1324
- "tool.step": step,
1325
- });
1326
- }
1327
- logger.debug(`[GoogleVertex] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
1328
- pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
1329
- const functionResponses = await executeNativeToolCalls("[GoogleVertex]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
1330
- // Function/tool responses must use role: "user" — the
1331
- // @google/genai SDK's validateHistory() only accepts "user"
1332
- // and "model" roles (matching automaticFunctionCalling).
1333
- currentContents.push({
1334
- role: "user",
1335
- parts: functionResponses,
1336
- });
1337
- }
1338
- catch (error) {
1339
- logger.error("[GoogleVertex] Native SDK error", error);
1340
- throw this.handleProviderError(error);
1341
- }
1342
- }
1343
- // Handle max-steps termination: if the model was still calling
1344
- // tools when we hit the limit, push a synthetic final message.
1345
- if (step >= maxSteps && !completedWithFinalAnswer) {
1346
- const fallback = handleMaxStepsTermination("[GoogleVertex]", step, maxSteps, "", // finalText is empty — model didn't stop on its own
1347
- lastStepText);
1348
- if (fallback) {
1349
- channel.push(fallback);
1350
- }
1351
- }
1352
- const responseTime = Date.now() - startTime;
1353
- // Propagate final values to the shared metadata object so that
1354
- // the already-returned StreamResult reflects accurate telemetry.
1355
- metadata.responseTime = responseTime;
1356
- metadata.totalToolExecutions = allToolCalls.length;
1357
- // Set token usage and finish reason on the span
1358
- span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
1359
- span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
1360
- span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= maxSteps && !completedWithFinalAnswer
1361
- ? "max_steps"
1362
- : "stop");
1363
- analyticsResolve({
1364
- provider: this.providerName,
1365
- model: modelName,
1366
- tokenUsage: {
1367
- input: totalInputTokens,
1368
- output: totalOutputTokens,
1369
- total: totalInputTokens + totalOutputTokens,
1370
- },
1371
- requestDuration: responseTime,
1372
- timestamp: new Date().toISOString(),
1373
- });
1374
- channel.close();
1375
- }
1376
- catch (err) {
1377
- channel.error(err);
1378
- analyticsReject(err);
1379
- }
1380
- finally {
1381
- timeoutController?.cleanup();
1382
- }
1383
- })();
1384
- // Suppress unhandled-rejection warnings on loopPromise — errors are
1385
- // forwarded to the channel and will surface when the caller iterates.
1386
- loopPromise.catch(() => undefined);
1387
- return {
1388
- stream: channel.iterable,
1389
- provider: this.providerName,
1390
- model: modelName,
1391
- toolCalls: allToolCalls,
1392
- analytics: analyticsPromise,
1393
- metadata,
1394
- };
1395
- });
1396
- }
1397
- /**
1398
- * Execute generate using native @google/genai SDK for Gemini 3 models on Vertex AI
1399
- * This bypasses @ai-sdk/google-vertex to properly handle thought_signature
1400
- */
1401
- async executeNativeGemini3Generate(options) {
1402
- const modelName = this.resolveAlias(options.model || this.modelName || getDefaultVertexModel());
1403
- return withClientSpan({
1404
- name: "neurolink.provider.generate",
1405
- tracer: tracers.provider,
1406
- attributes: {
1407
- [ATTR.GEN_AI_SYSTEM]: "vertex",
1408
- [ATTR.GEN_AI_MODEL]: modelName,
1409
- [ATTR.GEN_AI_OPERATION]: "generate",
1410
- [ATTR.NL_PROVIDER]: this.providerName,
1411
- },
1412
- }, async (span) => {
1413
- const client = await this.createVertexGenAIClient(options.region);
1414
- const effectiveLocation = options.region || this.location || getVertexLocation();
1415
- logger.debug("[GoogleVertex] Using native @google/genai for Gemini 3 generate", {
1416
- model: modelName,
1417
- project: this.projectId,
1418
- location: effectiveLocation,
1419
- });
1420
- // Build contents from input with multimodal support
1421
- // Prefer input.text over prompt — processCSVFilesForNativeSDK enriches
1422
- // input.text with inlined CSV data, so using prompt first would discard it.
1423
- const inputText = options.input?.text || options.prompt || "Please respond.";
1424
- const multimodalInput = options.input;
1425
- const contents = this.buildNativeContentParts(inputText, multimodalInput, "native generate");
1426
- // Get tools from SDK and options
1427
- let shouldUseTools = !options.disableTools && this.supportsTools();
1428
- // Guard: Gemini cannot use tools + JSON schema simultaneously
1429
- const wantsJsonOutputGen = options.output?.format === "json" || options.schema;
1430
- if (wantsJsonOutputGen && shouldUseTools) {
1431
- logger.warn("[GoogleVertex] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
1432
- shouldUseTools = false;
1433
- }
1434
- const sdkTools = shouldUseTools ? await this.getAllTools() : {};
1435
- const combinedTools = shouldUseTools
1436
- ? { ...sdkTools, ...(options.tools || {}) }
1437
- : {};
1438
- let toolsConfig;
1439
- let executeMap = new Map();
1440
- if (Object.keys(combinedTools).length > 0) {
1441
- const result = buildNativeToolDeclarations(combinedTools);
1442
- toolsConfig = result.toolsConfig;
1443
- executeMap = result.executeMap;
1444
- logger.debug("[GoogleVertex] Converted tools for native SDK generate", {
1445
- toolCount: toolsConfig[0].functionDeclarations.length,
1446
- toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
1447
- });
1448
- }
1449
- // Build config — systemInstruction stays in config for Gemini 3.x.
1450
- // See stream path comment for rationale.
1451
- const config = buildNativeConfig(options, toolsConfig);
1452
- // Note: Schema/JSON output for Gemini 3 native SDK is complex due to $ref resolution issues
1453
- // For now, schemas are handled via the AI SDK fallback path, not native SDK
1454
- // TODO: Implement proper $ref resolution for complex nested schemas
1455
- const startTime = Date.now();
1456
- const timeout = this.getTimeout(options);
1457
- const timeoutController = createTimeoutController(timeout, this.providerName, "generate");
1458
- const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
1459
- const maxSteps = computeMaxStepsShared(options.maxSteps);
1460
- // Inject conversation history so the native path has multi-turn context
1461
- const currentContents = this.prependConversationHistory([...contents], options.conversationMessages);
1462
- let finalText = "";
1463
- let lastStepText = "";
1464
- let totalInputTokens = 0;
1465
- let totalOutputTokens = 0;
1466
- const allToolCalls = [];
1467
- const toolExecutions = [];
1468
- let step = 0;
1469
- const failedTools = new Map();
1470
- try {
1471
- // Agentic loop for tool calling
1472
- while (step < maxSteps) {
1473
- if (composedSignal?.aborted) {
1474
- throw composedSignal.reason instanceof Error
1475
- ? composedSignal.reason
1476
- : new Error("Request aborted");
1477
- }
1478
- step++;
1479
- logger.debug(`[GoogleVertex] Native SDK generate step ${step}/${maxSteps}`);
1480
- try {
1481
- // Use generateContentStream and collect all chunks (same as GoogleAIStudio)
1482
- const stream = await client.models.generateContentStream({
1483
- model: modelName,
1484
- contents: currentContents,
1485
- config,
1486
- ...(composedSignal
1487
- ? { httpOptions: { signal: composedSignal } }
1488
- : {}),
1489
- });
1490
- const chunkResult = await collectStreamChunks(stream);
1491
- totalInputTokens += chunkResult.inputTokens;
1492
- totalOutputTokens += chunkResult.outputTokens;
1493
- const stepText = extractTextFromParts(chunkResult.rawResponseParts);
1494
- if (chunkResult.stepFunctionCalls.length === 0) {
1495
- finalText = stepText;
1496
- break;
1497
- }
1498
- lastStepText = stepText;
1499
- // Record tool call events on the span
1500
- for (const fc of chunkResult.stepFunctionCalls) {
1501
- span.addEvent("gen_ai.tool_call", {
1502
- "tool.name": fc.name,
1503
- "tool.step": step,
1504
- });
1505
- }
1506
- logger.debug(`[GoogleVertex] Generate executing ${chunkResult.stepFunctionCalls.length} function calls`);
1507
- pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
1508
- const functionResponses = await executeNativeToolCalls("[GoogleVertex]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
1509
- // Function/tool responses must use role: "user" — the
1510
- // @google/genai SDK's validateHistory() only accepts "user"
1511
- // and "model" roles (matching automaticFunctionCalling).
1512
- currentContents.push({
1513
- role: "user",
1514
- parts: functionResponses,
1515
- });
1516
- }
1517
- catch (error) {
1518
- logger.error("[GoogleVertex] Native SDK generate error", error);
1519
- throw this.handleProviderError(error);
1520
- }
1521
- }
1522
- }
1523
- finally {
1524
- timeoutController?.cleanup();
1525
- }
1526
- finalText = handleMaxStepsTermination("[GoogleVertex]", step, maxSteps, finalText, lastStepText);
1527
- const responseTime = Date.now() - startTime;
1528
- // Set token usage and finish reason on the span
1529
- span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
1530
- span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
1531
- span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= maxSteps ? "max_steps" : "stop");
1532
- // Build EnhancedGenerateResult
1533
- return {
1534
- content: finalText,
1535
- provider: this.providerName,
1536
- model: modelName,
1537
- usage: {
1538
- input: totalInputTokens,
1539
- output: totalOutputTokens,
1540
- total: totalInputTokens + totalOutputTokens,
1541
- },
1542
- responseTime,
1543
- toolsUsed: allToolCalls.map((tc) => tc.toolName),
1544
- toolExecutions: toolExecutions,
1545
- enhancedWithTools: allToolCalls.length > 0,
1546
- };
1547
- });
1548
- }
1549
- /**
1550
- * Process CSV files and append content to options.input.text
1551
- * This ensures CSV data is available in the prompt for native Gemini 3 SDK calls
1552
- * Returns a new options object with modified input (immutable pattern)
1553
- */
1554
- async processCSVFilesForNativeSDK(options) {
1555
- const input = options.input;
1556
- if (!input?.csvFiles || input.csvFiles.length === 0) {
1557
- return options;
1558
- }
1559
- logger.info(`[GoogleVertex] Processing ${input.csvFiles.length} CSV file(s) for native Gemini 3 SDK`);
1560
- let modifiedText = input.text || "";
1561
- for (let i = 0; i < input.csvFiles.length; i++) {
1562
- const csvFile = input.csvFiles[i];
1563
- try {
1564
- const result = await FileDetector.detectAndProcess(csvFile, {
1565
- allowedTypes: ["csv"],
1566
- csvOptions: "csvOptions" in options
1567
- ? options.csvOptions
1568
- : undefined,
1569
- });
1570
- // Extract filename for display
1571
- const filename = typeof csvFile === "string"
1572
- ? path.basename(csvFile)
1573
- : `csv_file_${i + 1}.csv`;
1574
- let csvSection = `\n\n## CSV Data from "${filename}":\n`;
1575
- // Add metadata if available
1576
- if (result.metadata) {
1577
- const meta = result.metadata;
1578
- if (meta.rowCount || meta.columnCount || meta.columnNames) {
1579
- csvSection += `**File Info:**\n`;
1580
- if (meta.rowCount) {
1581
- csvSection += `- Rows: ${meta.rowCount}\n`;
1582
- }
1583
- if (meta.columnCount) {
1584
- csvSection += `- Columns: ${meta.columnCount}\n`;
1585
- }
1586
- if (meta.columnNames && Array.isArray(meta.columnNames)) {
1587
- csvSection += `- Column Names: ${meta.columnNames.join(", ")}\n`;
1588
- }
1589
- csvSection += "\n";
1590
- }
1591
- }
1592
- // Add strong instructions to use the CSV data directly
1593
- csvSection += `\n**CRITICAL INSTRUCTION**: The complete CSV data is included below. You MUST use this data directly from this prompt.\n`;
1594
- csvSection += `DO NOT use any external tools (github, search_code, get_file_contents, etc.) to access this data.\n`;
1595
- csvSection += `The data you need is right here in this message - read it carefully and answer based on it.\n\n`;
1596
- csvSection += result.content;
1597
- // Prepend CSV to ensure data appears before user's question
1598
- modifiedText =
1599
- csvSection + "\n\n---\n\n**USER QUESTION:**\n" + modifiedText;
1600
- logger.info(`[GoogleVertex] āœ… Processed CSV: ${filename}`);
1601
- }
1602
- catch (error) {
1603
- logger.error(`[GoogleVertex] āŒ Failed to process CSV file ${i + 1}:`, error);
1604
- const filename = typeof csvFile === "string"
1605
- ? path.basename(csvFile)
1606
- : `csv_file_${i + 1}.csv`;
1607
- modifiedText += `\n\n## CSV Data Error: Failed to process "${filename}"\nReason: ${error instanceof Error ? error.message : "Unknown error"}`;
1608
- }
1609
- }
1610
- // Return new options with modified input (immutable pattern)
1611
- // Preserve the full type of options.input by spreading options.input directly
1612
- return {
1613
- ...options,
1614
- input: { ...options.input, text: modifiedText },
1615
- };
1616
- }
1617
- /**
1618
- * Override generate to route Gemini 3 models with tools to native SDK
1619
- */
1620
- async generate(optionsOrPrompt) {
1621
- // Normalize options
1622
- const options = typeof optionsOrPrompt === "string"
1623
- ? { prompt: optionsOrPrompt }
1624
- : optionsOrPrompt;
1625
- const modelName = this.resolveAlias(options.model || this.modelName || getDefaultVertexModel());
1626
- // Structured output (JSON format or schema) is incompatible with tools on Gemini.
1627
- // Mirror the stream path pattern to prevent silent downgrade on the generate path.
1628
- const wantsStructuredOutput = options.output?.format === "json" || !!options.schema;
1629
- // Check if we should use native SDK for Gemini 3 with tools
1630
- const shouldUseTools = !options.disableTools && this.supportsTools() && !wantsStructuredOutput;
1631
- const sdkTools = shouldUseTools ? await this.getAllTools() : {};
1632
- const hasTools = shouldUseTools &&
1633
- (Object.keys(sdkTools).length > 0 ||
1634
- (options.tools && Object.keys(options.tools).length > 0));
1635
- if (isGemini3Model(modelName) && hasTools && !wantsStructuredOutput) {
1636
- // Process CSV files before routing to native SDK (bypasses normal message builder)
1637
- const processedOptions = await this.processCSVFilesForNativeSDK(options);
1638
- // Merge SDK tools into options for native SDK path
1639
- const mergedOptions = {
1640
- ...processedOptions,
1641
- tools: { ...sdkTools, ...(processedOptions.tools || {}) },
1642
- };
1643
- logger.info("[GoogleVertex] Routing Gemini 3 generate to native SDK for tool calling", {
1644
- model: modelName,
1645
- sdkToolCount: Object.keys(sdkTools).length,
1646
- optionToolCount: Object.keys(processedOptions.tools || {}).length,
1647
- totalToolCount: Object.keys(sdkTools).length +
1648
- Object.keys(processedOptions.tools || {}).length,
1649
- });
1650
- return this.executeNativeGemini3Generate(mergedOptions);
1651
- }
1652
- // Fall back to BaseProvider implementation
1653
- return super.generate(optionsOrPrompt);
1654
- }
1655
- formatProviderError(error) {
1656
- // Pass through AbortError as-is so callers can detect cancellation
1657
- const errorRecord = error;
1658
- if (typeof errorRecord?.name === "string" &&
1659
- errorRecord.name === "AbortError") {
1660
- return error;
1661
- }
1662
- if (typeof errorRecord?.name === "string" &&
1663
- errorRecord.name === "TimeoutError") {
1664
- return new NetworkError(`Google Vertex AI request timed out. Consider increasing timeout or using a lighter model.`, this.providerName);
1665
- }
1666
- const message = typeof errorRecord?.message === "string"
1667
- ? errorRecord.message
1668
- : "Unknown error occurred";
1669
- const code = typeof errorRecord?.code === "string" ? errorRecord.code : undefined;
1670
- if (code === "ECONNRESET" ||
1671
- code === "ENOTFOUND" ||
1672
- code === "ECONNREFUSED" ||
1673
- message.includes("ECONNRESET") ||
1674
- message.includes("ENOTFOUND") ||
1675
- message.includes("ECONNREFUSED") ||
1676
- message.includes("connect ETIMEDOUT")) {
1677
- return new NetworkError(`Google Vertex AI network error: ${message}`, this.providerName);
1678
- }
1679
- if (message.includes("PERMISSION_DENIED")) {
1680
- return new AuthenticationError(`āŒ Google Vertex AI Permission Denied\n\nYour Google Cloud credentials don't have permission to access Vertex AI.\n\nRequired Steps:\n1. Ensure your service account has Vertex AI User role\n2. Check if Vertex AI API is enabled in your project\n3. Verify your project ID is correct\n4. Confirm your location/region has Vertex AI available`, this.providerName);
1681
- }
1682
- if (message.includes("NOT_FOUND")) {
1683
- const modelSuggestions = this.getModelSuggestions(this.modelName);
1684
- return new InvalidModelError(`āŒ Google Vertex AI Model Not Found\n\n${message}\n\nModel '${this.modelName}' is not available.\n\nSuggested alternatives:\n${modelSuggestions}\n\nTroubleshooting:\n1. Check model name spelling and format\n2. Verify model is available in your region (${this.location})\n3. Ensure your project has access to the model\n4. For Claude models, enable Anthropic integration in Google Cloud Console`, this.providerName);
1685
- }
1686
- if (message.includes("QUOTA_EXCEEDED")) {
1687
- return new RateLimitError(`āŒ Google Vertex AI Quota Exceeded\n\n${message}\n\nSolutions:\n1. Check your Vertex AI quotas in Google Cloud Console\n2. Request quota increase if needed\n3. Try a different model or reduce request frequency\n4. Consider using a different region`, this.providerName);
1688
- }
1689
- if (message.includes("INVALID_ARGUMENT")) {
1690
- return new ProviderError(`āŒ Google Vertex AI Invalid Request\n\n${message}\n\nCheck:\n1. Request parameters are within model limits\n2. Input text is properly formatted\n3. Temperature and other settings are valid\n4. Model supports your request type`, this.providerName);
1691
- }
1692
- return new ProviderError(`āŒ Google Vertex AI Provider Error\n\n${message}\n\nTroubleshooting:\n1. Check Google Cloud credentials and permissions\n2. Verify project ID and location settings\n3. Ensure Vertex AI API is enabled\n4. Check network connectivity`, this.providerName);
1693
- }
1694
- /**
1695
- * Memory-safe cache management for model configurations
1696
- * Implements LRU eviction to prevent memory leaks in long-running processes
1697
- */
1698
- static evictLRUCacheEntries(cache) {
1699
- if (cache.size <= GoogleVertexProvider.MAX_CACHE_SIZE) {
1700
- return;
1701
- }
1702
- // Evict oldest entries (first entries in Map are oldest in insertion order)
1703
- const entriesToRemove = cache.size - GoogleVertexProvider.MAX_CACHE_SIZE + 5; // Remove extra to avoid frequent evictions
1704
- let removed = 0;
1705
- for (const key of cache.keys()) {
1706
- if (removed >= entriesToRemove) {
1707
- break;
1708
- }
1709
- cache.delete(key);
1710
- removed++;
1711
- }
1712
- logger.debug("GoogleVertexProvider: Evicted LRU cache entries", {
1713
- entriesRemoved: removed,
1714
- currentCacheSize: cache.size,
1715
- });
1716
- }
1717
- /**
1718
- * Access and refresh cache entry (moves to end for LRU)
1719
- */
1720
- static accessCacheEntry(cache, key) {
1721
- const value = cache.get(key);
1722
- if (value !== undefined) {
1723
- // Move to end (most recently used)
1724
- cache.delete(key);
1725
- cache.set(key, value);
1726
- }
1727
- return value;
1728
- }
1729
- /**
1730
- * Memory-safe cached check for whether maxTokens should be set for the given model
1731
- * Optimized for streaming performance with LRU eviction to prevent memory leaks
1732
- */
1733
- shouldSetMaxTokensCached(modelName) {
1734
- const now = Date.now();
1735
- // Check if cache is valid (within 5 minutes)
1736
- if (now - GoogleVertexProvider.maxTokensCacheTime >
1737
- GoogleVertexProvider.CACHE_DURATION) {
1738
- // Cache expired, refresh all cached results
1739
- GoogleVertexProvider.maxTokensCache.clear();
1740
- GoogleVertexProvider.maxTokensCacheTime = now;
1741
- }
1742
- // Check if we have cached result for this model (with LRU access)
1743
- const cachedResult = GoogleVertexProvider.accessCacheEntry(GoogleVertexProvider.maxTokensCache, modelName);
1744
- if (cachedResult !== undefined) {
1745
- return cachedResult;
1746
- }
1747
- // Calculate and cache the result with memory management
1748
- const shouldSet = !this.modelHasMaxTokensIssues(modelName);
1749
- GoogleVertexProvider.maxTokensCache.set(modelName, shouldSet);
1750
- // Prevent memory leaks by evicting old entries if cache grows too large
1751
- GoogleVertexProvider.evictLRUCacheEntries(GoogleVertexProvider.maxTokensCache);
1752
- return shouldSet;
1753
- }
1754
- /**
1755
- * Memory-safe check if model has maxTokens issues using configuration-based approach
1756
- * This replaces hardcoded model-specific logic with configurable behavior
1757
- * Includes LRU caching to avoid repeated configuration lookups during streaming
1758
- */
1759
- modelHasMaxTokensIssues(modelName) {
1760
- const now = Date.now();
1761
- const cacheKey = "google-vertex-config";
1762
- // Check if cache is valid (within 5 minutes)
1763
- if (now - GoogleVertexProvider.modelConfigCacheTime >
1764
- GoogleVertexProvider.CACHE_DURATION) {
1765
- // Cache expired, refresh it with memory management
1766
- GoogleVertexProvider.modelConfigCache.clear();
1767
- const config = ModelConfigurationManager.getInstance();
1768
- const vertexConfig = config.getProviderConfiguration("google-vertex");
1769
- GoogleVertexProvider.modelConfigCache.set(cacheKey, vertexConfig);
1770
- GoogleVertexProvider.modelConfigCacheTime = now;
1771
- }
1772
- // Access cached config with LRU behavior
1773
- const vertexConfig = GoogleVertexProvider.accessCacheEntry(GoogleVertexProvider.modelConfigCache, cacheKey);
1774
- // Check if model is in the list of models with maxTokens issues
1775
- const modelsWithIssues = vertexConfig?.modelBehavior?.maxTokensIssues || [
1776
- "gemini-2.5-flash",
1777
- "gemini-2.5-pro",
1778
- ];
1779
- return modelsWithIssues.some((problematicModel) => modelName.includes(problematicModel));
1780
- }
1781
- /**
1782
- * Check if Anthropic models are available
1783
- * @returns Promise<boolean> indicating if Anthropic support is available
1784
- */
1785
- async hasAnthropicSupport() {
1786
- return hasAnthropicSupport();
1787
- }
1788
- /**
1789
- * Resolve a shorthand model name to its full versioned Vertex AI identifier.
1790
- * Returns the original name unchanged if no alias exists.
1791
- *
1792
- * @param modelName - A model name, possibly a shorthand alias
1793
- * @returns The resolved full versioned model name
1794
- *
1795
- * @example
1796
- * ```typescript
1797
- * provider.resolveModelAlias("claude-sonnet-4-5"); // "claude-sonnet-4-5@20250929"
1798
- * provider.resolveModelAlias("gemini-3-pro"); // "gemini-3.1-pro-preview"
1799
- * provider.resolveModelAlias("gemini-2.5-flash"); // "gemini-2.5-flash" (unchanged)
1800
- * ```
1801
- */
1802
- resolveModelAlias(modelName) {
1803
- return VERTEX_MODEL_ALIASES[modelName] ?? modelName;
1804
- }
1805
- /**
1806
- * Create an Anthropic model instance using vertexAnthropic provider
1807
- * Uses fresh vertex settings for each request with comprehensive validation
1808
- * @param modelName Anthropic model name (e.g., 'claude-3-sonnet@20240229')
1809
- * @returns LanguageModel instance or null if not available
1810
- */
1811
- async createAnthropicModel(modelName) {
1812
- const validationId = `anthropic-validation-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
1813
- logger.debug("[GoogleVertexProvider] 🧠 Starting comprehensive Anthropic model validation", {
1814
- validationId,
1815
- modelName,
1816
- timestamp: new Date().toISOString(),
1817
- });
1818
- // 1. SDK Module Availability Validation
1819
- if (!hasAnthropicSupport()) {
1820
- logger.error("[GoogleVertexProvider] āŒ SDK module validation failed", {
1821
- validationId,
1822
- issue: "createVertexAnthropic function not available",
1823
- solution: "Update @ai-sdk/google-vertex to latest version with Anthropic support",
1824
- command: "npm install @ai-sdk/google-vertex@latest",
1825
- documentation: "https://sdk.vercel.ai/providers/ai-sdk-providers/google-vertex#anthropic-models",
1826
- });
1827
- return null;
1828
- }
1829
- // 2. Authentication Validation
1830
- const authValidation = await this.validateVertexAuthentication();
1831
- if (!authValidation.isValid) {
1832
- logger.error("[GoogleVertexProvider] āŒ Authentication validation failed", {
1833
- validationId,
1834
- method: authValidation.method,
1835
- issues: authValidation.issues,
1836
- solutions: [
1837
- "Option 1: Set GOOGLE_APPLICATION_CREDENTIALS to valid service account OR ADC file",
1838
- "Option 2: Set individual env vars: GOOGLE_AUTH_CLIENT_EMAIL, GOOGLE_AUTH_PRIVATE_KEY",
1839
- "Option 3: Use gcloud auth application-default login for ADC",
1840
- "Documentation: https://cloud.google.com/docs/authentication/provide-credentials-adc",
1841
- ],
1842
- });
1843
- return null;
1844
- }
1845
- // 3. Project Configuration Validation
1846
- const projectValidation = await this.validateVertexProjectConfiguration();
1847
- if (!projectValidation.isValid) {
1848
- logger.error("[GoogleVertexProvider] āŒ Project configuration validation failed", {
1849
- validationId,
1850
- projectId: projectValidation.projectId,
1851
- region: projectValidation.region,
1852
- issues: projectValidation.issues,
1853
- solutions: [
1854
- "Set GOOGLE_VERTEX_PROJECT or GOOGLE_CLOUD_PROJECT environment variable",
1855
- "Ensure project exists and has Vertex AI API enabled",
1856
- "Check: https://console.cloud.google.com/apis/library/aiplatform.googleapis.com",
1857
- ],
1858
- });
1859
- return null;
1860
- }
1861
- // 4. Regional Support Validation
1862
- const regionSupported = await this.checkVertexRegionalSupport(projectValidation.region);
1863
- if (!regionSupported) {
1864
- logger.warn("[GoogleVertexProvider] āš ļø Regional support warning", {
1865
- validationId,
1866
- region: projectValidation.region,
1867
- warning: "Anthropic models may not be available in this region",
1868
- supportedRegions: [
1869
- "us-central1",
1870
- "us-east4",
1871
- "us-east5",
1872
- "us-west1",
1873
- "us-west4",
1874
- "europe-west1",
1875
- "europe-west4",
1876
- "asia-southeast1",
1877
- "asia-northeast1",
1878
- ],
1879
- solution: "Set GOOGLE_CLOUD_LOCATION to a supported region",
1880
- });
1881
- // Continue with warning, don't block
1882
- }
1883
- // 5. Model Name Validation
1884
- const modelValidation = this.validateAnthropicModelName(modelName);
1885
- if (!modelValidation.isValid) {
1886
- logger.error("[GoogleVertexProvider] āŒ Model name validation failed", {
1887
- validationId,
1888
- modelName,
1889
- issue: modelValidation.issue,
1890
- recommendedModels: [
1891
- "claude-sonnet-4-6",
1892
- "claude-opus-4-6",
1893
- "claude-sonnet-4-5@20250929",
1894
- "claude-opus-4@20250514",
1895
- "claude-3-5-sonnet-20241022",
1896
- ],
1897
- });
1898
- return null;
1899
- }
1900
- try {
1901
- // 6. Settings Creation with Enhanced Error Handling
1902
- logger.debug("[GoogleVertexProvider] šŸ”§ Creating vertexAnthropic settings", {
1903
- validationId,
1904
- authMethod: authValidation.method,
1905
- projectId: projectValidation.projectId,
1906
- region: projectValidation.region,
1907
- });
1908
- const vertexAnthropicSettings = await createVertexAnthropicSettings(this.location);
1909
- // 7. Settings Validation
1910
- if (!vertexAnthropicSettings.project ||
1911
- !vertexAnthropicSettings.location) {
1912
- logger.error("[GoogleVertexProvider] āŒ Settings validation failed", {
1913
- validationId,
1914
- hasProject: !!vertexAnthropicSettings.project,
1915
- hasLocation: !!vertexAnthropicSettings.location,
1916
- hasProxy: !!vertexAnthropicSettings.fetch,
1917
- hasAuth: !!vertexAnthropicSettings.googleAuthOptions,
1918
- });
1919
- return null;
1920
- }
1921
- logger.debug("[GoogleVertexProvider] āœ… Creating vertexAnthropic instance", {
1922
- validationId,
1923
- modelName,
1924
- project: vertexAnthropicSettings.project,
1925
- location: vertexAnthropicSettings.location,
1926
- hasProxy: !!vertexAnthropicSettings.fetch,
1927
- hasAuth: !!vertexAnthropicSettings.googleAuthOptions,
1928
- });
1929
- // 8. Provider Instance Creation
1930
- const vertexAnthropicInstance = createVertexAnthropic(vertexAnthropicSettings);
1931
- // 9. Model Instance Creation with Network Error Handling
1932
- const model = vertexAnthropicInstance(modelName);
1933
- logger.info("[GoogleVertexProvider] āœ… Anthropic model created successfully", {
1934
- validationId,
1935
- modelName,
1936
- hasModel: !!model,
1937
- modelType: typeof model,
1938
- authMethod: authValidation.method,
1939
- projectId: projectValidation.projectId,
1940
- region: projectValidation.region,
1941
- validationsPassed: [
1942
- "SDK_MODULE_AVAILABLE",
1943
- "AUTHENTICATION_VALID",
1944
- "PROJECT_CONFIGURED",
1945
- "MODEL_NAME_VALID",
1946
- "SETTINGS_CREATED",
1947
- "PROVIDER_INSTANCE_CREATED",
1948
- "MODEL_INSTANCE_CREATED",
1949
- ],
1950
- });
1951
- return model;
1952
- }
1953
- catch (error) {
1954
- // Enhanced error analysis and reporting
1955
- const errorAnalysis = this.analyzeAnthropicCreationError(error, {
1956
- validationId,
1957
- modelName,
1958
- projectId: projectValidation.projectId,
1959
- region: projectValidation.region,
1960
- authMethod: authValidation.method,
1961
- });
1962
- logger.error("[GoogleVertexProvider] āŒ Anthropic model creation failed", {
1963
- validationId,
1964
- modelName,
1965
- ...errorAnalysis,
1966
- detailedTroubleshooting: this.getAnthropicTroubleshootingSteps(errorAnalysis),
1967
- });
1968
- return null;
1969
- }
1970
- }
1971
- /**
1972
- * Validate Vertex AI authentication configuration
1973
- */
1974
- async validateVertexAuthentication() {
1975
- const result = {
1976
- isValid: false,
1977
- method: "none",
1978
- issues: [],
1979
- };
1980
- try {
1981
- // Check for service account file authentication (preferred)
1982
- if (process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK ||
1983
- process.env.GOOGLE_APPLICATION_CREDENTIALS) {
1984
- const credentialsPath = process.env
1985
- .GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK
1986
- ? process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK
1987
- : process.env.GOOGLE_APPLICATION_CREDENTIALS || "";
1988
- try {
1989
- if (fs.existsSync(credentialsPath)) {
1990
- // Validate JSON structure
1991
- const credentialsContent = fs.readFileSync(credentialsPath, "utf8");
1992
- const credentials = JSON.parse(credentialsContent);
1993
- if (credentials.type === "service_account" &&
1994
- credentials.project_id &&
1995
- credentials.client_email &&
1996
- credentials.private_key) {
1997
- result.isValid = true;
1998
- result.method = "service_account_file";
1999
- return result;
2000
- }
2001
- else if (credentials.client_id &&
2002
- credentials.client_secret &&
2003
- credentials.refresh_token &&
2004
- credentials.type !== "service_account") {
2005
- result.isValid = true;
2006
- result.method = "application_default_credentials";
2007
- return result;
2008
- }
2009
- else {
2010
- result.issues.push("Credentials file missing required fields (not service account or ADC format)");
2011
- }
2012
- }
2013
- else {
2014
- result.issues.push(`Service account file not found: ${credentialsPath}`);
2015
- }
2016
- }
2017
- catch (fileError) {
2018
- result.issues.push(`Service account file validation failed: ${fileError}`);
2019
- }
2020
- }
2021
- // Check for individual environment variables
2022
- if (process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
2023
- process.env.GOOGLE_AUTH_PRIVATE_KEY) {
2024
- const email = process.env.GOOGLE_AUTH_CLIENT_EMAIL;
2025
- const privateKey = process.env.GOOGLE_AUTH_PRIVATE_KEY;
2026
- if (email.includes("@") && privateKey.includes("BEGIN PRIVATE KEY")) {
2027
- result.isValid = true;
2028
- result.method = "environment_variables";
2029
- return result;
2030
- }
2031
- else {
2032
- result.issues.push("Individual credentials format validation failed");
2033
- }
2034
- }
2035
- else {
2036
- result.issues.push("Missing individual credential environment variables");
2037
- }
2038
- if (!result.isValid) {
2039
- result.issues.push("No valid authentication method found");
2040
- }
2041
- }
2042
- catch (error) {
2043
- result.issues.push(`Authentication validation error: ${error}`);
2044
- }
2045
- return result;
2046
- }
2047
- /**
2048
- * Validate Vertex AI project configuration
2049
- */
2050
- async validateVertexProjectConfiguration() {
2051
- const result = {
2052
- isValid: false,
2053
- projectId: undefined,
2054
- region: undefined,
2055
- issues: [],
2056
- };
2057
- // Check project ID
2058
- const projectId = process.env.GOOGLE_VERTEX_PROJECT ||
2059
- process.env.GOOGLE_CLOUD_PROJECT_ID ||
2060
- process.env.GOOGLE_PROJECT_ID ||
2061
- process.env.GOOGLE_CLOUD_PROJECT;
2062
- if (projectId) {
2063
- result.projectId = projectId;
2064
- // Validate project ID format
2065
- const projectIdPattern = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;
2066
- if (projectIdPattern.test(projectId)) {
2067
- result.isValid = true;
2068
- }
2069
- else {
2070
- result.issues.push(`Invalid project ID format: ${projectId}`);
2071
- }
2072
- }
2073
- else {
2074
- result.issues.push("No project ID configured");
2075
- }
2076
- // Check region/location
2077
- const region = process.env.GOOGLE_CLOUD_LOCATION ||
2078
- process.env.VERTEX_LOCATION ||
2079
- process.env.GOOGLE_VERTEX_LOCATION ||
2080
- "us-central1";
2081
- result.region = region;
2082
- // Validate region format (regional format like us-central1 or global endpoint)
2083
- const regionPattern = /^([a-z]+-[a-z]+\d+|global)$/;
2084
- if (!regionPattern.test(region)) {
2085
- result.issues.push(`Invalid region format: ${region} (expected format: 'us-central1' or 'global')`);
2086
- result.isValid = false;
2087
- }
2088
- return result;
2089
- }
2090
- /**
2091
- * Check if the specified region supports Anthropic models
2092
- */
2093
- async checkVertexRegionalSupport(region = "us-central1") {
2094
- // Based on Google Cloud documentation, these regions support Anthropic models
2095
- const supportedRegions = [
2096
- // Global endpoint (routed automatically)
2097
- "global",
2098
- // North America
2099
- "us-central1",
2100
- "us-east1",
2101
- "us-east4",
2102
- "us-east5",
2103
- "us-south1",
2104
- "us-west1",
2105
- "us-west4",
2106
- "northamerica-northeast1",
2107
- "northamerica-northeast2",
2108
- // Europe
2109
- "europe-west1",
2110
- "europe-west2",
2111
- "europe-west3",
2112
- "europe-west4",
2113
- "europe-west6",
2114
- "europe-west8",
2115
- "europe-west9",
2116
- "europe-north1",
2117
- "europe-central2",
2118
- "europe-southwest1",
2119
- // Asia Pacific
2120
- "asia-east1",
2121
- "asia-east2",
2122
- "asia-northeast1",
2123
- "asia-northeast2",
2124
- "asia-northeast3",
2125
- "asia-south1",
2126
- "asia-southeast1",
2127
- "asia-southeast2",
2128
- "australia-southeast1",
2129
- "australia-southeast2",
2130
- // Middle East & Africa
2131
- "me-west1",
2132
- "me-central1",
2133
- "africa-south1",
2134
- // South America
2135
- "southamerica-east1",
2136
- "southamerica-west1",
2137
- ];
2138
- return supportedRegions.includes(region);
2139
- }
2140
- /**
2141
- * Validate Anthropic model name format and availability
2142
- */
2143
- validateAnthropicModelName(modelName) {
2144
- if (!modelName || typeof modelName !== "string") {
2145
- return {
2146
- isValid: false,
2147
- issue: "Model name is required and must be a string",
2148
- };
2149
- }
2150
- // Check if it's a Claude model
2151
- if (!modelName.toLowerCase().includes("claude")) {
2152
- return {
2153
- isValid: false,
2154
- issue: 'Model name must be a Claude model (should contain "claude")',
2155
- };
2156
- }
2157
- // Validate against known Claude model patterns
2158
- const validPatterns = [
2159
- // Claude 4.6 — versionless IDs (no @YYYYMMDD suffix)
2160
- /^claude-opus-4-6$/,
2161
- /^claude-sonnet-4-6$/,
2162
- // Claude 4.x versioned
2163
- /^claude-sonnet-4@\d{8}$/,
2164
- /^claude-sonnet-4-5@\d{8}$/,
2165
- /^claude-opus-4@\d{8}$/,
2166
- /^claude-opus-4-1@\d{8}$/,
2167
- /^claude-opus-4-5@\d{8}$/,
2168
- /^claude-haiku-4-5@\d{8}$/,
2169
- // Claude 3.x
2170
- /^claude-3-7-sonnet@\d{8}$/,
2171
- /^claude-3-5-sonnet-\d{8}$/,
2172
- /^claude-3-5-haiku-\d{8}$/,
2173
- /^claude-3-sonnet-\d{8}$/,
2174
- /^claude-3-haiku-\d{8}$/,
2175
- /^claude-3-opus-\d{8}$/,
2176
- ];
2177
- const isValidFormat = validPatterns.some((pattern) => pattern.test(modelName));
2178
- if (!isValidFormat) {
2179
- return {
2180
- isValid: false,
2181
- issue: `Model name format not recognized. Expected formats like "claude-3-5-sonnet-20241022" or "claude-sonnet-4@20250514"`,
2182
- };
2183
- }
2184
- return { isValid: true };
2185
- }
2186
- /**
2187
- * Analyze Anthropic model creation errors for detailed troubleshooting
2188
- */
2189
- analyzeAnthropicCreationError(error, context) {
2190
- const errorMessage = error instanceof Error ? error.message : String(error);
2191
- const errorName = error instanceof Error ? error.name : "UnknownError";
2192
- const analysis = {
2193
- error: errorMessage,
2194
- errorName,
2195
- errorType: "UNKNOWN",
2196
- isNetworkError: false,
2197
- isAuthError: false,
2198
- isConfigurationError: false,
2199
- isModelError: false,
2200
- isRegionalError: false,
2201
- specificIssue: "Unknown error occurred",
2202
- errorStack: error instanceof Error ? error.stack : undefined,
2203
- };
2204
- // Network-related errors
2205
- if (errorMessage.includes("ETIMEDOUT") ||
2206
- errorMessage.includes("ECONNREFUSED") ||
2207
- errorMessage.includes("ENOTFOUND") ||
2208
- errorMessage.includes("timeout")) {
2209
- analysis.errorType = "NETWORK";
2210
- analysis.isNetworkError = true;
2211
- analysis.specificIssue =
2212
- "Network connectivity issue - cannot reach Google Cloud endpoints";
2213
- }
2214
- // Authentication errors
2215
- else if (errorMessage.includes("PERMISSION_DENIED") ||
2216
- errorMessage.includes("401") ||
2217
- errorMessage.includes("403") ||
2218
- errorMessage.includes("Unauthorized") ||
2219
- errorMessage.includes("Forbidden")) {
2220
- analysis.errorType = "AUTHENTICATION";
2221
- analysis.isAuthError = true;
2222
- analysis.specificIssue =
2223
- "Authentication failed - invalid credentials or insufficient permissions";
2224
- }
2225
- // Model availability errors
2226
- else if (errorMessage.includes("NOT_FOUND") ||
2227
- errorMessage.includes("404") ||
2228
- (errorMessage.includes("model") && errorMessage.includes("not available"))) {
2229
- analysis.errorType = "MODEL_AVAILABILITY";
2230
- analysis.isModelError = true;
2231
- analysis.specificIssue = `Model "${context.modelName}" not available in region "${context.region}"`;
2232
- }
2233
- // Regional/quota errors
2234
- else if (errorMessage.includes("QUOTA_EXCEEDED") ||
2235
- errorMessage.includes("quota") ||
2236
- errorMessage.includes("limit")) {
2237
- analysis.errorType = "QUOTA";
2238
- analysis.isRegionalError = true;
2239
- analysis.specificIssue = "Quota exceeded or rate limit reached";
2240
- }
2241
- // Configuration errors
2242
- else if (errorMessage.includes("INVALID_ARGUMENT") ||
2243
- errorMessage.includes("BadRequest") ||
2244
- errorMessage.includes("400")) {
2245
- analysis.errorType = "CONFIGURATION";
2246
- analysis.isConfigurationError = true;
2247
- analysis.specificIssue = "Invalid configuration or request parameters";
2248
- }
2249
- return analysis;
2250
- }
2251
- /**
2252
- * Get detailed troubleshooting steps based on error analysis
2253
- */
2254
- getAnthropicTroubleshootingSteps(errorAnalysis) {
2255
- const steps = [];
2256
- switch (errorAnalysis.errorType) {
2257
- case "NETWORK":
2258
- steps.push("🌐 Network Troubleshooting:", "1. Check internet connectivity", "2. Verify proxy configuration if behind corporate firewall", "3. Ensure firewall allows HTTPS to *.googleapis.com", "4. Try different network or wait for network issues to resolve", "5. Check if using VPN that might block Google Cloud endpoints");
2259
- break;
2260
- case "AUTHENTICATION":
2261
- steps.push("šŸ” Authentication Troubleshooting:", "1. Verify GOOGLE_APPLICATION_CREDENTIALS file exists and is valid", "2. Check individual credentials: GOOGLE_AUTH_CLIENT_EMAIL, GOOGLE_AUTH_PRIVATE_KEY", '3. Ensure service account has "Vertex AI User" role', "4. Verify project ID matches the one in your credentials", "5. Enable Vertex AI API: https://console.cloud.google.com/apis/library/aiplatform.googleapis.com");
2262
- break;
2263
- case "MODEL_AVAILABILITY":
2264
- steps.push("šŸ¤– Model Availability Troubleshooting:", "1. Verify model name format and spelling", "2. Check if Anthropic integration is enabled in your project", "3. Enable Claude models: https://console.cloud.google.com/vertex-ai/publishers/anthropic", "4. Try a different region if current region lacks Anthropic support", "5. Accept Anthropic terms and conditions in Google Cloud Console");
2265
- break;
2266
- case "QUOTA":
2267
- steps.push("šŸ“Š Quota Troubleshooting:", "1. Check Vertex AI quotas in Google Cloud Console", "2. Request quota increase if needed", "3. Try a different model with lower resource requirements", "4. Wait before retrying if rate limited", "5. Consider using a different region with available quota");
2268
- break;
2269
- case "CONFIGURATION":
2270
- steps.push("āš™ļø Configuration Troubleshooting:", "1. Verify all required environment variables are set", "2. Check project ID and region format", "3. Ensure model name follows correct format", "4. Verify request parameters are within model limits", "5. Update @ai-sdk/google-vertex to latest version");
2271
- break;
2272
- default:
2273
- steps.push("šŸ”§ General Troubleshooting:", "1. Update @ai-sdk/google-vertex to latest version", "2. Check Google Cloud service status", "3. Verify all authentication and configuration", "4. Try with a simple Claude model like claude-3-haiku-20240307", "5. Enable debug logging with NEUROLINK_DEBUG=true");
2274
- }
2275
- return steps;
2276
- }
2277
- /**
2278
- * Register a tool with the AI provider
2279
- * @param name The name of the tool
2280
- * @param schema The Zod schema defining the tool's parameters
2281
- * @param description A description of what the tool does
2282
- * @param handler The function to execute when the tool is called
2283
- */
2284
- registerTool(name, schema, description, handler) {
2285
- const functionTag = "GoogleVertexProvider.registerTool";
2286
- try {
2287
- const tool = {
2288
- description,
2289
- parameters: schema,
2290
- execute: async (params) => {
2291
- try {
2292
- const contextEnrichedParams = {
2293
- ...params,
2294
- __context: this.toolContext,
2295
- };
2296
- return await handler(contextEnrichedParams);
2297
- }
2298
- catch (error) {
2299
- logger.error(`${functionTag}: Tool execution error`, {
2300
- toolName: name,
2301
- error: error instanceof Error ? error.message : String(error),
2302
- });
2303
- throw error;
2304
- }
2305
- },
2306
- };
2307
- this.registeredTools.set(name, tool);
2308
- logger.debug(`${functionTag}: Tool registered`, {
2309
- toolName: name,
2310
- modelName: this.modelName,
2311
- });
2312
- }
2313
- catch (error) {
2314
- logger.error(`${functionTag}: Tool registration error`, {
2315
- toolName: name,
2316
- error: error instanceof Error ? error.message : String(error),
2317
- });
2318
- throw error;
2319
- }
2320
- }
2321
- /**
2322
- * Set the context for tool execution
2323
- * @param context The context to use for tool execution
2324
- */
2325
- setToolContext(context) {
2326
- this.toolContext = { ...this.toolContext, ...context };
2327
- logger.debug("GoogleVertexProvider.setToolContext: Tool context set", {
2328
- contextKeys: Object.keys(context),
2329
- });
2330
- }
2331
- /**
2332
- * Get the current tool execution context
2333
- * @returns The current tool execution context
2334
- */
2335
- getToolContext() {
2336
- return { ...this.toolContext };
2337
- }
2338
- /**
2339
- * Set the tool executor function for custom tool execution
2340
- * This method is called by BaseProvider.setupToolExecutor()
2341
- * @param executor Function to execute tools by name
2342
- */
2343
- setToolExecutor(executor) {
2344
- this.toolExecutor = executor;
2345
- logger.debug("GoogleVertexProvider.setToolExecutor: Tool executor set", {
2346
- hasExecutor: typeof executor === "function",
2347
- });
2348
- }
2349
- /**
2350
- * Clear all static caches - useful for testing and memory cleanup
2351
- * Public method to allow external cache management
2352
- */
2353
- static clearCaches() {
2354
- GoogleVertexProvider.modelConfigCache.clear();
2355
- GoogleVertexProvider.maxTokensCache.clear();
2356
- GoogleVertexProvider.modelConfigCacheTime = 0;
2357
- GoogleVertexProvider.maxTokensCacheTime = 0;
2358
- logger.debug("GoogleVertexProvider: All caches cleared", {
2359
- clearedAt: Date.now(),
2360
- });
2361
- }
2362
- /**
2363
- * Get cache statistics for monitoring and debugging
2364
- */
2365
- static getCacheStats() {
2366
- const now = Date.now();
2367
- return {
2368
- modelConfigCacheSize: GoogleVertexProvider.modelConfigCache.size,
2369
- maxTokensCacheSize: GoogleVertexProvider.maxTokensCache.size,
2370
- maxCacheSize: GoogleVertexProvider.MAX_CACHE_SIZE,
2371
- cacheAge: {
2372
- modelConfig: now - GoogleVertexProvider.modelConfigCacheTime,
2373
- maxTokens: now - GoogleVertexProvider.maxTokensCacheTime,
2374
- },
2375
- };
2376
- }
2377
- /**
2378
- * Detect image MIME type from buffer
2379
- */
2380
- detectImageType(buffer) {
2381
- // Check PNG signature
2382
- if (buffer.length >= 8 &&
2383
- buffer[0] === 0x89 &&
2384
- buffer[1] === 0x50 &&
2385
- buffer[2] === 0x4e &&
2386
- buffer[3] === 0x47) {
2387
- return "image/png";
2388
- }
2389
- // Check JPEG signature
2390
- if (buffer.length >= 3 &&
2391
- buffer[0] === 0xff &&
2392
- buffer[1] === 0xd8 &&
2393
- buffer[2] === 0xff) {
2394
- return "image/jpeg";
2395
- }
2396
- // Check WebP signature
2397
- if (buffer.length >= 12 &&
2398
- buffer[0] === 0x52 &&
2399
- buffer[1] === 0x49 &&
2400
- buffer[2] === 0x46 &&
2401
- buffer[3] === 0x46 &&
2402
- buffer[8] === 0x57 &&
2403
- buffer[9] === 0x45 &&
2404
- buffer[10] === 0x42 &&
2405
- buffer[11] === 0x50) {
2406
- return "image/webp";
2407
- }
2408
- // Check GIF signature
2409
- if (buffer.length >= 6 &&
2410
- buffer[0] === 0x47 &&
2411
- buffer[1] === 0x49 &&
2412
- buffer[2] === 0x46) {
2413
- return "image/gif";
2414
- }
2415
- // Default to PNG if unknown
2416
- return "image/png";
2417
- }
2418
- /**
2419
- * Estimate token count from text using centralized estimation with provider multipliers
2420
- */
2421
- estimateTokenCount(text) {
2422
- return estimateTokens(text, "vertex");
2423
- }
2424
- /**
2425
- * Obtain a Google Auth access token for Vertex AI REST API calls.
2426
- */
2427
- async getImageGenerationAccessToken() {
2428
- const maxRetries = 3;
2429
- const baseDelay = 500;
2430
- const authTimeoutMs = 15000;
2431
- const { GoogleAuth } = await import("google-auth-library");
2432
- // Priority: GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK > GOOGLE_APPLICATION_CREDENTIALS
2433
- const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS_NEUROLINK ||
2434
- process.env.GOOGLE_APPLICATION_CREDENTIALS;
2435
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
2436
- // Enforce per-attempt timeout with AbortController
2437
- const controller = new AbortController();
2438
- const authTimer = setTimeout(() => {
2439
- controller.abort();
2440
- logger.warn(`[GoogleVertexProvider] Auth token refresh exceeded ${authTimeoutMs}ms timeout (attempt ${attempt}/${maxRetries})`);
2441
- }, authTimeoutMs);
2442
- try {
2443
- const auth = new GoogleAuth({
2444
- ...(credentialsPath && { keyFilename: credentialsPath }),
2445
- scopes: ["https://www.googleapis.com/auth/cloud-platform"],
2446
- });
2447
- const client = await auth.getClient();
2448
- const accessToken = await client.getAccessToken();
2449
- if (!accessToken.token) {
2450
- throw new AuthenticationError("Failed to obtain access token from Google Auth", this.providerName);
2451
- }
2452
- return accessToken.token;
2453
- }
2454
- catch (error) {
2455
- const err = error;
2456
- const isRetryable = controller.signal.aborted ||
2457
- err?.code === "ECONNRESET" ||
2458
- err?.code === "ETIMEDOUT" ||
2459
- err?.code === "ENOTFOUND" ||
2460
- err?.message?.includes("socket hang up") ||
2461
- err?.message?.includes("network socket disconnected");
2462
- if (!isRetryable || attempt === maxRetries) {
2463
- throw error;
2464
- }
2465
- const delay = baseDelay * 2 ** (attempt - 1);
2466
- logger.warn(`[GoogleVertexProvider] Auth token transient error (${err?.code || err?.message}), retrying in ${delay}ms (attempt ${attempt}/${maxRetries})`);
2467
- await new Promise((r) => setTimeout(r, delay));
2468
- }
2469
- finally {
2470
- clearTimeout(authTimer);
2471
- }
2472
- }
2473
- throw new AuthenticationError("Failed to obtain access token after retries", this.providerName);
2474
- }
2475
- /**
2476
- * Build request parts for image generation from prompt, PDFs, and images.
2477
- */
2478
- async buildImageGenerationParts(prompt, pdfFiles, inputImages) {
2479
- const parts = [];
2480
- if (prompt) {
2481
- parts.push({ text: prompt });
2482
- }
2483
- // Add PDF files as inline data
2484
- for (const pdfFile of pdfFiles) {
2485
- let pdfBase64;
2486
- if (Buffer.isBuffer(pdfFile)) {
2487
- pdfBase64 = pdfFile.toString("base64");
2488
- }
2489
- else if (typeof pdfFile === "string") {
2490
- const isFilePath = pdfFile.startsWith("/") ||
2491
- /^[a-zA-Z]:\\/.test(pdfFile) ||
2492
- pdfFile.startsWith("./") ||
2493
- pdfFile.startsWith("../") ||
2494
- pdfFile.startsWith("..\\") ||
2495
- pdfFile.startsWith(".\\");
2496
- if (isFilePath) {
2497
- const normalizedPath = path.resolve(pdfFile);
2498
- const cwd = process.cwd();
2499
- if (!normalizedPath.startsWith(cwd + path.sep) &&
2500
- normalizedPath !== cwd) {
2501
- throw new ProviderError(`PDF file path must be within current directory for security`, this.providerName);
2502
- }
2503
- if (!fs.existsSync(normalizedPath)) {
2504
- throw new ProviderError(`PDF file not found: ${normalizedPath}`, this.providerName);
2505
- }
2506
- const pdfBuffer = fs.readFileSync(normalizedPath);
2507
- pdfBase64 = pdfBuffer.toString("base64");
2508
- }
2509
- else {
2510
- pdfBase64 = pdfFile;
2511
- }
2512
- }
2513
- else {
2514
- logger.warn("Invalid PDF file format, skipping", {
2515
- type: typeof pdfFile,
2516
- });
2517
- continue;
2518
- }
2519
- parts.push({
2520
- inlineData: {
2521
- mimeType: "application/pdf",
2522
- data: pdfBase64,
2523
- },
2524
- });
2525
- logger.debug("Added PDF file to request", {
2526
- dataLength: pdfBase64.length,
2527
- });
2528
- }
2529
- // Add images (including those converted from PDF by baseProvider)
2530
- for (let i = 0; i < inputImages.length; i++) {
2531
- const image = inputImages[i];
2532
- let imageBase64;
2533
- let mimeType;
2534
- if (Buffer.isBuffer(image)) {
2535
- imageBase64 = image.toString("base64");
2536
- mimeType = this.detectImageType(image);
2537
- }
2538
- else if (typeof image === "string") {
2539
- const isFilePath = image.startsWith("/") ||
2540
- /^[a-zA-Z]:\\/.test(image) ||
2541
- image.startsWith("./") ||
2542
- image.startsWith("../") ||
2543
- image.startsWith("..\\") ||
2544
- image.startsWith(".\\");
2545
- if (isFilePath) {
2546
- const normalizedPath = path.resolve(image);
2547
- if (!fs.existsSync(normalizedPath)) {
2548
- logger.warn(`Image file not found: ${normalizedPath}, skipping`);
2549
- continue;
2550
- }
2551
- const imageBuffer = fs.readFileSync(normalizedPath);
2552
- imageBase64 = imageBuffer.toString("base64");
2553
- mimeType = this.detectImageType(imageBuffer);
2554
- }
2555
- else if (image.startsWith("data:")) {
2556
- const matches = image.match(/^data:([^;]+);base64,(.+)$/);
2557
- if (matches) {
2558
- mimeType = matches[1];
2559
- imageBase64 = matches[2];
2560
- }
2561
- else {
2562
- logger.warn("Invalid data URL format, skipping image", {
2563
- index: i,
2564
- });
2565
- continue;
2566
- }
2567
- }
2568
- else if (image.startsWith("http://") ||
2569
- image.startsWith("https://")) {
2570
- // Download URL image and convert to base64
2571
- try {
2572
- // Validate URL to prevent SSRF attacks
2573
- const parsedUrl = new URL(image);
2574
- const hostname = parsedUrl.hostname;
2575
- const blockedHosts = ["localhost", "127.0.0.1", "0.0.0.0", "[::1]"];
2576
- if (blockedHosts.some((h) => hostname === h) ||
2577
- /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(hostname)) {
2578
- logger.warn(`[GoogleVertexProvider] Blocked fetch to private/local URL: ${hostname}`, { index: i });
2579
- continue;
2580
- }
2581
- // DNS resolution check — verify resolved IPs are not private/loopback
2582
- try {
2583
- const { resolve4, resolve6 } = dns.promises;
2584
- const addresses = [];
2585
- try {
2586
- addresses.push(...(await resolve4(hostname)));
2587
- }
2588
- catch {
2589
- /* hostname may not have A records */
2590
- }
2591
- try {
2592
- addresses.push(...(await resolve6(hostname)));
2593
- }
2594
- catch {
2595
- /* hostname may not have AAAA records */
2596
- }
2597
- if (addresses.length > 0 &&
2598
- addresses.every((addr) => isPrivateOrLoopbackAddress(addr))) {
2599
- logger.warn(`[GoogleVertexProvider] Blocked fetch: hostname ${hostname} resolves to private/loopback address`, { index: i, addresses });
2600
- continue;
2601
- }
2602
- }
2603
- catch (dnsError) {
2604
- logger.warn(`[GoogleVertexProvider] DNS resolution failed for ${hostname}, blocking fetch`, {
2605
- index: i,
2606
- error: dnsError instanceof Error
2607
- ? dnsError.message
2608
- : String(dnsError),
2609
- });
2610
- continue;
2611
- }
2612
- const response = await fetch(image, {
2613
- signal: AbortSignal.timeout(15_000),
2614
- });
2615
- if (!response.ok) {
2616
- logger.warn(`Failed to fetch image URL (${response.status}), skipping`, { index: i, url: image });
2617
- continue;
2618
- }
2619
- // Size guard — reject downloads exceeding 10 MB
2620
- const contentLength = response.headers.get("content-length");
2621
- if (contentLength &&
2622
- Number(contentLength) > MAX_IMAGE_DOWNLOAD_BYTES) {
2623
- logger.warn(`[GoogleVertexProvider] Image URL exceeds ${MAX_IMAGE_DOWNLOAD_BYTES} byte limit (Content-Length: ${contentLength}), skipping`, { index: i, url: image });
2624
- continue;
2625
- }
2626
- const buffer = Buffer.from(await response.arrayBuffer());
2627
- if (buffer.byteLength > MAX_IMAGE_DOWNLOAD_BYTES) {
2628
- logger.warn(`[GoogleVertexProvider] Downloaded image exceeds ${MAX_IMAGE_DOWNLOAD_BYTES} byte limit (${buffer.byteLength} bytes), skipping`, { index: i, url: image });
2629
- continue;
2630
- }
2631
- imageBase64 = buffer.toString("base64");
2632
- mimeType = this.detectImageType(buffer);
2633
- }
2634
- catch (fetchError) {
2635
- logger.warn(`Failed to download image from URL, skipping: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`, { index: i, url: image });
2636
- continue;
2637
- }
2638
- }
2639
- else {
2640
- imageBase64 = image;
2641
- const decodedBuffer = Buffer.from(imageBase64, "base64");
2642
- mimeType = this.detectImageType(decodedBuffer);
2643
- }
2644
- }
2645
- else {
2646
- logger.warn("Invalid image format, skipping", {
2647
- type: typeof image,
2648
- index: i,
2649
- });
2650
- continue;
2651
- }
2652
- parts.push({
2653
- inlineData: {
2654
- mimeType: mimeType,
2655
- data: imageBase64,
2656
- },
2657
- });
2658
- logger.debug("Added image to request", {
2659
- index: i,
2660
- mimeType,
2661
- dataLength: imageBase64.length,
2662
- });
2663
- }
2664
- return parts;
2665
- }
2666
- /**
2667
- * Parse the Vertex AI image generation REST API response and extract image data.
2668
- */
2669
- parseImageGenerationResponse(data, imageModelName) {
2670
- const candidate = data.candidates?.[0];
2671
- if (!candidate?.content?.parts) {
2672
- throw new ProviderError("No content parts in Vertex AI response", this.providerName);
2673
- }
2674
- // Find image part (check both camelCase and snake_case)
2675
- const imagePart = candidate.content.parts.find((part) => (part.inlineData || part.inline_data) &&
2676
- ((part.inlineData && part.inlineData.mimeType) ||
2677
- (part.inline_data && part.inline_data.mime_type)) &&
2678
- ((part.inlineData && part.inlineData.mimeType?.startsWith("image/")) ||
2679
- (part.inline_data &&
2680
- part.inline_data.mime_type?.startsWith("image/"))));
2681
- if (!imagePart) {
2682
- const hasTextContent = candidate.content.parts.some((part) => part.text);
2683
- throw new ProviderError(hasTextContent
2684
- ? `Image generation completed but model returned text instead of image data. Model: ${imageModelName}`
2685
- : `Image generation completed but no image data was returned. Model: ${imageModelName}`, this.providerName);
2686
- }
2687
- const imageData = imagePart.inlineData?.data || imagePart.inline_data?.data;
2688
- const mimeType = imagePart.inlineData?.mimeType ||
2689
- imagePart.inline_data?.mime_type ||
2690
- "image/png";
2691
- if (!imageData) {
2692
- throw new ProviderError("Image part found but no data available", this.providerName);
2693
- }
2694
- return { imageData, mimeType };
2695
- }
2696
- /**
2697
- * Overrides the BaseProvider's image generation method to implement it for Vertex AI.
2698
- * Uses REST API approach with google-auth-library for authentication.
2699
- * Supports PDF input for image generation with gemini-3-pro-image-preview (Nano Banana Pro).
2700
- * @param options The generation options containing the prompt and optional PDF files.
2701
- * @returns A promise that resolves to the generation result, including the image data.
2702
- */
2703
- async executeImageGeneration(options) {
2704
- const prompt = options.prompt || options.input?.text || "";
2705
- const pdfFiles = options.input?.pdfFiles || [];
2706
- const inputImages = options.input?.images || [];
2707
- const hasPdfInput = pdfFiles.length > 0;
2708
- const hasImageInput = inputImages.length > 0;
2709
- if (!prompt.trim() && !hasPdfInput && !hasImageInput) {
2710
- throw new ProviderError("Image generation requires either a prompt, PDF file, or image as input", this.providerName);
2711
- }
2712
- // Select appropriate model
2713
- let imageModelName = options.model || this.modelName || "gemini-3-pro-image-preview";
2714
- if (hasPdfInput && !imageModelName.includes("gemini-3-pro-image")) {
2715
- imageModelName = "gemini-3-pro-image-preview";
2716
- }
2717
- // Determine location - some image models require 'global' location
2718
- const imageLocation = process.env.GOOGLE_VERTEX_IMAGE_LOCATION || "global";
2719
- const requiresGlobalLocation = GLOBAL_LOCATION_MODELS.some((model) => imageModelName.includes(model) || model.includes(imageModelName));
2720
- const location = requiresGlobalLocation ? imageLocation : this.location;
2721
- const startTime = Date.now();
2722
- logger.info("šŸŽØ Starting Vertex AI image generation (REST API)", {
2723
- model: imageModelName,
2724
- prompt: prompt.substring(0, 100),
2725
- provider: this.providerName,
2726
- projectId: this.projectId,
2727
- location: location,
2728
- hasPdfInput,
2729
- pdfCount: pdfFiles.length,
2730
- hasImageInput,
2731
- imageCount: inputImages.length,
2732
- });
2733
- try {
2734
- const token = await this.getImageGenerationAccessToken();
2735
- const parts = await this.buildImageGenerationParts(prompt, pdfFiles, inputImages);
2736
- // Build request body with CRITICAL response_modalities setting
2737
- const requestBody = {
2738
- contents: [{ role: "user", parts }],
2739
- generation_config: {
2740
- response_modalities: ["TEXT", "IMAGE"],
2741
- temperature: options.temperature || 0.7,
2742
- candidate_count: 1,
2743
- },
2744
- };
2745
- // Construct Vertex AI endpoint
2746
- let url;
2747
- if (location === "global") {
2748
- url = `https://aiplatform.googleapis.com/v1/projects/${this.projectId}/locations/global/publishers/google/models/${imageModelName}:generateContent`;
2749
- }
2750
- else {
2751
- url = `https://${location}-aiplatform.googleapis.com/v1/projects/${this.projectId}/locations/${location}/publishers/google/models/${imageModelName}:generateContent`;
2752
- }
2753
- logger.debug("Making REST API call to Vertex AI", {
2754
- url,
2755
- model: imageModelName,
2756
- hasAccessToken: true,
2757
- });
2758
- // Add timeout protection (120 seconds for image generation)
2759
- const timeoutMs = 120000;
2760
- const fetchPromise = fetch(url, {
2761
- method: "POST",
2762
- headers: {
2763
- Authorization: `Bearer ${token}`,
2764
- "Content-Type": "application/json",
2765
- },
2766
- body: JSON.stringify(requestBody),
2767
- });
2768
- const timeoutPromise = new Promise((_, reject) => {
2769
- setTimeout(() => {
2770
- reject(new TimeoutError(`Vertex AI image generation timed out after ${timeoutMs}ms`, timeoutMs));
2771
- }, timeoutMs);
2772
- });
2773
- const response = await Promise.race([fetchPromise, timeoutPromise]);
2774
- if (!response.ok) {
2775
- const errorText = await response.text();
2776
- throw new ProviderError(`Vertex AI API error (${response.status}): ${errorText}`, this.providerName);
2777
- }
2778
- const data = (await response.json());
2779
- const { imageData, mimeType } = this.parseImageGenerationResponse(data, imageModelName);
2780
- logger.info("Image generation successful", {
2781
- model: imageModelName,
2782
- mimeType,
2783
- dataLength: imageData.length,
2784
- responseTime: Date.now() - startTime,
2785
- });
2786
- const result = {
2787
- content: `Generated image using ${imageModelName} (${mimeType})`,
2788
- imageOutput: {
2789
- base64: imageData,
2790
- },
2791
- provider: this.providerName,
2792
- model: imageModelName,
2793
- usage: {
2794
- input: this.estimateTokenCount(prompt),
2795
- output: 0,
2796
- total: this.estimateTokenCount(prompt),
2797
- },
2798
- };
2799
- return await this.enhanceResult(result, options, startTime);
2800
- }
2801
- catch (error) {
2802
- logger.error("Image generation failed", {
2803
- error: error instanceof Error ? error.message : String(error),
2804
- model: imageModelName,
2805
- prompt: prompt.substring(0, 100),
2806
- });
2807
- throw this.handleProviderError(error);
2808
- }
2809
- }
2810
- /**
2811
- * Generate embeddings for text using Google Vertex AI text-embedding models
2812
- * @param text - The text to embed
2813
- * @param modelName - The embedding model to use (default: text-embedding-004)
2814
- * @returns Promise resolving to the embedding vector
2815
- */
2816
- async embed(text, modelName) {
2817
- const embeddingModelName = modelName || "text-embedding-004";
2818
- logger.debug("Generating embedding", {
2819
- provider: this.providerName,
2820
- model: embeddingModelName,
2821
- textLength: text.length,
2822
- });
2823
- try {
2824
- // Create the Vertex provider with current settings
2825
- const vertexSettings = await createVertexSettings(this.location);
2826
- const vertex = createVertex(vertexSettings);
2827
- // Get the text embedding model
2828
- const embeddingModel = vertex.textEmbeddingModel(embeddingModelName);
2829
- // Generate the embedding
2830
- const result = await embed({
2831
- model: embeddingModel,
2832
- value: text,
2833
- });
2834
- logger.debug("Embedding generated successfully", {
2835
- provider: this.providerName,
2836
- model: embeddingModelName,
2837
- embeddingDimension: result.embedding.length,
2838
- });
2839
- return result.embedding;
2840
- }
2841
- catch (error) {
2842
- logger.error("Embedding generation failed", {
2843
- error: error instanceof Error ? error.message : String(error),
2844
- model: embeddingModelName,
2845
- textLength: text.length,
2846
- });
2847
- throw this.handleProviderError(error);
2848
- }
2849
- }
2850
- /**
2851
- * Generate embeddings for multiple texts in a single batch
2852
- * @param texts - The texts to embed
2853
- * @param modelName - The embedding model to use (default: text-embedding-004)
2854
- * @returns Promise resolving to an array of embedding vectors
2855
- */
2856
- async embedMany(texts, modelName) {
2857
- const embeddingModelName = modelName || this.getDefaultEmbeddingModel() || "text-embedding-004";
2858
- logger.debug("Generating batch embeddings", {
2859
- provider: this.providerName,
2860
- model: embeddingModelName,
2861
- count: texts.length,
2862
- });
2863
- try {
2864
- const vertexSettings = await createVertexSettings(this.location);
2865
- const vertex = createVertex(vertexSettings);
2866
- const embeddingModel = vertex.textEmbeddingModel(embeddingModelName);
2867
- const result = await embedMany({
2868
- model: embeddingModel,
2869
- values: texts,
2870
- });
2871
- logger.debug("Batch embeddings generated successfully", {
2872
- provider: this.providerName,
2873
- model: embeddingModelName,
2874
- count: result.embeddings.length,
2875
- embeddingDimension: result.embeddings[0]?.length,
2876
- });
2877
- return result.embeddings;
2878
- }
2879
- catch (error) {
2880
- logger.error("Batch embedding generation failed", {
2881
- error: error instanceof Error ? error.message : String(error),
2882
- model: embeddingModelName,
2883
- count: texts.length,
2884
- });
2885
- throw this.handleProviderError(error);
2886
- }
2887
- }
2888
- /**
2889
- * Get model suggestions when a model is not found
2890
- */
2891
- getModelSuggestions(requestedModel) {
2892
- const availableModels = {
2893
- google: [
2894
- "gemini-3.1-pro-preview",
2895
- "gemini-3.1-flash-lite-preview",
2896
- "gemini-3-flash-preview",
2897
- "gemini-2.5-pro",
2898
- "gemini-2.5-flash",
2899
- "gemini-2.5-flash-lite",
2900
- "gemini-2.0-flash-001",
2901
- "gemini-2.0-flash-lite",
2902
- "gemini-1.5-pro",
2903
- "gemini-1.5-flash",
2904
- ],
2905
- claude: [
2906
- "claude-sonnet-4-5@20250929",
2907
- "claude-sonnet-4@20250514",
2908
- "claude-opus-4@20250514",
2909
- "claude-3-5-sonnet-20241022",
2910
- "claude-3-5-haiku-20241022",
2911
- "claude-3-sonnet-20240229",
2912
- "claude-3-haiku-20240307",
2913
- "claude-3-opus-20240229",
2914
- ],
2915
- };
2916
- let suggestions = "\nšŸ¤– Google Models (always available):\n";
2917
- availableModels.google.forEach((model) => {
2918
- suggestions += ` • ${model}\n`;
2919
- });
2920
- suggestions += "\n🧠 Claude Models (requires Anthropic integration):\n";
2921
- availableModels.claude.forEach((model) => {
2922
- suggestions += ` • ${model}\n`;
2923
- });
2924
- // If the requested model looks like a Claude model, provide specific guidance
2925
- if (requestedModel && requestedModel.toLowerCase().includes("claude")) {
2926
- suggestions += `\nšŸ’” Tip: "${requestedModel}" appears to be a Claude model.\n`;
2927
- suggestions +=
2928
- "Ensure Anthropic integration is enabled in your Google Cloud project.\n";
2929
- suggestions += "Try using an available Claude model from the list above.";
2930
- }
2931
- return suggestions;
2932
- }
2933
- }
2934
- export default GoogleVertexProvider;
2935
- // Re-export for compatibility
2936
- export { GoogleVertexProvider as GoogleVertexAI };