@juspay/neurolink 11.6.0 → 11.7.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 +2 -6
- package/dist/browser/neurolink.min.js +392 -392
- package/dist/core/loopEngine.js +21 -2
- package/dist/lib/core/loopEngine.js +21 -2
- package/dist/lib/providers/amazonBedrock/client.d.ts +23 -7
- package/dist/lib/providers/amazonBedrock/client.js +276 -950
- package/dist/lib/providers/amazonBedrock/loopAdapter.d.ts +36 -0
- package/dist/lib/providers/amazonBedrock/loopAdapter.js +278 -0
- package/dist/lib/types/loopEngine.d.ts +52 -0
- package/dist/lib/types/providers.d.ts +9 -0
- package/dist/providers/amazonBedrock/client.d.ts +23 -7
- package/dist/providers/amazonBedrock/client.js +276 -950
- package/dist/providers/amazonBedrock/loopAdapter.d.ts +36 -0
- package/dist/providers/amazonBedrock/loopAdapter.js +277 -0
- package/dist/types/loopEngine.d.ts +52 -0
- package/dist/types/providers.d.ts +9 -0
- package/package.json +3 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedrock's adapter onto the shared agentic loop engine.
|
|
3
|
+
*
|
|
4
|
+
* Bedrock had two hand-rolled turn loops — one for `Converse`, one for
|
|
5
|
+
* `ConverseStream` — that between them duplicated the same content-block
|
|
6
|
+
* accumulation three times. Everything here is the wire-format half of that:
|
|
7
|
+
* issuing the right command, folding a response into blocks, serializing tool
|
|
8
|
+
* results back into the conversation, and mapping the stop reason. The turn
|
|
9
|
+
* loop itself, the step cap, tool dispatch and usage accumulation belong to
|
|
10
|
+
* `runAgenticLoop`.
|
|
11
|
+
*
|
|
12
|
+
* One adapter covers both operations because the two differ only in how a
|
|
13
|
+
* step's content blocks arrive: `ConverseStream` delivers them as events that
|
|
14
|
+
* are pushed to the consumer as they land, `Converse` returns them whole.
|
|
15
|
+
* Block accumulation, tool-call extraction and the conversation shape are
|
|
16
|
+
* identical, and were identical in the hand-rolled loops too — which is why
|
|
17
|
+
* they drifted apart in the details.
|
|
18
|
+
*/
|
|
19
|
+
import type { ConverseCommandInput, ConverseStreamCommandInput } from "@aws-sdk/client-bedrock-runtime";
|
|
20
|
+
import { type BedrockRuntimeClient } from "@aws-sdk/client-bedrock-runtime";
|
|
21
|
+
import type { AgenticLoopAdapter, BedrockContentBlock, BedrockMessage } from "../../types/index.js";
|
|
22
|
+
export declare function createBedrockLoopAdapter(config: {
|
|
23
|
+
client: BedrockRuntimeClient;
|
|
24
|
+
/** `ConverseStream` when true, `Converse` when false. */
|
|
25
|
+
streaming: boolean;
|
|
26
|
+
/** The region the client was constructed with, for profile resolution. */
|
|
27
|
+
region: string;
|
|
28
|
+
maxSteps: number;
|
|
29
|
+
/**
|
|
30
|
+
* Build the request for one step. Synchronous by contract, so anything
|
|
31
|
+
* async (tool declarations in particular) is resolved once before the turn
|
|
32
|
+
* starts rather than per step. Bedrock has no mid-turn tool discovery, so
|
|
33
|
+
* there is nothing that would need re-resolving.
|
|
34
|
+
*/
|
|
35
|
+
buildCommandInput: (conversation: BedrockMessage[], step: number) => ConverseCommandInput & ConverseStreamCommandInput;
|
|
36
|
+
}): AgenticLoopAdapter<BedrockMessage[], BedrockContentBlock[]>;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedrock's adapter onto the shared agentic loop engine.
|
|
3
|
+
*
|
|
4
|
+
* Bedrock had two hand-rolled turn loops — one for `Converse`, one for
|
|
5
|
+
* `ConverseStream` — that between them duplicated the same content-block
|
|
6
|
+
* accumulation three times. Everything here is the wire-format half of that:
|
|
7
|
+
* issuing the right command, folding a response into blocks, serializing tool
|
|
8
|
+
* results back into the conversation, and mapping the stop reason. The turn
|
|
9
|
+
* loop itself, the step cap, tool dispatch and usage accumulation belong to
|
|
10
|
+
* `runAgenticLoop`.
|
|
11
|
+
*
|
|
12
|
+
* One adapter covers both operations because the two differ only in how a
|
|
13
|
+
* step's content blocks arrive: `ConverseStream` delivers them as events that
|
|
14
|
+
* are pushed to the consumer as they land, `Converse` returns them whole.
|
|
15
|
+
* Block accumulation, tool-call extraction and the conversation shape are
|
|
16
|
+
* identical, and were identical in the hand-rolled loops too — which is why
|
|
17
|
+
* they drifted apart in the details.
|
|
18
|
+
*/
|
|
19
|
+
import { ConverseCommand, ConverseStreamCommand, } from "@aws-sdk/client-bedrock-runtime";
|
|
20
|
+
import { withTimeout } from "../../utils/errorHandling.js";
|
|
21
|
+
import { withInferenceProfileFallback } from "./inferenceProfile.js";
|
|
22
|
+
const STEP_TIMEOUT_MS = 120_000;
|
|
23
|
+
function newToolUseId() {
|
|
24
|
+
return `tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Finalize a block once its `contentBlockStop` arrives: parse whatever tool
|
|
28
|
+
* input accumulated, and attach the text a plain block collected.
|
|
29
|
+
*
|
|
30
|
+
* Attaching the text matters beyond this step. `buildToolResultMessages`
|
|
31
|
+
* replays these blocks back to the model as the assistant turn, so a text
|
|
32
|
+
* block left empty here silently drops the assistant's own words from the
|
|
33
|
+
* conversation on every subsequent step of the turn.
|
|
34
|
+
*/
|
|
35
|
+
function finalizeBlock(block, text) {
|
|
36
|
+
if (!block) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (block.toolUse && block._inputBuffer) {
|
|
40
|
+
try {
|
|
41
|
+
block.toolUse.input = JSON.parse(block._inputBuffer);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
block.toolUse.input = {};
|
|
45
|
+
}
|
|
46
|
+
delete block._inputBuffer;
|
|
47
|
+
}
|
|
48
|
+
if (text && !block.toolUse) {
|
|
49
|
+
block.text = text;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function toolCallsFrom(blocks) {
|
|
53
|
+
return blocks
|
|
54
|
+
.filter((b) => b.toolUse)
|
|
55
|
+
.map((b) => ({
|
|
56
|
+
id: b.toolUse?.toolUseId ?? newToolUseId(),
|
|
57
|
+
name: b.toolUse?.name ?? "",
|
|
58
|
+
args: b.toolUse?.input ?? {},
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
/** Fold a `ConverseStream` event sequence into finished content blocks. */
|
|
62
|
+
async function readStreamedStep(response, channel, signal) {
|
|
63
|
+
const blocks = [];
|
|
64
|
+
// Blocks are keyed by the index Bedrock stamps on every event rather than
|
|
65
|
+
// by arrival order, because a text block does not necessarily announce
|
|
66
|
+
// itself with `contentBlockStart` — the first thing seen for it can be a
|
|
67
|
+
// delta. Creating the block on whichever event arrives first is what keeps
|
|
68
|
+
// assistant text that precedes a tool call from being dropped: `raw` is
|
|
69
|
+
// replayed as the assistant turn on the next step, so a lost text block
|
|
70
|
+
// means the model stops seeing its own reasoning mid-turn.
|
|
71
|
+
const blocksByIndex = new Map();
|
|
72
|
+
const textByIndex = new Map();
|
|
73
|
+
const blockFor = (index) => {
|
|
74
|
+
let block = blocksByIndex.get(index);
|
|
75
|
+
if (!block) {
|
|
76
|
+
block = {};
|
|
77
|
+
blocksByIndex.set(index, block);
|
|
78
|
+
blocks.push(block);
|
|
79
|
+
}
|
|
80
|
+
return block;
|
|
81
|
+
};
|
|
82
|
+
let text = "";
|
|
83
|
+
let rawStopReason;
|
|
84
|
+
let inputTokens = 0;
|
|
85
|
+
let outputTokens = 0;
|
|
86
|
+
let cacheReadTokens = 0;
|
|
87
|
+
let cacheWriteTokens = 0;
|
|
88
|
+
if (response.stream) {
|
|
89
|
+
for await (const rawChunk of response.stream) {
|
|
90
|
+
if (signal.aborted) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
const chunk = rawChunk;
|
|
94
|
+
if (chunk.contentBlockStart) {
|
|
95
|
+
blockFor(chunk.contentBlockStart.contentBlockIndex ?? 0);
|
|
96
|
+
}
|
|
97
|
+
if (chunk.contentBlockDelta?.delta?.text) {
|
|
98
|
+
const index = chunk.contentBlockDelta.contentBlockIndex ?? 0;
|
|
99
|
+
const delta = chunk.contentBlockDelta.delta.text;
|
|
100
|
+
blockFor(index);
|
|
101
|
+
text += delta;
|
|
102
|
+
textByIndex.set(index, (textByIndex.get(index) ?? "") + delta);
|
|
103
|
+
channel.push({ content: delta });
|
|
104
|
+
}
|
|
105
|
+
if (chunk.contentBlockStart?.start?.toolUse) {
|
|
106
|
+
const block = blockFor(chunk.contentBlockStart.contentBlockIndex ?? 0);
|
|
107
|
+
block.toolUse = {
|
|
108
|
+
name: chunk.contentBlockStart.start.toolUse.name ?? "",
|
|
109
|
+
input: {},
|
|
110
|
+
toolUseId: chunk.contentBlockStart.start.toolUse.toolUseId ?? newToolUseId(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (chunk.contentBlockDelta?.delta?.toolUse) {
|
|
114
|
+
const block = blockFor(chunk.contentBlockDelta.contentBlockIndex ?? 0);
|
|
115
|
+
block.toolUse ??= {
|
|
116
|
+
name: "",
|
|
117
|
+
input: {},
|
|
118
|
+
toolUseId: newToolUseId(),
|
|
119
|
+
};
|
|
120
|
+
const deltaInput = chunk.contentBlockDelta.delta.toolUse.input;
|
|
121
|
+
if (typeof deltaInput === "string") {
|
|
122
|
+
block._inputBuffer = (block._inputBuffer ?? "") + deltaInput;
|
|
123
|
+
}
|
|
124
|
+
else if (typeof deltaInput === "object" &&
|
|
125
|
+
deltaInput !== null &&
|
|
126
|
+
!Array.isArray(deltaInput)) {
|
|
127
|
+
block.toolUse.input = {
|
|
128
|
+
...(block.toolUse.input ?? {}),
|
|
129
|
+
...deltaInput,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (chunk.contentBlockStop) {
|
|
134
|
+
const index = chunk.contentBlockStop.contentBlockIndex ?? 0;
|
|
135
|
+
finalizeBlock(blocksByIndex.get(index), textByIndex.get(index) ?? "");
|
|
136
|
+
}
|
|
137
|
+
if (chunk.messageStop) {
|
|
138
|
+
rawStopReason = chunk.messageStop.stopReason ?? "end_turn";
|
|
139
|
+
// Not a break: the metadata event carrying usage arrives after this.
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (chunk.metadata?.usage) {
|
|
143
|
+
inputTokens += chunk.metadata.usage.inputTokens ?? 0;
|
|
144
|
+
outputTokens += chunk.metadata.usage.outputTokens ?? 0;
|
|
145
|
+
// Converse follows the Anthropic additive convention — inputTokens is
|
|
146
|
+
// the uncached remainder, so cache reads/writes are counted apart.
|
|
147
|
+
cacheReadTokens += chunk.metadata.usage.cacheReadInputTokens ?? 0;
|
|
148
|
+
cacheWriteTokens += chunk.metadata.usage.cacheWriteInputTokens ?? 0;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Finalize anything the stream never closed. `contentBlockStop` is not
|
|
154
|
+
// guaranteed to arrive for every block: an abort breaks the loop, the
|
|
155
|
+
// metadata event breaks it, and a truncated stream simply ends. A block
|
|
156
|
+
// left unfinalized keeps `text` undefined and its tool input unparsed, and
|
|
157
|
+
// since `raw` is replayed as the assistant message — where an unrecognized
|
|
158
|
+
// block maps to `{ text: "" }`, which Bedrock rejects — that would fail the
|
|
159
|
+
// NEXT step of the turn rather than this one.
|
|
160
|
+
for (const [index, block] of blocksByIndex) {
|
|
161
|
+
finalizeBlock(block, textByIndex.get(index) ?? "");
|
|
162
|
+
}
|
|
163
|
+
// A block that opened and then received nothing carries no content at all,
|
|
164
|
+
// and `convertToAWSMessages` maps an unrecognized block to `{ text: "" }`,
|
|
165
|
+
// which Bedrock rejects. Drop them rather than replay them.
|
|
166
|
+
const usableBlocks = blocks.filter((block) => block.text || block.toolUse || block.image || block.document);
|
|
167
|
+
return {
|
|
168
|
+
text,
|
|
169
|
+
toolCalls: toolCallsFrom(usableBlocks),
|
|
170
|
+
usage: { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens },
|
|
171
|
+
rawStopReason,
|
|
172
|
+
raw: usableBlocks,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Fold a non-streaming `Converse` response into the same block shape. */
|
|
176
|
+
function readGeneratedStep(response) {
|
|
177
|
+
const blocks = [];
|
|
178
|
+
let text = "";
|
|
179
|
+
for (const item of response.output?.message?.content ?? []) {
|
|
180
|
+
const block = {};
|
|
181
|
+
if ("text" in item && item.text) {
|
|
182
|
+
block.text = item.text;
|
|
183
|
+
text += text ? ` ${item.text}` : item.text;
|
|
184
|
+
}
|
|
185
|
+
if ("toolUse" in item && item.toolUse) {
|
|
186
|
+
block.toolUse = {
|
|
187
|
+
toolUseId: item.toolUse.toolUseId ?? newToolUseId(),
|
|
188
|
+
name: item.toolUse.name ?? "",
|
|
189
|
+
input: item.toolUse.input ?? {},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
blocks.push(block);
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
text,
|
|
196
|
+
toolCalls: toolCallsFrom(blocks),
|
|
197
|
+
usage: {
|
|
198
|
+
inputTokens: response.usage?.inputTokens ?? 0,
|
|
199
|
+
outputTokens: response.usage?.outputTokens ?? 0,
|
|
200
|
+
cacheReadTokens: response.usage?.cacheReadInputTokens ?? 0,
|
|
201
|
+
cacheWriteTokens: response.usage?.cacheWriteInputTokens ?? 0,
|
|
202
|
+
},
|
|
203
|
+
rawStopReason: response.stopReason,
|
|
204
|
+
raw: blocks,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
export function createBedrockLoopAdapter(config) {
|
|
208
|
+
return {
|
|
209
|
+
providerLabel: "bedrock",
|
|
210
|
+
maxSteps: config.maxSteps,
|
|
211
|
+
// No toolFailureBreaker: Bedrock has never had strike counting — a failing
|
|
212
|
+
// tool becomes one error tool-result with no cross-step memory. Preserved.
|
|
213
|
+
buildStepRequest(conversation, step) {
|
|
214
|
+
return { raw: config.buildCommandInput(conversation, step) };
|
|
215
|
+
},
|
|
216
|
+
async executeStep(request, channel, signal) {
|
|
217
|
+
const commandInput = request.raw;
|
|
218
|
+
// Both sends go through the inference-profile fallback: most current
|
|
219
|
+
// Bedrock models are not invocable by their bare id in a given region
|
|
220
|
+
// and need a geography- or global-prefixed profile id instead. This is
|
|
221
|
+
// where the provider's two Converse call sites now live.
|
|
222
|
+
if (!config.streaming) {
|
|
223
|
+
const response = await withInferenceProfileFallback(commandInput.modelId ?? "", config.region, (effectiveModelId) => withTimeout(config.client.send(new ConverseCommand({
|
|
224
|
+
...commandInput,
|
|
225
|
+
modelId: effectiveModelId,
|
|
226
|
+
})), STEP_TIMEOUT_MS, new Error("Bedrock API call timed out")));
|
|
227
|
+
if (!response.output?.message) {
|
|
228
|
+
throw new Error("Invalid response structure from Bedrock API");
|
|
229
|
+
}
|
|
230
|
+
return readGeneratedStep(response);
|
|
231
|
+
}
|
|
232
|
+
const response = await withInferenceProfileFallback(commandInput.modelId ?? "", config.region, (effectiveModelId) => withTimeout(config.client.send(new ConverseStreamCommand({
|
|
233
|
+
...commandInput,
|
|
234
|
+
modelId: effectiveModelId,
|
|
235
|
+
})), STEP_TIMEOUT_MS, new Error("Bedrock streaming API call timed out")));
|
|
236
|
+
return readStreamedStep(response, channel, signal);
|
|
237
|
+
},
|
|
238
|
+
buildToolResultMessages(conversation, stepResult, toolResults) {
|
|
239
|
+
const assistantMessage = {
|
|
240
|
+
role: "assistant",
|
|
241
|
+
content: stepResult.raw,
|
|
242
|
+
};
|
|
243
|
+
const toolResultMessage = {
|
|
244
|
+
role: "user",
|
|
245
|
+
content: toolResults.map((result) => ({
|
|
246
|
+
toolResult: {
|
|
247
|
+
toolUseId: result.id,
|
|
248
|
+
content: [
|
|
249
|
+
{
|
|
250
|
+
text: result.error
|
|
251
|
+
? `Error executing tool ${result.name}: ${result.error}`
|
|
252
|
+
: String(typeof result.output === "string"
|
|
253
|
+
? result.output
|
|
254
|
+
: JSON.stringify(result.output)),
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
status: result.error ? "error" : "success",
|
|
258
|
+
},
|
|
259
|
+
})),
|
|
260
|
+
};
|
|
261
|
+
return [...conversation, assistantMessage, toolResultMessage];
|
|
262
|
+
},
|
|
263
|
+
mapFinishReason(rawStopReason, hadToolCallsAtCap) {
|
|
264
|
+
switch (rawStopReason) {
|
|
265
|
+
case "end_turn":
|
|
266
|
+
case "stop_sequence":
|
|
267
|
+
return "stop";
|
|
268
|
+
case "max_tokens":
|
|
269
|
+
return "length";
|
|
270
|
+
case "tool_use":
|
|
271
|
+
return "tool-calls";
|
|
272
|
+
default:
|
|
273
|
+
return hadToolCallsAtCap ? "tool-calls" : "stop";
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -35,6 +35,46 @@ export type AgenticLoopReclaimResult<TConversation> = {
|
|
|
35
35
|
export type AgenticLoopToolFailureBreaker = {
|
|
36
36
|
maxRetries: number;
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* DESIGN DECISION — mid-turn tool-discovery hydration (Plan 08 blocker 2,
|
|
40
|
+
* Task 7): resolved by the single optional `resolveToolOnMiss` field below,
|
|
41
|
+
* NOT by a broader `dispatchTools?` full-dispatch override. A full-dispatch
|
|
42
|
+
* override would let an adapter replace the engine's entire per-call
|
|
43
|
+
* dispatch — breaker bookkeeping, execution, toolExecutions aggregation — so
|
|
44
|
+
* every adapter needing hydration would have to reimplement that bookkeeping,
|
|
45
|
+
* and any later engine-level fix to dispatch would silently not apply to the
|
|
46
|
+
* adapters using the override. `resolveToolOnMiss` plugs into the existing
|
|
47
|
+
* dispatch at the one decision point that needs a second lookup, leaving
|
|
48
|
+
* breaker bookkeeping, retries and aggregation engine-owned for every
|
|
49
|
+
* provider, hydrated or not.
|
|
50
|
+
*
|
|
51
|
+
* DESIGN DECISION — originalNameMap propagation (blocker 3): needs ZERO
|
|
52
|
+
* engine or type change. Google's function-name sanitization is a translation
|
|
53
|
+
* concern between the wire (sanitized names out, sanitized names back on
|
|
54
|
+
* tool_call.name) and the engine's shape, which only ever sees plain string
|
|
55
|
+
* names. An adapter that needs the map threads it as a constructor-time
|
|
56
|
+
* closure and translates inside its own `executeStep` /
|
|
57
|
+
* `buildToolResultMessages`, before those names cross the engine boundary.
|
|
58
|
+
*
|
|
59
|
+
* DESIGN DECISION — reserved-step + forced finalization (blocker 1, part 2):
|
|
60
|
+
* stays OUTSIDE `runAgenticLoop`, in Vertex+Claude's own wrapper around
|
|
61
|
+
* `resultPromise`. The reserved step needs no engine change at all — an
|
|
62
|
+
* adapter declaring `maxSteps: requested - 1` means the engine's own loop
|
|
63
|
+
* never touches the reserved slot. The forced call is a one-shot action taken
|
|
64
|
+
* on the RESULT of a turn, not a repeatable step within one, so folding it in
|
|
65
|
+
* would teach the engine a family-specific concept (forced tool_choice, a
|
|
66
|
+
* distinguished terminal tool name) that every other adapter would then carry
|
|
67
|
+
* and never set.
|
|
68
|
+
*
|
|
69
|
+
* DESIGN DECISION — terminal tool-call marking (blocker 1, part 1): needs
|
|
70
|
+
* ZERO engine or type change. An adapter treats a detected terminal call as
|
|
71
|
+
* terminal by omitting it from `toolCalls` and putting its parsed payload in
|
|
72
|
+
* `text`. The engine already ends a turn the moment a step yields zero tool
|
|
73
|
+
* calls, so such a step is indistinguishable from an ordinary final text
|
|
74
|
+
* turn: never looked up in `options.tools`, never reaching TOOL_NOT_FOUND,
|
|
75
|
+
* never counted against the breaker. Proven by a case in the loop-engine
|
|
76
|
+
* suite rather than asserted here.
|
|
77
|
+
*/
|
|
38
78
|
export type AgenticLoopAdapter<TConversation = unknown, TRaw = unknown> = {
|
|
39
79
|
readonly providerLabel: string;
|
|
40
80
|
readonly maxSteps: number;
|
|
@@ -43,6 +83,18 @@ export type AgenticLoopAdapter<TConversation = unknown, TRaw = unknown> = {
|
|
|
43
83
|
readonly stallTimeoutMs?: number;
|
|
44
84
|
/** Set only for adapter instances whose client has the TOOL_NOT_FOUND strike breaker today: both Gemini adapters (AI Studio, Vertex+Gemini) AND the Vertex+Claude call to createAnthropicLoopAdapter — NOT the native-Anthropic call to that same factory, and not Bedrock. See Verified Fact 4. */
|
|
45
85
|
readonly toolFailureBreaker?: AgenticLoopToolFailureBreaker;
|
|
86
|
+
/**
|
|
87
|
+
* Second lookup path, consulted when a tool call names nothing executable
|
|
88
|
+
* in the caller's `options.tools` — used by adapters supporting mid-turn
|
|
89
|
+
* discovery to hydrate a tool the model just found via `search_tools`, or a
|
|
90
|
+
* deferred-catalog tool called by its advertised name, before the engine
|
|
91
|
+
* falls through to TOOL_NOT_FOUND and the breaker strike. See the design
|
|
92
|
+
* decision above for why this is a narrow lookup and not a dispatch
|
|
93
|
+
* override.
|
|
94
|
+
*/
|
|
95
|
+
readonly resolveToolOnMiss?: (name: string) => {
|
|
96
|
+
execute: (args: Record<string, unknown>, opts: unknown) => Promise<unknown>;
|
|
97
|
+
} | undefined;
|
|
46
98
|
buildStepRequest(conversation: TConversation, step: number): AgenticLoopStepRequest;
|
|
47
99
|
executeStep(request: AgenticLoopStepRequest, channel: {
|
|
48
100
|
push(chunk: {
|
|
@@ -833,6 +833,15 @@ export type BedrockContentBlock = {
|
|
|
833
833
|
toolUse?: BedrockToolUse;
|
|
834
834
|
toolResult?: BedrockToolResult;
|
|
835
835
|
};
|
|
836
|
+
/**
|
|
837
|
+
* A Bedrock content block still being assembled from a ConverseStream event
|
|
838
|
+
* sequence. `_inputBuffer` holds the partial tool-call JSON that arrives
|
|
839
|
+
* across several `contentBlockDelta` events and is parsed away at
|
|
840
|
+
* `contentBlockStop`, so it never appears on a finished block.
|
|
841
|
+
*/
|
|
842
|
+
export type BedrockPendingContentBlock = BedrockContentBlock & {
|
|
843
|
+
_inputBuffer?: string;
|
|
844
|
+
};
|
|
836
845
|
/**
|
|
837
846
|
* Bedrock message structure
|
|
838
847
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.7.0",
|
|
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": {
|
|
@@ -211,7 +211,8 @@
|
|
|
211
211
|
"test:model-pool": "npx tsx test/continuous-test-suite-model-pool.ts",
|
|
212
212
|
"test:vector-chroma": "npx tsx test/continuous-test-suite-vector-chroma.ts",
|
|
213
213
|
"test:vector-pgvector": "npx tsx test/continuous-test-suite-vector-pgvector.ts",
|
|
214
|
-
"test:vector-pinecone": "npx tsx test/continuous-test-suite-vector-pinecone.ts"
|
|
214
|
+
"test:vector-pinecone": "npx tsx test/continuous-test-suite-vector-pinecone.ts",
|
|
215
|
+
"test:bedrock-loop-characterization": "tsx test/continuous-test-suite-bedrock-loop-characterization.ts"
|
|
215
216
|
},
|
|
216
217
|
"files": [
|
|
217
218
|
"dist",
|