@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.
- package/CHANGELOG.md +6 -0
- package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/adapters/video/videoAnalyzer.js +10 -8
- package/dist/cli/commands/setup-anthropic.js +1 -14
- package/dist/cli/commands/setup-azure.js +1 -12
- package/dist/cli/commands/setup-bedrock.js +1 -9
- package/dist/cli/commands/setup-google-ai.js +1 -12
- package/dist/cli/commands/setup-openai.js +1 -14
- package/dist/cli/commands/workflow.d.ts +27 -0
- package/dist/cli/commands/workflow.js +216 -0
- package/dist/cli/factories/commandFactory.js +79 -20
- package/dist/cli/index.js +0 -1
- package/dist/cli/parser.js +4 -1
- package/dist/cli/utils/maskCredential.d.ts +11 -0
- package/dist/cli/utils/maskCredential.js +23 -0
- package/dist/constants/contextWindows.js +107 -16
- package/dist/constants/enums.d.ts +99 -15
- package/dist/constants/enums.js +152 -22
- package/dist/context/budgetChecker.js +1 -1
- package/dist/context/contextCompactor.js +31 -4
- package/dist/context/emergencyTruncation.d.ts +21 -0
- package/dist/context/emergencyTruncation.js +88 -0
- package/dist/context/errorDetection.d.ts +16 -0
- package/dist/context/errorDetection.js +48 -1
- package/dist/context/errors.d.ts +19 -0
- package/dist/context/errors.js +21 -0
- package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/core/baseProvider.js +306 -200
- package/dist/core/conversationMemoryManager.js +104 -61
- package/dist/core/evaluationProviders.js +16 -33
- package/dist/core/factory.js +237 -164
- package/dist/core/modules/GenerationHandler.js +175 -116
- package/dist/core/modules/MessageBuilder.js +222 -170
- package/dist/core/modules/StreamHandler.d.ts +1 -0
- package/dist/core/modules/StreamHandler.js +95 -27
- package/dist/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/core/modules/TelemetryHandler.js +25 -7
- package/dist/core/modules/ToolsManager.js +115 -191
- package/dist/core/redisConversationMemoryManager.js +418 -282
- package/dist/factories/providerRegistry.d.ts +5 -0
- package/dist/factories/providerRegistry.js +20 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
- package/dist/lib/constants/contextWindows.js +107 -16
- package/dist/lib/constants/enums.d.ts +99 -15
- package/dist/lib/constants/enums.js +152 -22
- package/dist/lib/context/budgetChecker.js +1 -1
- package/dist/lib/context/contextCompactor.js +31 -4
- package/dist/lib/context/emergencyTruncation.d.ts +21 -0
- package/dist/lib/context/emergencyTruncation.js +89 -0
- package/dist/lib/context/errorDetection.d.ts +16 -0
- package/dist/lib/context/errorDetection.js +48 -1
- package/dist/lib/context/errors.d.ts +19 -0
- package/dist/lib/context/errors.js +22 -0
- package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/lib/core/baseProvider.js +306 -200
- package/dist/lib/core/conversationMemoryManager.js +104 -61
- package/dist/lib/core/evaluationProviders.js +16 -33
- package/dist/lib/core/factory.js +237 -164
- package/dist/lib/core/modules/GenerationHandler.js +175 -116
- package/dist/lib/core/modules/MessageBuilder.js +222 -170
- package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
- package/dist/lib/core/modules/StreamHandler.js +95 -27
- package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/lib/core/modules/TelemetryHandler.js +25 -7
- package/dist/lib/core/modules/ToolsManager.js +115 -191
- package/dist/lib/core/redisConversationMemoryManager.js +418 -282
- package/dist/lib/factories/providerRegistry.d.ts +5 -0
- package/dist/lib/factories/providerRegistry.js +20 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +4 -2
- package/dist/lib/mcp/externalServerManager.js +66 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/lib/mcp/mcpClientFactory.js +16 -0
- package/dist/lib/mcp/toolDiscoveryService.js +32 -6
- package/dist/lib/mcp/toolRegistry.js +193 -123
- package/dist/lib/neurolink.d.ts +6 -0
- package/dist/lib/neurolink.js +1162 -646
- package/dist/lib/providers/amazonBedrock.d.ts +1 -1
- package/dist/lib/providers/amazonBedrock.js +521 -319
- package/dist/lib/providers/anthropic.js +73 -17
- package/dist/lib/providers/anthropicBaseProvider.js +77 -17
- package/dist/lib/providers/googleAiStudio.d.ts +1 -1
- package/dist/lib/providers/googleAiStudio.js +292 -227
- package/dist/lib/providers/googleVertex.d.ts +36 -1
- package/dist/lib/providers/googleVertex.js +553 -260
- package/dist/lib/providers/ollama.js +329 -278
- package/dist/lib/providers/openAI.js +77 -19
- package/dist/lib/providers/sagemaker/parsers.js +3 -3
- package/dist/lib/providers/sagemaker/streaming.js +3 -3
- package/dist/lib/proxy/proxyFetch.js +81 -48
- package/dist/lib/rag/ChunkerFactory.js +1 -1
- package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/lib/rag/chunking/markdownChunker.js +174 -2
- package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
- package/dist/lib/rag/ragIntegration.d.ts +18 -1
- package/dist/lib/rag/ragIntegration.js +94 -14
- package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
- package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/lib/telemetry/attributes.d.ts +52 -0
- package/dist/lib/telemetry/attributes.js +61 -0
- package/dist/lib/telemetry/index.d.ts +3 -0
- package/dist/lib/telemetry/index.js +3 -0
- package/dist/lib/telemetry/telemetryService.d.ts +6 -0
- package/dist/lib/telemetry/telemetryService.js +6 -0
- package/dist/lib/telemetry/tracers.d.ts +15 -0
- package/dist/lib/telemetry/tracers.js +17 -0
- package/dist/lib/telemetry/withSpan.d.ts +9 -0
- package/dist/lib/telemetry/withSpan.js +35 -0
- package/dist/lib/types/contextTypes.d.ts +10 -0
- package/dist/lib/types/streamTypes.d.ts +14 -0
- package/dist/lib/utils/conversationMemory.js +121 -82
- package/dist/lib/utils/logger.d.ts +5 -0
- package/dist/lib/utils/logger.js +50 -2
- package/dist/lib/utils/messageBuilder.js +22 -42
- package/dist/lib/utils/modelDetection.js +3 -3
- package/dist/lib/utils/providerRetry.d.ts +41 -0
- package/dist/lib/utils/providerRetry.js +114 -0
- package/dist/lib/utils/retryability.d.ts +14 -0
- package/dist/lib/utils/retryability.js +23 -0
- package/dist/lib/utils/sanitizers/svg.js +4 -5
- package/dist/lib/utils/tokenEstimation.d.ts +11 -1
- package/dist/lib/utils/tokenEstimation.js +19 -4
- package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
- package/dist/mcp/externalServerManager.js +66 -0
- package/dist/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/mcp/mcpClientFactory.js +16 -0
- package/dist/mcp/toolDiscoveryService.js +32 -6
- package/dist/mcp/toolRegistry.js +193 -123
- package/dist/neurolink.d.ts +6 -0
- package/dist/neurolink.js +1162 -646
- package/dist/providers/amazonBedrock.d.ts +1 -1
- package/dist/providers/amazonBedrock.js +521 -319
- package/dist/providers/anthropic.js +73 -17
- package/dist/providers/anthropicBaseProvider.js +77 -17
- package/dist/providers/googleAiStudio.d.ts +1 -1
- package/dist/providers/googleAiStudio.js +292 -227
- package/dist/providers/googleVertex.d.ts +36 -1
- package/dist/providers/googleVertex.js +553 -260
- package/dist/providers/ollama.js +329 -278
- package/dist/providers/openAI.js +77 -19
- package/dist/providers/sagemaker/parsers.js +3 -3
- package/dist/providers/sagemaker/streaming.js +3 -3
- package/dist/proxy/proxyFetch.js +81 -48
- package/dist/rag/ChunkerFactory.js +1 -1
- package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/rag/chunking/markdownChunker.js +174 -2
- package/dist/rag/pipeline/contextAssembly.js +2 -1
- package/dist/rag/ragIntegration.d.ts +18 -1
- package/dist/rag/ragIntegration.js +94 -14
- package/dist/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/server/abstract/baseServerAdapter.js +4 -1
- package/dist/server/adapters/fastifyAdapter.js +35 -30
- package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/telemetry/attributes.d.ts +52 -0
- package/dist/telemetry/attributes.js +60 -0
- package/dist/telemetry/index.d.ts +3 -0
- package/dist/telemetry/index.js +3 -0
- package/dist/telemetry/telemetryService.d.ts +6 -0
- package/dist/telemetry/telemetryService.js +6 -0
- package/dist/telemetry/tracers.d.ts +15 -0
- package/dist/telemetry/tracers.js +16 -0
- package/dist/telemetry/withSpan.d.ts +9 -0
- package/dist/telemetry/withSpan.js +34 -0
- package/dist/types/contextTypes.d.ts +10 -0
- package/dist/types/streamTypes.d.ts +14 -0
- package/dist/utils/conversationMemory.js +121 -82
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.js +50 -2
- package/dist/utils/messageBuilder.js +22 -42
- package/dist/utils/modelDetection.js +3 -3
- package/dist/utils/providerRetry.d.ts +41 -0
- package/dist/utils/providerRetry.js +113 -0
- package/dist/utils/retryability.d.ts +14 -0
- package/dist/utils/retryability.js +22 -0
- package/dist/utils/sanitizers/svg.js +4 -5
- package/dist/utils/tokenEstimation.d.ts +11 -1
- package/dist/utils/tokenEstimation.js +19 -4
- package/dist/utils/videoAnalysisProcessor.js +7 -3
- package/dist/workflow/config.d.ts +26 -26
- 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
|
|
382
|
+
* Estimate token count from text using centralized estimation with provider multipliers
|
|
381
383
|
*/
|
|
382
384
|
estimateTokenCount(text) {
|
|
383
|
-
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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
|
-
|
|
592
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
-
|
|
718
|
-
|
|
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
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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(
|
|
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
|
|
271
|
+
* Estimate token count from text using centralized estimation with provider multipliers
|
|
237
272
|
*/
|
|
238
273
|
private estimateTokenCount;
|
|
239
274
|
/**
|