@juspay/neurolink 9.15.0 → 9.16.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 (193) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
  3. package/dist/adapters/video/videoAnalyzer.js +10 -8
  4. package/dist/cli/commands/setup-anthropic.js +1 -14
  5. package/dist/cli/commands/setup-azure.js +1 -12
  6. package/dist/cli/commands/setup-bedrock.js +1 -9
  7. package/dist/cli/commands/setup-google-ai.js +1 -12
  8. package/dist/cli/commands/setup-openai.js +1 -14
  9. package/dist/cli/commands/workflow.d.ts +27 -0
  10. package/dist/cli/commands/workflow.js +216 -0
  11. package/dist/cli/factories/commandFactory.js +79 -20
  12. package/dist/cli/index.js +0 -1
  13. package/dist/cli/parser.js +4 -1
  14. package/dist/cli/utils/maskCredential.d.ts +11 -0
  15. package/dist/cli/utils/maskCredential.js +23 -0
  16. package/dist/constants/contextWindows.js +107 -16
  17. package/dist/constants/enums.d.ts +99 -15
  18. package/dist/constants/enums.js +152 -22
  19. package/dist/context/budgetChecker.js +1 -1
  20. package/dist/context/contextCompactor.js +31 -4
  21. package/dist/context/emergencyTruncation.d.ts +21 -0
  22. package/dist/context/emergencyTruncation.js +88 -0
  23. package/dist/context/errorDetection.d.ts +16 -0
  24. package/dist/context/errorDetection.js +48 -1
  25. package/dist/context/errors.d.ts +19 -0
  26. package/dist/context/errors.js +21 -0
  27. package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
  28. package/dist/context/stages/slidingWindowTruncator.js +159 -24
  29. package/dist/core/baseProvider.js +306 -200
  30. package/dist/core/conversationMemoryManager.js +104 -61
  31. package/dist/core/evaluationProviders.js +16 -33
  32. package/dist/core/factory.js +237 -164
  33. package/dist/core/modules/GenerationHandler.js +175 -116
  34. package/dist/core/modules/MessageBuilder.js +222 -170
  35. package/dist/core/modules/StreamHandler.d.ts +1 -0
  36. package/dist/core/modules/StreamHandler.js +95 -27
  37. package/dist/core/modules/TelemetryHandler.d.ts +10 -1
  38. package/dist/core/modules/TelemetryHandler.js +25 -7
  39. package/dist/core/modules/ToolsManager.js +115 -191
  40. package/dist/core/redisConversationMemoryManager.js +418 -282
  41. package/dist/factories/providerRegistry.d.ts +5 -0
  42. package/dist/factories/providerRegistry.js +20 -2
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +4 -2
  45. package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
  46. package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
  47. package/dist/lib/constants/contextWindows.js +107 -16
  48. package/dist/lib/constants/enums.d.ts +99 -15
  49. package/dist/lib/constants/enums.js +152 -22
  50. package/dist/lib/context/budgetChecker.js +1 -1
  51. package/dist/lib/context/contextCompactor.js +31 -4
  52. package/dist/lib/context/emergencyTruncation.d.ts +21 -0
  53. package/dist/lib/context/emergencyTruncation.js +89 -0
  54. package/dist/lib/context/errorDetection.d.ts +16 -0
  55. package/dist/lib/context/errorDetection.js +48 -1
  56. package/dist/lib/context/errors.d.ts +19 -0
  57. package/dist/lib/context/errors.js +22 -0
  58. package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
  59. package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
  60. package/dist/lib/core/baseProvider.js +306 -200
  61. package/dist/lib/core/conversationMemoryManager.js +104 -61
  62. package/dist/lib/core/evaluationProviders.js +16 -33
  63. package/dist/lib/core/factory.js +237 -164
  64. package/dist/lib/core/modules/GenerationHandler.js +175 -116
  65. package/dist/lib/core/modules/MessageBuilder.js +222 -170
  66. package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
  67. package/dist/lib/core/modules/StreamHandler.js +95 -27
  68. package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
  69. package/dist/lib/core/modules/TelemetryHandler.js +25 -7
  70. package/dist/lib/core/modules/ToolsManager.js +115 -191
  71. package/dist/lib/core/redisConversationMemoryManager.js +418 -282
  72. package/dist/lib/factories/providerRegistry.d.ts +5 -0
  73. package/dist/lib/factories/providerRegistry.js +20 -2
  74. package/dist/lib/index.d.ts +2 -2
  75. package/dist/lib/index.js +4 -2
  76. package/dist/lib/mcp/externalServerManager.js +66 -0
  77. package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
  78. package/dist/lib/mcp/mcpClientFactory.js +16 -0
  79. package/dist/lib/mcp/toolDiscoveryService.js +32 -6
  80. package/dist/lib/mcp/toolRegistry.js +193 -123
  81. package/dist/lib/neurolink.d.ts +6 -0
  82. package/dist/lib/neurolink.js +1162 -646
  83. package/dist/lib/providers/amazonBedrock.d.ts +1 -1
  84. package/dist/lib/providers/amazonBedrock.js +521 -319
  85. package/dist/lib/providers/anthropic.js +73 -17
  86. package/dist/lib/providers/anthropicBaseProvider.js +77 -17
  87. package/dist/lib/providers/googleAiStudio.d.ts +1 -1
  88. package/dist/lib/providers/googleAiStudio.js +292 -227
  89. package/dist/lib/providers/googleVertex.d.ts +36 -1
  90. package/dist/lib/providers/googleVertex.js +553 -260
  91. package/dist/lib/providers/ollama.js +329 -278
  92. package/dist/lib/providers/openAI.js +77 -19
  93. package/dist/lib/providers/sagemaker/parsers.js +3 -3
  94. package/dist/lib/providers/sagemaker/streaming.js +3 -3
  95. package/dist/lib/proxy/proxyFetch.js +81 -48
  96. package/dist/lib/rag/ChunkerFactory.js +1 -1
  97. package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
  98. package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
  99. package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
  100. package/dist/lib/rag/chunking/markdownChunker.js +174 -2
  101. package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
  102. package/dist/lib/rag/ragIntegration.d.ts +18 -1
  103. package/dist/lib/rag/ragIntegration.js +94 -14
  104. package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
  105. package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
  106. package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
  107. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
  108. package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
  109. package/dist/lib/telemetry/attributes.d.ts +52 -0
  110. package/dist/lib/telemetry/attributes.js +61 -0
  111. package/dist/lib/telemetry/index.d.ts +3 -0
  112. package/dist/lib/telemetry/index.js +3 -0
  113. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  114. package/dist/lib/telemetry/telemetryService.js +6 -0
  115. package/dist/lib/telemetry/tracers.d.ts +15 -0
  116. package/dist/lib/telemetry/tracers.js +17 -0
  117. package/dist/lib/telemetry/withSpan.d.ts +9 -0
  118. package/dist/lib/telemetry/withSpan.js +35 -0
  119. package/dist/lib/types/contextTypes.d.ts +10 -0
  120. package/dist/lib/types/streamTypes.d.ts +14 -0
  121. package/dist/lib/utils/conversationMemory.js +121 -82
  122. package/dist/lib/utils/logger.d.ts +5 -0
  123. package/dist/lib/utils/logger.js +50 -2
  124. package/dist/lib/utils/messageBuilder.js +22 -42
  125. package/dist/lib/utils/modelDetection.js +3 -3
  126. package/dist/lib/utils/providerRetry.d.ts +41 -0
  127. package/dist/lib/utils/providerRetry.js +114 -0
  128. package/dist/lib/utils/retryability.d.ts +14 -0
  129. package/dist/lib/utils/retryability.js +23 -0
  130. package/dist/lib/utils/sanitizers/svg.js +4 -5
  131. package/dist/lib/utils/tokenEstimation.d.ts +11 -1
  132. package/dist/lib/utils/tokenEstimation.js +19 -4
  133. package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
  134. package/dist/mcp/externalServerManager.js +66 -0
  135. package/dist/mcp/mcpCircuitBreaker.js +24 -0
  136. package/dist/mcp/mcpClientFactory.js +16 -0
  137. package/dist/mcp/toolDiscoveryService.js +32 -6
  138. package/dist/mcp/toolRegistry.js +193 -123
  139. package/dist/neurolink.d.ts +6 -0
  140. package/dist/neurolink.js +1162 -646
  141. package/dist/providers/amazonBedrock.d.ts +1 -1
  142. package/dist/providers/amazonBedrock.js +521 -319
  143. package/dist/providers/anthropic.js +73 -17
  144. package/dist/providers/anthropicBaseProvider.js +77 -17
  145. package/dist/providers/googleAiStudio.d.ts +1 -1
  146. package/dist/providers/googleAiStudio.js +292 -227
  147. package/dist/providers/googleVertex.d.ts +36 -1
  148. package/dist/providers/googleVertex.js +553 -260
  149. package/dist/providers/ollama.js +329 -278
  150. package/dist/providers/openAI.js +77 -19
  151. package/dist/providers/sagemaker/parsers.js +3 -3
  152. package/dist/providers/sagemaker/streaming.js +3 -3
  153. package/dist/proxy/proxyFetch.js +81 -48
  154. package/dist/rag/ChunkerFactory.js +1 -1
  155. package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
  156. package/dist/rag/chunkers/MarkdownChunker.js +213 -9
  157. package/dist/rag/chunking/markdownChunker.d.ts +16 -0
  158. package/dist/rag/chunking/markdownChunker.js +174 -2
  159. package/dist/rag/pipeline/contextAssembly.js +2 -1
  160. package/dist/rag/ragIntegration.d.ts +18 -1
  161. package/dist/rag/ragIntegration.js +94 -14
  162. package/dist/rag/retrieval/vectorQueryTool.js +21 -4
  163. package/dist/server/abstract/baseServerAdapter.js +4 -1
  164. package/dist/server/adapters/fastifyAdapter.js +35 -30
  165. package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
  166. package/dist/services/server/ai/observability/instrumentation.js +39 -0
  167. package/dist/telemetry/attributes.d.ts +52 -0
  168. package/dist/telemetry/attributes.js +60 -0
  169. package/dist/telemetry/index.d.ts +3 -0
  170. package/dist/telemetry/index.js +3 -0
  171. package/dist/telemetry/telemetryService.d.ts +6 -0
  172. package/dist/telemetry/telemetryService.js +6 -0
  173. package/dist/telemetry/tracers.d.ts +15 -0
  174. package/dist/telemetry/tracers.js +16 -0
  175. package/dist/telemetry/withSpan.d.ts +9 -0
  176. package/dist/telemetry/withSpan.js +34 -0
  177. package/dist/types/contextTypes.d.ts +10 -0
  178. package/dist/types/streamTypes.d.ts +14 -0
  179. package/dist/utils/conversationMemory.js +121 -82
  180. package/dist/utils/logger.d.ts +5 -0
  181. package/dist/utils/logger.js +50 -2
  182. package/dist/utils/messageBuilder.js +22 -42
  183. package/dist/utils/modelDetection.js +3 -3
  184. package/dist/utils/providerRetry.d.ts +41 -0
  185. package/dist/utils/providerRetry.js +113 -0
  186. package/dist/utils/retryability.d.ts +14 -0
  187. package/dist/utils/retryability.js +22 -0
  188. package/dist/utils/sanitizers/svg.js +4 -5
  189. package/dist/utils/tokenEstimation.d.ts +11 -1
  190. package/dist/utils/tokenEstimation.js +19 -4
  191. package/dist/utils/videoAnalysisProcessor.js +7 -3
  192. package/dist/workflow/config.d.ts +26 -26
  193. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
2
2
  import { streamText } from "ai";
3
3
  import { ErrorCategory, ErrorSeverity, GoogleAIModels, } from "../constants/enums.js";
4
+ import { estimateTokens } from "../utils/tokenEstimation.js";
4
5
  import { BaseProvider } from "../core/baseProvider.js";
5
6
  import { DEFAULT_MAX_STEPS } from "../core/constants.js";
6
7
  import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
@@ -8,6 +9,7 @@ import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } fro
8
9
  import { ERROR_CODES, NeuroLinkError } from "../utils/errorHandling.js";
9
10
  import { logger } from "../utils/logger.js";
10
11
  import { isGemini3Model } from "../utils/modelDetection.js";
12
+ import { tracers, ATTR, withClientSpan } from "../telemetry/index.js";
11
13
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
12
14
  import { buildNativeToolDeclarations, buildNativeConfig, computeMaxSteps, collectStreamChunks, extractTextFromParts, executeNativeToolCalls, handleMaxStepsTermination, pushModelResponseToHistory, } from "./googleNativeGemini3.js";
13
15
  // Google AI Live API types now imported from ../types/providerSpecific.js
@@ -377,11 +379,10 @@ export class GoogleAIStudioProvider extends BaseProvider {
377
379
  return "image/png";
378
380
  }
379
381
  /**
380
- * Estimate token count from text (simple character-based estimation)
382
+ * Estimate token count from text using centralized estimation with provider multipliers
381
383
  */
382
384
  estimateTokenCount(text) {
383
- // Rough estimation: ~4 characters per token
384
- return Math.ceil(text.length / 4);
385
+ return estimateTokens(text, "google-ai");
385
386
  }
386
387
  // executeGenerate removed - BaseProvider handles all generation with tools
387
388
  async executeStream(options, _analysisSchema) {
@@ -409,13 +410,21 @@ export class GoogleAIStudioProvider extends BaseProvider {
409
410
  logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
410
411
  mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
411
412
  }
412
- logger.info("[GoogleAIStudio] Routing Gemini 3 to native SDK for tool calling", {
413
- model: gemini3CheckModelName,
414
- optionToolCount: Object.keys(optionTools).length,
415
- sdkToolCount: Object.keys(sdkTools).length,
416
- totalToolCount: combinedToolCount,
417
- });
418
- return this.executeNativeGemini3Stream(mergedOptions);
413
+ // Only route to native path if tools are still active after conflict check
414
+ const hasActiveTools = !mergedOptions.disableTools &&
415
+ mergedOptions.tools &&
416
+ Object.keys(mergedOptions.tools).length > 0;
417
+ if (hasActiveTools) {
418
+ logger.info("[GoogleAIStudio] Routing Gemini 3 to native SDK for tool calling", {
419
+ model: gemini3CheckModelName,
420
+ optionToolCount: Object.keys(optionTools).length,
421
+ sdkToolCount: Object.keys(sdkTools).length,
422
+ totalToolCount: combinedToolCount,
423
+ });
424
+ return this.executeNativeGemini3Stream(mergedOptions);
425
+ }
426
+ // Fall through to standard stream path using merged options (tools disabled for schema)
427
+ options = mergedOptions;
419
428
  }
420
429
  // Phase 1: if audio input present, bridge to Gemini Live (Studio) using @google/genai
421
430
  if (options.input?.audio) {
@@ -508,232 +517,280 @@ export class GoogleAIStudioProvider extends BaseProvider {
508
517
  * This bypasses @ai-sdk/google to properly handle thought_signature
509
518
  */
510
519
  async executeNativeGemini3Stream(options) {
511
- const startTime = Date.now();
512
- const timeout = this.getTimeout(options);
513
- const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
514
- const apiKey = this.getApiKey();
515
- const client = await createGoogleGenAIClient(apiKey);
516
520
  const modelName = options.model || this.modelName;
517
- logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3", {
518
- model: modelName,
519
- hasTools: !!options.tools && Object.keys(options.tools).length > 0,
520
- });
521
- // Build contents from input
522
- const currentContents = [{ role: "user", parts: [{ text: options.input.text }] }];
523
- // Convert tools
524
- let toolsConfig;
525
- let executeMap = new Map();
526
- if (options.tools &&
527
- Object.keys(options.tools).length > 0 &&
528
- !options.disableTools) {
529
- const result = buildNativeToolDeclarations(options.tools);
530
- toolsConfig = result.toolsConfig;
531
- executeMap = result.executeMap;
532
- logger.debug("[GoogleAIStudio] Converted tools for native SDK", {
533
- toolCount: toolsConfig[0].functionDeclarations.length,
534
- toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
535
- });
536
- }
537
- const config = buildNativeConfig(options, toolsConfig);
538
- const maxSteps = computeMaxSteps(options.maxSteps);
539
- let finalText = "";
540
- let lastStepText = "";
541
- let totalInputTokens = 0;
542
- let totalOutputTokens = 0;
543
- const allToolCalls = [];
544
- let step = 0;
545
- const failedTools = new Map();
546
- // Compose abort signal from user signal + timeout
547
- const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
548
- try {
549
- // Agentic loop for tool calling
550
- while (step < maxSteps) {
551
- if (composedSignal?.aborted) {
552
- break;
553
- }
554
- step++;
555
- logger.debug(`[GoogleAIStudio] Native SDK step ${step}/${maxSteps}`);
556
- try {
557
- const stream = await client.models.generateContentStream({
558
- model: modelName,
559
- contents: currentContents,
560
- config,
561
- ...(composedSignal
562
- ? { httpOptions: { signal: composedSignal } }
563
- : {}),
521
+ return withClientSpan({
522
+ name: "neurolink.provider.stream",
523
+ tracer: tracers.provider,
524
+ attributes: {
525
+ [ATTR.GEN_AI_SYSTEM]: "google-ai",
526
+ [ATTR.GEN_AI_MODEL]: modelName,
527
+ [ATTR.GEN_AI_OPERATION]: "stream",
528
+ [ATTR.NL_PROVIDER]: this.providerName,
529
+ },
530
+ }, async (span) => {
531
+ const startTime = Date.now();
532
+ const timeout = this.getTimeout(options);
533
+ const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
534
+ try {
535
+ const apiKey = this.getApiKey();
536
+ const client = await createGoogleGenAIClient(apiKey);
537
+ logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3", {
538
+ model: modelName,
539
+ hasTools: !!options.tools && Object.keys(options.tools).length > 0,
540
+ });
541
+ // Build contents from input
542
+ const currentContents = [{ role: "user", parts: [{ text: options.input.text }] }];
543
+ // Convert tools
544
+ let toolsConfig;
545
+ let executeMap = new Map();
546
+ if (options.tools &&
547
+ Object.keys(options.tools).length > 0 &&
548
+ !options.disableTools) {
549
+ const result = buildNativeToolDeclarations(options.tools);
550
+ toolsConfig = result.toolsConfig;
551
+ executeMap = result.executeMap;
552
+ logger.debug("[GoogleAIStudio] Converted tools for native SDK", {
553
+ toolCount: toolsConfig[0].functionDeclarations.length,
554
+ toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
564
555
  });
565
- const chunkResult = await collectStreamChunks(stream);
566
- totalInputTokens += chunkResult.inputTokens;
567
- totalOutputTokens += chunkResult.outputTokens;
568
- const stepText = extractTextFromParts(chunkResult.rawResponseParts);
569
- // If no function calls, we're done
570
- if (chunkResult.stepFunctionCalls.length === 0) {
571
- finalText = stepText;
572
- break;
556
+ }
557
+ const config = buildNativeConfig(options, toolsConfig);
558
+ const maxSteps = computeMaxSteps(options.maxSteps);
559
+ let finalText = "";
560
+ let lastStepText = "";
561
+ let totalInputTokens = 0;
562
+ let totalOutputTokens = 0;
563
+ const allToolCalls = [];
564
+ let step = 0;
565
+ const failedTools = new Map();
566
+ // Compose abort signal from user signal + timeout
567
+ const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
568
+ // Agentic loop for tool calling
569
+ while (step < maxSteps) {
570
+ if (composedSignal?.aborted) {
571
+ throw composedSignal.reason instanceof Error
572
+ ? composedSignal.reason
573
+ : new Error("Request aborted");
574
+ }
575
+ step++;
576
+ logger.debug(`[GoogleAIStudio] Native SDK step ${step}/${maxSteps}`);
577
+ try {
578
+ const stream = await client.models.generateContentStream({
579
+ model: modelName,
580
+ contents: currentContents,
581
+ config,
582
+ ...(composedSignal
583
+ ? { httpOptions: { signal: composedSignal } }
584
+ : {}),
585
+ });
586
+ const chunkResult = await collectStreamChunks(stream);
587
+ totalInputTokens += chunkResult.inputTokens;
588
+ totalOutputTokens += chunkResult.outputTokens;
589
+ const stepText = extractTextFromParts(chunkResult.rawResponseParts);
590
+ // If no function calls, we're done
591
+ if (chunkResult.stepFunctionCalls.length === 0) {
592
+ finalText = stepText;
593
+ break;
594
+ }
595
+ lastStepText = stepText;
596
+ // Record tool call events on the span
597
+ for (const fc of chunkResult.stepFunctionCalls) {
598
+ span.addEvent("gen_ai.tool_call", {
599
+ "tool.name": fc.name,
600
+ "tool.step": step,
601
+ });
602
+ }
603
+ logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
604
+ // Add model response with ALL parts (including thoughtSignature) to history
605
+ pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
606
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
607
+ // Add function responses to history
608
+ currentContents.push({
609
+ role: "function",
610
+ parts: functionResponses,
611
+ });
612
+ }
613
+ catch (error) {
614
+ logger.error("[GoogleAIStudio] Native SDK error", error);
615
+ throw this.handleProviderError(error);
573
616
  }
574
- lastStepText = stepText;
575
- logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
576
- // Add model response with ALL parts (including thoughtSignature) to history
577
- pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
578
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
579
- // Add function responses to history
580
- currentContents.push({
581
- role: "function",
582
- parts: functionResponses,
583
- });
584
617
  }
585
- catch (error) {
586
- logger.error("[GoogleAIStudio] Native SDK error", error);
587
- throw this.handleProviderError(error);
618
+ finalText = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, finalText, lastStepText);
619
+ const responseTime = Date.now() - startTime;
620
+ // Set token usage and finish reason on the span
621
+ span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
622
+ span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
623
+ span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= maxSteps ? "max_steps" : "stop");
624
+ // Create async iterable for streaming result
625
+ async function* createTextStream() {
626
+ yield { content: finalText };
588
627
  }
628
+ return {
629
+ stream: createTextStream(),
630
+ provider: this.providerName,
631
+ model: modelName,
632
+ toolCalls: allToolCalls.map((tc) => ({
633
+ toolName: tc.toolName,
634
+ args: tc.args,
635
+ })),
636
+ analytics: Promise.resolve({
637
+ provider: this.providerName,
638
+ model: modelName,
639
+ tokenUsage: {
640
+ input: totalInputTokens,
641
+ output: totalOutputTokens,
642
+ total: totalInputTokens + totalOutputTokens,
643
+ },
644
+ requestDuration: responseTime,
645
+ timestamp: new Date().toISOString(),
646
+ }),
647
+ metadata: {
648
+ streamId: `native-${Date.now()}`,
649
+ startTime,
650
+ responseTime,
651
+ totalToolExecutions: allToolCalls.length,
652
+ },
653
+ };
589
654
  }
590
- }
591
- finally {
592
- timeoutController?.cleanup();
593
- }
594
- finalText = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, finalText, lastStepText);
595
- const responseTime = Date.now() - startTime;
596
- // Create async iterable for streaming result
597
- async function* createTextStream() {
598
- yield { content: finalText };
599
- }
600
- return {
601
- stream: createTextStream(),
602
- provider: this.providerName,
603
- model: modelName,
604
- toolCalls: allToolCalls.map((tc) => ({
605
- toolName: tc.toolName,
606
- args: tc.args,
607
- })),
608
- analytics: Promise.resolve({
609
- provider: this.providerName,
610
- model: modelName,
611
- tokenUsage: {
612
- input: totalInputTokens,
613
- output: totalOutputTokens,
614
- total: totalInputTokens + totalOutputTokens,
615
- },
616
- requestDuration: responseTime,
617
- timestamp: new Date().toISOString(),
618
- }),
619
- metadata: {
620
- streamId: `native-${Date.now()}`,
621
- startTime,
622
- responseTime,
623
- totalToolExecutions: allToolCalls.length,
624
- },
625
- };
655
+ finally {
656
+ timeoutController?.cleanup();
657
+ }
658
+ });
626
659
  }
627
660
  /**
628
661
  * Execute generate using native @google/genai SDK for Gemini 3 models
629
662
  * This bypasses @ai-sdk/google to properly handle thought_signature
630
663
  */
631
664
  async executeNativeGemini3Generate(options) {
632
- const apiKey = this.getApiKey();
633
- const client = await createGoogleGenAIClient(apiKey);
634
665
  const modelName = options.model || this.modelName;
635
- logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3 generate", {
636
- model: modelName,
637
- hasTools: !!options.tools && Object.keys(options.tools).length > 0,
638
- });
639
- // Build contents from input
640
- const promptText = options.prompt || options.input?.text || "";
641
- const currentContents = [{ role: "user", parts: [{ text: promptText }] }];
642
- // Convert tools (merge SDK tools with options.tools)
643
- let toolsConfig;
644
- let executeMap = new Map();
645
- const shouldUseTools = !options.disableTools;
646
- if (shouldUseTools) {
647
- const sdkTools = await this.getAllTools();
648
- const mergedTools = { ...sdkTools, ...(options.tools || {}) };
649
- if (Object.keys(mergedTools).length > 0) {
650
- const result = buildNativeToolDeclarations(mergedTools);
651
- toolsConfig = result.toolsConfig;
652
- executeMap = result.executeMap;
653
- logger.debug("[GoogleAIStudio] Converted tools for native SDK generate", {
654
- toolCount: toolsConfig[0].functionDeclarations.length,
655
- toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
666
+ return withClientSpan({
667
+ name: "neurolink.provider.generate",
668
+ tracer: tracers.provider,
669
+ attributes: {
670
+ [ATTR.GEN_AI_SYSTEM]: "google-ai",
671
+ [ATTR.GEN_AI_MODEL]: modelName,
672
+ [ATTR.GEN_AI_OPERATION]: "generate",
673
+ [ATTR.NL_PROVIDER]: this.providerName,
674
+ },
675
+ }, async (span) => {
676
+ const startTime = Date.now();
677
+ const timeout = this.getTimeout(options);
678
+ const timeoutController = createTimeoutController(timeout, this.providerName, "generate");
679
+ try {
680
+ const apiKey = this.getApiKey();
681
+ const client = await createGoogleGenAIClient(apiKey);
682
+ logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3 generate", {
683
+ model: modelName,
684
+ hasTools: !!options.tools && Object.keys(options.tools).length > 0,
656
685
  });
657
- }
658
- }
659
- const config = buildNativeConfig(options, toolsConfig);
660
- const startTime = Date.now();
661
- const timeout = this.getTimeout(options);
662
- const timeoutController = createTimeoutController(timeout, this.providerName, "generate");
663
- const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
664
- const maxSteps = computeMaxSteps(options.maxSteps);
665
- let finalText = "";
666
- let lastStepText = "";
667
- let totalInputTokens = 0;
668
- let totalOutputTokens = 0;
669
- const allToolCalls = [];
670
- const toolExecutions = [];
671
- let step = 0;
672
- const failedTools = new Map();
673
- try {
674
- // Agentic loop for tool calling
675
- while (step < maxSteps) {
676
- if (composedSignal?.aborted) {
677
- break;
678
- }
679
- step++;
680
- logger.debug(`[GoogleAIStudio] Native SDK generate step ${step}/${maxSteps}`);
681
- try {
682
- const stream = await client.models.generateContentStream({
683
- model: modelName,
684
- contents: currentContents,
685
- config,
686
- ...(composedSignal
687
- ? { httpOptions: { signal: composedSignal } }
688
- : {}),
689
- });
690
- const chunkResult = await collectStreamChunks(stream);
691
- totalInputTokens += chunkResult.inputTokens;
692
- totalOutputTokens += chunkResult.outputTokens;
693
- const stepText = extractTextFromParts(chunkResult.rawResponseParts);
694
- // If no function calls, we're done
695
- if (chunkResult.stepFunctionCalls.length === 0) {
696
- finalText = stepText;
697
- break;
686
+ // Build contents from input
687
+ const promptText = options.prompt || options.input?.text || "";
688
+ const currentContents = [{ role: "user", parts: [{ text: promptText }] }];
689
+ // Convert tools (merge SDK tools with options.tools)
690
+ let toolsConfig;
691
+ let executeMap = new Map();
692
+ const shouldUseTools = !options.disableTools;
693
+ if (shouldUseTools) {
694
+ const sdkTools = await this.getAllTools();
695
+ const mergedTools = { ...sdkTools, ...(options.tools || {}) };
696
+ if (Object.keys(mergedTools).length > 0) {
697
+ const result = buildNativeToolDeclarations(mergedTools);
698
+ toolsConfig = result.toolsConfig;
699
+ executeMap = result.executeMap;
700
+ logger.debug("[GoogleAIStudio] Converted tools for native SDK generate", {
701
+ toolCount: toolsConfig[0].functionDeclarations.length,
702
+ toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
703
+ });
698
704
  }
699
- lastStepText = stepText;
700
- logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls in generate`);
701
- // Add model response with ALL parts (including thoughtSignature) to history
702
- // This is critical for Gemini 3 - it requires thought signatures in subsequent turns
703
- pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
704
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
705
- // Add function responses to history
706
- currentContents.push({
707
- role: "function",
708
- parts: functionResponses,
709
- });
710
705
  }
711
- catch (error) {
712
- logger.error("[GoogleAIStudio] Native SDK generate error", error);
713
- throw this.handleProviderError(error);
706
+ const config = buildNativeConfig(options, toolsConfig);
707
+ const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
708
+ const maxSteps = computeMaxSteps(options.maxSteps);
709
+ let finalText = "";
710
+ let lastStepText = "";
711
+ let totalInputTokens = 0;
712
+ let totalOutputTokens = 0;
713
+ const allToolCalls = [];
714
+ const toolExecutions = [];
715
+ let step = 0;
716
+ const failedTools = new Map();
717
+ // Agentic loop for tool calling
718
+ while (step < maxSteps) {
719
+ if (composedSignal?.aborted) {
720
+ throw composedSignal.reason instanceof Error
721
+ ? composedSignal.reason
722
+ : new Error("Request aborted");
723
+ }
724
+ step++;
725
+ logger.debug(`[GoogleAIStudio] Native SDK generate step ${step}/${maxSteps}`);
726
+ try {
727
+ const stream = await client.models.generateContentStream({
728
+ model: modelName,
729
+ contents: currentContents,
730
+ config,
731
+ ...(composedSignal
732
+ ? { httpOptions: { signal: composedSignal } }
733
+ : {}),
734
+ });
735
+ const chunkResult = await collectStreamChunks(stream);
736
+ totalInputTokens += chunkResult.inputTokens;
737
+ totalOutputTokens += chunkResult.outputTokens;
738
+ const stepText = extractTextFromParts(chunkResult.rawResponseParts);
739
+ // If no function calls, we're done
740
+ if (chunkResult.stepFunctionCalls.length === 0) {
741
+ finalText = stepText;
742
+ break;
743
+ }
744
+ lastStepText = stepText;
745
+ // Record tool call events on the span
746
+ for (const fc of chunkResult.stepFunctionCalls) {
747
+ span.addEvent("gen_ai.tool_call", {
748
+ "tool.name": fc.name,
749
+ "tool.step": step,
750
+ });
751
+ }
752
+ logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls in generate`);
753
+ // Add model response with ALL parts (including thoughtSignature) to history
754
+ // This is critical for Gemini 3 - it requires thought signatures in subsequent turns
755
+ pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
756
+ const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
757
+ // Add function responses to history
758
+ currentContents.push({
759
+ role: "function",
760
+ parts: functionResponses,
761
+ });
762
+ }
763
+ catch (error) {
764
+ logger.error("[GoogleAIStudio] Native SDK generate error", error);
765
+ throw this.handleProviderError(error);
766
+ }
714
767
  }
768
+ finalText = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, finalText, lastStepText);
769
+ const responseTime = Date.now() - startTime;
770
+ // Set token usage and finish reason on the span
771
+ span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
772
+ span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
773
+ span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= maxSteps ? "max_steps" : "stop");
774
+ // Build EnhancedGenerateResult
775
+ return {
776
+ content: finalText,
777
+ provider: this.providerName,
778
+ model: modelName,
779
+ usage: {
780
+ input: totalInputTokens,
781
+ output: totalOutputTokens,
782
+ total: totalInputTokens + totalOutputTokens,
783
+ },
784
+ responseTime,
785
+ toolsUsed: allToolCalls.map((tc) => tc.toolName),
786
+ toolExecutions: toolExecutions,
787
+ enhancedWithTools: allToolCalls.length > 0,
788
+ };
715
789
  }
716
- }
717
- finally {
718
- timeoutController?.cleanup();
719
- }
720
- finalText = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, finalText, lastStepText);
721
- const responseTime = Date.now() - startTime;
722
- // Build EnhancedGenerateResult
723
- return {
724
- content: finalText,
725
- provider: this.providerName,
726
- model: modelName,
727
- usage: {
728
- input: totalInputTokens,
729
- output: totalOutputTokens,
730
- total: totalInputTokens + totalOutputTokens,
731
- },
732
- responseTime,
733
- toolsUsed: allToolCalls.map((tc) => tc.toolName),
734
- toolExecutions: toolExecutions,
735
- enhancedWithTools: allToolCalls.length > 0,
736
- };
790
+ finally {
791
+ timeoutController?.cleanup();
792
+ }
793
+ });
737
794
  }
738
795
  /**
739
796
  * Override generate to route Gemini 3 models with tools to native SDK
@@ -765,17 +822,25 @@ export class GoogleAIStudioProvider extends BaseProvider {
765
822
  logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
766
823
  mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
767
824
  }
768
- logger.info("[GoogleAIStudio] Routing Gemini 3 generate to native SDK for tool calling", {
769
- model: modelName,
770
- sdkToolCount: Object.keys(sdkTools).length,
771
- optionToolCount: Object.keys(options.tools || {}).length,
772
- totalToolCount: Object.keys(sdkTools).length +
773
- Object.keys(options.tools || {}).length,
774
- });
775
- return this.executeNativeGemini3Generate(mergedOptions);
825
+ // Only route to native path if tools are still active after conflict check
826
+ const hasActiveTools = !mergedOptions.disableTools &&
827
+ mergedOptions.tools &&
828
+ Object.keys(mergedOptions.tools).length > 0;
829
+ if (hasActiveTools) {
830
+ logger.info("[GoogleAIStudio] Routing Gemini 3 generate to native SDK for tool calling", {
831
+ model: modelName,
832
+ sdkToolCount: Object.keys(sdkTools).length,
833
+ optionToolCount: Object.keys(options.tools || {}).length,
834
+ totalToolCount: Object.keys(sdkTools).length +
835
+ Object.keys(options.tools || {}).length,
836
+ });
837
+ return this.executeNativeGemini3Generate(mergedOptions);
838
+ }
839
+ // Fall through to standard generate path using merged options (tools disabled for schema)
840
+ return super.generate(mergedOptions);
776
841
  }
777
842
  // Fall back to BaseProvider implementation
778
- return super.generate(optionsOrPrompt);
843
+ return super.generate(options);
779
844
  }
780
845
  // ===================
781
846
  // HELPER METHODS
@@ -4,6 +4,21 @@ import { type AIProviderName } from "../constants/enums.js";
4
4
  import { BaseProvider } from "../core/baseProvider.js";
5
5
  import type { EnhancedGenerateResult, TextGenerationOptions } from "../types/generateTypes.js";
6
6
  import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
7
+ /**
8
+ * Vertex Model Aliases
9
+ *
10
+ * Maps shorthand model names to their full versioned IDs required by the
11
+ * Vertex AI API. This allows users to pass convenient names like
12
+ * "claude-sonnet-4-5" instead of "claude-sonnet-4-5@20250929".
13
+ *
14
+ * Alias resolution runs at the very start of getModel() so that all
15
+ * downstream code (isAnthropicModel, validateAnthropicModelName, etc.)
16
+ * sees the canonical versioned name.
17
+ *
18
+ * To add a new model: simply add an entry mapping the shorthand to the
19
+ * full versioned string. No other changes are needed.
20
+ */
21
+ export declare const VERTEX_MODEL_ALIASES: Record<string, string>;
7
22
  /**
8
23
  * Google Vertex AI Provider v2 - BaseProvider Implementation
9
24
  *
@@ -74,6 +89,11 @@ export declare class GoogleVertexProvider extends BaseProvider {
74
89
  * Creates fresh model instances for each request
75
90
  */
76
91
  protected getAISDKModel(): Promise<LanguageModel>;
92
+ /**
93
+ * Resolve a raw model name through the alias map.
94
+ * Used internally to normalize model names before any API calls.
95
+ */
96
+ private resolveAlias;
77
97
  /**
78
98
  * Initialize model creation tracking
79
99
  */
@@ -156,6 +176,21 @@ export declare class GoogleVertexProvider extends BaseProvider {
156
176
  * @returns Promise<boolean> indicating if Anthropic support is available
157
177
  */
158
178
  hasAnthropicSupport(): Promise<boolean>;
179
+ /**
180
+ * Resolve a shorthand model name to its full versioned Vertex AI identifier.
181
+ * Returns the original name unchanged if no alias exists.
182
+ *
183
+ * @param modelName - A model name, possibly a shorthand alias
184
+ * @returns The resolved full versioned model name
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * provider.resolveModelAlias("claude-sonnet-4-5"); // "claude-sonnet-4-5@20250929"
189
+ * provider.resolveModelAlias("gemini-3-pro"); // "gemini-3-pro-latest"
190
+ * provider.resolveModelAlias("gemini-2.5-flash"); // "gemini-2.5-flash" (unchanged)
191
+ * ```
192
+ */
193
+ resolveModelAlias(modelName: string): string;
159
194
  /**
160
195
  * Create an Anthropic model instance using vertexAnthropic provider
161
196
  * Uses fresh vertex settings for each request with comprehensive validation
@@ -233,7 +268,7 @@ export declare class GoogleVertexProvider extends BaseProvider {
233
268
  */
234
269
  private detectImageType;
235
270
  /**
236
- * Estimate token count from text (simple character-based estimation)
271
+ * Estimate token count from text using centralized estimation with provider multipliers
237
272
  */
238
273
  private estimateTokenCount;
239
274
  /**