@juspay/neurolink 9.26.1 → 9.27.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 +12 -0
- package/dist/adapters/providerImageAdapter.js +6 -0
- package/dist/constants/contextWindows.js +2 -0
- package/dist/constants/enums.d.ts +2 -0
- package/dist/constants/enums.js +2 -0
- package/dist/lib/adapters/providerImageAdapter.js +6 -0
- package/dist/lib/constants/contextWindows.js +2 -0
- package/dist/lib/constants/enums.d.ts +2 -0
- package/dist/lib/constants/enums.js +2 -0
- package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/lib/memory/hippocampusInitializer.js +1 -1
- package/dist/lib/providers/googleAiStudio.js +135 -89
- package/dist/lib/providers/googleNativeGemini3.d.ts +43 -0
- package/dist/lib/providers/googleNativeGemini3.js +148 -18
- package/dist/lib/providers/googleVertex.js +162 -140
- package/dist/lib/types/conversation.d.ts +2 -2
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/memory/hippocampusInitializer.js +1 -1
- package/dist/providers/googleAiStudio.js +135 -89
- package/dist/providers/googleNativeGemini3.d.ts +43 -0
- package/dist/providers/googleNativeGemini3.js +148 -18
- package/dist/providers/googleVertex.js +162 -140
- package/dist/types/conversation.d.ts +2 -2
- package/dist/types/index.d.ts +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [9.27.0](https://github.com/juspay/neurolink/compare/v9.26.2...v9.27.0) (2026-03-18)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- **(memory):** add CustomStorageConfig type to Hippocampus integration ([dfcea2b](https://github.com/juspay/neurolink/commit/dfcea2b04834a86812f13c5d85ad3d7b9ab530a7))
|
|
6
|
+
|
|
7
|
+
## [9.26.2](https://github.com/juspay/neurolink/compare/v9.26.1...v9.26.2) (2026-03-18)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **(providers):** fix Gemini 3.1 native SDK tool calling, streaming, and multimodal on Vertex global endpoint ([b95089a](https://github.com/juspay/neurolink/commit/b95089a5bc900b38678b919a6ae2567c9500ca8b))
|
|
12
|
+
|
|
1
13
|
## [9.26.1](https://github.com/juspay/neurolink/compare/v9.26.0...v9.26.1) (2026-03-16)
|
|
2
14
|
|
|
3
15
|
### Bug Fixes
|
|
@@ -90,6 +90,9 @@ const VISION_CAPABILITIES = {
|
|
|
90
90
|
"gpt-4-vision-preview",
|
|
91
91
|
],
|
|
92
92
|
"google-ai": [
|
|
93
|
+
// Gemini 3.1 Series (Preview)
|
|
94
|
+
"gemini-3.1-pro-preview",
|
|
95
|
+
"gemini-3.1-flash-lite-preview",
|
|
93
96
|
// Gemini 3 Series (Preview - November 2025)
|
|
94
97
|
"gemini-3-pro-preview",
|
|
95
98
|
"gemini-3-pro-preview-11-2025",
|
|
@@ -168,6 +171,9 @@ const VISION_CAPABILITIES = {
|
|
|
168
171
|
"gpt-4",
|
|
169
172
|
],
|
|
170
173
|
vertex: [
|
|
174
|
+
// Gemini 3.1 models on Vertex AI (Preview)
|
|
175
|
+
"gemini-3.1-pro-preview",
|
|
176
|
+
"gemini-3.1-flash-lite-preview",
|
|
171
177
|
// Gemini 3.x models on Vertex AI (Preview)
|
|
172
178
|
"gemini-3-pro-preview-11-2025",
|
|
173
179
|
"gemini-3-pro-latest",
|
|
@@ -92,6 +92,7 @@ export const MODEL_CONTEXT_WINDOWS = {
|
|
|
92
92
|
"gemini-3.1-pro-preview": 1_048_576,
|
|
93
93
|
"gemini-3.1-flash": 1_048_576,
|
|
94
94
|
"gemini-3.1-flash-lite": 1_048_576,
|
|
95
|
+
"gemini-3.1-flash-lite-preview": 1_048_576,
|
|
95
96
|
"gemini-3-pro-preview": 1_048_576,
|
|
96
97
|
"gemini-3-pro-image-preview": 65_536,
|
|
97
98
|
"gemini-3-flash-preview": 1_048_576,
|
|
@@ -121,6 +122,7 @@ export const MODEL_CONTEXT_WINDOWS = {
|
|
|
121
122
|
"gemini-3.1-pro-preview": 1_048_576,
|
|
122
123
|
"gemini-3.1-flash": 1_048_576,
|
|
123
124
|
"gemini-3.1-flash-lite": 1_048_576,
|
|
125
|
+
"gemini-3.1-flash-lite-preview": 1_048_576,
|
|
124
126
|
"gemini-3-pro-preview": 1_048_576,
|
|
125
127
|
"gemini-3-pro-latest": 1_048_576,
|
|
126
128
|
"gemini-3-flash-preview": 1_048_576,
|
|
@@ -238,6 +238,7 @@ export declare enum VertexModels {
|
|
|
238
238
|
GEMINI_3_1_PRO_PREVIEW = "gemini-3.1-pro-preview",
|
|
239
239
|
GEMINI_3_1_FLASH = "gemini-3.1-flash",
|
|
240
240
|
GEMINI_3_1_FLASH_LITE = "gemini-3.1-flash-lite",
|
|
241
|
+
GEMINI_3_1_FLASH_LITE_PREVIEW = "gemini-3.1-flash-lite-preview",
|
|
241
242
|
GEMINI_3_PRO = "gemini-3-pro",
|
|
242
243
|
GEMINI_3_PRO_PREVIEW_11_2025 = "gemini-3-pro-preview-11-2025",
|
|
243
244
|
GEMINI_3_PRO_LATEST = "gemini-3-pro-latest",
|
|
@@ -265,6 +266,7 @@ export declare enum GoogleAIModels {
|
|
|
265
266
|
GEMINI_3_1_PRO_PREVIEW = "gemini-3.1-pro-preview",
|
|
266
267
|
GEMINI_3_1_FLASH = "gemini-3.1-flash",
|
|
267
268
|
GEMINI_3_1_FLASH_LITE = "gemini-3.1-flash-lite",
|
|
269
|
+
GEMINI_3_1_FLASH_LITE_PREVIEW = "gemini-3.1-flash-lite-preview",
|
|
268
270
|
GEMINI_3_PRO_PREVIEW = "gemini-3-pro-preview",
|
|
269
271
|
GEMINI_3_PRO_IMAGE_PREVIEW = "gemini-3-pro-image-preview",
|
|
270
272
|
GEMINI_3_FLASH = "gemini-3-flash",
|
package/dist/constants/enums.js
CHANGED
|
@@ -336,6 +336,7 @@ export var VertexModels;
|
|
|
336
336
|
VertexModels["GEMINI_3_1_PRO_PREVIEW"] = "gemini-3.1-pro-preview";
|
|
337
337
|
VertexModels["GEMINI_3_1_FLASH"] = "gemini-3.1-flash";
|
|
338
338
|
VertexModels["GEMINI_3_1_FLASH_LITE"] = "gemini-3.1-flash-lite";
|
|
339
|
+
VertexModels["GEMINI_3_1_FLASH_LITE_PREVIEW"] = "gemini-3.1-flash-lite-preview";
|
|
339
340
|
// Gemini 3 Series (Preview)
|
|
340
341
|
VertexModels["GEMINI_3_PRO"] = "gemini-3-pro";
|
|
341
342
|
VertexModels["GEMINI_3_PRO_PREVIEW_11_2025"] = "gemini-3-pro-preview-11-2025";
|
|
@@ -369,6 +370,7 @@ export var GoogleAIModels;
|
|
|
369
370
|
GoogleAIModels["GEMINI_3_1_PRO_PREVIEW"] = "gemini-3.1-pro-preview";
|
|
370
371
|
GoogleAIModels["GEMINI_3_1_FLASH"] = "gemini-3.1-flash";
|
|
371
372
|
GoogleAIModels["GEMINI_3_1_FLASH_LITE"] = "gemini-3.1-flash-lite";
|
|
373
|
+
GoogleAIModels["GEMINI_3_1_FLASH_LITE_PREVIEW"] = "gemini-3.1-flash-lite-preview";
|
|
372
374
|
// Gemini 3 Series (Preview)
|
|
373
375
|
GoogleAIModels["GEMINI_3_PRO_PREVIEW"] = "gemini-3-pro-preview";
|
|
374
376
|
GoogleAIModels["GEMINI_3_PRO_IMAGE_PREVIEW"] = "gemini-3-pro-image-preview";
|
|
@@ -90,6 +90,9 @@ const VISION_CAPABILITIES = {
|
|
|
90
90
|
"gpt-4-vision-preview",
|
|
91
91
|
],
|
|
92
92
|
"google-ai": [
|
|
93
|
+
// Gemini 3.1 Series (Preview)
|
|
94
|
+
"gemini-3.1-pro-preview",
|
|
95
|
+
"gemini-3.1-flash-lite-preview",
|
|
93
96
|
// Gemini 3 Series (Preview - November 2025)
|
|
94
97
|
"gemini-3-pro-preview",
|
|
95
98
|
"gemini-3-pro-preview-11-2025",
|
|
@@ -168,6 +171,9 @@ const VISION_CAPABILITIES = {
|
|
|
168
171
|
"gpt-4",
|
|
169
172
|
],
|
|
170
173
|
vertex: [
|
|
174
|
+
// Gemini 3.1 models on Vertex AI (Preview)
|
|
175
|
+
"gemini-3.1-pro-preview",
|
|
176
|
+
"gemini-3.1-flash-lite-preview",
|
|
171
177
|
// Gemini 3.x models on Vertex AI (Preview)
|
|
172
178
|
"gemini-3-pro-preview-11-2025",
|
|
173
179
|
"gemini-3-pro-latest",
|
|
@@ -92,6 +92,7 @@ export const MODEL_CONTEXT_WINDOWS = {
|
|
|
92
92
|
"gemini-3.1-pro-preview": 1_048_576,
|
|
93
93
|
"gemini-3.1-flash": 1_048_576,
|
|
94
94
|
"gemini-3.1-flash-lite": 1_048_576,
|
|
95
|
+
"gemini-3.1-flash-lite-preview": 1_048_576,
|
|
95
96
|
"gemini-3-pro-preview": 1_048_576,
|
|
96
97
|
"gemini-3-pro-image-preview": 65_536,
|
|
97
98
|
"gemini-3-flash-preview": 1_048_576,
|
|
@@ -121,6 +122,7 @@ export const MODEL_CONTEXT_WINDOWS = {
|
|
|
121
122
|
"gemini-3.1-pro-preview": 1_048_576,
|
|
122
123
|
"gemini-3.1-flash": 1_048_576,
|
|
123
124
|
"gemini-3.1-flash-lite": 1_048_576,
|
|
125
|
+
"gemini-3.1-flash-lite-preview": 1_048_576,
|
|
124
126
|
"gemini-3-pro-preview": 1_048_576,
|
|
125
127
|
"gemini-3-pro-latest": 1_048_576,
|
|
126
128
|
"gemini-3-flash-preview": 1_048_576,
|
|
@@ -238,6 +238,7 @@ export declare enum VertexModels {
|
|
|
238
238
|
GEMINI_3_1_PRO_PREVIEW = "gemini-3.1-pro-preview",
|
|
239
239
|
GEMINI_3_1_FLASH = "gemini-3.1-flash",
|
|
240
240
|
GEMINI_3_1_FLASH_LITE = "gemini-3.1-flash-lite",
|
|
241
|
+
GEMINI_3_1_FLASH_LITE_PREVIEW = "gemini-3.1-flash-lite-preview",
|
|
241
242
|
GEMINI_3_PRO = "gemini-3-pro",
|
|
242
243
|
GEMINI_3_PRO_PREVIEW_11_2025 = "gemini-3-pro-preview-11-2025",
|
|
243
244
|
GEMINI_3_PRO_LATEST = "gemini-3-pro-latest",
|
|
@@ -265,6 +266,7 @@ export declare enum GoogleAIModels {
|
|
|
265
266
|
GEMINI_3_1_PRO_PREVIEW = "gemini-3.1-pro-preview",
|
|
266
267
|
GEMINI_3_1_FLASH = "gemini-3.1-flash",
|
|
267
268
|
GEMINI_3_1_FLASH_LITE = "gemini-3.1-flash-lite",
|
|
269
|
+
GEMINI_3_1_FLASH_LITE_PREVIEW = "gemini-3.1-flash-lite-preview",
|
|
268
270
|
GEMINI_3_PRO_PREVIEW = "gemini-3-pro-preview",
|
|
269
271
|
GEMINI_3_PRO_IMAGE_PREVIEW = "gemini-3-pro-image-preview",
|
|
270
272
|
GEMINI_3_FLASH = "gemini-3-flash",
|
|
@@ -336,6 +336,7 @@ export var VertexModels;
|
|
|
336
336
|
VertexModels["GEMINI_3_1_PRO_PREVIEW"] = "gemini-3.1-pro-preview";
|
|
337
337
|
VertexModels["GEMINI_3_1_FLASH"] = "gemini-3.1-flash";
|
|
338
338
|
VertexModels["GEMINI_3_1_FLASH_LITE"] = "gemini-3.1-flash-lite";
|
|
339
|
+
VertexModels["GEMINI_3_1_FLASH_LITE_PREVIEW"] = "gemini-3.1-flash-lite-preview";
|
|
339
340
|
// Gemini 3 Series (Preview)
|
|
340
341
|
VertexModels["GEMINI_3_PRO"] = "gemini-3-pro";
|
|
341
342
|
VertexModels["GEMINI_3_PRO_PREVIEW_11_2025"] = "gemini-3-pro-preview-11-2025";
|
|
@@ -369,6 +370,7 @@ export var GoogleAIModels;
|
|
|
369
370
|
GoogleAIModels["GEMINI_3_1_PRO_PREVIEW"] = "gemini-3.1-pro-preview";
|
|
370
371
|
GoogleAIModels["GEMINI_3_1_FLASH"] = "gemini-3.1-flash";
|
|
371
372
|
GoogleAIModels["GEMINI_3_1_FLASH_LITE"] = "gemini-3.1-flash-lite";
|
|
373
|
+
GoogleAIModels["GEMINI_3_1_FLASH_LITE_PREVIEW"] = "gemini-3.1-flash-lite-preview";
|
|
372
374
|
// Gemini 3 Series (Preview)
|
|
373
375
|
GoogleAIModels["GEMINI_3_PRO_PREVIEW"] = "gemini-3-pro-preview";
|
|
374
376
|
GoogleAIModels["GEMINI_3_PRO_IMAGE_PREVIEW"] = "gemini-3-pro-image-preview";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Hippocampus, type HippocampusConfig } from "@juspay/hippocampus";
|
|
2
|
-
export type { HippocampusConfig };
|
|
1
|
+
import { Hippocampus, type HippocampusConfig, type CustomStorageConfig } from "@juspay/hippocampus";
|
|
2
|
+
export type { HippocampusConfig, CustomStorageConfig };
|
|
3
3
|
export type Memory = HippocampusConfig & {
|
|
4
4
|
enabled?: boolean;
|
|
5
5
|
};
|
|
@@ -11,7 +11,7 @@ import { logger } from "../utils/logger.js";
|
|
|
11
11
|
import { isGemini3Model } from "../utils/modelDetection.js";
|
|
12
12
|
import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
|
|
13
13
|
import { estimateTokens } from "../utils/tokenEstimation.js";
|
|
14
|
-
import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, computeMaxSteps, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
|
|
14
|
+
import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps, createTextChannel, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
|
|
15
15
|
// Google AI Live API types now imported from ../types/providerSpecific.js
|
|
16
16
|
// Import proper types for multimodal message handling
|
|
17
17
|
// Create Google GenAI client
|
|
@@ -578,107 +578,151 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
578
578
|
}
|
|
579
579
|
const config = buildNativeConfig(options, toolsConfig);
|
|
580
580
|
const maxSteps = computeMaxSteps(options.maxSteps);
|
|
581
|
-
let finalText = "";
|
|
582
|
-
let lastStepText = "";
|
|
583
|
-
let totalInputTokens = 0;
|
|
584
|
-
let totalOutputTokens = 0;
|
|
585
|
-
const allToolCalls = [];
|
|
586
|
-
let step = 0;
|
|
587
|
-
const failedTools = new Map();
|
|
588
581
|
// Compose abort signal from user signal + timeout
|
|
589
582
|
const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
583
|
+
// Create a push-based text channel so the caller receives tokens as
|
|
584
|
+
// they arrive from the network rather than after full buffering.
|
|
585
|
+
const channel = createTextChannel();
|
|
586
|
+
// Shared mutable state updated by the background agentic loop.
|
|
587
|
+
const allToolCalls = [];
|
|
588
|
+
// analyticsResolvers lets the background loop settle the analytics
|
|
589
|
+
// promise once token counts are known (after the loop completes).
|
|
590
|
+
let analyticsResolve;
|
|
591
|
+
let analyticsReject;
|
|
592
|
+
const analyticsPromise = new Promise((res, rej) => {
|
|
593
|
+
analyticsResolve = res;
|
|
594
|
+
analyticsReject = rej;
|
|
595
|
+
});
|
|
596
|
+
// Shared metadata object mutated by the background loop so the
|
|
597
|
+
// returned object reflects the final values after stream completion.
|
|
598
|
+
const metadata = {
|
|
599
|
+
streamId: `native-${Date.now()}`,
|
|
600
|
+
startTime,
|
|
601
|
+
responseTime: 0,
|
|
602
|
+
totalToolExecutions: 0,
|
|
603
|
+
};
|
|
604
|
+
// Run the agentic loop in the background without awaiting it here,
|
|
605
|
+
// so we can return the StreamResult (with channel.iterable) immediately.
|
|
606
|
+
const loopPromise = (async () => {
|
|
607
|
+
let lastStepText = "";
|
|
608
|
+
let totalInputTokens = 0;
|
|
609
|
+
let totalOutputTokens = 0;
|
|
610
|
+
let step = 0;
|
|
611
|
+
let completedWithFinalAnswer = false;
|
|
612
|
+
const failedTools = new Map();
|
|
599
613
|
try {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
614
|
+
// Agentic loop for tool calling
|
|
615
|
+
while (step < maxSteps) {
|
|
616
|
+
if (composedSignal?.aborted) {
|
|
617
|
+
throw composedSignal.reason instanceof Error
|
|
618
|
+
? composedSignal.reason
|
|
619
|
+
: new Error("Request aborted");
|
|
620
|
+
}
|
|
621
|
+
step++;
|
|
622
|
+
logger.debug(`[GoogleAIStudio] Native SDK step ${step}/${maxSteps}`);
|
|
623
|
+
try {
|
|
624
|
+
const rawStream = await client.models.generateContentStream({
|
|
625
|
+
model: modelName,
|
|
626
|
+
contents: currentContents,
|
|
627
|
+
config,
|
|
628
|
+
...(composedSignal
|
|
629
|
+
? { httpOptions: { signal: composedSignal } }
|
|
630
|
+
: {}),
|
|
631
|
+
});
|
|
632
|
+
// For every step, use incremental collection so text parts
|
|
633
|
+
// are pushed to the channel as they arrive. For intermediate
|
|
634
|
+
// steps (those that produce function calls) we still need the
|
|
635
|
+
// complete rawResponseParts for pushModelResponseToHistory,
|
|
636
|
+
// which collectStreamChunksIncremental provides at stream end.
|
|
637
|
+
const chunkResult = await collectStreamChunksIncremental(rawStream, channel);
|
|
638
|
+
totalInputTokens += chunkResult.inputTokens;
|
|
639
|
+
totalOutputTokens += chunkResult.outputTokens;
|
|
640
|
+
const stepText = extractTextFromParts(chunkResult.rawResponseParts);
|
|
641
|
+
// If no function calls, this was the final step — channel
|
|
642
|
+
// already received all text parts incrementally.
|
|
643
|
+
if (chunkResult.stepFunctionCalls.length === 0) {
|
|
644
|
+
completedWithFinalAnswer = true;
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
lastStepText = stepText;
|
|
648
|
+
// Record tool call events on the span
|
|
649
|
+
for (const fc of chunkResult.stepFunctionCalls) {
|
|
650
|
+
span.addEvent("gen_ai.tool_call", {
|
|
651
|
+
"tool.name": fc.name,
|
|
652
|
+
"tool.step": step,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
|
|
656
|
+
// Add model response with ALL parts (including thoughtSignature) to history
|
|
657
|
+
pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
|
|
658
|
+
const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { abortSignal: composedSignal });
|
|
659
|
+
// Add function responses to history — the @google/genai SDK
|
|
660
|
+
// only accepts "user" and "model" as valid roles in contents.
|
|
661
|
+
// Function/tool responses must use role: "user" (matching the
|
|
662
|
+
// SDK's own automaticFunctionCalling implementation).
|
|
663
|
+
currentContents.push({
|
|
664
|
+
role: "user",
|
|
665
|
+
parts: functionResponses,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
logger.error("[GoogleAIStudio] Native SDK error", error);
|
|
670
|
+
throw this.handleProviderError(error);
|
|
671
|
+
}
|
|
616
672
|
}
|
|
617
|
-
|
|
618
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
673
|
+
// Handle max-steps termination: if the model was still calling
|
|
674
|
+
// tools when we hit the limit, push a synthetic final message.
|
|
675
|
+
const hitStepLimitWithoutFinalAnswer = step >= maxSteps && !completedWithFinalAnswer;
|
|
676
|
+
if (hitStepLimitWithoutFinalAnswer) {
|
|
677
|
+
const fallback = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, "", // finalText is empty — model didn't stop on its own
|
|
678
|
+
lastStepText);
|
|
679
|
+
if (fallback) {
|
|
680
|
+
channel.push(fallback);
|
|
681
|
+
}
|
|
624
682
|
}
|
|
625
|
-
|
|
626
|
-
//
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
//
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
683
|
+
const responseTime = Date.now() - startTime;
|
|
684
|
+
// Update shared metadata so the returned object reflects final values.
|
|
685
|
+
metadata.responseTime = responseTime;
|
|
686
|
+
metadata.totalToolExecutions = allToolCalls.length;
|
|
687
|
+
// Set token usage and finish reason on the span
|
|
688
|
+
span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
|
|
689
|
+
span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
|
|
690
|
+
span.setAttribute(ATTR.GEN_AI_FINISH_REASON, hitStepLimitWithoutFinalAnswer ? "max_steps" : "stop");
|
|
691
|
+
analyticsResolve({
|
|
692
|
+
provider: this.providerName,
|
|
693
|
+
model: modelName,
|
|
694
|
+
tokenUsage: {
|
|
695
|
+
input: totalInputTokens,
|
|
696
|
+
output: totalOutputTokens,
|
|
697
|
+
total: totalInputTokens + totalOutputTokens,
|
|
698
|
+
},
|
|
699
|
+
requestDuration: responseTime,
|
|
700
|
+
timestamp: new Date().toISOString(),
|
|
636
701
|
});
|
|
702
|
+
channel.close();
|
|
637
703
|
}
|
|
638
|
-
catch (
|
|
639
|
-
|
|
640
|
-
|
|
704
|
+
catch (err) {
|
|
705
|
+
channel.error(err);
|
|
706
|
+
analyticsReject(err);
|
|
641
707
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
// Create async iterable for streaming result
|
|
650
|
-
async function* createTextStream() {
|
|
651
|
-
yield { content: finalText };
|
|
652
|
-
}
|
|
708
|
+
finally {
|
|
709
|
+
timeoutController?.cleanup();
|
|
710
|
+
}
|
|
711
|
+
})();
|
|
712
|
+
// Suppress unhandled-rejection warnings on loopPromise — errors are
|
|
713
|
+
// forwarded to the channel and will surface when the caller iterates.
|
|
714
|
+
loopPromise.catch(() => undefined);
|
|
653
715
|
return {
|
|
654
|
-
stream:
|
|
716
|
+
stream: channel.iterable,
|
|
655
717
|
provider: this.providerName,
|
|
656
718
|
model: modelName,
|
|
657
|
-
toolCalls: allToolCalls
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
})),
|
|
661
|
-
analytics: Promise.resolve({
|
|
662
|
-
provider: this.providerName,
|
|
663
|
-
model: modelName,
|
|
664
|
-
tokenUsage: {
|
|
665
|
-
input: totalInputTokens,
|
|
666
|
-
output: totalOutputTokens,
|
|
667
|
-
total: totalInputTokens + totalOutputTokens,
|
|
668
|
-
},
|
|
669
|
-
requestDuration: responseTime,
|
|
670
|
-
timestamp: new Date().toISOString(),
|
|
671
|
-
}),
|
|
672
|
-
metadata: {
|
|
673
|
-
streamId: `native-${Date.now()}`,
|
|
674
|
-
startTime,
|
|
675
|
-
responseTime,
|
|
676
|
-
totalToolExecutions: allToolCalls.length,
|
|
677
|
-
},
|
|
719
|
+
toolCalls: allToolCalls,
|
|
720
|
+
analytics: analyticsPromise,
|
|
721
|
+
metadata,
|
|
678
722
|
};
|
|
679
723
|
}
|
|
680
724
|
finally {
|
|
681
|
-
|
|
725
|
+
// Timeout controller cleanup is managed inside the background loop
|
|
682
726
|
}
|
|
683
727
|
});
|
|
684
728
|
}
|
|
@@ -709,7 +753,9 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
709
753
|
hasTools: !!options.tools && Object.keys(options.tools).length > 0,
|
|
710
754
|
});
|
|
711
755
|
// Build contents from input
|
|
712
|
-
|
|
756
|
+
// Prefer input.text over prompt — processCSVFilesForNativeSDK enriches
|
|
757
|
+
// input.text with inlined CSV data, so using prompt first would discard it.
|
|
758
|
+
const promptText = options.input?.text || options.prompt || "";
|
|
713
759
|
const currentContents = [{ role: "user", parts: [{ text: promptText }] }];
|
|
714
760
|
// Convert tools (merge SDK tools with options.tools)
|
|
715
761
|
let toolsConfig;
|
|
@@ -98,6 +98,49 @@ export declare function collectStreamChunks(stream: AsyncIterable<{
|
|
|
98
98
|
functionCalls?: NativeFunctionCall[];
|
|
99
99
|
[key: string]: unknown;
|
|
100
100
|
}>): Promise<CollectedChunkResult>;
|
|
101
|
+
/**
|
|
102
|
+
* A push-based text channel that decouples producers (agentic loop) from
|
|
103
|
+
* consumers (the caller's async iterable).
|
|
104
|
+
*
|
|
105
|
+
* The producer calls `push(text)` for each chunk and `close()` / `error(err)`
|
|
106
|
+
* when done. The consumer iterates the `iterable` async generator.
|
|
107
|
+
*/
|
|
108
|
+
export type TextChannel = {
|
|
109
|
+
/** Push a text chunk to the consumer. */
|
|
110
|
+
push(text: string): void;
|
|
111
|
+
/** Signal that no more chunks will arrive. */
|
|
112
|
+
close(): void;
|
|
113
|
+
/** Signal that the producer encountered a fatal error. */
|
|
114
|
+
error(err: unknown): void;
|
|
115
|
+
/** Async iterable consumed by the StreamResult. */
|
|
116
|
+
iterable: AsyncIterable<{
|
|
117
|
+
content: string;
|
|
118
|
+
}>;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Create a push-based text channel that bridges a background producer
|
|
122
|
+
* (the agentic tool-calling loop) with an async-iterable consumer.
|
|
123
|
+
*
|
|
124
|
+
* This enables truly incremental streaming: text parts are yielded to the
|
|
125
|
+
* caller as they arrive from the network, rather than being buffered until
|
|
126
|
+
* the model finishes generating.
|
|
127
|
+
*/
|
|
128
|
+
export declare function createTextChannel(): TextChannel;
|
|
129
|
+
/**
|
|
130
|
+
* Iterate a single stream step incrementally, pushing text parts to `channel`
|
|
131
|
+
* as they arrive from the network while simultaneously accumulating the full
|
|
132
|
+
* `CollectedChunkResult` needed for history and token accounting.
|
|
133
|
+
*
|
|
134
|
+
* Used for all steps (both intermediate tool-calling steps and the final
|
|
135
|
+
* text-only step). Text parts are pushed to the channel as they arrive,
|
|
136
|
+
* enabling truly incremental streaming. The complete `rawResponseParts`
|
|
137
|
+
* (including thoughtSignature) are still returned at the end for use by
|
|
138
|
+
* `pushModelResponseToHistory`.
|
|
139
|
+
*/
|
|
140
|
+
export declare function collectStreamChunksIncremental(stream: AsyncIterable<{
|
|
141
|
+
functionCalls?: NativeFunctionCall[];
|
|
142
|
+
[key: string]: unknown;
|
|
143
|
+
}>, channel: TextChannel): Promise<CollectedChunkResult>;
|
|
101
144
|
/**
|
|
102
145
|
* Extract text from raw response parts, filtering out non-text parts
|
|
103
146
|
* (thoughtSignature, functionCall) to avoid SDK warnings.
|