@juspay/neurolink 9.65.0 → 9.65.1
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/agent/directTools.js +11 -3
- package/dist/browser/neurolink.min.js +352 -352
- package/dist/core/constants.d.ts +1 -0
- package/dist/core/constants.js +3 -0
- package/dist/core/redisConversationMemoryManager.js +0 -6
- package/dist/lib/agent/directTools.js +11 -3
- package/dist/lib/core/constants.d.ts +1 -0
- package/dist/lib/core/constants.js +3 -0
- package/dist/lib/core/redisConversationMemoryManager.js +0 -6
- package/dist/lib/providers/googleAiStudio.js +82 -5
- package/dist/lib/providers/googleNativeGemini3.d.ts +2 -5
- package/dist/lib/providers/googleNativeGemini3.js +103 -8
- package/dist/lib/providers/googleVertex.js +466 -164
- package/dist/lib/types/conversation.d.ts +16 -0
- package/dist/providers/googleAiStudio.js +82 -5
- package/dist/providers/googleNativeGemini3.d.ts +2 -5
- package/dist/providers/googleNativeGemini3.js +103 -8
- package/dist/providers/googleVertex.js +466 -164
- package/dist/types/conversation.d.ts +16 -0
- package/package.json +1 -1
|
@@ -544,3 +544,19 @@ export type ProviderDetails = {
|
|
|
544
544
|
provider: string;
|
|
545
545
|
model: string;
|
|
546
546
|
};
|
|
547
|
+
/**
|
|
548
|
+
* Reduced ChatMessage shape used by callers (typically tests and history
|
|
549
|
+
* reconstructors) that pass synthetic entries into the Gemini history
|
|
550
|
+
* reconstructor without filling every `ChatMessage` field. Mirrors the
|
|
551
|
+
* fields actually read by `prependConversationMessages`.
|
|
552
|
+
*/
|
|
553
|
+
export type MinimalChatMessage = {
|
|
554
|
+
role: ChatMessage["role"];
|
|
555
|
+
content: string;
|
|
556
|
+
tool?: string;
|
|
557
|
+
args?: Record<string, unknown>;
|
|
558
|
+
metadata?: {
|
|
559
|
+
stepIndex?: number;
|
|
560
|
+
thoughtSignature?: string;
|
|
561
|
+
};
|
|
562
|
+
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ErrorCategory, ErrorSeverity, GoogleAIModels, } from "../constants/enums.js";
|
|
2
2
|
import { BaseProvider } from "../core/baseProvider.js";
|
|
3
|
-
import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
|
|
3
|
+
import { IMAGE_GENERATION_MODELS, TOOL_STORAGE_TIMEOUT_MS, } from "../core/constants.js";
|
|
4
4
|
import { processUnifiedFilesArray } from "../utils/messageBuilder.js";
|
|
5
5
|
import { ATTR, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../telemetry/index.js";
|
|
6
6
|
import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
|
|
7
7
|
import { ERROR_CODES, NeuroLinkError } from "../utils/errorHandling.js";
|
|
8
8
|
import { logger } from "../utils/logger.js";
|
|
9
9
|
import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
|
|
10
|
+
import { withTimeout } from "../utils/async/index.js";
|
|
10
11
|
import { estimateTokens } from "../utils/tokenEstimation.js";
|
|
11
|
-
import {
|
|
12
|
+
import { transformToolExecutions } from "../utils/transformationUtils.js";
|
|
13
|
+
import { buildGeminiResponseSchema, buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps, createTextChannel, buildUserPartsWithMultimodal, executeNativeToolCalls, extractTextFromParts, extractThoughtSignature, handleMaxStepsTermination, prependConversationMessages, pushModelResponseToHistory, } from "./googleNativeGemini3.js";
|
|
12
14
|
import { createProxyFetch } from "../proxy/proxyFetch.js";
|
|
13
15
|
// Google AI Live API types now imported from ../types/providerSpecific.js
|
|
14
16
|
// Import proper types for multimodal message handling
|
|
@@ -556,6 +558,10 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
556
558
|
const channel = createTextChannel();
|
|
557
559
|
// Shared mutable state updated by the background agentic loop.
|
|
558
560
|
const allToolCalls = [];
|
|
561
|
+
// Mirror the Vertex Gemini stream path: track tool executions so
|
|
562
|
+
// the storage hook can persist real outputs and StreamResult can
|
|
563
|
+
// surface toolsUsed/toolExecutions for tool-bearing turns.
|
|
564
|
+
const toolExecutions = [];
|
|
559
565
|
// analyticsResolvers lets the background loop settle the analytics
|
|
560
566
|
// promise once token counts are known (after the loop completes).
|
|
561
567
|
let analyticsResolve;
|
|
@@ -626,7 +632,40 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
626
632
|
logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
|
|
627
633
|
// Add model response with ALL parts (including thoughtSignature) to history
|
|
628
634
|
pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
|
|
629
|
-
const
|
|
635
|
+
const toolCallsBefore = allToolCalls.length;
|
|
636
|
+
const toolExecsBefore = toolExecutions.length;
|
|
637
|
+
const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, {
|
|
638
|
+
abortSignal: composedSignal,
|
|
639
|
+
originalNameMap,
|
|
640
|
+
toolExecutions,
|
|
641
|
+
});
|
|
642
|
+
// Persist this step's tool calls/results into conversation
|
|
643
|
+
// memory. Without this, tool_call / tool_result rows never
|
|
644
|
+
// reach Redis and the chat-history UI loses every tool
|
|
645
|
+
// invocation.
|
|
646
|
+
const stepToolCalls = allToolCalls.slice(toolCallsBefore);
|
|
647
|
+
const stepToolExecs = toolExecutions.slice(toolExecsBefore);
|
|
648
|
+
if (stepToolCalls.length > 0 || stepToolExecs.length > 0) {
|
|
649
|
+
const stepThoughtSig = extractThoughtSignature(chunkResult.rawResponseParts);
|
|
650
|
+
withTimeout(this.handleToolExecutionStorage(stepToolCalls.map((tc, i) => ({
|
|
651
|
+
toolName: tc.toolName,
|
|
652
|
+
args: tc.args,
|
|
653
|
+
...(i === 0 && stepThoughtSig
|
|
654
|
+
? { thoughtSignature: stepThoughtSig }
|
|
655
|
+
: {}),
|
|
656
|
+
stepIndex: step,
|
|
657
|
+
})), stepToolExecs.map((te) => ({
|
|
658
|
+
toolName: te.name,
|
|
659
|
+
output: te.output,
|
|
660
|
+
stepIndex: step,
|
|
661
|
+
})), options, new Date()), TOOL_STORAGE_TIMEOUT_MS, "tool storage write timed out").catch((error) => {
|
|
662
|
+
logger.warn("[GoogleAIStudio] Failed to store native tool executions", {
|
|
663
|
+
error: error instanceof Error
|
|
664
|
+
? error.message
|
|
665
|
+
: String(error),
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
}
|
|
630
669
|
// Add function responses to history — the @google/genai SDK
|
|
631
670
|
// only accepts "user" and "model" as valid roles in contents.
|
|
632
671
|
// Function/tool responses must use role: "user" (matching the
|
|
@@ -683,7 +722,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
683
722
|
// Suppress unhandled-rejection warnings on loopPromise — errors are
|
|
684
723
|
// forwarded to the channel and will surface when the caller iterates.
|
|
685
724
|
loopPromise.catch(() => undefined);
|
|
686
|
-
|
|
725
|
+
const result = {
|
|
687
726
|
stream: channel.iterable,
|
|
688
727
|
provider: this.providerName,
|
|
689
728
|
model: modelName,
|
|
@@ -691,6 +730,20 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
691
730
|
analytics: analyticsPromise,
|
|
692
731
|
metadata,
|
|
693
732
|
};
|
|
733
|
+
// Surface tools-used + executions via getters so they resolve at
|
|
734
|
+
// access time, after the background loop has populated the live
|
|
735
|
+
// arrays. Same lazy pattern used for `structuredOutput` elsewhere.
|
|
736
|
+
Object.defineProperty(result, "toolsUsed", {
|
|
737
|
+
enumerable: true,
|
|
738
|
+
configurable: true,
|
|
739
|
+
get: () => allToolCalls.map((tc) => tc.toolName),
|
|
740
|
+
});
|
|
741
|
+
Object.defineProperty(result, "toolExecutions", {
|
|
742
|
+
enumerable: true,
|
|
743
|
+
configurable: true,
|
|
744
|
+
get: () => transformToolExecutions(toolExecutions),
|
|
745
|
+
});
|
|
746
|
+
return result;
|
|
694
747
|
}
|
|
695
748
|
finally {
|
|
696
749
|
// Timeout controller cleanup is managed inside the background loop
|
|
@@ -821,11 +874,35 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
821
874
|
// Add model response with ALL parts (including thoughtSignature) to history
|
|
822
875
|
// This is critical for Gemini 3 - it requires thought signatures in subsequent turns
|
|
823
876
|
pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
|
|
877
|
+
const toolCallsBefore = allToolCalls.length;
|
|
878
|
+
const toolExecsBefore = toolExecutions.length;
|
|
824
879
|
const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, {
|
|
825
880
|
toolExecutions,
|
|
826
881
|
abortSignal: composedSignal,
|
|
827
882
|
originalNameMap,
|
|
828
883
|
});
|
|
884
|
+
// Persist this step's tool calls/results into conversation memory.
|
|
885
|
+
const stepToolCalls = allToolCalls.slice(toolCallsBefore);
|
|
886
|
+
const stepToolExecs = toolExecutions.slice(toolExecsBefore);
|
|
887
|
+
if (stepToolCalls.length > 0 || stepToolExecs.length > 0) {
|
|
888
|
+
const stepThoughtSig = extractThoughtSignature(chunkResult.rawResponseParts);
|
|
889
|
+
withTimeout(this.handleToolExecutionStorage(stepToolCalls.map((tc, i) => ({
|
|
890
|
+
toolName: tc.toolName,
|
|
891
|
+
args: tc.args,
|
|
892
|
+
...(i === 0 && stepThoughtSig
|
|
893
|
+
? { thoughtSignature: stepThoughtSig }
|
|
894
|
+
: {}),
|
|
895
|
+
stepIndex: step,
|
|
896
|
+
})), stepToolExecs.map((te) => ({
|
|
897
|
+
toolName: te.name,
|
|
898
|
+
output: te.output,
|
|
899
|
+
stepIndex: step,
|
|
900
|
+
})), options, new Date()), TOOL_STORAGE_TIMEOUT_MS, "tool storage write timed out").catch((error) => {
|
|
901
|
+
logger.warn("[GoogleAIStudio] Failed to store native generate tool executions", {
|
|
902
|
+
error: error instanceof Error ? error.message : String(error),
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
}
|
|
829
906
|
// Add function responses to history — the @google/genai SDK
|
|
830
907
|
// only accepts "user" and "model" as valid roles in contents.
|
|
831
908
|
// Function/tool responses must use role: "user" (matching the
|
|
@@ -861,7 +938,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
861
938
|
},
|
|
862
939
|
responseTime,
|
|
863
940
|
toolsUsed: allToolCalls.map((tc) => tc.toolName),
|
|
864
|
-
toolExecutions: toolExecutions,
|
|
941
|
+
toolExecutions: transformToolExecutions(toolExecutions),
|
|
865
942
|
enhancedWithTools: allToolCalls.length > 0,
|
|
866
943
|
};
|
|
867
944
|
return this.enhanceResult(baseResult, options, startTime);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* providers so they can share a single implementation.
|
|
10
10
|
*/
|
|
11
11
|
import { type Tool } from "ai";
|
|
12
|
-
import type { ThinkingConfig, CollectedChunkResult, NativeFunctionCall, NativeFunctionResponse, NativeToolDeclarationsResult, NativeToolsConfig, TextChannel, VertexNativePart, GeminiMultimodalInput } from "../types/index.js";
|
|
12
|
+
import type { ThinkingConfig, ChatMessage, CollectedChunkResult, MinimalChatMessage, NativeFunctionCall, NativeFunctionResponse, NativeToolDeclarationsResult, NativeToolsConfig, TextChannel, VertexNativePart, GeminiMultimodalInput } from "../types/index.js";
|
|
13
13
|
export declare function sanitizeForGoogleFunctionName(name: string): string;
|
|
14
14
|
/**
|
|
15
15
|
* Resolve a sanitized Gemini tool name to one that is both unique within
|
|
@@ -218,10 +218,7 @@ export declare function buildGeminiResponseSchema(schema: unknown): Record<strin
|
|
|
218
218
|
export declare function prependConversationMessages(contents: Array<{
|
|
219
219
|
role: string;
|
|
220
220
|
parts: unknown[];
|
|
221
|
-
}>, conversationMessages?: Array<
|
|
222
|
-
role: string;
|
|
223
|
-
content: string;
|
|
224
|
-
}>): void;
|
|
221
|
+
}>, conversationMessages?: Array<ChatMessage | MinimalChatMessage>): void;
|
|
225
222
|
/**
|
|
226
223
|
* Build the `parts` array for the current user turn of a Gemini native
|
|
227
224
|
* `generateContent` request, including inline image + PDF blobs.
|
|
@@ -646,6 +646,10 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
646
646
|
// original name. Falls back to the safe name if the map is missing or
|
|
647
647
|
// doesn't contain the call (e.g. tool added mid-conversation).
|
|
648
648
|
const externalName = (safeName) => options?.originalNameMap?.get(safeName) ?? safeName;
|
|
649
|
+
// Note: tool:start / tool:end events are emitted by ToolsManager's
|
|
650
|
+
// `execute` wrapper (see src/lib/core/modules/ToolsManager.ts:355 and :790)
|
|
651
|
+
// around every tool's execute function. The native paths invoke that same
|
|
652
|
+
// wrapped execute via the executeMap, so emitting here would duplicate.
|
|
649
653
|
for (const call of stepFunctionCalls) {
|
|
650
654
|
const exposedName = externalName(call.name);
|
|
651
655
|
allToolCalls.push({ toolName: exposedName, args: call.args });
|
|
@@ -808,22 +812,113 @@ export function buildGeminiResponseSchema(schema) {
|
|
|
808
812
|
* - The current user input should be appended AFTER calling this helper
|
|
809
813
|
* so the prior turns appear first in chronological order.
|
|
810
814
|
*/
|
|
811
|
-
export function prependConversationMessages(contents,
|
|
815
|
+
export function prependConversationMessages(contents,
|
|
816
|
+
// Accept either the full ChatMessage shape (when callers pass real Redis-
|
|
817
|
+
// backed history) or the reduced MinimalChatMessage shape (tests / synthetic
|
|
818
|
+
// callers). Only role, content, tool, args, and metadata.* are read here.
|
|
819
|
+
conversationMessages) {
|
|
812
820
|
if (!conversationMessages || conversationMessages.length === 0) {
|
|
813
821
|
return;
|
|
814
822
|
}
|
|
823
|
+
// Walk prior turns building ordered segments. Tool_call / tool_result rows
|
|
824
|
+
// get grouped by (turnCounter, stepIndex) so parallel calls within a step
|
|
825
|
+
// stay together and don't bleed across turn boundaries. Regular user/
|
|
826
|
+
// assistant messages act as those boundaries.
|
|
827
|
+
//
|
|
828
|
+
// Without this reconstruction, a text-only mapper would strip tool rows
|
|
829
|
+
// from history — leaving the model unaware of any tools it called in
|
|
830
|
+
// prior turns. The grouped emit (model with functionCall parts → user
|
|
831
|
+
// with functionResponse parts) is what @google/genai's own
|
|
832
|
+
// automaticFunctionCalling produces, so the SDK validates it as a
|
|
833
|
+
// well-formed multi-turn conversation.
|
|
834
|
+
const stepMap = new Map();
|
|
835
|
+
const segments = [];
|
|
836
|
+
let turnCounter = 0;
|
|
837
|
+
const makeKey = (stepIndex) => `${turnCounter}:${stepIndex ?? "undefined"}`;
|
|
838
|
+
const getOrCreateStep = (stepIndex) => {
|
|
839
|
+
const key = makeKey(stepIndex);
|
|
840
|
+
const existing = stepMap.get(key);
|
|
841
|
+
if (existing) {
|
|
842
|
+
return existing;
|
|
843
|
+
}
|
|
844
|
+
const step = {
|
|
845
|
+
type: "tool_step",
|
|
846
|
+
callParts: [],
|
|
847
|
+
resultParts: [],
|
|
848
|
+
};
|
|
849
|
+
stepMap.set(key, step);
|
|
850
|
+
segments.push(step);
|
|
851
|
+
return step;
|
|
852
|
+
};
|
|
815
853
|
for (const msg of conversationMessages) {
|
|
816
|
-
if (msg.role
|
|
854
|
+
if (msg.role === "tool_call") {
|
|
855
|
+
const step = getOrCreateStep(msg.metadata?.stepIndex);
|
|
856
|
+
const fcPart = {
|
|
857
|
+
functionCall: {
|
|
858
|
+
name: msg.tool || "unknown",
|
|
859
|
+
args: msg.args || {},
|
|
860
|
+
},
|
|
861
|
+
};
|
|
862
|
+
if (msg.metadata?.thoughtSignature) {
|
|
863
|
+
fcPart.thoughtSignature = msg.metadata.thoughtSignature;
|
|
864
|
+
}
|
|
865
|
+
step.callParts.push(fcPart);
|
|
817
866
|
continue;
|
|
818
867
|
}
|
|
819
|
-
|
|
820
|
-
|
|
868
|
+
if (msg.role === "tool_result") {
|
|
869
|
+
const step = getOrCreateStep(msg.metadata?.stepIndex);
|
|
870
|
+
let responsePayload;
|
|
871
|
+
try {
|
|
872
|
+
responsePayload =
|
|
873
|
+
msg.content !== undefined && msg.content !== null
|
|
874
|
+
? { result: JSON.parse(msg.content) }
|
|
875
|
+
: { result: "success" };
|
|
876
|
+
}
|
|
877
|
+
catch {
|
|
878
|
+
responsePayload = { result: msg.content ?? "success" };
|
|
879
|
+
}
|
|
880
|
+
step.resultParts.push({
|
|
881
|
+
functionResponse: {
|
|
882
|
+
name: msg.tool || "unknown",
|
|
883
|
+
response: responsePayload,
|
|
884
|
+
},
|
|
885
|
+
});
|
|
821
886
|
continue;
|
|
822
887
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
888
|
+
// Regular (user / assistant) message — acts as a turn boundary.
|
|
889
|
+
const role = msg.role === "assistant" ? "model" : msg.role;
|
|
890
|
+
if (role !== "user" && role !== "model") {
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (!msg.content || msg.content.trim().length === 0) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
// Increment turn counter BEFORE pushing the segment so any tool_calls
|
|
897
|
+
// that follow this message get a fresh (turnCounter, stepIndex) namespace.
|
|
898
|
+
turnCounter++;
|
|
899
|
+
const textPart = { text: msg.content };
|
|
900
|
+
if (msg.metadata?.thoughtSignature) {
|
|
901
|
+
textPart.thoughtSignature = msg.metadata.thoughtSignature;
|
|
902
|
+
}
|
|
903
|
+
segments.push({ type: "regular", role, parts: [textPart] });
|
|
904
|
+
}
|
|
905
|
+
// Emit in order: each ToolStep → model turn (calls) + user turn (results)
|
|
906
|
+
// — same ordering @google/genai's automaticFunctionCalling produces.
|
|
907
|
+
for (const seg of segments) {
|
|
908
|
+
if (seg.type === "regular") {
|
|
909
|
+
contents.push({ role: seg.role, parts: seg.parts });
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
if (seg.callParts.length === 0) {
|
|
913
|
+
if (seg.resultParts.length > 0) {
|
|
914
|
+
logger.debug("[GoogleNativeGemini3] Dropping orphan tool_result segment with no matching tool_call rows", { resultCount: seg.resultParts.length });
|
|
915
|
+
}
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
contents.push({ role: "model", parts: seg.callParts });
|
|
919
|
+
if (seg.resultParts.length > 0) {
|
|
920
|
+
contents.push({ role: "user", parts: seg.resultParts });
|
|
921
|
+
}
|
|
827
922
|
}
|
|
828
923
|
}
|
|
829
924
|
/**
|