@juspay/neurolink 12.41.4 → 12.41.5
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 +375 -373
- package/dist/providers/amazonBedrock/client.d.ts +27 -0
- package/dist/providers/amazonBedrock/client.js +418 -113
- package/dist/providers/amazonBedrock/modelBridge.d.ts +100 -0
- package/dist/providers/amazonBedrock/modelBridge.js +226 -0
- package/package.json +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges Bedrock's native `BedrockMessage[]` conversation shape and
|
|
3
|
+
* `AgenticLoopChunk` stream to the AI SDK V3 model-middleware protocol
|
|
4
|
+
* (`LanguageModelV3`), so `BaseProvider.applyMiddlewareToModel()` can wrap a
|
|
5
|
+
* turn built entirely out of `runAgenticLoop` — the same engine Bedrock's
|
|
6
|
+
* `generate()` and `executeStream()` already run on.
|
|
7
|
+
*
|
|
8
|
+
* Scope: only the conversation `generate()`/`executeStream()` hand to the
|
|
9
|
+
* loop as its STARTING point ever crosses this boundary. Both reset
|
|
10
|
+
* `conversationHistory` to a single fresh message before calling the loop
|
|
11
|
+
* (see `client.ts`), so that starting point is always `system + one user
|
|
12
|
+
* message` — never prior assistant/tool-result turns, which are constructed
|
|
13
|
+
* entirely inside the loop's own steps and stay in native
|
|
14
|
+
* `BedrockMessage[]`/`ConverseCommand` shape. A general, lossless
|
|
15
|
+
* `BedrockMessage` <-> `ModelMessage` converter is not attempted here, and
|
|
16
|
+
* would not be possible anyway: `BedrockToolResult` carries no `toolName`,
|
|
17
|
+
* which a faithful `ToolModelMessage` requires.
|
|
18
|
+
*
|
|
19
|
+
* Further scoped to TEXT-ONLY turns. A multimodal starting message (image,
|
|
20
|
+
* document, or — pathologically — a tool-use/tool-result block) skips
|
|
21
|
+
* wrapping entirely; the caller checks `isTextOnlyBedrockConversation` and
|
|
22
|
+
* falls back to calling the loop directly, logging that the turn was
|
|
23
|
+
* excluded. This mirrors the plan's AI Studio precedent: an excluded shape
|
|
24
|
+
* must be explicit in code, not implied by no test ever sending one.
|
|
25
|
+
*
|
|
26
|
+
* Who owns the outcome. When the wrapped `doGenerate`/`doStream` ran the loop,
|
|
27
|
+
* the loop's own result is authoritative for `finishReason`, `usage`,
|
|
28
|
+
* `toolExecutions` and the final conversation: a middleware may replace the
|
|
29
|
+
* content or the stream parts a caller sees, but it cannot rewrite the
|
|
30
|
+
* metadata of a turn that already reached the wire. When a middleware never
|
|
31
|
+
* calls through (a cache hit, a refusal, a synthetic answer) no loop ran, and
|
|
32
|
+
* its own V3 result is what gets reported.
|
|
33
|
+
*/
|
|
34
|
+
import type { ToolInputSchema } from "@aws-sdk/client-bedrock-runtime";
|
|
35
|
+
import type { AgenticLoopChunk, BedrockMessage, LanguageModelV3CallOptions, LanguageModelV3StreamPart, ModelMessage } from "../../types/index.js";
|
|
36
|
+
/** Names of the unsent options `params` carries; an empty list counts as unset. */
|
|
37
|
+
export declare function unsentBedrockMiddlewareOptions(params: LanguageModelV3CallOptions): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Unwrap Bedrock's `ToolSpecification.inputSchema` — always `{ json:
|
|
40
|
+
* DocumentType }` on the wire (see `ToolInputSchema.JsonMember` in the AWS
|
|
41
|
+
* SDK), never the bare schema — to the plain JSON schema object the V3 tool
|
|
42
|
+
* shape's `inputSchema` field expects. `client.ts`'s own tool-formatting path
|
|
43
|
+
* (`formatToolsForBedrock`) always sends `{ json: schema }` to AWS for this
|
|
44
|
+
* exact reason; handing a middleware the wrapper itself instead of the
|
|
45
|
+
* schema breaks any schema-inspecting middleware (e.g. a guardrail
|
|
46
|
+
* validating tool arguments against the declared shape).
|
|
47
|
+
*/
|
|
48
|
+
export declare function unwrapBedrockToolInputSchema(inputSchema: ToolInputSchema | undefined): unknown;
|
|
49
|
+
/** True when every content block of every message is plain text. */
|
|
50
|
+
export declare function isTextOnlyBedrockConversation(conversation: BedrockMessage[]): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* `system` + the (already text-only) starting conversation, as a V3 prompt.
|
|
53
|
+
* The system prompt is always sent as its own leading message — Bedrock
|
|
54
|
+
* carries it out-of-band (`ConverseCommandInput.system`), but the V3 prompt
|
|
55
|
+
* shape has no separate slot, so `bedrockConversationFromV3Prompt` is the
|
|
56
|
+
* inverse that pulls it back out.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildV3PromptFromBedrock(systemPrompt: string, conversation: BedrockMessage[]): ModelMessage[];
|
|
59
|
+
/**
|
|
60
|
+
* Inverse of `buildV3PromptFromBedrock`, read AFTER `transformParams` has
|
|
61
|
+
* run — this is what makes a middleware's prompt rewrite (correction 1)
|
|
62
|
+
* reach the wire: the caller passes the ORIGINAL prompt into `doGenerate`/
|
|
63
|
+
* `doStream`, and by AI SDK contract `transformParams` has already produced
|
|
64
|
+
* the version handed to the closure by the time this runs.
|
|
65
|
+
*
|
|
66
|
+
* A `tool` role message has no faithful Bedrock shape at this boundary (no
|
|
67
|
+
* toolUseId/toolName pairing survives `ToolModelMessage`), so one is
|
|
68
|
+
* dropped with a WARN rather than guessed at. The starting turn Bedrock
|
|
69
|
+
* ever hands across this boundary never contains one; seeing one here means
|
|
70
|
+
* middleware injected it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function bedrockConversationFromV3Prompt(prompt: ModelMessage[]): {
|
|
73
|
+
system: string;
|
|
74
|
+
conversation: BedrockMessage[];
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Wrap a native `AgenticLoopChunk` source into the V3 stream contract, one
|
|
78
|
+
* chunk at a time, forwarding cancellation to both the source iterator and
|
|
79
|
+
* `cancel` (which the caller wires to the turn's own `AbortController`, so
|
|
80
|
+
* breaking out of a wrapped stream tears down the underlying AWS SDK
|
|
81
|
+
* request — correction 4). `finish` resolves the terminal `finish` part
|
|
82
|
+
* once the loop settles; middleware inspecting the returned stream sees a
|
|
83
|
+
* genuine usage-bearing finish part even when nothing downstream reads it
|
|
84
|
+
* back out (a guardrail that blocks before calling `doStream` never
|
|
85
|
+
* produces this stream at all, which is the `!loopPromise` case its own
|
|
86
|
+
* caller handles).
|
|
87
|
+
*/
|
|
88
|
+
export declare function bedrockChunksToV3Stream(source: AsyncIterable<AgenticLoopChunk>, finish: Promise<LanguageModelV3StreamPart>, cancel: () => void): ReadableStream<LanguageModelV3StreamPart>;
|
|
89
|
+
/**
|
|
90
|
+
* Inverse of `bedrockChunksToV3Stream`: drains a (possibly
|
|
91
|
+
* middleware-wrapped) V3 stream back into Bedrock's native chunk shape.
|
|
92
|
+
* `finish`/`error` parts are consumed here but not surfaced as chunks —
|
|
93
|
+
* `streamingConversationLoop` reads the turn's outcome from the loop's own
|
|
94
|
+
* `resultPromise`, which already carries richer usage/finishReason/
|
|
95
|
+
* toolExecutions than a V3 finish part does; this function exists only to
|
|
96
|
+
* satisfy the wrapped stream's own contract (a middleware may expect its
|
|
97
|
+
* returned stream to be drained to completion) and to `throw` a middleware
|
|
98
|
+
* `error` part into the consumer.
|
|
99
|
+
*/
|
|
100
|
+
export declare function bedrockV3StreamToChunks(stream: ReadableStream<LanguageModelV3StreamPart>): AsyncIterable<AgenticLoopChunk>;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges Bedrock's native `BedrockMessage[]` conversation shape and
|
|
3
|
+
* `AgenticLoopChunk` stream to the AI SDK V3 model-middleware protocol
|
|
4
|
+
* (`LanguageModelV3`), so `BaseProvider.applyMiddlewareToModel()` can wrap a
|
|
5
|
+
* turn built entirely out of `runAgenticLoop` — the same engine Bedrock's
|
|
6
|
+
* `generate()` and `executeStream()` already run on.
|
|
7
|
+
*
|
|
8
|
+
* Scope: only the conversation `generate()`/`executeStream()` hand to the
|
|
9
|
+
* loop as its STARTING point ever crosses this boundary. Both reset
|
|
10
|
+
* `conversationHistory` to a single fresh message before calling the loop
|
|
11
|
+
* (see `client.ts`), so that starting point is always `system + one user
|
|
12
|
+
* message` — never prior assistant/tool-result turns, which are constructed
|
|
13
|
+
* entirely inside the loop's own steps and stay in native
|
|
14
|
+
* `BedrockMessage[]`/`ConverseCommand` shape. A general, lossless
|
|
15
|
+
* `BedrockMessage` <-> `ModelMessage` converter is not attempted here, and
|
|
16
|
+
* would not be possible anyway: `BedrockToolResult` carries no `toolName`,
|
|
17
|
+
* which a faithful `ToolModelMessage` requires.
|
|
18
|
+
*
|
|
19
|
+
* Further scoped to TEXT-ONLY turns. A multimodal starting message (image,
|
|
20
|
+
* document, or — pathologically — a tool-use/tool-result block) skips
|
|
21
|
+
* wrapping entirely; the caller checks `isTextOnlyBedrockConversation` and
|
|
22
|
+
* falls back to calling the loop directly, logging that the turn was
|
|
23
|
+
* excluded. This mirrors the plan's AI Studio precedent: an excluded shape
|
|
24
|
+
* must be explicit in code, not implied by no test ever sending one.
|
|
25
|
+
*
|
|
26
|
+
* Who owns the outcome. When the wrapped `doGenerate`/`doStream` ran the loop,
|
|
27
|
+
* the loop's own result is authoritative for `finishReason`, `usage`,
|
|
28
|
+
* `toolExecutions` and the final conversation: a middleware may replace the
|
|
29
|
+
* content or the stream parts a caller sees, but it cannot rewrite the
|
|
30
|
+
* metadata of a turn that already reached the wire. When a middleware never
|
|
31
|
+
* calls through (a cache hit, a refusal, a synthetic answer) no loop ran, and
|
|
32
|
+
* its own V3 result is what gets reported.
|
|
33
|
+
*/
|
|
34
|
+
import { logger } from "../../utils/logger.js";
|
|
35
|
+
/**
|
|
36
|
+
* Sampling and output-shaping options of a V3 call that Converse is never
|
|
37
|
+
* sent: both turn builders fill `inferenceConfig` with `maxTokens` and
|
|
38
|
+
* `temperature` alone. The bridge seeds none of these, so a value present
|
|
39
|
+
* when `doGenerate`/`doStream` runs was put there by a middleware, and would
|
|
40
|
+
* otherwise be dropped without a trace.
|
|
41
|
+
*/
|
|
42
|
+
const UNSENT_V3_OPTIONS = [
|
|
43
|
+
"topP",
|
|
44
|
+
"topK",
|
|
45
|
+
"presencePenalty",
|
|
46
|
+
"frequencyPenalty",
|
|
47
|
+
"stopSequences",
|
|
48
|
+
"seed",
|
|
49
|
+
"toolChoice",
|
|
50
|
+
"responseFormat",
|
|
51
|
+
];
|
|
52
|
+
/** Names of the unsent options `params` carries; an empty list counts as unset. */
|
|
53
|
+
export function unsentBedrockMiddlewareOptions(params) {
|
|
54
|
+
return UNSENT_V3_OPTIONS.filter((key) => {
|
|
55
|
+
const value = params[key];
|
|
56
|
+
return Array.isArray(value) ? value.length > 0 : value !== undefined;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Unwrap Bedrock's `ToolSpecification.inputSchema` — always `{ json:
|
|
61
|
+
* DocumentType }` on the wire (see `ToolInputSchema.JsonMember` in the AWS
|
|
62
|
+
* SDK), never the bare schema — to the plain JSON schema object the V3 tool
|
|
63
|
+
* shape's `inputSchema` field expects. `client.ts`'s own tool-formatting path
|
|
64
|
+
* (`formatToolsForBedrock`) always sends `{ json: schema }` to AWS for this
|
|
65
|
+
* exact reason; handing a middleware the wrapper itself instead of the
|
|
66
|
+
* schema breaks any schema-inspecting middleware (e.g. a guardrail
|
|
67
|
+
* validating tool arguments against the declared shape).
|
|
68
|
+
*/
|
|
69
|
+
export function unwrapBedrockToolInputSchema(inputSchema) {
|
|
70
|
+
return inputSchema && "json" in inputSchema ? inputSchema.json : undefined;
|
|
71
|
+
}
|
|
72
|
+
/** True when every content block of every message is plain text. */
|
|
73
|
+
export function isTextOnlyBedrockConversation(conversation) {
|
|
74
|
+
return conversation.every((message) => message.content.every((block) => block.text !== undefined &&
|
|
75
|
+
block.image === undefined &&
|
|
76
|
+
block.document === undefined &&
|
|
77
|
+
block.toolUse === undefined &&
|
|
78
|
+
block.toolResult === undefined));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* `system` + the (already text-only) starting conversation, as a V3 prompt.
|
|
82
|
+
* The system prompt is always sent as its own leading message — Bedrock
|
|
83
|
+
* carries it out-of-band (`ConverseCommandInput.system`), but the V3 prompt
|
|
84
|
+
* shape has no separate slot, so `bedrockConversationFromV3Prompt` is the
|
|
85
|
+
* inverse that pulls it back out.
|
|
86
|
+
*/
|
|
87
|
+
export function buildV3PromptFromBedrock(systemPrompt, conversation) {
|
|
88
|
+
const prompt = [{ role: "system", content: systemPrompt }];
|
|
89
|
+
for (const message of conversation) {
|
|
90
|
+
const text = message.content.map((block) => block.text ?? "").join("");
|
|
91
|
+
if (message.role === "user") {
|
|
92
|
+
prompt.push({ role: "user", content: text });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
prompt.push({ role: "assistant", content: text });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return prompt;
|
|
99
|
+
}
|
|
100
|
+
function contentToText(content) {
|
|
101
|
+
if (typeof content === "string") {
|
|
102
|
+
return content;
|
|
103
|
+
}
|
|
104
|
+
return content
|
|
105
|
+
.filter((part) => typeof part === "object" && part !== null && part.type === "text")
|
|
106
|
+
.map((part) => part.text)
|
|
107
|
+
.join("");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Inverse of `buildV3PromptFromBedrock`, read AFTER `transformParams` has
|
|
111
|
+
* run — this is what makes a middleware's prompt rewrite (correction 1)
|
|
112
|
+
* reach the wire: the caller passes the ORIGINAL prompt into `doGenerate`/
|
|
113
|
+
* `doStream`, and by AI SDK contract `transformParams` has already produced
|
|
114
|
+
* the version handed to the closure by the time this runs.
|
|
115
|
+
*
|
|
116
|
+
* A `tool` role message has no faithful Bedrock shape at this boundary (no
|
|
117
|
+
* toolUseId/toolName pairing survives `ToolModelMessage`), so one is
|
|
118
|
+
* dropped with a WARN rather than guessed at. The starting turn Bedrock
|
|
119
|
+
* ever hands across this boundary never contains one; seeing one here means
|
|
120
|
+
* middleware injected it.
|
|
121
|
+
*/
|
|
122
|
+
export function bedrockConversationFromV3Prompt(prompt) {
|
|
123
|
+
const systemParts = [];
|
|
124
|
+
const conversation = [];
|
|
125
|
+
for (const message of prompt) {
|
|
126
|
+
if (message.role === "system") {
|
|
127
|
+
systemParts.push(message.content);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (message.role === "tool") {
|
|
131
|
+
logger.warn("[AmazonBedrockProvider] middleware transformParams introduced a 'tool' role message; it has no Bedrock wire shape at this boundary and was dropped");
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
conversation.push({
|
|
135
|
+
role: message.role,
|
|
136
|
+
content: [{ text: contentToText(message.content) }],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return { system: systemParts.join("\n\n"), conversation };
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Wrap a native `AgenticLoopChunk` source into the V3 stream contract, one
|
|
143
|
+
* chunk at a time, forwarding cancellation to both the source iterator and
|
|
144
|
+
* `cancel` (which the caller wires to the turn's own `AbortController`, so
|
|
145
|
+
* breaking out of a wrapped stream tears down the underlying AWS SDK
|
|
146
|
+
* request — correction 4). `finish` resolves the terminal `finish` part
|
|
147
|
+
* once the loop settles; middleware inspecting the returned stream sees a
|
|
148
|
+
* genuine usage-bearing finish part even when nothing downstream reads it
|
|
149
|
+
* back out (a guardrail that blocks before calling `doStream` never
|
|
150
|
+
* produces this stream at all, which is the `!loopPromise` case its own
|
|
151
|
+
* caller handles).
|
|
152
|
+
*/
|
|
153
|
+
export function bedrockChunksToV3Stream(source, finish, cancel) {
|
|
154
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
155
|
+
return new ReadableStream({
|
|
156
|
+
async pull(controller) {
|
|
157
|
+
try {
|
|
158
|
+
const next = await iterator.next();
|
|
159
|
+
if (next.done) {
|
|
160
|
+
controller.enqueue(await finish);
|
|
161
|
+
controller.close();
|
|
162
|
+
}
|
|
163
|
+
else if (next.value.reasoning) {
|
|
164
|
+
controller.enqueue({
|
|
165
|
+
type: "reasoning-delta",
|
|
166
|
+
delta: next.value.reasoning,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
controller.enqueue({ type: "text-delta", delta: next.value.content });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
controller.error(error);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
async cancel() {
|
|
178
|
+
cancel();
|
|
179
|
+
await iterator.return?.();
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Inverse of `bedrockChunksToV3Stream`: drains a (possibly
|
|
185
|
+
* middleware-wrapped) V3 stream back into Bedrock's native chunk shape.
|
|
186
|
+
* `finish`/`error` parts are consumed here but not surfaced as chunks —
|
|
187
|
+
* `streamingConversationLoop` reads the turn's outcome from the loop's own
|
|
188
|
+
* `resultPromise`, which already carries richer usage/finishReason/
|
|
189
|
+
* toolExecutions than a V3 finish part does; this function exists only to
|
|
190
|
+
* satisfy the wrapped stream's own contract (a middleware may expect its
|
|
191
|
+
* returned stream to be drained to completion) and to `throw` a middleware
|
|
192
|
+
* `error` part into the consumer.
|
|
193
|
+
*/
|
|
194
|
+
export async function* bedrockV3StreamToChunks(stream) {
|
|
195
|
+
const reader = stream.getReader();
|
|
196
|
+
let done = false;
|
|
197
|
+
try {
|
|
198
|
+
while (true) {
|
|
199
|
+
const next = await reader.read();
|
|
200
|
+
if (next.done) {
|
|
201
|
+
done = true;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const part = next.value;
|
|
205
|
+
if (part.type === "text-delta") {
|
|
206
|
+
yield { content: part.delta };
|
|
207
|
+
}
|
|
208
|
+
else if (part.type === "reasoning-delta") {
|
|
209
|
+
yield { content: "", reasoning: part.delta };
|
|
210
|
+
}
|
|
211
|
+
else if (part.type === "error") {
|
|
212
|
+
throw part.error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
try {
|
|
218
|
+
if (!done) {
|
|
219
|
+
await reader.cancel();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
reader.releaseLock();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.41.
|
|
3
|
+
"version": "12.41.5",
|
|
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": {
|