@librechat/agents 3.0.18 → 3.0.20
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 +6 -4
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +3 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +87 -14
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +179 -6
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +6 -4
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/messages/cache.mjs +86 -15
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +179 -7
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/types/messages/cache.d.ts +16 -0
- package/dist/types/messages/format.d.ts +14 -0
- package/package.json +2 -1
- package/src/graphs/Graph.ts +8 -4
- package/src/messages/cache.test.ts +499 -3
- package/src/messages/cache.ts +115 -25
- package/src/messages/format.ts +231 -6
- package/src/messages/labelContentByAgent.test.ts +887 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +116 -10
- package/src/scripts/test-parallel-agent-labeling.ts +325 -0
package/src/messages/cache.ts
CHANGED
|
@@ -9,6 +9,9 @@ type MessageWithContent = {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Anthropic API: Adds cache control to the appropriate user messages in the payload.
|
|
12
|
+
* Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,
|
|
13
|
+
* then adds fresh cache control to the last 2 user messages in a single backward pass.
|
|
14
|
+
* This ensures we don't accumulate stale cache points across multiple turns.
|
|
12
15
|
* @param messages - The array of message objects.
|
|
13
16
|
* @returns - The updated array of message objects with cache control added.
|
|
14
17
|
*/
|
|
@@ -22,15 +25,26 @@ export function addCacheControl<T extends AnthropicMessage | BaseMessage>(
|
|
|
22
25
|
const updatedMessages = [...messages];
|
|
23
26
|
let userMessagesModified = 0;
|
|
24
27
|
|
|
25
|
-
for (
|
|
26
|
-
let i = updatedMessages.length - 1;
|
|
27
|
-
i >= 0 && userMessagesModified < 2;
|
|
28
|
-
i--
|
|
29
|
-
) {
|
|
28
|
+
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
30
29
|
const message = updatedMessages[i];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const isUserMessage =
|
|
31
|
+
('getType' in message && message.getType() === 'human') ||
|
|
32
|
+
('role' in message && message.role === 'user');
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(message.content)) {
|
|
35
|
+
message.content = message.content.filter(
|
|
36
|
+
(block) => !isCachePoint(block as MessageContentComplex)
|
|
37
|
+
) as typeof message.content;
|
|
38
|
+
|
|
39
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
40
|
+
const block = message.content[j] as Record<string, unknown>;
|
|
41
|
+
if ('cache_control' in block) {
|
|
42
|
+
delete block.cache_control;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (userMessagesModified >= 2 || !isUserMessage) {
|
|
34
48
|
continue;
|
|
35
49
|
}
|
|
36
50
|
|
|
@@ -60,10 +74,77 @@ export function addCacheControl<T extends AnthropicMessage | BaseMessage>(
|
|
|
60
74
|
return updatedMessages;
|
|
61
75
|
}
|
|
62
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Checks if a content block is a cache point
|
|
79
|
+
*/
|
|
80
|
+
function isCachePoint(block: MessageContentComplex): boolean {
|
|
81
|
+
return 'cachePoint' in block && !('type' in block);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Removes all Anthropic cache_control fields from messages
|
|
86
|
+
* Used when switching from Anthropic to Bedrock provider
|
|
87
|
+
*/
|
|
88
|
+
export function stripAnthropicCacheControl<T extends MessageWithContent>(
|
|
89
|
+
messages: T[]
|
|
90
|
+
): T[] {
|
|
91
|
+
if (!Array.isArray(messages)) {
|
|
92
|
+
return messages;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const updatedMessages = [...messages];
|
|
96
|
+
|
|
97
|
+
for (let i = 0; i < updatedMessages.length; i++) {
|
|
98
|
+
const message = updatedMessages[i];
|
|
99
|
+
const content = message.content;
|
|
100
|
+
|
|
101
|
+
if (Array.isArray(content)) {
|
|
102
|
+
for (let j = 0; j < content.length; j++) {
|
|
103
|
+
const block = content[j] as Record<string, unknown>;
|
|
104
|
+
if ('cache_control' in block) {
|
|
105
|
+
delete block.cache_control;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return updatedMessages;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Removes all Bedrock cachePoint blocks from messages
|
|
116
|
+
* Used when switching from Bedrock to Anthropic provider
|
|
117
|
+
*/
|
|
118
|
+
export function stripBedrockCacheControl<T extends MessageWithContent>(
|
|
119
|
+
messages: T[]
|
|
120
|
+
): T[] {
|
|
121
|
+
if (!Array.isArray(messages)) {
|
|
122
|
+
return messages;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const updatedMessages = [...messages];
|
|
126
|
+
|
|
127
|
+
for (let i = 0; i < updatedMessages.length; i++) {
|
|
128
|
+
const message = updatedMessages[i];
|
|
129
|
+
const content = message.content;
|
|
130
|
+
|
|
131
|
+
if (Array.isArray(content)) {
|
|
132
|
+
message.content = content.filter(
|
|
133
|
+
(block) => !isCachePoint(block as MessageContentComplex)
|
|
134
|
+
) as typeof content;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return updatedMessages;
|
|
139
|
+
}
|
|
140
|
+
|
|
63
141
|
/**
|
|
64
142
|
* Adds Bedrock Converse API cache points to the last two messages.
|
|
65
143
|
* Inserts `{ cachePoint: { type: 'default' } }` as a separate content block
|
|
66
144
|
* immediately after the last text block in each targeted message.
|
|
145
|
+
* Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,
|
|
146
|
+
* then adds fresh cache points to the last 2 messages in a single backward pass.
|
|
147
|
+
* This ensures we don't accumulate stale cache points across multiple turns.
|
|
67
148
|
* @param messages - The array of message objects.
|
|
68
149
|
* @returns - The updated array of message objects with cache points added.
|
|
69
150
|
*/
|
|
@@ -77,23 +158,32 @@ export function addBedrockCacheControl<
|
|
|
77
158
|
const updatedMessages: T[] = messages.slice();
|
|
78
159
|
let messagesModified = 0;
|
|
79
160
|
|
|
80
|
-
for (
|
|
81
|
-
let i = updatedMessages.length - 1;
|
|
82
|
-
i >= 0 && messagesModified < 2;
|
|
83
|
-
i--
|
|
84
|
-
) {
|
|
161
|
+
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
85
162
|
const message = updatedMessages[i];
|
|
86
|
-
|
|
87
|
-
if (
|
|
163
|
+
const isToolMessage =
|
|
88
164
|
'getType' in message &&
|
|
89
165
|
typeof message.getType === 'function' &&
|
|
90
|
-
message.getType() === 'tool'
|
|
91
|
-
) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
166
|
+
message.getType() === 'tool';
|
|
94
167
|
|
|
95
168
|
const content = message.content;
|
|
96
169
|
|
|
170
|
+
if (Array.isArray(content)) {
|
|
171
|
+
message.content = content.filter(
|
|
172
|
+
(block) => !isCachePoint(block)
|
|
173
|
+
) as typeof content;
|
|
174
|
+
|
|
175
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
176
|
+
const block = message.content[j] as Record<string, unknown>;
|
|
177
|
+
if ('cache_control' in block) {
|
|
178
|
+
delete block.cache_control;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (messagesModified >= 2 || isToolMessage) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
97
187
|
if (typeof content === 'string' && content === '') {
|
|
98
188
|
continue;
|
|
99
189
|
}
|
|
@@ -107,9 +197,9 @@ export function addBedrockCacheControl<
|
|
|
107
197
|
continue;
|
|
108
198
|
}
|
|
109
199
|
|
|
110
|
-
if (Array.isArray(content)) {
|
|
200
|
+
if (Array.isArray(message.content)) {
|
|
111
201
|
let hasCacheableContent = false;
|
|
112
|
-
for (const block of content) {
|
|
202
|
+
for (const block of message.content) {
|
|
113
203
|
if (block.type === ContentTypes.TEXT) {
|
|
114
204
|
if (typeof block.text === 'string' && block.text !== '') {
|
|
115
205
|
hasCacheableContent = true;
|
|
@@ -123,15 +213,15 @@ export function addBedrockCacheControl<
|
|
|
123
213
|
}
|
|
124
214
|
|
|
125
215
|
let inserted = false;
|
|
126
|
-
for (let j = content.length - 1; j >= 0; j--) {
|
|
127
|
-
const block = content[j] as MessageContentComplex;
|
|
216
|
+
for (let j = message.content.length - 1; j >= 0; j--) {
|
|
217
|
+
const block = message.content[j] as MessageContentComplex;
|
|
128
218
|
const type = (block as { type?: string }).type;
|
|
129
219
|
if (type === ContentTypes.TEXT || type === 'text') {
|
|
130
220
|
const text = (block as { text?: string }).text;
|
|
131
221
|
if (text === '' || text === undefined) {
|
|
132
222
|
continue;
|
|
133
223
|
}
|
|
134
|
-
content.splice(j + 1, 0, {
|
|
224
|
+
message.content.splice(j + 1, 0, {
|
|
135
225
|
cachePoint: { type: 'default' },
|
|
136
226
|
} as MessageContentComplex);
|
|
137
227
|
inserted = true;
|
|
@@ -139,7 +229,7 @@ export function addBedrockCacheControl<
|
|
|
139
229
|
}
|
|
140
230
|
}
|
|
141
231
|
if (!inserted) {
|
|
142
|
-
content.push({
|
|
232
|
+
message.content.push({
|
|
143
233
|
cachePoint: { type: 'default' },
|
|
144
234
|
} as MessageContentComplex);
|
|
145
235
|
}
|
package/src/messages/format.ts
CHANGED
|
@@ -13,6 +13,8 @@ import type { ToolCall } from '@langchain/core/messages/tool';
|
|
|
13
13
|
import type {
|
|
14
14
|
ExtendedMessageContent,
|
|
15
15
|
MessageContentComplex,
|
|
16
|
+
ReasoningContentText,
|
|
17
|
+
ToolCallContent,
|
|
16
18
|
ToolCallPart,
|
|
17
19
|
TPayload,
|
|
18
20
|
TMessage,
|
|
@@ -308,12 +310,12 @@ function formatAssistantMessage(
|
|
|
308
310
|
}
|
|
309
311
|
// Create a new AIMessage with this text and prepare for tool calls
|
|
310
312
|
lastAIMessage = new AIMessage({
|
|
311
|
-
content: part.text
|
|
313
|
+
content: part.text != null ? part.text : '',
|
|
312
314
|
});
|
|
313
315
|
formattedMessages.push(lastAIMessage);
|
|
314
316
|
} else if (part.type === ContentTypes.TOOL_CALL) {
|
|
315
317
|
// Skip malformed tool call entries without tool_call property
|
|
316
|
-
if (
|
|
318
|
+
if (part.tool_call == null) {
|
|
317
319
|
continue;
|
|
318
320
|
}
|
|
319
321
|
|
|
@@ -325,7 +327,10 @@ function formatAssistantMessage(
|
|
|
325
327
|
} = part.tool_call as ToolCallPart;
|
|
326
328
|
|
|
327
329
|
// Skip invalid tool calls that have no name AND no output
|
|
328
|
-
if (
|
|
330
|
+
if (
|
|
331
|
+
_tool_call.name == null ||
|
|
332
|
+
(_tool_call.name === '' && (output == null || output === ''))
|
|
333
|
+
) {
|
|
329
334
|
continue;
|
|
330
335
|
}
|
|
331
336
|
|
|
@@ -358,7 +363,7 @@ function formatAssistantMessage(
|
|
|
358
363
|
new ToolMessage({
|
|
359
364
|
tool_call_id: tool_call.id ?? '',
|
|
360
365
|
name: tool_call.name,
|
|
361
|
-
content: output
|
|
366
|
+
content: output != null ? output : '',
|
|
362
367
|
})
|
|
363
368
|
);
|
|
364
369
|
} else if (part.type === ContentTypes.THINK) {
|
|
@@ -395,6 +400,226 @@ function formatAssistantMessage(
|
|
|
395
400
|
return formattedMessages;
|
|
396
401
|
}
|
|
397
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Labels all agent content for parallel patterns (fan-out/fan-in)
|
|
405
|
+
* Groups consecutive content by agent and wraps with clear labels
|
|
406
|
+
*/
|
|
407
|
+
function labelAllAgentContent(
|
|
408
|
+
contentParts: MessageContentComplex[],
|
|
409
|
+
agentIdMap: Record<number, string>,
|
|
410
|
+
agentNames?: Record<string, string>
|
|
411
|
+
): MessageContentComplex[] {
|
|
412
|
+
const result: MessageContentComplex[] = [];
|
|
413
|
+
let currentAgentId: string | undefined;
|
|
414
|
+
let agentContentBuffer: MessageContentComplex[] = [];
|
|
415
|
+
|
|
416
|
+
const flushAgentBuffer = (): void => {
|
|
417
|
+
if (agentContentBuffer.length === 0) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (currentAgentId != null && currentAgentId !== '') {
|
|
422
|
+
const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
|
|
423
|
+
const formattedParts: string[] = [];
|
|
424
|
+
|
|
425
|
+
formattedParts.push(`--- ${agentName} ---`);
|
|
426
|
+
|
|
427
|
+
for (const part of agentContentBuffer) {
|
|
428
|
+
if (part.type === ContentTypes.THINK) {
|
|
429
|
+
const thinkContent = (part as ReasoningContentText).think || '';
|
|
430
|
+
if (thinkContent) {
|
|
431
|
+
formattedParts.push(
|
|
432
|
+
`${agentName}: ${JSON.stringify({
|
|
433
|
+
type: 'think',
|
|
434
|
+
think: thinkContent,
|
|
435
|
+
})}`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
} else if (part.type === ContentTypes.TEXT) {
|
|
439
|
+
const textContent: string = part.text ?? '';
|
|
440
|
+
if (textContent) {
|
|
441
|
+
formattedParts.push(`${agentName}: ${textContent}`);
|
|
442
|
+
}
|
|
443
|
+
} else if (part.type === ContentTypes.TOOL_CALL) {
|
|
444
|
+
formattedParts.push(
|
|
445
|
+
`${agentName}: ${JSON.stringify({
|
|
446
|
+
type: 'tool_call',
|
|
447
|
+
tool_call: (part as ToolCallContent).tool_call,
|
|
448
|
+
})}`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
formattedParts.push(`--- End of ${agentName} ---`);
|
|
454
|
+
|
|
455
|
+
// Create a single text content part with all agent content
|
|
456
|
+
result.push({
|
|
457
|
+
type: ContentTypes.TEXT,
|
|
458
|
+
text: formattedParts.join('\n\n'),
|
|
459
|
+
} as MessageContentComplex);
|
|
460
|
+
} else {
|
|
461
|
+
// No agent ID, pass through as-is
|
|
462
|
+
result.push(...agentContentBuffer);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
agentContentBuffer = [];
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
for (let i = 0; i < contentParts.length; i++) {
|
|
469
|
+
const part = contentParts[i];
|
|
470
|
+
const agentId = agentIdMap[i];
|
|
471
|
+
|
|
472
|
+
// If agent changed, flush previous buffer
|
|
473
|
+
if (agentId !== currentAgentId && currentAgentId !== undefined) {
|
|
474
|
+
flushAgentBuffer();
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
currentAgentId = agentId;
|
|
478
|
+
agentContentBuffer.push(part);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Flush any remaining content
|
|
482
|
+
flushAgentBuffer();
|
|
483
|
+
|
|
484
|
+
return result;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Groups content parts by agent and formats them with agent labels
|
|
489
|
+
* This preprocesses multi-agent content to prevent identity confusion
|
|
490
|
+
*
|
|
491
|
+
* @param contentParts - The content parts from a run
|
|
492
|
+
* @param agentIdMap - Map of content part index to agent ID
|
|
493
|
+
* @param agentNames - Optional map of agent ID to display name
|
|
494
|
+
* @param options - Configuration options
|
|
495
|
+
* @param options.labelNonTransferContent - If true, labels all agent transitions (for parallel patterns)
|
|
496
|
+
* @returns Modified content parts with agent labels where appropriate
|
|
497
|
+
*/
|
|
498
|
+
export const labelContentByAgent = (
|
|
499
|
+
contentParts: MessageContentComplex[],
|
|
500
|
+
agentIdMap?: Record<number, string>,
|
|
501
|
+
agentNames?: Record<string, string>,
|
|
502
|
+
options?: { labelNonTransferContent?: boolean }
|
|
503
|
+
): MessageContentComplex[] => {
|
|
504
|
+
if (!agentIdMap || Object.keys(agentIdMap).length === 0) {
|
|
505
|
+
return contentParts;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// If labelNonTransferContent is true, use a different strategy for parallel patterns
|
|
509
|
+
if (options?.labelNonTransferContent === true) {
|
|
510
|
+
return labelAllAgentContent(contentParts, agentIdMap, agentNames);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const result: MessageContentComplex[] = [];
|
|
514
|
+
let currentAgentId: string | undefined;
|
|
515
|
+
let agentContentBuffer: MessageContentComplex[] = [];
|
|
516
|
+
let transferToolCallIndex: number | undefined;
|
|
517
|
+
let transferToolCallId: string | undefined;
|
|
518
|
+
|
|
519
|
+
const flushAgentBuffer = (): void => {
|
|
520
|
+
if (agentContentBuffer.length === 0) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// If this is content from a transferred agent, format it specially
|
|
525
|
+
if (
|
|
526
|
+
currentAgentId != null &&
|
|
527
|
+
currentAgentId !== '' &&
|
|
528
|
+
transferToolCallIndex !== undefined
|
|
529
|
+
) {
|
|
530
|
+
const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
|
|
531
|
+
const formattedParts: string[] = [];
|
|
532
|
+
|
|
533
|
+
formattedParts.push(`--- Transfer to ${agentName} ---`);
|
|
534
|
+
|
|
535
|
+
for (const part of agentContentBuffer) {
|
|
536
|
+
if (part.type === ContentTypes.THINK) {
|
|
537
|
+
formattedParts.push(
|
|
538
|
+
`${agentName}: ${JSON.stringify({
|
|
539
|
+
type: 'think',
|
|
540
|
+
think: (part as ReasoningContentText).think,
|
|
541
|
+
})}`
|
|
542
|
+
);
|
|
543
|
+
} else if ('text' in part && part.type === ContentTypes.TEXT) {
|
|
544
|
+
const textContent: string = part.text ?? '';
|
|
545
|
+
if (textContent) {
|
|
546
|
+
formattedParts.push(
|
|
547
|
+
`${agentName}: ${JSON.stringify({
|
|
548
|
+
type: 'text',
|
|
549
|
+
text: textContent,
|
|
550
|
+
})}`
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
} else if (part.type === ContentTypes.TOOL_CALL) {
|
|
554
|
+
formattedParts.push(
|
|
555
|
+
`${agentName}: ${JSON.stringify({
|
|
556
|
+
type: 'tool_call',
|
|
557
|
+
tool_call: (part as ToolCallContent).tool_call,
|
|
558
|
+
})}`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
formattedParts.push(`--- End of ${agentName} response ---`);
|
|
564
|
+
|
|
565
|
+
// Find the tool call that triggered this transfer and update its output
|
|
566
|
+
if (transferToolCallIndex < result.length) {
|
|
567
|
+
const transferToolCall = result[transferToolCallIndex];
|
|
568
|
+
if (
|
|
569
|
+
transferToolCall.type === ContentTypes.TOOL_CALL &&
|
|
570
|
+
transferToolCall.tool_call?.id === transferToolCallId
|
|
571
|
+
) {
|
|
572
|
+
transferToolCall.tool_call.output = formattedParts.join('\n\n');
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
// Not from a transfer, add as-is
|
|
577
|
+
result.push(...agentContentBuffer);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
agentContentBuffer = [];
|
|
581
|
+
transferToolCallIndex = undefined;
|
|
582
|
+
transferToolCallId = undefined;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
for (let i = 0; i < contentParts.length; i++) {
|
|
586
|
+
const part = contentParts[i];
|
|
587
|
+
const agentId = agentIdMap[i];
|
|
588
|
+
|
|
589
|
+
// Check if this is a transfer tool call
|
|
590
|
+
const isTransferTool =
|
|
591
|
+
(part.type === ContentTypes.TOOL_CALL &&
|
|
592
|
+
(part as ToolCallContent).tool_call?.name?.startsWith(
|
|
593
|
+
'lc_transfer_to_'
|
|
594
|
+
)) ??
|
|
595
|
+
false;
|
|
596
|
+
|
|
597
|
+
// If agent changed, flush previous buffer
|
|
598
|
+
if (agentId !== currentAgentId && currentAgentId !== undefined) {
|
|
599
|
+
flushAgentBuffer();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
currentAgentId = agentId;
|
|
603
|
+
|
|
604
|
+
if (isTransferTool) {
|
|
605
|
+
// Flush any existing buffer first
|
|
606
|
+
flushAgentBuffer();
|
|
607
|
+
// Add the transfer tool call to result
|
|
608
|
+
result.push(part);
|
|
609
|
+
// Mark that the next agent's content should be captured
|
|
610
|
+
transferToolCallIndex = result.length - 1;
|
|
611
|
+
transferToolCallId = (part as ToolCallContent).tool_call?.id;
|
|
612
|
+
currentAgentId = undefined; // Reset to capture the next agent
|
|
613
|
+
} else {
|
|
614
|
+
agentContentBuffer.push(part);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
flushAgentBuffer();
|
|
619
|
+
|
|
620
|
+
return result;
|
|
621
|
+
};
|
|
622
|
+
|
|
398
623
|
/**
|
|
399
624
|
* Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
|
|
400
625
|
*
|
|
@@ -463,7 +688,7 @@ export const formatAgentMessages = (
|
|
|
463
688
|
}
|
|
464
689
|
// Protect against malformed tool call entries
|
|
465
690
|
if (
|
|
466
|
-
|
|
691
|
+
part.tool_call == null ||
|
|
467
692
|
part.tool_call.name == null ||
|
|
468
693
|
part.tool_call.name === ''
|
|
469
694
|
) {
|
|
@@ -495,7 +720,7 @@ export const formatAgentMessages = (
|
|
|
495
720
|
// Check if this is a continuation of the tool sequence
|
|
496
721
|
let isToolResponse = false;
|
|
497
722
|
const content = payload[j].content;
|
|
498
|
-
if (content && Array.isArray(content)) {
|
|
723
|
+
if (content != null && Array.isArray(content)) {
|
|
499
724
|
for (const part of content) {
|
|
500
725
|
if (part.type === ContentTypes.TOOL_CALL) {
|
|
501
726
|
isToolResponse = true;
|