@juspay/neurolink 9.54.8 → 9.55.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 +8 -0
- package/README.md +17 -15
- package/dist/browser/neurolink.min.js +309 -309
- package/dist/core/redisConversationMemoryManager.js +17 -1
- package/dist/lib/core/redisConversationMemoryManager.js +17 -1
- package/dist/lib/providers/googleNativeGemini3.d.ts +5 -0
- package/dist/lib/providers/googleNativeGemini3.js +18 -0
- package/dist/lib/providers/googleVertex.js +168 -11
- package/dist/lib/rag/chunkers/BaseChunker.js +2 -2
- package/dist/lib/types/conversation.d.ts +4 -0
- package/dist/lib/types/generate.d.ts +2 -0
- package/dist/lib/types/providers.d.ts +15 -0
- package/dist/lib/types/tools.d.ts +4 -0
- package/dist/lib/utils/conversationMemory.js +1 -0
- package/dist/providers/googleNativeGemini3.d.ts +5 -0
- package/dist/providers/googleNativeGemini3.js +18 -0
- package/dist/providers/googleVertex.js +168 -11
- package/dist/rag/chunkers/BaseChunker.js +2 -2
- package/dist/types/conversation.d.ts +4 -0
- package/dist/types/generate.d.ts +2 -0
- package/dist/types/providers.d.ts +15 -0
- package/dist/types/tools.d.ts +4 -0
- package/dist/utils/conversationMemory.js +1 -0
- package/package.json +1 -2
|
@@ -463,6 +463,9 @@ export class RedisConversationMemoryManager {
|
|
|
463
463
|
role: "assistant",
|
|
464
464
|
content: options.aiResponse,
|
|
465
465
|
events: options.events || undefined,
|
|
466
|
+
...(options.thoughtSignature && {
|
|
467
|
+
metadata: { thoughtSignature: options.thoughtSignature },
|
|
468
|
+
}),
|
|
466
469
|
};
|
|
467
470
|
conversation.messages.push(assistantMsg);
|
|
468
471
|
// Store API-reported token counts if available
|
|
@@ -1292,13 +1295,22 @@ User message: "${userMessage}"`;
|
|
|
1292
1295
|
toolCall.arguments ||
|
|
1293
1296
|
toolCall.parameters ||
|
|
1294
1297
|
{}),
|
|
1298
|
+
metadata: {
|
|
1299
|
+
...(toolCall.thoughtSignature
|
|
1300
|
+
? { thoughtSignature: String(toolCall.thoughtSignature) }
|
|
1301
|
+
: {}),
|
|
1302
|
+
...(toolCall.stepIndex !== null && toolCall.stepIndex !== undefined
|
|
1303
|
+
? { stepIndex: Number(toolCall.stepIndex) }
|
|
1304
|
+
: {}),
|
|
1305
|
+
},
|
|
1295
1306
|
};
|
|
1296
1307
|
conversation.messages.push(toolCallMessage);
|
|
1297
1308
|
}
|
|
1298
1309
|
// Create separate messages for tool results using the mapping
|
|
1299
1310
|
for (const toolResult of pendingData.toolResults) {
|
|
1300
1311
|
const toolCallId = String(toolResult.toolCallId || toolResult.id || "unknown");
|
|
1301
|
-
const toolName = toolCallMap.get(toolCallId) ||
|
|
1312
|
+
const toolName = toolCallMap.get(toolCallId) ||
|
|
1313
|
+
String(toolResult.toolName || "unknown");
|
|
1302
1314
|
// Serialize the tool result to string for content field
|
|
1303
1315
|
let serializedResult;
|
|
1304
1316
|
if (typeof toolResult.result === "string") {
|
|
@@ -1345,6 +1357,10 @@ User message: "${userMessage}"`;
|
|
|
1345
1357
|
...(truncated && { toolOutputPreview: preview }),
|
|
1346
1358
|
...(truncated && { originalSize }),
|
|
1347
1359
|
...(artifactId && { artifactId }),
|
|
1360
|
+
...(toolResult.stepIndex !== null &&
|
|
1361
|
+
toolResult.stepIndex !== undefined
|
|
1362
|
+
? { stepIndex: Number(toolResult.stepIndex) }
|
|
1363
|
+
: {}),
|
|
1348
1364
|
};
|
|
1349
1365
|
// Build result — success/error metadata only, NOT the output data
|
|
1350
1366
|
const result = {
|
|
@@ -463,6 +463,9 @@ export class RedisConversationMemoryManager {
|
|
|
463
463
|
role: "assistant",
|
|
464
464
|
content: options.aiResponse,
|
|
465
465
|
events: options.events || undefined,
|
|
466
|
+
...(options.thoughtSignature && {
|
|
467
|
+
metadata: { thoughtSignature: options.thoughtSignature },
|
|
468
|
+
}),
|
|
466
469
|
};
|
|
467
470
|
conversation.messages.push(assistantMsg);
|
|
468
471
|
// Store API-reported token counts if available
|
|
@@ -1292,13 +1295,22 @@ User message: "${userMessage}"`;
|
|
|
1292
1295
|
toolCall.arguments ||
|
|
1293
1296
|
toolCall.parameters ||
|
|
1294
1297
|
{}),
|
|
1298
|
+
metadata: {
|
|
1299
|
+
...(toolCall.thoughtSignature
|
|
1300
|
+
? { thoughtSignature: String(toolCall.thoughtSignature) }
|
|
1301
|
+
: {}),
|
|
1302
|
+
...(toolCall.stepIndex !== null && toolCall.stepIndex !== undefined
|
|
1303
|
+
? { stepIndex: Number(toolCall.stepIndex) }
|
|
1304
|
+
: {}),
|
|
1305
|
+
},
|
|
1295
1306
|
};
|
|
1296
1307
|
conversation.messages.push(toolCallMessage);
|
|
1297
1308
|
}
|
|
1298
1309
|
// Create separate messages for tool results using the mapping
|
|
1299
1310
|
for (const toolResult of pendingData.toolResults) {
|
|
1300
1311
|
const toolCallId = String(toolResult.toolCallId || toolResult.id || "unknown");
|
|
1301
|
-
const toolName = toolCallMap.get(toolCallId) ||
|
|
1312
|
+
const toolName = toolCallMap.get(toolCallId) ||
|
|
1313
|
+
String(toolResult.toolName || "unknown");
|
|
1302
1314
|
// Serialize the tool result to string for content field
|
|
1303
1315
|
let serializedResult;
|
|
1304
1316
|
if (typeof toolResult.result === "string") {
|
|
@@ -1345,6 +1357,10 @@ User message: "${userMessage}"`;
|
|
|
1345
1357
|
...(truncated && { toolOutputPreview: preview }),
|
|
1346
1358
|
...(truncated && { originalSize }),
|
|
1347
1359
|
...(artifactId && { artifactId }),
|
|
1360
|
+
...(toolResult.stepIndex !== null &&
|
|
1361
|
+
toolResult.stepIndex !== undefined
|
|
1362
|
+
? { stepIndex: Number(toolResult.stepIndex) }
|
|
1363
|
+
: {}),
|
|
1348
1364
|
};
|
|
1349
1365
|
// Build result — success/error metadata only, NOT the output data
|
|
1350
1366
|
const result = {
|
|
@@ -92,6 +92,11 @@ export declare function collectStreamChunksIncremental(stream: AsyncIterable<{
|
|
|
92
92
|
functionCalls?: NativeFunctionCall[];
|
|
93
93
|
[key: string]: unknown;
|
|
94
94
|
}>, channel: TextChannel): Promise<CollectedChunkResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Extract the thoughtSignature token from raw response parts.
|
|
97
|
+
* Returns the last thoughtSignature found (each step may produce one).
|
|
98
|
+
*/
|
|
99
|
+
export declare function extractThoughtSignature(rawResponseParts: unknown[]): string | undefined;
|
|
95
100
|
/**
|
|
96
101
|
* Extract text from raw response parts, filtering out non-text parts
|
|
97
102
|
* (thoughtSignature, functionCall) to avoid SDK warnings.
|
|
@@ -437,6 +437,24 @@ export async function collectStreamChunksIncremental(stream, channel) {
|
|
|
437
437
|
}
|
|
438
438
|
return { rawResponseParts, stepFunctionCalls, inputTokens, outputTokens };
|
|
439
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Extract the thoughtSignature token from raw response parts.
|
|
442
|
+
* Returns the last thoughtSignature found (each step may produce one).
|
|
443
|
+
*/
|
|
444
|
+
export function extractThoughtSignature(rawResponseParts) {
|
|
445
|
+
for (let i = rawResponseParts.length - 1; i >= 0; i--) {
|
|
446
|
+
const part = rawResponseParts[i];
|
|
447
|
+
if (part !== null &&
|
|
448
|
+
part !== undefined &&
|
|
449
|
+
typeof part === "object" &&
|
|
450
|
+
"thoughtSignature" in part &&
|
|
451
|
+
typeof part.thoughtSignature ===
|
|
452
|
+
"string") {
|
|
453
|
+
return part.thoughtSignature;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
440
458
|
/**
|
|
441
459
|
* Extract text from raw response parts, filtering out non-text parts
|
|
442
460
|
* (thoughtSignature, functionCall) to avoid SDK warnings.
|
|
@@ -24,7 +24,7 @@ import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../
|
|
|
24
24
|
import { estimateTokens } from "../utils/tokenEstimation.js";
|
|
25
25
|
import { resolveToolChoice } from "../utils/toolChoice.js";
|
|
26
26
|
import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
|
|
27
|
-
import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps as computeMaxStepsShared, createTextChannel, executeNativeToolCalls, extractTextFromParts, handleMaxStepsTermination, normalizeToolsForJsonSchemaProvider, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
|
|
27
|
+
import { buildNativeConfig, buildNativeToolDeclarations, collectStreamChunks, collectStreamChunksIncremental, computeMaxSteps as computeMaxStepsShared, createTextChannel, executeNativeToolCalls, extractTextFromParts, extractThoughtSignature, handleMaxStepsTermination, normalizeToolsForJsonSchemaProvider, pushModelResponseToHistory, sanitizeToolsForGemini, } from "./googleNativeGemini3.js";
|
|
28
28
|
import { getModelId } from "./providerTypeUtils.js";
|
|
29
29
|
// Import proper types for multimodal message handling
|
|
30
30
|
// Keep-alive note: Node.js native fetch and undici (used by createProxyFetch)
|
|
@@ -1310,10 +1310,63 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1310
1310
|
return currentContents;
|
|
1311
1311
|
}
|
|
1312
1312
|
const history = [];
|
|
1313
|
+
// Composite key: "<turnCounter>:<stepIndex>" ensures step 1 of
|
|
1314
|
+
// conversational turn 1 never collides with step 1 of turn 2.
|
|
1315
|
+
const stepMap = new Map();
|
|
1316
|
+
const segments = [];
|
|
1317
|
+
// Incremented each time a regular user/assistant message is encountered,
|
|
1318
|
+
// acting as a logical turn boundary between agentic loop iterations.
|
|
1319
|
+
let turnCounter = 0;
|
|
1320
|
+
const makeKey = (stepIndex) => `${turnCounter}:${stepIndex ?? "undefined"}`;
|
|
1321
|
+
const getOrCreateStep = (stepIndex) => {
|
|
1322
|
+
const key = makeKey(stepIndex);
|
|
1323
|
+
if (stepMap.has(key)) {
|
|
1324
|
+
return stepMap.get(key);
|
|
1325
|
+
}
|
|
1326
|
+
const step = {
|
|
1327
|
+
type: "tool_step",
|
|
1328
|
+
callParts: [],
|
|
1329
|
+
resultParts: [],
|
|
1330
|
+
};
|
|
1331
|
+
stepMap.set(key, step);
|
|
1332
|
+
segments.push(step);
|
|
1333
|
+
return step;
|
|
1334
|
+
};
|
|
1313
1335
|
for (const msg of conversationMessages) {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1336
|
+
if (msg.role === "tool_call") {
|
|
1337
|
+
const step = getOrCreateStep(msg.metadata?.stepIndex);
|
|
1338
|
+
const fcPart = {
|
|
1339
|
+
functionCall: {
|
|
1340
|
+
name: msg.tool || "unknown",
|
|
1341
|
+
args: msg.args || {},
|
|
1342
|
+
},
|
|
1343
|
+
};
|
|
1344
|
+
if (msg.metadata?.thoughtSignature) {
|
|
1345
|
+
fcPart.thoughtSignature = msg.metadata.thoughtSignature;
|
|
1346
|
+
}
|
|
1347
|
+
step.callParts.push(fcPart);
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (msg.role === "tool_result") {
|
|
1351
|
+
const step = getOrCreateStep(msg.metadata?.stepIndex);
|
|
1352
|
+
let responsePayload;
|
|
1353
|
+
try {
|
|
1354
|
+
responsePayload = msg.content
|
|
1355
|
+
? { result: JSON.parse(msg.content) }
|
|
1356
|
+
: { result: "success" };
|
|
1357
|
+
}
|
|
1358
|
+
catch {
|
|
1359
|
+
responsePayload = { result: msg.content || "success" };
|
|
1360
|
+
}
|
|
1361
|
+
step.resultParts.push({
|
|
1362
|
+
functionResponse: {
|
|
1363
|
+
name: msg.tool || "unknown",
|
|
1364
|
+
response: responsePayload,
|
|
1365
|
+
},
|
|
1366
|
+
});
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
// Regular (user / assistant) message — acts as a turn boundary.
|
|
1317
1370
|
const role = msg.role === "assistant" ? "model" : msg.role;
|
|
1318
1371
|
if (role !== "user" && role !== "model") {
|
|
1319
1372
|
continue;
|
|
@@ -1321,9 +1374,29 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1321
1374
|
if (!msg.content || msg.content.trim().length === 0) {
|
|
1322
1375
|
continue;
|
|
1323
1376
|
}
|
|
1324
|
-
|
|
1377
|
+
// Increment turn counter BEFORE pushing the segment so that any
|
|
1378
|
+
// tool_calls that follow this message get a fresh namespace.
|
|
1379
|
+
turnCounter++;
|
|
1380
|
+
const textPart = { text: msg.content };
|
|
1381
|
+
if (msg.metadata?.thoughtSignature) {
|
|
1382
|
+
textPart.thoughtSignature = msg.metadata.thoughtSignature;
|
|
1383
|
+
}
|
|
1384
|
+
segments.push({ type: "regular", role, parts: [textPart] });
|
|
1385
|
+
}
|
|
1386
|
+
// Emit in order: each ToolStep → model turn (calls) + user turn (results)
|
|
1387
|
+
for (const seg of segments) {
|
|
1388
|
+
if (seg.type === "regular") {
|
|
1389
|
+
history.push({ role: seg.role, parts: seg.parts });
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
if (seg.callParts.length > 0) {
|
|
1393
|
+
history.push({ role: "model", parts: seg.callParts });
|
|
1394
|
+
}
|
|
1395
|
+
if (seg.resultParts.length > 0) {
|
|
1396
|
+
history.push({ role: "user", parts: seg.resultParts });
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1325
1399
|
}
|
|
1326
|
-
// Prepend history before current user message
|
|
1327
1400
|
return [...history, ...currentContents];
|
|
1328
1401
|
}
|
|
1329
1402
|
// ── Shared Gemini 3 helpers are now in ./googleNativeGemini3.ts ──
|
|
@@ -1425,6 +1498,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1425
1498
|
timeoutController,
|
|
1426
1499
|
composedSignal,
|
|
1427
1500
|
maxSteps,
|
|
1501
|
+
options,
|
|
1428
1502
|
});
|
|
1429
1503
|
loopPromise.catch(() => undefined);
|
|
1430
1504
|
return {
|
|
@@ -1438,11 +1512,13 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1438
1512
|
}
|
|
1439
1513
|
async runNativeGemini3StreamLoop(params) {
|
|
1440
1514
|
let lastStepText = "";
|
|
1515
|
+
let lastThoughtSignature;
|
|
1441
1516
|
let totalInputTokens = 0;
|
|
1442
1517
|
let totalOutputTokens = 0;
|
|
1443
1518
|
let step = 0;
|
|
1444
1519
|
let completedWithFinalAnswer = false;
|
|
1445
1520
|
const failedTools = new Map();
|
|
1521
|
+
const toolExecutions = [];
|
|
1446
1522
|
try {
|
|
1447
1523
|
while (step < params.maxSteps) {
|
|
1448
1524
|
if (params.composedSignal?.aborted) {
|
|
@@ -1465,6 +1541,10 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1465
1541
|
totalInputTokens += chunkResult.inputTokens;
|
|
1466
1542
|
totalOutputTokens += chunkResult.outputTokens;
|
|
1467
1543
|
const stepText = extractTextFromParts(chunkResult.rawResponseParts);
|
|
1544
|
+
const stepThoughtSig = extractThoughtSignature(chunkResult.rawResponseParts);
|
|
1545
|
+
if (stepThoughtSig) {
|
|
1546
|
+
lastThoughtSignature = stepThoughtSig;
|
|
1547
|
+
}
|
|
1468
1548
|
if (chunkResult.stepFunctionCalls.length === 0) {
|
|
1469
1549
|
completedWithFinalAnswer = true;
|
|
1470
1550
|
break;
|
|
@@ -1478,7 +1558,36 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1478
1558
|
}
|
|
1479
1559
|
logger.debug(`[GoogleVertex] Executing ${chunkResult.stepFunctionCalls.length} function calls`);
|
|
1480
1560
|
pushModelResponseToHistory(params.currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
|
|
1481
|
-
|
|
1561
|
+
// Track array lengths before execution to extract this step's additions
|
|
1562
|
+
const toolCallsBefore = params.allToolCalls.length;
|
|
1563
|
+
const toolExecsBefore = toolExecutions.length;
|
|
1564
|
+
const functionResponses = await executeNativeToolCalls("[GoogleVertex]", chunkResult.stepFunctionCalls, params.executeMap, failedTools, params.allToolCalls, { toolExecutions, abortSignal: params.composedSignal });
|
|
1565
|
+
// Store this step's tool calls/results in conversation memory
|
|
1566
|
+
// (mirrors onStepFinish in the standard GenerationHandler path)
|
|
1567
|
+
const stepToolCalls = params.allToolCalls.slice(toolCallsBefore);
|
|
1568
|
+
const stepToolExecs = toolExecutions.slice(toolExecsBefore);
|
|
1569
|
+
// Tag each tool call with the step's thoughtSignature and stepIndex
|
|
1570
|
+
// so they can be stored on the tool_call ChatMessage metadata in Redis
|
|
1571
|
+
// and reconstructed correctly in prependConversationHistory.
|
|
1572
|
+
const taggedToolCalls = stepToolCalls.map((tc, i) => ({
|
|
1573
|
+
...tc,
|
|
1574
|
+
...(i === 0 && stepThoughtSig
|
|
1575
|
+
? { thoughtSignature: stepThoughtSig }
|
|
1576
|
+
: {}),
|
|
1577
|
+
stepIndex: step,
|
|
1578
|
+
}));
|
|
1579
|
+
const taggedToolResults = stepToolExecs.map((te) => ({
|
|
1580
|
+
toolName: te.name,
|
|
1581
|
+
result: te.output,
|
|
1582
|
+
stepIndex: step,
|
|
1583
|
+
}));
|
|
1584
|
+
if (taggedToolCalls.length > 0 || taggedToolResults.length > 0) {
|
|
1585
|
+
this.handleToolExecutionStorage(taggedToolCalls, taggedToolResults, params.options, new Date()).catch((error) => {
|
|
1586
|
+
logger.warn("[GoogleVertex] Failed to store native stream tool executions", {
|
|
1587
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1588
|
+
});
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1482
1591
|
params.currentContents.push({
|
|
1483
1592
|
role: "user",
|
|
1484
1593
|
parts: functionResponses,
|
|
@@ -1498,6 +1607,10 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1498
1607
|
const responseTime = Date.now() - params.startTime;
|
|
1499
1608
|
params.metadata.responseTime = responseTime;
|
|
1500
1609
|
params.metadata.totalToolExecutions = params.allToolCalls.length;
|
|
1610
|
+
if (lastThoughtSignature) {
|
|
1611
|
+
params.metadata.thoughtSignature =
|
|
1612
|
+
lastThoughtSignature;
|
|
1613
|
+
}
|
|
1501
1614
|
params.span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
|
|
1502
1615
|
params.span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
|
|
1503
1616
|
params.span.setAttribute(ATTR.GEN_AI_FINISH_REASON, step >= params.maxSteps && !completedWithFinalAnswer
|
|
@@ -1603,10 +1716,8 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1603
1716
|
// Build config — systemInstruction stays in config for Gemini 3.x.
|
|
1604
1717
|
// See stream path comment for rationale.
|
|
1605
1718
|
const config = buildNativeConfig(options, toolsConfig);
|
|
1606
|
-
// Note: Schema/JSON output for Gemini 3 native SDK is complex due to $ref resolution issues
|
|
1607
|
-
// For now, schemas are handled via the AI SDK fallback path, not native SDK
|
|
1608
|
-
// TODO: Implement proper $ref resolution for complex nested schemas
|
|
1609
1719
|
const startTime = Date.now();
|
|
1720
|
+
// 5-min default: timeout covers ALL agentic steps, 30s is too short.
|
|
1610
1721
|
const timeout = this.getTimeout(options);
|
|
1611
1722
|
const timeoutController = createTimeoutController(timeout, this.providerName, "generate");
|
|
1612
1723
|
const composedSignal = composeAbortSignals(options.abortSignal, timeoutController?.controller.signal);
|
|
@@ -1615,6 +1726,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1615
1726
|
const currentContents = this.prependConversationHistory([...contents], options.conversationMessages);
|
|
1616
1727
|
let finalText = "";
|
|
1617
1728
|
let lastStepText = "";
|
|
1729
|
+
let lastThoughtSignature;
|
|
1618
1730
|
let totalInputTokens = 0;
|
|
1619
1731
|
let totalOutputTokens = 0;
|
|
1620
1732
|
const allToolCalls = [];
|
|
@@ -1642,11 +1754,24 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1642
1754
|
: {}),
|
|
1643
1755
|
});
|
|
1644
1756
|
const chunkResult = await collectStreamChunks(stream);
|
|
1757
|
+
// Silent timeout: abort signal may terminate the stream without
|
|
1758
|
+
// throwing, yielding zero parts. Surface error explicitly.
|
|
1759
|
+
if (composedSignal?.aborted &&
|
|
1760
|
+
chunkResult.rawResponseParts.length === 0) {
|
|
1761
|
+
throw composedSignal.reason instanceof Error
|
|
1762
|
+
? composedSignal.reason
|
|
1763
|
+
: new Error("Request timed out (no response parts received)");
|
|
1764
|
+
}
|
|
1645
1765
|
totalInputTokens += chunkResult.inputTokens;
|
|
1646
1766
|
totalOutputTokens += chunkResult.outputTokens;
|
|
1767
|
+
const stepThoughtSig = extractThoughtSignature(chunkResult.rawResponseParts);
|
|
1768
|
+
if (stepThoughtSig) {
|
|
1769
|
+
lastThoughtSignature = stepThoughtSig;
|
|
1770
|
+
}
|
|
1647
1771
|
const stepText = extractTextFromParts(chunkResult.rawResponseParts);
|
|
1648
1772
|
if (chunkResult.stepFunctionCalls.length === 0) {
|
|
1649
|
-
|
|
1773
|
+
// Fall back to lastStepText when final step has no text parts
|
|
1774
|
+
finalText = stepText || lastStepText;
|
|
1650
1775
|
break;
|
|
1651
1776
|
}
|
|
1652
1777
|
lastStepText = stepText;
|
|
@@ -1659,7 +1784,36 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1659
1784
|
}
|
|
1660
1785
|
logger.debug(`[GoogleVertex] Generate executing ${chunkResult.stepFunctionCalls.length} function calls`);
|
|
1661
1786
|
pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
|
|
1787
|
+
// Track array lengths before execution to extract this step's additions
|
|
1788
|
+
const toolCallsBefore = allToolCalls.length;
|
|
1789
|
+
const toolExecsBefore = toolExecutions.length;
|
|
1662
1790
|
const functionResponses = await executeNativeToolCalls("[GoogleVertex]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, { toolExecutions, abortSignal: composedSignal });
|
|
1791
|
+
// Store this step's tool calls/results in conversation memory
|
|
1792
|
+
// (mirrors onStepFinish in the standard GenerationHandler path)
|
|
1793
|
+
const stepToolCalls = allToolCalls.slice(toolCallsBefore);
|
|
1794
|
+
const stepToolExecs = toolExecutions.slice(toolExecsBefore);
|
|
1795
|
+
// Tag each tool call with the step's thoughtSignature and stepIndex
|
|
1796
|
+
// so they can be stored on the tool_call ChatMessage metadata in Redis
|
|
1797
|
+
// and reconstructed correctly in prependConversationHistory.
|
|
1798
|
+
const taggedToolCalls = stepToolCalls.map((tc, i) => ({
|
|
1799
|
+
...tc,
|
|
1800
|
+
...(i === 0 && stepThoughtSig
|
|
1801
|
+
? { thoughtSignature: stepThoughtSig }
|
|
1802
|
+
: {}),
|
|
1803
|
+
stepIndex: step,
|
|
1804
|
+
}));
|
|
1805
|
+
const taggedToolResults = stepToolExecs.map((te) => ({
|
|
1806
|
+
toolName: te.name,
|
|
1807
|
+
result: te.output,
|
|
1808
|
+
stepIndex: step,
|
|
1809
|
+
}));
|
|
1810
|
+
if (taggedToolCalls.length > 0 || taggedToolResults.length > 0) {
|
|
1811
|
+
this.handleToolExecutionStorage(taggedToolCalls, taggedToolResults, options, new Date()).catch((error) => {
|
|
1812
|
+
logger.warn("[GoogleVertex] Failed to store native tool executions", {
|
|
1813
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1814
|
+
});
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1663
1817
|
// Function/tool responses must use role: "user" — the
|
|
1664
1818
|
// @google/genai SDK's validateHistory() only accepts "user"
|
|
1665
1819
|
// and "model" roles (matching automaticFunctionCalling).
|
|
@@ -1720,6 +1874,9 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1720
1874
|
toolsUsed: allToolCalls.map((tc) => tc.toolName),
|
|
1721
1875
|
toolExecutions: toolExecutions,
|
|
1722
1876
|
enhancedWithTools: allToolCalls.length > 0,
|
|
1877
|
+
...(lastThoughtSignature && {
|
|
1878
|
+
thoughtSignature: lastThoughtSignature,
|
|
1879
|
+
}),
|
|
1723
1880
|
};
|
|
1724
1881
|
});
|
|
1725
1882
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Abstract base class for all chunker implementations.
|
|
5
5
|
* Provides common functionality and interface contract.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { ChunkingError, RAGErrorCodes } from "../errors/RAGError.js";
|
|
9
9
|
import { withSpan } from "../../telemetry/withSpan.js";
|
|
10
10
|
import { tracers } from "../../telemetry/tracers.js";
|
|
@@ -121,7 +121,7 @@ export class BaseChunker {
|
|
|
121
121
|
custom: this.config.preserveMetadata ? customMetadata : undefined,
|
|
122
122
|
};
|
|
123
123
|
return {
|
|
124
|
-
id:
|
|
124
|
+
id: randomUUID(),
|
|
125
125
|
text,
|
|
126
126
|
metadata,
|
|
127
127
|
};
|
|
@@ -224,6 +224,8 @@ export type ChatMessageMetadata = {
|
|
|
224
224
|
thoughtHash?: string;
|
|
225
225
|
/** Whether extended thinking was used for this message */
|
|
226
226
|
thinkingExpanded?: boolean;
|
|
227
|
+
/** Step index for reconstructing parallel vs sequential tool calls */
|
|
228
|
+
stepIndex?: number;
|
|
227
229
|
/**
|
|
228
230
|
* Head/tail preview of a large tool output.
|
|
229
231
|
* Only present on tool_result messages where the output exceeded truncation limits.
|
|
@@ -371,6 +373,8 @@ export type StoreConversationTurnOptions = {
|
|
|
371
373
|
cacheReadTokens?: number;
|
|
372
374
|
cacheWriteTokens?: number;
|
|
373
375
|
};
|
|
376
|
+
/** Gemini 3 thought signature for reasoning continuity across turns */
|
|
377
|
+
thoughtSignature?: string;
|
|
374
378
|
};
|
|
375
379
|
/**
|
|
376
380
|
* Lightweight session metadata for efficient session listing
|
|
@@ -999,6 +999,8 @@ export type TextGenerationResult = {
|
|
|
999
999
|
imageOutput?: {
|
|
1000
1000
|
base64: string;
|
|
1001
1001
|
} | null;
|
|
1002
|
+
/** Gemini 3 thought signature for reasoning continuity across turns */
|
|
1003
|
+
thoughtSignature?: string;
|
|
1002
1004
|
retries?: {
|
|
1003
1005
|
count: number;
|
|
1004
1006
|
errors: Array<{
|
|
@@ -1577,3 +1577,18 @@ export type VertexNativePart = {
|
|
|
1577
1577
|
data: string;
|
|
1578
1578
|
};
|
|
1579
1579
|
};
|
|
1580
|
+
/**
|
|
1581
|
+
* Internal helpers used by the conversation-history builder in
|
|
1582
|
+
* providers/googleVertex.ts to merge interleaved tool call / result turns.
|
|
1583
|
+
*/
|
|
1584
|
+
export type VertexToolStep = {
|
|
1585
|
+
type: "tool_step";
|
|
1586
|
+
callParts: unknown[];
|
|
1587
|
+
resultParts: unknown[];
|
|
1588
|
+
};
|
|
1589
|
+
export type VertexRegularSegment = {
|
|
1590
|
+
type: "regular";
|
|
1591
|
+
role: string;
|
|
1592
|
+
parts: unknown[];
|
|
1593
|
+
};
|
|
1594
|
+
export type VertexSegment = VertexToolStep | VertexRegularSegment;
|
|
@@ -432,13 +432,17 @@ export type PendingToolExecution = {
|
|
|
432
432
|
toolName?: string;
|
|
433
433
|
args?: Record<string, unknown>;
|
|
434
434
|
timestamp?: Date;
|
|
435
|
+
thoughtSignature?: string;
|
|
436
|
+
stepIndex?: number;
|
|
435
437
|
[key: string]: unknown;
|
|
436
438
|
}>;
|
|
437
439
|
toolResults: Array<{
|
|
438
440
|
toolCallId?: string;
|
|
441
|
+
toolName?: string;
|
|
439
442
|
result?: unknown;
|
|
440
443
|
error?: string;
|
|
441
444
|
timestamp?: Date;
|
|
445
|
+
stepIndex?: number;
|
|
442
446
|
[key: string]: unknown;
|
|
443
447
|
}>;
|
|
444
448
|
timestamp: number;
|
|
@@ -170,6 +170,7 @@ export async function storeConversationTurn(conversationMemory, originalOptions,
|
|
|
170
170
|
cacheWriteTokens: result.usage.cacheCreationTokens,
|
|
171
171
|
}
|
|
172
172
|
: undefined,
|
|
173
|
+
thoughtSignature: result.thoughtSignature,
|
|
173
174
|
});
|
|
174
175
|
logger.debug("[conversationMemoryUtils] Conversation turn stored successfully", {
|
|
175
176
|
requestId,
|
|
@@ -92,6 +92,11 @@ export declare function collectStreamChunksIncremental(stream: AsyncIterable<{
|
|
|
92
92
|
functionCalls?: NativeFunctionCall[];
|
|
93
93
|
[key: string]: unknown;
|
|
94
94
|
}>, channel: TextChannel): Promise<CollectedChunkResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Extract the thoughtSignature token from raw response parts.
|
|
97
|
+
* Returns the last thoughtSignature found (each step may produce one).
|
|
98
|
+
*/
|
|
99
|
+
export declare function extractThoughtSignature(rawResponseParts: unknown[]): string | undefined;
|
|
95
100
|
/**
|
|
96
101
|
* Extract text from raw response parts, filtering out non-text parts
|
|
97
102
|
* (thoughtSignature, functionCall) to avoid SDK warnings.
|
|
@@ -437,6 +437,24 @@ export async function collectStreamChunksIncremental(stream, channel) {
|
|
|
437
437
|
}
|
|
438
438
|
return { rawResponseParts, stepFunctionCalls, inputTokens, outputTokens };
|
|
439
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Extract the thoughtSignature token from raw response parts.
|
|
442
|
+
* Returns the last thoughtSignature found (each step may produce one).
|
|
443
|
+
*/
|
|
444
|
+
export function extractThoughtSignature(rawResponseParts) {
|
|
445
|
+
for (let i = rawResponseParts.length - 1; i >= 0; i--) {
|
|
446
|
+
const part = rawResponseParts[i];
|
|
447
|
+
if (part !== null &&
|
|
448
|
+
part !== undefined &&
|
|
449
|
+
typeof part === "object" &&
|
|
450
|
+
"thoughtSignature" in part &&
|
|
451
|
+
typeof part.thoughtSignature ===
|
|
452
|
+
"string") {
|
|
453
|
+
return part.thoughtSignature;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
440
458
|
/**
|
|
441
459
|
* Extract text from raw response parts, filtering out non-text parts
|
|
442
460
|
* (thoughtSignature, functionCall) to avoid SDK warnings.
|