@librechat/agents 3.2.63 → 3.2.65
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/dist/cjs/graphs/Graph.cjs +3 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +73 -9
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +41 -9
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +3 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/stream_events.cjs +337 -0
- package/dist/cjs/llm/anthropic/utils/stream_events.cjs.map +1 -0
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +82 -34
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-scraper.cjs +165 -0
- package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/crw-search.cjs +105 -0
- package/dist/cjs/tools/search/crw-search.cjs.map +1 -0
- package/dist/cjs/tools/search/search.cjs +4 -2
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +15 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +3 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +73 -9
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +41 -9
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +3 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/stream_events.mjs +337 -0
- package/dist/esm/llm/anthropic/utils/stream_events.mjs.map +1 -0
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +82 -34
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/tools/search/crw-scraper.mjs +163 -0
- package/dist/esm/tools/search/crw-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/crw-search.mjs +103 -0
- package/dist/esm/tools/search/crw-search.mjs.map +1 -0
- package/dist/esm/tools/search/search.mjs +4 -2
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +15 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +2 -0
- package/dist/types/llm/anthropic/types.d.ts +2 -0
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -2
- package/dist/types/llm/anthropic/utils/stream_events.d.ts +25 -0
- package/dist/types/tools/search/crw-scraper.d.ts +41 -0
- package/dist/types/tools/search/crw-search.d.ts +4 -0
- package/dist/types/tools/search/types.d.ts +88 -3
- package/package.json +1 -1
- package/src/graphs/Graph.ts +15 -0
- package/src/llm/anthropic/index.ts +134 -10
- package/src/llm/anthropic/inherited-content-utils.spec.ts +10 -5
- package/src/llm/anthropic/inherited-stream-events.spec.ts +513 -9
- package/src/llm/anthropic/llm.spec.ts +100 -16
- package/src/llm/anthropic/types.ts +3 -0
- package/src/llm/anthropic/utils/message_inputs.ts +60 -3
- package/src/llm/anthropic/utils/message_outputs.ts +10 -2
- package/src/llm/anthropic/utils/stream_events.ts +471 -0
- package/src/llm/bedrock/utils/message_inputs.test.ts +276 -1
- package/src/llm/bedrock/utils/message_inputs.ts +121 -73
- package/src/tools/search/crw-scraper.ts +244 -0
- package/src/tools/search/crw-search.ts +167 -0
- package/src/tools/search/crw.test.ts +836 -0
- package/src/tools/search/search.ts +7 -1
- package/src/tools/search/tool.ts +23 -3
- package/src/tools/search/types.ts +103 -3
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
const require_message_outputs = require("./message_outputs.cjs");
|
|
2
|
+
//#region src/llm/anthropic/utils/stream_events.ts
|
|
3
|
+
/**
|
|
4
|
+
* Convert an async iterable of raw Anthropic stream events into
|
|
5
|
+
* LangChain `ChatModelStreamEvent`s with typed deltas.
|
|
6
|
+
*/
|
|
7
|
+
async function* convertAnthropicStream(source, options = {}) {
|
|
8
|
+
const shouldStreamUsage = options.streamUsage ?? true;
|
|
9
|
+
const blockAccumulators = /* @__PURE__ */ new Map();
|
|
10
|
+
let usageSnapshot;
|
|
11
|
+
let eventUsage;
|
|
12
|
+
let stopReason = null;
|
|
13
|
+
for await (const data of source) switch (data.type) {
|
|
14
|
+
case "message_start": {
|
|
15
|
+
const { usage, id, model } = data.message;
|
|
16
|
+
if (shouldStreamUsage) {
|
|
17
|
+
eventUsage = {
|
|
18
|
+
inputTokens: usage.input_tokens,
|
|
19
|
+
cacheCreationInputTokens: usage.cache_creation_input_tokens ?? 0,
|
|
20
|
+
cacheReadInputTokens: usage.cache_read_input_tokens ?? 0,
|
|
21
|
+
outputTokens: usage.output_tokens
|
|
22
|
+
};
|
|
23
|
+
usageSnapshot = buildUsageSnapshot(eventUsage);
|
|
24
|
+
}
|
|
25
|
+
yield {
|
|
26
|
+
event: "message-start",
|
|
27
|
+
id,
|
|
28
|
+
...usageSnapshot ? { usage: usageSnapshot } : {}
|
|
29
|
+
};
|
|
30
|
+
yield {
|
|
31
|
+
event: "provider",
|
|
32
|
+
provider: "anthropic",
|
|
33
|
+
name: "message_start",
|
|
34
|
+
payload: {
|
|
35
|
+
model,
|
|
36
|
+
id
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "message_delta":
|
|
42
|
+
stopReason = data.delta.stop_reason;
|
|
43
|
+
if (shouldStreamUsage) {
|
|
44
|
+
eventUsage = updateEventUsage(eventUsage, data.usage);
|
|
45
|
+
usageSnapshot = buildUsageSnapshot(eventUsage);
|
|
46
|
+
yield {
|
|
47
|
+
event: "usage",
|
|
48
|
+
usage: usageSnapshot
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if ("context_management" in data.delta && data.delta.context_management != null) yield {
|
|
52
|
+
event: "provider",
|
|
53
|
+
provider: "anthropic",
|
|
54
|
+
name: "context_management",
|
|
55
|
+
payload: data.delta.context_management
|
|
56
|
+
};
|
|
57
|
+
break;
|
|
58
|
+
case "message_stop":
|
|
59
|
+
yield {
|
|
60
|
+
event: "message-finish",
|
|
61
|
+
reason: mapStopReason(stopReason),
|
|
62
|
+
...usageSnapshot ? { usage: usageSnapshot } : {},
|
|
63
|
+
responseMetadata: { model_provider: "anthropic" }
|
|
64
|
+
};
|
|
65
|
+
break;
|
|
66
|
+
case "error": {
|
|
67
|
+
const streamError = new Error(data.error.message);
|
|
68
|
+
streamError.name = data.error.type;
|
|
69
|
+
throw streamError;
|
|
70
|
+
}
|
|
71
|
+
case "content_block_start": {
|
|
72
|
+
const { index, content_block } = data;
|
|
73
|
+
const mapped = mapBlockToContentBlock(content_block, index);
|
|
74
|
+
blockAccumulators.set(index, { ...mapped });
|
|
75
|
+
yield {
|
|
76
|
+
event: "content-block-start",
|
|
77
|
+
index,
|
|
78
|
+
content: mapped
|
|
79
|
+
};
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "content_block_delta": {
|
|
83
|
+
const { index, delta } = data;
|
|
84
|
+
const acc = blockAccumulators.get(index);
|
|
85
|
+
if (!acc) break;
|
|
86
|
+
const { contentDelta, accumulated } = applyAnthropicDelta(acc, delta);
|
|
87
|
+
blockAccumulators.set(index, accumulated);
|
|
88
|
+
yield {
|
|
89
|
+
event: "content-block-delta",
|
|
90
|
+
index,
|
|
91
|
+
delta: contentDelta
|
|
92
|
+
};
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "content_block_stop": {
|
|
96
|
+
const { index } = data;
|
|
97
|
+
const acc = blockAccumulators.get(index);
|
|
98
|
+
if (!acc) break;
|
|
99
|
+
yield {
|
|
100
|
+
event: "content-block-finish",
|
|
101
|
+
index,
|
|
102
|
+
content: finalizeBlock(acc)
|
|
103
|
+
};
|
|
104
|
+
blockAccumulators.delete(index);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
default: {
|
|
108
|
+
const providerData = data;
|
|
109
|
+
yield {
|
|
110
|
+
event: "provider",
|
|
111
|
+
provider: "anthropic",
|
|
112
|
+
name: providerData.type,
|
|
113
|
+
payload: providerData
|
|
114
|
+
};
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function mapStopReason(stopReason) {
|
|
120
|
+
switch (stopReason) {
|
|
121
|
+
case "end_turn":
|
|
122
|
+
case "stop_sequence": return "stop";
|
|
123
|
+
case "tool_use": return "tool_use";
|
|
124
|
+
case "max_tokens":
|
|
125
|
+
case "model_context_window_exceeded": return "length";
|
|
126
|
+
case "refusal": return "content_filter";
|
|
127
|
+
default: return "stop";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function updateEventUsage(previous, current) {
|
|
131
|
+
return {
|
|
132
|
+
inputTokens: getCumulativeUsageValue(current.input_tokens, previous?.inputTokens),
|
|
133
|
+
cacheCreationInputTokens: getCumulativeUsageValue(current.cache_creation_input_tokens, previous?.cacheCreationInputTokens),
|
|
134
|
+
cacheReadInputTokens: getCumulativeUsageValue(current.cache_read_input_tokens, previous?.cacheReadInputTokens),
|
|
135
|
+
outputTokens: getCumulativeUsageValue(current.output_tokens, previous?.outputTokens)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function getCumulativeUsageValue(current, previous) {
|
|
139
|
+
return Math.max(previous ?? 0, current ?? 0);
|
|
140
|
+
}
|
|
141
|
+
function buildUsageSnapshot(usage) {
|
|
142
|
+
const metadata = require_message_outputs.getAnthropicUsageMetadata({
|
|
143
|
+
input_tokens: usage.inputTokens,
|
|
144
|
+
output_tokens: usage.outputTokens,
|
|
145
|
+
cache_creation_input_tokens: usage.cacheCreationInputTokens,
|
|
146
|
+
cache_read_input_tokens: usage.cacheReadInputTokens
|
|
147
|
+
});
|
|
148
|
+
if (metadata == null) throw new Error("Anthropic usage metadata was not created");
|
|
149
|
+
return metadata;
|
|
150
|
+
}
|
|
151
|
+
function getStringField(record, key) {
|
|
152
|
+
const value = record[key];
|
|
153
|
+
return typeof value === "string" ? value : "";
|
|
154
|
+
}
|
|
155
|
+
function getArrayField(record, key) {
|
|
156
|
+
const value = record[key];
|
|
157
|
+
return Array.isArray(value) ? value : [];
|
|
158
|
+
}
|
|
159
|
+
function mapBlockToContentBlock(block, index) {
|
|
160
|
+
switch (block.type) {
|
|
161
|
+
case "text": return {
|
|
162
|
+
type: "text",
|
|
163
|
+
text: block.text,
|
|
164
|
+
...block.citations != null ? { citations: block.citations } : {},
|
|
165
|
+
index
|
|
166
|
+
};
|
|
167
|
+
case "thinking": return {
|
|
168
|
+
type: "reasoning",
|
|
169
|
+
reasoning: block.thinking,
|
|
170
|
+
index
|
|
171
|
+
};
|
|
172
|
+
case "redacted_thinking": return {
|
|
173
|
+
...block,
|
|
174
|
+
index
|
|
175
|
+
};
|
|
176
|
+
case "tool_use": return {
|
|
177
|
+
type: "tool_call_chunk",
|
|
178
|
+
id: block.id,
|
|
179
|
+
name: block.name,
|
|
180
|
+
args: "",
|
|
181
|
+
index
|
|
182
|
+
};
|
|
183
|
+
case "server_tool_use": return {
|
|
184
|
+
type: "server_tool_call_chunk",
|
|
185
|
+
id: block.id,
|
|
186
|
+
name: block.name,
|
|
187
|
+
args: "",
|
|
188
|
+
index
|
|
189
|
+
};
|
|
190
|
+
case "web_search_tool_result": return {
|
|
191
|
+
...block,
|
|
192
|
+
index
|
|
193
|
+
};
|
|
194
|
+
case "compaction": return {
|
|
195
|
+
...block,
|
|
196
|
+
index
|
|
197
|
+
};
|
|
198
|
+
default: return {
|
|
199
|
+
type: "non_standard",
|
|
200
|
+
value: { ...block },
|
|
201
|
+
index
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Map an Anthropic content_block_delta to a content block delta
|
|
207
|
+
* and update the accumulated state.
|
|
208
|
+
*/
|
|
209
|
+
function applyAnthropicDelta(accumulated, delta) {
|
|
210
|
+
const rawDelta = delta;
|
|
211
|
+
switch (delta.type) {
|
|
212
|
+
case "text_delta": return {
|
|
213
|
+
contentDelta: {
|
|
214
|
+
type: "text-delta",
|
|
215
|
+
text: delta.text
|
|
216
|
+
},
|
|
217
|
+
accumulated: {
|
|
218
|
+
...accumulated,
|
|
219
|
+
text: getStringField(accumulated, "text") + delta.text
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
case "thinking_delta": return {
|
|
223
|
+
contentDelta: {
|
|
224
|
+
type: "reasoning-delta",
|
|
225
|
+
reasoning: delta.thinking
|
|
226
|
+
},
|
|
227
|
+
accumulated: {
|
|
228
|
+
...accumulated,
|
|
229
|
+
reasoning: getStringField(accumulated, "reasoning") + delta.thinking
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
case "input_json_delta": {
|
|
233
|
+
const newArgs = getStringField(accumulated, "args") + delta.partial_json;
|
|
234
|
+
return {
|
|
235
|
+
contentDelta: {
|
|
236
|
+
type: "block-delta",
|
|
237
|
+
fields: {
|
|
238
|
+
type: getStringField(accumulated, "type"),
|
|
239
|
+
args: newArgs
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
accumulated: {
|
|
243
|
+
...accumulated,
|
|
244
|
+
args: newArgs
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
case "citations_delta": {
|
|
249
|
+
const citations = [...getArrayField(accumulated, "citations"), delta.citation];
|
|
250
|
+
return {
|
|
251
|
+
contentDelta: {
|
|
252
|
+
type: "block-delta",
|
|
253
|
+
fields: {
|
|
254
|
+
type: getStringField(accumulated, "type"),
|
|
255
|
+
citations
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
accumulated: {
|
|
259
|
+
...accumulated,
|
|
260
|
+
citations
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
case "signature_delta": return {
|
|
265
|
+
contentDelta: {
|
|
266
|
+
type: "block-delta",
|
|
267
|
+
fields: {
|
|
268
|
+
type: getStringField(accumulated, "type"),
|
|
269
|
+
signature: delta.signature
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
accumulated: {
|
|
273
|
+
...accumulated,
|
|
274
|
+
signature: delta.signature
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
case "compaction_delta": {
|
|
278
|
+
const previousContent = accumulated.content;
|
|
279
|
+
const content = delta.content == null ? previousContent ?? null : getStringField(accumulated, "content") + delta.content;
|
|
280
|
+
return {
|
|
281
|
+
contentDelta: {
|
|
282
|
+
type: "block-delta",
|
|
283
|
+
fields: {
|
|
284
|
+
type: "compaction",
|
|
285
|
+
content,
|
|
286
|
+
encrypted_content: delta.encrypted_content
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
accumulated: {
|
|
290
|
+
...accumulated,
|
|
291
|
+
content,
|
|
292
|
+
encrypted_content: delta.encrypted_content
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
default: return {
|
|
297
|
+
contentDelta: {
|
|
298
|
+
type: "block-delta",
|
|
299
|
+
fields: {
|
|
300
|
+
type: getStringField(accumulated, "type"),
|
|
301
|
+
...rawDelta
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
accumulated
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function finalizeBlock(accumulated) {
|
|
309
|
+
if (accumulated.type === "tool_call_chunk" || accumulated.type === "server_tool_call_chunk") {
|
|
310
|
+
const finalType = accumulated.type === "tool_call_chunk" ? "tool_call" : "server_tool_call";
|
|
311
|
+
const args = getStringField(accumulated, "args");
|
|
312
|
+
let parsedArgs;
|
|
313
|
+
try {
|
|
314
|
+
parsedArgs = JSON.parse(args || "{}");
|
|
315
|
+
} catch {
|
|
316
|
+
return {
|
|
317
|
+
type: "invalid_tool_call",
|
|
318
|
+
id: getStringField(accumulated, "id"),
|
|
319
|
+
name: getStringField(accumulated, "name"),
|
|
320
|
+
args,
|
|
321
|
+
error: "Failed to parse tool call arguments as JSON"
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
type: finalType,
|
|
326
|
+
id: getStringField(accumulated, "id"),
|
|
327
|
+
name: getStringField(accumulated, "name"),
|
|
328
|
+
args: parsedArgs
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const { index: _index, ...rest } = accumulated;
|
|
332
|
+
return rest;
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
335
|
+
exports.convertAnthropicStream = convertAnthropicStream;
|
|
336
|
+
|
|
337
|
+
//# sourceMappingURL=stream_events.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream_events.cjs","names":["getAnthropicUsageMetadata"],"sources":["../../../../../src/llm/anthropic/utils/stream_events.ts"],"sourcesContent":["/**\n * Converts a raw Anthropic SSE event stream into LangChain ChatModelStreamEvents.\n *\n * @module\n */\n\nimport type {\n ChatModelStreamEvent,\n ContentBlockDelta,\n FinishReason,\n} from '@langchain/core/language_models/event';\nimport type { ContentBlock, UsageMetadata } from '@langchain/core/messages';\nimport type {\n AnthropicCompactionBlock,\n AnthropicCompactionContentBlockDelta,\n AnthropicMessageStreamEvent,\n} from '../types';\nimport type { AnthropicUsageData } from './message_outputs';\nimport { getAnthropicUsageMetadata } from './message_outputs';\n\ntype AnthropicContentBlock =\n | Extract<\n AnthropicMessageStreamEvent,\n { type: 'content_block_start' }\n >['content_block']\n | AnthropicCompactionBlock;\n\ntype AnthropicContentDelta =\n | Extract<\n AnthropicMessageStreamEvent,\n { type: 'content_block_delta' }\n >['delta']\n | AnthropicCompactionContentBlockDelta;\n\ninterface AnthropicStreamErrorEvent {\n type: 'error';\n error: {\n type: string;\n message: string;\n };\n request_id?: string | null;\n}\n\ntype AnthropicStreamInputEvent =\n | AnthropicMessageStreamEvent\n | AnthropicStreamErrorEvent;\n\ntype BlockAccumulator = Record<string, unknown>;\n\ninterface AnthropicEventUsage {\n inputTokens: number;\n cacheCreationInputTokens: number;\n cacheReadInputTokens: number;\n outputTokens: number;\n}\n\n// ─── Public API ─────────────────────────────────────────────────\n\nexport interface ConvertAnthropicStreamOptions {\n streamUsage?: boolean;\n}\n\n/**\n * Convert an async iterable of raw Anthropic stream events into\n * LangChain `ChatModelStreamEvent`s with typed deltas.\n */\nexport async function* convertAnthropicStream(\n source: AsyncIterable<AnthropicStreamInputEvent>,\n options: ConvertAnthropicStreamOptions = {}\n): AsyncGenerator<ChatModelStreamEvent> {\n const shouldStreamUsage = options.streamUsage ?? true;\n\n // Track accumulated state per content block (for finalization)\n const blockAccumulators = new Map<number, BlockAccumulator>();\n let usageSnapshot: UsageMetadata | undefined;\n let eventUsage: AnthropicEventUsage | undefined;\n let stopReason: string | null = null;\n\n for await (const data of source) {\n switch (data.type) {\n // ── Message lifecycle ──────────────────────────────────\n case 'message_start': {\n const { usage, id, model } = data.message;\n if (shouldStreamUsage) {\n eventUsage = {\n inputTokens: usage.input_tokens,\n cacheCreationInputTokens: usage.cache_creation_input_tokens ?? 0,\n cacheReadInputTokens: usage.cache_read_input_tokens ?? 0,\n outputTokens: usage.output_tokens,\n };\n usageSnapshot = buildUsageSnapshot(eventUsage);\n }\n yield {\n event: 'message-start' as const,\n id,\n ...(usageSnapshot ? { usage: usageSnapshot } : {}),\n };\n yield {\n event: 'provider' as const,\n provider: 'anthropic',\n name: 'message_start',\n payload: { model, id },\n };\n break;\n }\n\n case 'message_delta': {\n stopReason = data.delta.stop_reason;\n if (shouldStreamUsage) {\n eventUsage = updateEventUsage(eventUsage, data.usage);\n usageSnapshot = buildUsageSnapshot(eventUsage);\n yield { event: 'usage' as const, usage: usageSnapshot };\n }\n if (\n 'context_management' in data.delta &&\n data.delta.context_management != null\n ) {\n yield {\n event: 'provider' as const,\n provider: 'anthropic',\n name: 'context_management',\n payload: data.delta.context_management,\n };\n }\n break;\n }\n\n case 'message_stop': {\n const finishEvent = {\n event: 'message-finish' as const,\n reason: mapStopReason(stopReason),\n ...(usageSnapshot ? { usage: usageSnapshot } : {}),\n responseMetadata: { model_provider: 'anthropic' },\n };\n yield finishEvent;\n break;\n }\n\n case 'error': {\n const streamError = new Error(data.error.message);\n streamError.name = data.error.type;\n throw streamError;\n }\n\n // ── Content block lifecycle ───────────────────────────\n case 'content_block_start': {\n const { index, content_block } = data;\n const mapped = mapBlockToContentBlock(content_block, index);\n blockAccumulators.set(index, { ...mapped });\n yield {\n event: 'content-block-start' as const,\n index,\n content: mapped as unknown as ContentBlock,\n };\n break;\n }\n\n case 'content_block_delta': {\n const { index, delta } = data;\n const acc = blockAccumulators.get(index);\n if (!acc) break;\n\n const { contentDelta, accumulated } = applyAnthropicDelta(acc, delta);\n blockAccumulators.set(index, accumulated);\n\n yield {\n event: 'content-block-delta' as const,\n index,\n delta: contentDelta,\n };\n break;\n }\n\n case 'content_block_stop': {\n const { index } = data;\n const acc = blockAccumulators.get(index);\n if (!acc) break;\n\n const finalized = finalizeBlock(acc);\n yield {\n event: 'content-block-finish' as const,\n index,\n content: finalized,\n };\n blockAccumulators.delete(index);\n break;\n }\n\n // ── Unhandled → provider passthrough ───────────────────\n default: {\n const providerData = data as AnthropicMessageStreamEvent;\n yield {\n event: 'provider' as const,\n provider: 'anthropic',\n name: providerData.type,\n payload: providerData,\n };\n break;\n }\n }\n }\n}\n\n// ─── Internal helpers ───────────────────────────────────────────\n\nfunction mapStopReason(stopReason: string | null | undefined): FinishReason {\n switch (stopReason) {\n case 'end_turn':\n case 'stop_sequence':\n return 'stop';\n case 'tool_use':\n return 'tool_use';\n case 'max_tokens':\n case 'model_context_window_exceeded':\n return 'length';\n case 'refusal':\n return 'content_filter';\n default:\n return 'stop';\n }\n}\n\nfunction updateEventUsage(\n previous: AnthropicEventUsage | undefined,\n current: AnthropicUsageData\n): AnthropicEventUsage {\n return {\n inputTokens: getCumulativeUsageValue(\n current.input_tokens,\n previous?.inputTokens\n ),\n cacheCreationInputTokens: getCumulativeUsageValue(\n current.cache_creation_input_tokens,\n previous?.cacheCreationInputTokens\n ),\n cacheReadInputTokens: getCumulativeUsageValue(\n current.cache_read_input_tokens,\n previous?.cacheReadInputTokens\n ),\n outputTokens: getCumulativeUsageValue(\n current.output_tokens,\n previous?.outputTokens\n ),\n };\n}\n\nfunction getCumulativeUsageValue(\n current: number | null | undefined,\n previous: number | undefined\n): number {\n return Math.max(previous ?? 0, current ?? 0);\n}\n\nfunction buildUsageSnapshot(usage: AnthropicEventUsage): UsageMetadata {\n const metadata = getAnthropicUsageMetadata({\n input_tokens: usage.inputTokens,\n output_tokens: usage.outputTokens,\n cache_creation_input_tokens: usage.cacheCreationInputTokens,\n cache_read_input_tokens: usage.cacheReadInputTokens,\n });\n if (metadata == null) {\n throw new Error('Anthropic usage metadata was not created');\n }\n return metadata;\n}\n\nfunction getStringField(record: BlockAccumulator, key: string): string {\n const value = record[key];\n return typeof value === 'string' ? value : '';\n}\n\nfunction getArrayField(record: BlockAccumulator, key: string): unknown[] {\n const value = record[key];\n return Array.isArray(value) ? value : [];\n}\n\nfunction mapBlockToContentBlock(\n block: AnthropicContentBlock,\n index: number\n): BlockAccumulator {\n switch (block.type) {\n case 'text':\n return {\n type: 'text' as const,\n text: block.text,\n ...(block.citations != null ? { citations: block.citations } : {}),\n index,\n };\n case 'thinking':\n return {\n type: 'reasoning' as const,\n reasoning: block.thinking,\n index,\n };\n case 'redacted_thinking':\n return { ...block, index };\n case 'tool_use':\n return {\n type: 'tool_call_chunk' as const,\n id: block.id,\n name: block.name,\n args: '',\n index,\n };\n case 'server_tool_use':\n return {\n type: 'server_tool_call_chunk' as const,\n id: block.id,\n name: block.name,\n args: '',\n index,\n };\n case 'web_search_tool_result':\n return { ...block, index };\n case 'compaction':\n return { ...block, index };\n default:\n return { type: 'non_standard' as const, value: { ...block }, index };\n }\n}\n\n/**\n * Map an Anthropic content_block_delta to a content block delta\n * and update the accumulated state.\n */\nfunction applyAnthropicDelta(\n accumulated: BlockAccumulator,\n delta: AnthropicContentDelta\n): {\n contentDelta: ContentBlockDelta;\n accumulated: BlockAccumulator;\n} {\n const rawDelta = delta as unknown as Record<string, unknown>;\n switch (delta.type) {\n case 'text_delta':\n return {\n contentDelta: { type: 'text-delta' as const, text: delta.text },\n accumulated: {\n ...accumulated,\n text: getStringField(accumulated, 'text') + delta.text,\n },\n };\n\n case 'thinking_delta':\n return {\n contentDelta: {\n type: 'reasoning-delta' as const,\n reasoning: delta.thinking,\n },\n accumulated: {\n ...accumulated,\n reasoning: getStringField(accumulated, 'reasoning') + delta.thinking,\n },\n };\n\n case 'input_json_delta': {\n const newArgs = getStringField(accumulated, 'args') + delta.partial_json;\n return {\n contentDelta: {\n type: 'block-delta' as const,\n fields: {\n type: getStringField(accumulated, 'type'),\n args: newArgs,\n },\n },\n accumulated: { ...accumulated, args: newArgs },\n };\n }\n\n case 'citations_delta': {\n const citations = [\n ...getArrayField(accumulated, 'citations'),\n delta.citation,\n ];\n return {\n contentDelta: {\n type: 'block-delta' as const,\n fields: {\n type: getStringField(accumulated, 'type'),\n citations,\n },\n },\n accumulated: {\n ...accumulated,\n citations,\n },\n };\n }\n\n case 'signature_delta':\n return {\n contentDelta: {\n type: 'block-delta' as const,\n fields: {\n type: getStringField(accumulated, 'type'),\n signature: delta.signature,\n },\n },\n accumulated: { ...accumulated, signature: delta.signature },\n };\n\n case 'compaction_delta': {\n const previousContent = accumulated.content;\n const content =\n delta.content == null\n ? (previousContent ?? null)\n : getStringField(accumulated, 'content') + delta.content;\n return {\n contentDelta: {\n type: 'block-delta' as const,\n fields: {\n type: 'compaction',\n content,\n encrypted_content: delta.encrypted_content,\n },\n },\n accumulated: {\n ...accumulated,\n content,\n encrypted_content: delta.encrypted_content,\n },\n };\n }\n\n default:\n return {\n contentDelta: {\n type: 'block-delta' as const,\n fields: {\n type: getStringField(accumulated, 'type'),\n ...rawDelta,\n },\n },\n accumulated,\n };\n }\n}\n\nfunction finalizeBlock(accumulated: BlockAccumulator): ContentBlock {\n if (\n accumulated.type === 'tool_call_chunk' ||\n accumulated.type === 'server_tool_call_chunk'\n ) {\n const finalType =\n accumulated.type === 'tool_call_chunk'\n ? ('tool_call' as const)\n : ('server_tool_call' as const);\n const args = getStringField(accumulated, 'args');\n let parsedArgs: unknown;\n try {\n parsedArgs = JSON.parse(args || '{}');\n } catch {\n return {\n type: 'invalid_tool_call' as const,\n id: getStringField(accumulated, 'id'),\n name: getStringField(accumulated, 'name'),\n args,\n error: 'Failed to parse tool call arguments as JSON',\n } as ContentBlock.Tools.InvalidToolCall;\n }\n return {\n type: finalType,\n id: getStringField(accumulated, 'id'),\n name: getStringField(accumulated, 'name'),\n args: parsedArgs,\n } as ContentBlock;\n }\n\n const { index: _index, ...rest } = accumulated;\n return rest as ContentBlock;\n}\n"],"mappings":"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"}
|
|
@@ -43,27 +43,58 @@ function isSerializableBedrockReasoningBlock(content) {
|
|
|
43
43
|
}
|
|
44
44
|
return content.redactedContent != null && content.redactedContent !== "";
|
|
45
45
|
}
|
|
46
|
+
function appendSerializableBedrockTextBlock(contentBlocks, text) {
|
|
47
|
+
if (text === "") return false;
|
|
48
|
+
if (text.replace(/\n/g, "").trim() !== "") {
|
|
49
|
+
contentBlocks.push({ text });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
53
|
+
if (lastBlock == null || !("text" in lastBlock)) return false;
|
|
54
|
+
lastBlock.text = `${lastBlock.text}${text}`;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
46
57
|
/**
|
|
47
58
|
* Concatenate consecutive reasoning blocks in content array.
|
|
48
59
|
*/
|
|
49
60
|
function concatenateLangchainReasoningBlocks(content) {
|
|
50
61
|
const result = [];
|
|
51
|
-
|
|
62
|
+
let pendingReasoning;
|
|
63
|
+
const flushPendingReasoning = () => {
|
|
64
|
+
if (pendingReasoning != null) {
|
|
65
|
+
result.push(pendingReasoning);
|
|
66
|
+
pendingReasoning = void 0;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
for (const block of content) {
|
|
70
|
+
if (block.type !== "reasoning_content") {
|
|
71
|
+
flushPendingReasoning();
|
|
72
|
+
result.push(block);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
52
75
|
const currentReasoning = block;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
76
|
+
if (currentReasoning.reasoningText != null) {
|
|
77
|
+
const previousText = pendingReasoning?.reasoningText?.text;
|
|
78
|
+
const previousSignature = pendingReasoning?.reasoningText?.signature;
|
|
79
|
+
const { text, signature } = currentReasoning.reasoningText;
|
|
80
|
+
const mergedReasoningText = {};
|
|
81
|
+
if (previousText !== void 0 || text !== void 0) mergedReasoningText.text = (previousText ?? "") + (text ?? "");
|
|
82
|
+
if (previousSignature !== void 0 || signature !== void 0) mergedReasoningText.signature = (previousSignature ?? "") + (signature ?? "");
|
|
83
|
+
pendingReasoning = {
|
|
84
|
+
type: "reasoning_content",
|
|
85
|
+
reasoningText: mergedReasoningText
|
|
86
|
+
};
|
|
87
|
+
if ("signature" in currentReasoning.reasoningText) flushPendingReasoning();
|
|
88
|
+
}
|
|
89
|
+
if (currentReasoning.redactedContent != null) {
|
|
90
|
+
flushPendingReasoning();
|
|
91
|
+
result.push({
|
|
92
|
+
type: "reasoning_content",
|
|
93
|
+
redactedContent: currentReasoning.redactedContent
|
|
94
|
+
});
|
|
64
95
|
}
|
|
65
|
-
|
|
66
|
-
|
|
96
|
+
}
|
|
97
|
+
flushPendingReasoning();
|
|
67
98
|
return result;
|
|
68
99
|
}
|
|
69
100
|
/**
|
|
@@ -328,16 +359,8 @@ function convertAIMessageToConverseMessage(msg) {
|
|
|
328
359
|
const concatenatedBlocks = concatenateLangchainReasoningBlocks(msg.content);
|
|
329
360
|
const contentBlocks = [];
|
|
330
361
|
concatenatedBlocks.forEach((block) => {
|
|
331
|
-
if (block.type === "text")
|
|
332
|
-
|
|
333
|
-
if (text === "") return;
|
|
334
|
-
if (text.replace(/\n/g, "").trim() === "") {
|
|
335
|
-
if (contentBlocks.length > 0) {
|
|
336
|
-
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
337
|
-
if ("text" in lastBlock) lastBlock.text = `${lastBlock.text}${text}`;
|
|
338
|
-
}
|
|
339
|
-
} else contentBlocks.push({ text });
|
|
340
|
-
} else if (block.type === "reasoning_content") {
|
|
362
|
+
if (block.type === "text") appendSerializableBedrockTextBlock(contentBlocks, block.text ?? "");
|
|
363
|
+
else if (block.type === "reasoning_content") {
|
|
341
364
|
const reasoningBlock = block;
|
|
342
365
|
if (!isSerializableBedrockReasoningBlock(reasoningBlock)) return;
|
|
343
366
|
contentBlocks.push({ reasoningContent: langchainReasoningBlockToBedrockReasoningBlock(reasoningBlock) });
|
|
@@ -370,33 +393,58 @@ function convertAIMessageToConverseMessage(msg) {
|
|
|
370
393
|
* (Implements PR #9766 fix for output_version v1 detection)
|
|
371
394
|
*/
|
|
372
395
|
function convertFromV1ToChatBedrockConverseMessage(msg) {
|
|
396
|
+
const contentBlocks = [];
|
|
373
397
|
const assistantMsg = {
|
|
374
398
|
role: "assistant",
|
|
375
|
-
content:
|
|
399
|
+
content: contentBlocks
|
|
376
400
|
};
|
|
401
|
+
let droppedUnserializableContent = false;
|
|
402
|
+
let unconvertedContentType;
|
|
377
403
|
if (Array.isArray(msg.content)) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
404
|
+
const concatenatedBlocks = concatenateLangchainReasoningBlocks(msg.content);
|
|
405
|
+
for (const block of concatenatedBlocks) if (typeof block === "string") {
|
|
406
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, block)) droppedUnserializableContent = true;
|
|
407
|
+
} else if (block.type === "text") {
|
|
408
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, block.text ?? "")) droppedUnserializableContent = true;
|
|
409
|
+
} else if (block.type === "tool_call") {
|
|
381
410
|
const toolCall = block;
|
|
382
|
-
|
|
411
|
+
contentBlocks.push({ toolUse: {
|
|
383
412
|
toolUseId: toolCall.id,
|
|
384
413
|
name: toolCall.name,
|
|
385
414
|
input: toolCall.args
|
|
386
415
|
} });
|
|
387
416
|
} else if (block.type === "reasoning") {
|
|
388
417
|
const reasoning = block;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
418
|
+
/** Bedrock Converse rejects `reasoningText` with a null/empty `text`
|
|
419
|
+
* (`Member must not be null`), e.g. when the producing model omitted
|
|
420
|
+
* reasoning text (`thinking.display: "omitted"`) — drop rather than send. */
|
|
421
|
+
if (reasoning.reasoning == null || reasoning.reasoning === "") {
|
|
422
|
+
droppedUnserializableContent = true;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
contentBlocks.push({ reasoningContent: { reasoningText: { text: reasoning.reasoning } } });
|
|
426
|
+
} else if (block.type === "reasoning_content") {
|
|
427
|
+
const reasoningBlock = block;
|
|
428
|
+
if (!isSerializableBedrockReasoningBlock(reasoningBlock)) {
|
|
429
|
+
droppedUnserializableContent = true;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
contentBlocks.push({ reasoningContent: langchainReasoningBlockToBedrockReasoningBlock(reasoningBlock) });
|
|
433
|
+
} else unconvertedContentType ??= block.type;
|
|
434
|
+
} else if (typeof msg.content === "string") {
|
|
435
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, msg.content)) droppedUnserializableContent = true;
|
|
436
|
+
}
|
|
392
437
|
if ((0, _langchain_core_messages.isAIMessage)(msg) && msg.tool_calls != null && msg.tool_calls.length > 0) {
|
|
393
|
-
const existingToolUseIds = new Set(
|
|
394
|
-
for (const tc of msg.tool_calls) if (!existingToolUseIds.has(tc.id ?? ""))
|
|
438
|
+
const existingToolUseIds = new Set(contentBlocks.filter((c) => "toolUse" in c).map((c) => c.toolUse.toolUseId));
|
|
439
|
+
for (const tc of msg.tool_calls) if (!existingToolUseIds.has(tc.id ?? "")) contentBlocks.push({ toolUse: {
|
|
395
440
|
toolUseId: tc.id,
|
|
396
441
|
name: tc.name,
|
|
397
442
|
input: tc.args
|
|
398
443
|
} });
|
|
399
444
|
}
|
|
445
|
+
const hasNoContent = contentBlocks.length === 0;
|
|
446
|
+
if (hasNoContent && unconvertedContentType != null) throw new Error(`Unsupported v1 content block type: ${unconvertedContentType}`);
|
|
447
|
+
if (hasNoContent && droppedUnserializableContent) contentBlocks.push({ text: BEDROCK_EMPTY_TEXT_PLACEHOLDER });
|
|
400
448
|
return assistantMsg;
|
|
401
449
|
}
|
|
402
450
|
/**
|