@juspay/neurolink 12.37.1 → 12.37.3
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 +2 -2
- package/dist/browser/neurolink.min.js +134 -134
- package/dist/core/geminiLoopAdapter.js +13 -3
- package/dist/neurolink.js +15 -0
- package/dist/providers/googleVertex/client.js +49 -3
- package/package.json +1 -1
|
@@ -37,6 +37,14 @@ export function createGeminiLoopAdapter(config) {
|
|
|
37
37
|
}
|
|
38
38
|
return original;
|
|
39
39
|
};
|
|
40
|
+
// Per-turn monotonic counter for `toolCallId` generation. `executeStep` is
|
|
41
|
+
// called once per loop step, and a fresh `createGeminiLoopAdapter` closure
|
|
42
|
+
// is made per turn (per generate()/stream() call), so this resets at the
|
|
43
|
+
// right lifetime while staying unique across every step of one turn —
|
|
44
|
+
// unlike the step-local array index it replaces, which restarted at 0 on
|
|
45
|
+
// every step and let two calls to the same tool in different steps collide
|
|
46
|
+
// on one id once both landed in the same flattened per-turn list.
|
|
47
|
+
let nextCallIndex = 0;
|
|
40
48
|
return {
|
|
41
49
|
providerLabel: config.providerLabel,
|
|
42
50
|
maxSteps: config.maxSteps,
|
|
@@ -138,9 +146,11 @@ export function createGeminiLoopAdapter(config) {
|
|
|
138
146
|
// projects the next request from.
|
|
139
147
|
config.noteUsage?.(collected.inputTokens, collected.outputTokens);
|
|
140
148
|
const text = extractTextFromParts(collected.rawResponseParts);
|
|
141
|
-
// Names cross the engine boundary in their ORIGINAL form.
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
// Names cross the engine boundary in their ORIGINAL form. `id` uses the
|
|
150
|
+
// turn-scoped counter above, not a step-local map index, so two calls
|
|
151
|
+
// to the same tool in different steps of one turn get distinct ids.
|
|
152
|
+
const allCalls = collected.stepFunctionCalls.map((call) => ({
|
|
153
|
+
id: `${config.providerLabel}_${nextCallIndex++}_${call.name}`,
|
|
144
154
|
name: originalNameFor(call.name),
|
|
145
155
|
args: call.args,
|
|
146
156
|
}));
|
package/dist/neurolink.js
CHANGED
|
@@ -8774,6 +8774,21 @@ Current user's request: ${currentInput}`;
|
|
|
8774
8774
|
let availableTools = await this.getAllAvailableTools();
|
|
8775
8775
|
// Apply per-call tool filtering for system prompt tool descriptions
|
|
8776
8776
|
availableTools = this.applyToolInfoFiltering(availableTools, options);
|
|
8777
|
+
// M3: a provider whose descriptor declares toolSupport "none" can never
|
|
8778
|
+
// call a tool in ANY form — not natively, not via prompt injection — so
|
|
8779
|
+
// `availableTools` here is dead weight, not a signal. Left non-empty, it
|
|
8780
|
+
// still drives createToolAwareSystemPrompt()'s prompt-injection branch
|
|
8781
|
+
// (that branch triggers on `!provider.supportsTools()`, which is also
|
|
8782
|
+
// true for these providers) and inflates the stream budget check below
|
|
8783
|
+
// via `toolDefinitions`, so an image-generation-only provider like
|
|
8784
|
+
// Recraft can fail a fabricated "exceeds model budget" check before its
|
|
8785
|
+
// own request-kind routing (BaseProvider.stream() -> resolveRequestKind)
|
|
8786
|
+
// ever runs. Clearing it here restores the provider's real outcome —
|
|
8787
|
+
// its own refusal, a genuine vendor error, or success — as what
|
|
8788
|
+
// surfaces, without touching the budget check for any text provider.
|
|
8789
|
+
if (ProviderFactory.getDescriptor(providerName)?.toolSupport === "none") {
|
|
8790
|
+
availableTools = [];
|
|
8791
|
+
}
|
|
8777
8792
|
// Skip tool prompt injection if skipToolPromptInjection is true.
|
|
8778
8793
|
// For providers with native tool calling (instance truth via
|
|
8779
8794
|
// supportsTools()), the listing is skipped and only the short damping
|
|
@@ -2595,7 +2595,11 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2595
2595
|
finalText = engineResult.text;
|
|
2596
2596
|
lastFinishReason = engineResult.rawStopReason ?? lastFinishReason;
|
|
2597
2597
|
for (const call of engineResult.toolCalls) {
|
|
2598
|
-
allToolCalls.push({
|
|
2598
|
+
allToolCalls.push({
|
|
2599
|
+
toolCallId: call.id,
|
|
2600
|
+
toolName: call.name,
|
|
2601
|
+
args: call.args,
|
|
2602
|
+
});
|
|
2599
2603
|
}
|
|
2600
2604
|
for (const execution of engineResult.toolExecutions) {
|
|
2601
2605
|
toolExecutions.push({
|
|
@@ -2747,6 +2751,17 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2747
2751
|
reasoningTokens: totalReasoningTokens,
|
|
2748
2752
|
}),
|
|
2749
2753
|
responseTime,
|
|
2754
|
+
// The complete attempted-call record (name/args/id), independent of
|
|
2755
|
+
// toolsUsed's success-only names — mirrors the native Anthropic direct
|
|
2756
|
+
// / OpenAI-compatible / SageMaker paths (toolCallsFromSummaries) and
|
|
2757
|
+
// the working Gemini-on-Vertex STREAM path just above. Previously
|
|
2758
|
+
// absent here, so a tool the loop ran internally never reached the
|
|
2759
|
+
// caller's result.toolCalls even though toolsUsed/toolExecutions did.
|
|
2760
|
+
toolCalls: externalToolCalls.map((tc) => ({
|
|
2761
|
+
toolCallId: tc.toolCallId,
|
|
2762
|
+
toolName: tc.toolName,
|
|
2763
|
+
args: tc.args,
|
|
2764
|
+
})),
|
|
2750
2765
|
toolsUsed: externalToolCalls.map((tc) => tc.toolName),
|
|
2751
2766
|
toolExecutions: resolveToolExecutionRecords(options, externalToolExecutions),
|
|
2752
2767
|
enhancedWithTools: externalToolCalls.length > 0,
|
|
@@ -4699,7 +4714,11 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4699
4714
|
},
|
|
4700
4715
|
buildToolResultMessages: (conversation, stepResult, toolResults, engineStep) => {
|
|
4701
4716
|
for (const result of toolResults) {
|
|
4702
|
-
allToolCalls.push({
|
|
4717
|
+
allToolCalls.push({
|
|
4718
|
+
toolCallId: result.id,
|
|
4719
|
+
toolName: result.name,
|
|
4720
|
+
args: result.args,
|
|
4721
|
+
});
|
|
4703
4722
|
// Recorded here as well: `toolExecutions` feeds
|
|
4704
4723
|
// resolveToolExecutionRecords and the result's own
|
|
4705
4724
|
// toolExecutions, and pushing only to allToolCalls left it empty
|
|
@@ -5059,7 +5078,23 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5059
5078
|
}
|
|
5060
5079
|
releaseTurnResources();
|
|
5061
5080
|
const responseTime = Date.now() - startTime;
|
|
5062
|
-
|
|
5081
|
+
// Sourced from `engineResult.toolCalls` when the turn produced one, not
|
|
5082
|
+
// the local `allToolCalls` (fed only by the `buildToolResultMessages`
|
|
5083
|
+
// hook below). The engine's own accumulator is pushed to unconditionally
|
|
5084
|
+
// per dispatched batch, before the abortedMidBatch check that skips
|
|
5085
|
+
// `buildToolResultMessages` for that step — so a tool that finished
|
|
5086
|
+
// executing in a batch cut short by an abort is present in
|
|
5087
|
+
// `engineResult.toolCalls` but would otherwise never reach
|
|
5088
|
+
// `allToolCalls`. `allToolCalls` remains the fallback for the rare case
|
|
5089
|
+
// where `resultPromise` itself rejects and `engineResult` never resolves.
|
|
5090
|
+
const engineToolCalls = engineResult
|
|
5091
|
+
? engineResult.toolCalls.map((call) => ({
|
|
5092
|
+
toolCallId: call.id,
|
|
5093
|
+
toolName: call.name,
|
|
5094
|
+
args: call.args,
|
|
5095
|
+
}))
|
|
5096
|
+
: allToolCalls;
|
|
5097
|
+
const externalToolCalls = engineToolCalls.filter((tc) => tc.toolName !== "final_result");
|
|
5063
5098
|
const externalToolExecutions = toolExecutions.filter((te) => te.name !== "final_result");
|
|
5064
5099
|
// Absolute backstop — no code path may surface empty content on a turn
|
|
5065
5100
|
// that ran tools (the consumer-facing "empty response" incident shape).
|
|
@@ -5136,6 +5171,17 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5136
5171
|
...(totalReasoningTokens > 0 && { reasoning: totalReasoningTokens }),
|
|
5137
5172
|
},
|
|
5138
5173
|
responseTime,
|
|
5174
|
+
// The complete attempted-call record (name/args/id), independent of
|
|
5175
|
+
// toolsUsed's success-only names — mirrors the native Anthropic direct
|
|
5176
|
+
// / OpenAI-compatible / SageMaker paths (toolCallsFromSummaries) and
|
|
5177
|
+
// the working Gemini-on-Vertex STREAM path. Previously absent here, so
|
|
5178
|
+
// a tool the loop ran internally never reached the caller's
|
|
5179
|
+
// result.toolCalls even though toolsUsed/toolExecutions did.
|
|
5180
|
+
toolCalls: externalToolCalls.map((tc) => ({
|
|
5181
|
+
toolCallId: tc.toolCallId,
|
|
5182
|
+
toolName: tc.toolName,
|
|
5183
|
+
args: tc.args,
|
|
5184
|
+
})),
|
|
5139
5185
|
toolsUsed: externalToolCalls.map((tc) => tc.toolName),
|
|
5140
5186
|
toolExecutions: resolveToolExecutionRecords(options, externalToolExecutions),
|
|
5141
5187
|
enhancedWithTools: externalToolCalls.length > 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.37.
|
|
3
|
+
"version": "12.37.3",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
|
|
6
6
|
"author": {
|