@juspay/neurolink 12.12.7 → 12.12.8
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 +3 -2
- package/dist/browser/neurolink.min.js +371 -371
- package/dist/core/toolExecutionRecorder.d.ts +16 -1
- package/dist/core/toolExecutionRecorder.js +21 -0
- package/dist/middleware/builtin/guardrails.js +67 -17
- package/dist/neurolink.js +6 -0
- package/dist/providers/amazonSagemaker.js +2 -1
- package/dist/providers/anthropic/client.js +2 -1
- package/dist/providers/openaiChatCompletionsBase.js +2 -1
- package/dist/types/generate.d.ts +6 -0
- package/dist/types/middleware.d.ts +1 -1
- package/package.json +2 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* params, timing, and error status. Memory is bounded: serialized results are
|
|
10
10
|
* capped at `maxResultChars` and the record list at `maxRecords`.
|
|
11
11
|
*/
|
|
12
|
-
import type { Tool, ToolExecutionCaptureOptions, ToolExecutionRecord } from "../types/index.js";
|
|
12
|
+
import type { Tool, ToolExecutionCaptureOptions, ToolExecutionRecord, StandardRecord, ToolExecutionSummaryInternal } from "../types/index.js";
|
|
13
13
|
/** Default cap on serialized result characters kept per record (~8KB). */
|
|
14
14
|
export declare const DEFAULT_TOOL_RESULT_CAPTURE_CHARS = 8192;
|
|
15
15
|
/** Default cap on records kept per turn. */
|
|
@@ -73,4 +73,19 @@ export declare function toToolExecutionRecords(legacyExecutions: unknown[] | und
|
|
|
73
73
|
* carried a recorder that saw executions, else a conversion of the loop's
|
|
74
74
|
* legacy accumulator entries.
|
|
75
75
|
*/
|
|
76
|
+
/**
|
|
77
|
+
* The public `toolCalls` view of a native turn's execution summaries.
|
|
78
|
+
*
|
|
79
|
+
* The native generate loops record every executed call in
|
|
80
|
+
* `ToolExecutionSummaryInternal[]` and surface it as `toolExecutions`, but
|
|
81
|
+
* never mapped it onto `EnhancedGenerateResult.toolCalls` — the field the type
|
|
82
|
+
* has always declared and the ai-package formatter used to fill. A caller
|
|
83
|
+
* reading `result.toolCalls` after a tool ran saw nothing. This is the one
|
|
84
|
+
* place that mapping lives, so the three native paths cannot drift apart.
|
|
85
|
+
*/
|
|
86
|
+
export declare function toolCallsFromSummaries(summaries: ReadonlyArray<ToolExecutionSummaryInternal>): Array<{
|
|
87
|
+
toolCallId: string;
|
|
88
|
+
toolName: string;
|
|
89
|
+
args: StandardRecord;
|
|
90
|
+
}>;
|
|
76
91
|
export declare function resolveToolExecutionRecords(options: unknown, legacyExecutions?: unknown[]): ToolExecutionRecord[];
|
|
@@ -247,6 +247,27 @@ export function toToolExecutionRecords(legacyExecutions, capture) {
|
|
|
247
247
|
* carried a recorder that saw executions, else a conversion of the loop's
|
|
248
248
|
* legacy accumulator entries.
|
|
249
249
|
*/
|
|
250
|
+
/**
|
|
251
|
+
* The public `toolCalls` view of a native turn's execution summaries.
|
|
252
|
+
*
|
|
253
|
+
* The native generate loops record every executed call in
|
|
254
|
+
* `ToolExecutionSummaryInternal[]` and surface it as `toolExecutions`, but
|
|
255
|
+
* never mapped it onto `EnhancedGenerateResult.toolCalls` — the field the type
|
|
256
|
+
* has always declared and the ai-package formatter used to fill. A caller
|
|
257
|
+
* reading `result.toolCalls` after a tool ran saw nothing. This is the one
|
|
258
|
+
* place that mapping lives, so the three native paths cannot drift apart.
|
|
259
|
+
*/
|
|
260
|
+
export function toolCallsFromSummaries(summaries) {
|
|
261
|
+
return summaries.map((summary) => ({
|
|
262
|
+
toolCallId: summary.toolCallId,
|
|
263
|
+
toolName: summary.toolName,
|
|
264
|
+
args: summary.input !== null &&
|
|
265
|
+
typeof summary.input === "object" &&
|
|
266
|
+
!Array.isArray(summary.input)
|
|
267
|
+
? summary.input
|
|
268
|
+
: {},
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
250
271
|
export function resolveToolExecutionRecords(options, legacyExecutions) {
|
|
251
272
|
const recorder = ToolExecutionRecorder.from(options);
|
|
252
273
|
if (recorder?.hasRecords()) {
|
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
import { createBlockedResponse, createBlockedStream, applyContentFiltering, handlePrecallGuardrails, } from "../utils/guardrailsUtils.js";
|
|
2
2
|
import { logger } from "../../utils/logger.js";
|
|
3
3
|
import { generateOnceNative } from "../../utils/nativeSingleShot.js";
|
|
4
|
+
/**
|
|
5
|
+
* Filter each contiguous run of text parts as one string.
|
|
6
|
+
*
|
|
7
|
+
* A prohibited term that straddles two adjacent text parts is invisible to a
|
|
8
|
+
* per-part filter, and the parts are concatenated downstream (`lifecycle.ts`
|
|
9
|
+
* joins adjacent text), so the term reached the caller whole. Anthropic's
|
|
10
|
+
* native path emits one text part per content block, so adjacent parts are a
|
|
11
|
+
* real shape, not a theoretical one. Non-text parts keep their position; a run
|
|
12
|
+
* is rebuilt as a single text part only when the filter changed it.
|
|
13
|
+
*/
|
|
14
|
+
const filterTextRuns = (content, badWords, context) => {
|
|
15
|
+
const out = [];
|
|
16
|
+
let run = [];
|
|
17
|
+
const flushRun = () => {
|
|
18
|
+
if (run.length === 0) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const merged = run.map((part) => part.text).join("");
|
|
22
|
+
const filtered = applyContentFiltering(merged, badWords, context);
|
|
23
|
+
if (filtered.hasChanges) {
|
|
24
|
+
out.push({ ...run[0], text: filtered.filteredText });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
out.push(...run);
|
|
28
|
+
}
|
|
29
|
+
run = [];
|
|
30
|
+
};
|
|
31
|
+
for (const part of content) {
|
|
32
|
+
if (part.type === "text") {
|
|
33
|
+
run.push(part);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
flushRun();
|
|
37
|
+
out.push(part);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
flushRun();
|
|
41
|
+
return out;
|
|
42
|
+
};
|
|
4
43
|
/**
|
|
5
44
|
* Create Guardrails AI middleware for content filtering and policy enforcement
|
|
6
45
|
* @param config Configuration for the guardrails middleware
|
|
@@ -61,12 +100,7 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
61
100
|
let result = await doGenerate();
|
|
62
101
|
result = {
|
|
63
102
|
...result,
|
|
64
|
-
content: result.content
|
|
65
|
-
? {
|
|
66
|
-
...part,
|
|
67
|
-
text: applyContentFiltering(part.text, config.badWords, "generate").filteredText,
|
|
68
|
-
}
|
|
69
|
-
: part),
|
|
103
|
+
content: filterTextRuns(result.content, config.badWords, "generate"),
|
|
70
104
|
};
|
|
71
105
|
if (config.modelFilter?.enabled && config.modelFilter.filterModel) {
|
|
72
106
|
logger.debug(`[GuardrailsMiddleware] Invoking model-based filter.`);
|
|
@@ -113,22 +147,38 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
113
147
|
}
|
|
114
148
|
const { stream, ...rest } = await doStream();
|
|
115
149
|
let hasYieldedChunks = false;
|
|
150
|
+
// With bad-word filtering on, a text run is buffered and filtered as one
|
|
151
|
+
// string: a term split across deltas is invisible per delta and every
|
|
152
|
+
// consumer reassembles it. The run is released when a non-text part
|
|
153
|
+
// arrives or the stream ends, so the guardrail trades incremental
|
|
154
|
+
// delivery of that run for not being bypassable by chunking. With
|
|
155
|
+
// filtering off, deltas pass through untouched and unbuffered.
|
|
156
|
+
const bufferTextRuns = config.badWords?.enabled === true;
|
|
157
|
+
let pendingText;
|
|
158
|
+
const releaseText = (controller) => {
|
|
159
|
+
if (!pendingText) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const filtered = applyContentFiltering(pendingText.delta, config.badWords, "stream");
|
|
163
|
+
controller.enqueue(filtered.hasChanges
|
|
164
|
+
? { ...pendingText, delta: filtered.filteredText }
|
|
165
|
+
: pendingText);
|
|
166
|
+
pendingText = undefined;
|
|
167
|
+
};
|
|
116
168
|
const transformStream = new TransformStream({
|
|
117
169
|
transform(chunk, controller) {
|
|
118
170
|
hasYieldedChunks = true;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
...filteredChunk,
|
|
125
|
-
delta: filterResult.filteredText,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
171
|
+
if (chunk.type === "text-delta" && bufferTextRuns) {
|
|
172
|
+
pendingText = pendingText
|
|
173
|
+
? { ...pendingText, delta: pendingText.delta + chunk.delta }
|
|
174
|
+
: chunk;
|
|
175
|
+
return;
|
|
128
176
|
}
|
|
129
|
-
controller
|
|
177
|
+
releaseText(controller);
|
|
178
|
+
controller.enqueue(chunk);
|
|
130
179
|
},
|
|
131
|
-
flush() {
|
|
180
|
+
flush(controller) {
|
|
181
|
+
releaseText(controller);
|
|
132
182
|
if (!hasYieldedChunks) {
|
|
133
183
|
logger.warn(`[GuardrailsMiddleware] Stream ended without yielding any chunks`);
|
|
134
184
|
}
|
package/dist/neurolink.js
CHANGED
|
@@ -4359,6 +4359,7 @@ Current user's request: ${currentInput}`;
|
|
|
4359
4359
|
: undefined,
|
|
4360
4360
|
responseTime: textResult.responseTime,
|
|
4361
4361
|
toolsUsed: textResult.toolsUsed,
|
|
4362
|
+
toolCalls: textResult.toolCalls ?? [],
|
|
4362
4363
|
toolExecutions: toToolExecutionRecords(textResult.toolExecutions),
|
|
4363
4364
|
enhancedWithTools: textResult.enhancedWithTools,
|
|
4364
4365
|
availableTools: transformAvailableTools(textResult.availableTools),
|
|
@@ -5877,6 +5878,7 @@ Current user's request: ${currentInput}`;
|
|
|
5877
5878
|
rawFinishReason: result.rawFinishReason,
|
|
5878
5879
|
stepsUsed: result.stepsUsed,
|
|
5879
5880
|
toolsUsed: result.toolsUsed || [],
|
|
5881
|
+
toolCalls: result.toolCalls ?? [],
|
|
5880
5882
|
toolExecutions: transformedToolExecutions,
|
|
5881
5883
|
enhancedWithTools: Boolean(hasToolExecutions),
|
|
5882
5884
|
availableTools: transformToolsForMCP(transformToolsToExpectedFormat(availableTools)),
|
|
@@ -6028,6 +6030,7 @@ Current user's request: ${currentInput}`;
|
|
|
6028
6030
|
rawFinishReason: poolResult.rawFinishReason,
|
|
6029
6031
|
stepsUsed: poolResult.stepsUsed,
|
|
6030
6032
|
toolsUsed: poolResult.toolsUsed || [],
|
|
6033
|
+
toolCalls: poolResult.toolCalls ?? [],
|
|
6031
6034
|
// Lossless pass-through: keep the full ToolExecutionRecord
|
|
6032
6035
|
// fields (params/resultText/isError/timing) alongside the
|
|
6033
6036
|
// legacy {toolName,executionTime,success} shape this internal
|
|
@@ -6398,6 +6401,9 @@ Current user's request: ${currentInput}`;
|
|
|
6398
6401
|
rawFinishReason: result.rawFinishReason,
|
|
6399
6402
|
stepsUsed: result.stepsUsed,
|
|
6400
6403
|
toolsUsed: result.toolsUsed || [],
|
|
6404
|
+
// The providers record executed calls; without this line the public
|
|
6405
|
+
// result never carried them, whatever the provider returned.
|
|
6406
|
+
toolCalls: result.toolCalls ?? [],
|
|
6401
6407
|
// Lossless pass-through: keep the full ToolExecutionRecord fields
|
|
6402
6408
|
// alongside the legacy {toolName,executionTime,success} shape this
|
|
6403
6409
|
// internal result declares, so the final GenerateResult mapping
|
|
@@ -2,7 +2,7 @@ import { BaseProvider } from "../core/baseProvider.js";
|
|
|
2
2
|
import { createStreamChannel } from "../core/streamChannel.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { resolveRequestKind } from "../core/resolveRequestKind.js";
|
|
5
|
-
import { resolveToolExecutionRecords } from "../core/toolExecutionRecorder.js";
|
|
5
|
+
import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../core/toolExecutionRecorder.js";
|
|
6
6
|
import { transformToolExecutions } from "../utils/transformationUtils.js";
|
|
7
7
|
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
8
8
|
import { withProviderRetry } from "../utils/providerRetry.js";
|
|
@@ -198,6 +198,7 @@ export class AmazonSageMakerProvider extends BaseProvider {
|
|
|
198
198
|
},
|
|
199
199
|
responseTime: Date.now() - startTime,
|
|
200
200
|
toolsUsed: loop.toolsUsed,
|
|
201
|
+
toolCalls: toolCallsFromSummaries(toolExecutionSummaries),
|
|
201
202
|
toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
|
|
202
203
|
enhancedWithTools: loop.toolsUsed.length > 0,
|
|
203
204
|
};
|
|
@@ -28,7 +28,7 @@ import { runAgenticLoop } from "../../core/loopEngine.js";
|
|
|
28
28
|
import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../../core/nativeGenerateLoop.js";
|
|
29
29
|
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
30
30
|
import { resolveRequestKind } from "../../core/resolveRequestKind.js";
|
|
31
|
-
import { resolveToolExecutionRecords } from "../../core/toolExecutionRecorder.js";
|
|
31
|
+
import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../../core/toolExecutionRecorder.js";
|
|
32
32
|
import { transformToolExecutions } from "../../utils/transformationUtils.js";
|
|
33
33
|
import { createAnthropicConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
|
|
34
34
|
import { composeAbortSignals, createTimeoutController, mergeAbortSignals, TimeoutError, } from "../../utils/timeout.js";
|
|
@@ -1479,6 +1479,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1479
1479
|
},
|
|
1480
1480
|
responseTime: Date.now() - startTime,
|
|
1481
1481
|
toolsUsed: loop.toolsUsed,
|
|
1482
|
+
toolCalls: toolCallsFromSummaries(toolExecutionSummaries),
|
|
1482
1483
|
toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
|
|
1483
1484
|
enhancedWithTools: loop.toolsUsed.length > 0,
|
|
1484
1485
|
};
|
|
@@ -34,7 +34,7 @@ import { composeAbortSignalsScoped, createTimeoutController, mergeAbortSignals,
|
|
|
34
34
|
import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
|
|
35
35
|
import { resolveRequestKind } from "../core/resolveRequestKind.js";
|
|
36
36
|
import { appendJsonSchemaInstruction, hasNativeDoGenerate, runNativeGenerateLoop, } from "../core/nativeGenerateLoop.js";
|
|
37
|
-
import { resolveToolExecutionRecords } from "../core/toolExecutionRecorder.js";
|
|
37
|
+
import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../core/toolExecutionRecorder.js";
|
|
38
38
|
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
39
39
|
import { coerceJsonToSchema, schemaAccepts } from "../utils/json/coerce.js";
|
|
40
40
|
import { resolveToolChoice } from "../utils/toolChoice.js";
|
|
@@ -989,6 +989,7 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
989
989
|
},
|
|
990
990
|
responseTime: Date.now() - startTime,
|
|
991
991
|
toolsUsed,
|
|
992
|
+
toolCalls: toolCallsFromSummaries(toolExecutionSummaries),
|
|
992
993
|
toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
|
|
993
994
|
enhancedWithTools: toolsUsed.length > 0,
|
|
994
995
|
};
|
package/dist/types/generate.d.ts
CHANGED
|
@@ -1490,6 +1490,12 @@ export type TextGenerationResult = {
|
|
|
1490
1490
|
model?: string;
|
|
1491
1491
|
usage?: TokenUsage;
|
|
1492
1492
|
responseTime?: number;
|
|
1493
|
+
/** The executed tool calls of a native turn — the same shape `GenerateResult` exposes. */
|
|
1494
|
+
toolCalls?: Array<{
|
|
1495
|
+
toolCallId: string;
|
|
1496
|
+
toolName: string;
|
|
1497
|
+
args: StandardRecord;
|
|
1498
|
+
}>;
|
|
1493
1499
|
toolsUsed?: string[];
|
|
1494
1500
|
toolExecutions?: Array<{
|
|
1495
1501
|
toolName: string;
|
|
@@ -3,7 +3,7 @@ import type { EvaluationData, GetPromptFunction } from "./evaluation.js";
|
|
|
3
3
|
import type { AuthenticatedUser, RouteDefinition, ServerContext } from "./server.js";
|
|
4
4
|
import type { LanguageModelMiddleware as BaseLanguageModelMiddleware } from "./aiCompat.js";
|
|
5
5
|
export type { LanguageModelMiddleware } from "./aiCompat.js";
|
|
6
|
-
export type { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3Message, LanguageModelV3Prompt, LanguageModelV3StreamPart, LanguageModelV3ToolCall, LanguageModelV3ToolChoice, LanguageModelV3Source, LanguageModelV3Middleware, LanguageModelV3GenerateResult, LanguageModelV3StreamResult, JSONSchema7, } from "./aiCompat.js";
|
|
6
|
+
export type { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3Message, LanguageModelV3Prompt, LanguageModelV3StreamPart, LanguageModelV3Content, LanguageModelV3ToolCall, LanguageModelV3ToolChoice, LanguageModelV3Source, LanguageModelV3Middleware, LanguageModelV3GenerateResult, LanguageModelV3StreamResult, JSONSchema7, } from "./aiCompat.js";
|
|
7
7
|
/**
|
|
8
8
|
* Metadata type for NeuroLink middleware
|
|
9
9
|
* Provides additional information about middleware without affecting execution
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.12.
|
|
3
|
+
"version": "12.12.8",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
|
|
6
6
|
"author": {
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"test:mcp:spans": "pnpm exec tsx test/continuous-test-suite-mcp-spans.ts",
|
|
113
113
|
"test:mcp:infra": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",
|
|
114
114
|
"test:vendor-recovery": "pnpm exec tsx test/continuous-test-suite-native-vendor-recovery.ts",
|
|
115
|
+
"test:browser-bundle": "pnpm exec tsx test/continuous-test-suite-browser-bundle.ts",
|
|
115
116
|
"test:providers-mocked": "pnpm exec tsx test/continuous-test-suite-providers-mocked.ts",
|
|
116
117
|
"scaffold:provider": "pnpm exec tsx tools/scaffold-provider.ts",
|
|
117
118
|
"verify:provider-onboarding": "pnpm exec tsx tools/verify-provider-onboarding.ts",
|