@librechat/agents 3.0.53 → 3.0.55
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 +28 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +108 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +22 -1
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/stream.cjs +22 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +28 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +108 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +22 -1
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/stream.mjs +22 -11
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +14 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +41 -0
- package/dist/types/messages/format.d.ts +8 -2
- package/dist/types/types/stream.d.ts +22 -2
- package/package.json +3 -2
- package/src/graphs/Graph.ts +30 -2
- package/src/graphs/MultiAgentGraph.ts +119 -0
- package/src/messages/format.ts +33 -3
- package/src/messages/formatAgentMessages.test.ts +168 -0
- package/src/scripts/multi-agent-chain.ts +59 -6
- package/src/scripts/multi-agent-parallel-start.ts +39 -6
- package/src/scripts/multi-agent-parallel.ts +61 -10
- package/src/scripts/multi-agent-sequence.ts +6 -1
- package/src/scripts/parallel-asymmetric-tools-test.ts +274 -0
- package/src/scripts/parallel-full-metadata-test.ts +240 -0
- package/src/scripts/parallel-tools-test.ts +340 -0
- package/src/scripts/sequential-full-metadata-test.ts +197 -0
- package/src/scripts/single-agent-metadata-test.ts +198 -0
- package/src/scripts/test-thinking-handoff.ts +8 -0
- package/src/scripts/tools.ts +31 -11
- package/src/stream.ts +25 -14
- package/src/types/stream.ts +23 -4
package/src/messages/format.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ExtendedMessageContent,
|
|
15
15
|
MessageContentComplex,
|
|
16
16
|
ReasoningContentText,
|
|
17
|
+
ContentMetadata,
|
|
17
18
|
ToolCallContent,
|
|
18
19
|
ToolCallPart,
|
|
19
20
|
TPayload,
|
|
@@ -626,23 +627,31 @@ export const labelContentByAgent = (
|
|
|
626
627
|
* @param payload - The array of messages to format.
|
|
627
628
|
* @param indexTokenCountMap - Optional map of message indices to token counts.
|
|
628
629
|
* @param tools - Optional set of tool names that are allowed in the request.
|
|
630
|
+
* @param options - Optional configuration for agent filtering.
|
|
631
|
+
* @param options.targetAgentId - If provided, only content parts from this agent will be included.
|
|
632
|
+
* @param options.contentMetadataMap - Map of content index to metadata (required when targetAgentId is provided).
|
|
629
633
|
* @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
|
|
630
634
|
*/
|
|
631
635
|
export const formatAgentMessages = (
|
|
632
636
|
payload: TPayload,
|
|
633
|
-
indexTokenCountMap?: Record<number, number>,
|
|
634
|
-
tools?: Set<string
|
|
637
|
+
indexTokenCountMap?: Record<number, number | undefined>,
|
|
638
|
+
tools?: Set<string>,
|
|
639
|
+
options?: {
|
|
640
|
+
targetAgentId?: string;
|
|
641
|
+
contentMetadataMap?: Map<number, ContentMetadata>;
|
|
642
|
+
}
|
|
635
643
|
): {
|
|
636
644
|
messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
|
|
637
645
|
indexTokenCountMap?: Record<number, number>;
|
|
638
646
|
} => {
|
|
647
|
+
const { targetAgentId, contentMetadataMap } = options ?? {};
|
|
639
648
|
const messages: Array<
|
|
640
649
|
HumanMessage | AIMessage | SystemMessage | ToolMessage
|
|
641
650
|
> = [];
|
|
642
651
|
// If indexTokenCountMap is provided, create a new map to track the updated indices
|
|
643
652
|
const updatedIndexTokenCountMap: Record<number, number> = {};
|
|
644
653
|
// Keep track of the mapping from original payload indices to result indices
|
|
645
|
-
const indexMapping: Record<number, number[]> = {};
|
|
654
|
+
const indexMapping: Record<number, number[] | undefined> = {};
|
|
646
655
|
|
|
647
656
|
// Process messages with tool conversion if tools set is provided
|
|
648
657
|
for (let i = 0; i < payload.length; i++) {
|
|
@@ -654,6 +663,27 @@ export const formatAgentMessages = (
|
|
|
654
663
|
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: message.content },
|
|
655
664
|
];
|
|
656
665
|
}
|
|
666
|
+
|
|
667
|
+
// Filter content parts by targetAgentId if provided (only for assistant messages with array content)
|
|
668
|
+
if (
|
|
669
|
+
targetAgentId != null &&
|
|
670
|
+
targetAgentId !== '' &&
|
|
671
|
+
contentMetadataMap != null &&
|
|
672
|
+
message.role === 'assistant' &&
|
|
673
|
+
Array.isArray(message.content)
|
|
674
|
+
) {
|
|
675
|
+
const filteredContent = message.content.filter((_, partIndex) => {
|
|
676
|
+
const metadata = contentMetadataMap.get(partIndex);
|
|
677
|
+
return metadata?.agentId === targetAgentId;
|
|
678
|
+
});
|
|
679
|
+
// Skip this message entirely if no content parts match the target agent
|
|
680
|
+
if (filteredContent.length === 0) {
|
|
681
|
+
indexMapping[i] = [];
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
message.content = filteredContent;
|
|
685
|
+
}
|
|
686
|
+
|
|
657
687
|
if (message.role !== 'assistant') {
|
|
658
688
|
messages.push(
|
|
659
689
|
formatMessage({
|
|
@@ -1141,4 +1141,172 @@ describe('formatAgentMessages', () => {
|
|
|
1141
1141
|
expect(result.messages[1].name).toBe('search');
|
|
1142
1142
|
expect(result.messages[1].content).toBe('');
|
|
1143
1143
|
});
|
|
1144
|
+
|
|
1145
|
+
describe('targetAgentId filtering', () => {
|
|
1146
|
+
it('should filter content parts to only include those from targetAgentId', () => {
|
|
1147
|
+
const payload: TPayload = [
|
|
1148
|
+
{ role: 'user', content: 'Hello' },
|
|
1149
|
+
{
|
|
1150
|
+
role: 'assistant',
|
|
1151
|
+
content: [
|
|
1152
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_a' },
|
|
1153
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_b' },
|
|
1154
|
+
{ type: ContentTypes.TEXT, text: 'Another from agent_a' },
|
|
1155
|
+
],
|
|
1156
|
+
},
|
|
1157
|
+
];
|
|
1158
|
+
|
|
1159
|
+
const contentMetadataMap = new Map([
|
|
1160
|
+
[0, { agentId: 'agent_a' }],
|
|
1161
|
+
[1, { agentId: 'agent_b' }],
|
|
1162
|
+
[2, { agentId: 'agent_a' }],
|
|
1163
|
+
]);
|
|
1164
|
+
|
|
1165
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1166
|
+
targetAgentId: 'agent_a',
|
|
1167
|
+
contentMetadataMap,
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
// Should have user message + filtered assistant message
|
|
1171
|
+
expect(result.messages).toHaveLength(2);
|
|
1172
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1173
|
+
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
1174
|
+
|
|
1175
|
+
// The AIMessage should only have agent_a's content parts
|
|
1176
|
+
const aiMessage = result.messages[1] as AIMessage;
|
|
1177
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1178
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1179
|
+
expect((aiMessage.content as Array<{ text: string }>)[0].text).toBe(
|
|
1180
|
+
'Response from agent_a'
|
|
1181
|
+
);
|
|
1182
|
+
expect((aiMessage.content as Array<{ text: string }>)[1].text).toBe(
|
|
1183
|
+
'Another from agent_a'
|
|
1184
|
+
);
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
it('should skip assistant message entirely if no content parts match targetAgentId', () => {
|
|
1188
|
+
const payload: TPayload = [
|
|
1189
|
+
{ role: 'user', content: 'Hello' },
|
|
1190
|
+
{
|
|
1191
|
+
role: 'assistant',
|
|
1192
|
+
content: [{ type: ContentTypes.TEXT, text: 'Response from agent_b' }],
|
|
1193
|
+
},
|
|
1194
|
+
];
|
|
1195
|
+
|
|
1196
|
+
const contentMetadataMap = new Map([[0, { agentId: 'agent_b' }]]);
|
|
1197
|
+
|
|
1198
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1199
|
+
targetAgentId: 'agent_a',
|
|
1200
|
+
contentMetadataMap,
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// Should only have the user message, assistant message skipped
|
|
1204
|
+
expect(result.messages).toHaveLength(1);
|
|
1205
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('should not filter when targetAgentId is not provided', () => {
|
|
1209
|
+
const payload: TPayload = [
|
|
1210
|
+
{
|
|
1211
|
+
role: 'assistant',
|
|
1212
|
+
content: [
|
|
1213
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_a' },
|
|
1214
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_b' },
|
|
1215
|
+
],
|
|
1216
|
+
},
|
|
1217
|
+
];
|
|
1218
|
+
|
|
1219
|
+
const contentMetadataMap = new Map([
|
|
1220
|
+
[0, { agentId: 'agent_a' }],
|
|
1221
|
+
[1, { agentId: 'agent_b' }],
|
|
1222
|
+
]);
|
|
1223
|
+
|
|
1224
|
+
// No targetAgentId provided - should include all content
|
|
1225
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1226
|
+
contentMetadataMap,
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
expect(result.messages).toHaveLength(1);
|
|
1230
|
+
const aiMessage = result.messages[0] as AIMessage;
|
|
1231
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1232
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
it('should not filter when contentMetadataMap is not provided', () => {
|
|
1236
|
+
const payload: TPayload = [
|
|
1237
|
+
{
|
|
1238
|
+
role: 'assistant',
|
|
1239
|
+
content: [
|
|
1240
|
+
{ type: ContentTypes.TEXT, text: 'Response 1' },
|
|
1241
|
+
{ type: ContentTypes.TEXT, text: 'Response 2' },
|
|
1242
|
+
],
|
|
1243
|
+
},
|
|
1244
|
+
];
|
|
1245
|
+
|
|
1246
|
+
// targetAgentId provided but no contentMetadataMap - should include all content
|
|
1247
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1248
|
+
targetAgentId: 'agent_a',
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
expect(result.messages).toHaveLength(1);
|
|
1252
|
+
const aiMessage = result.messages[0] as AIMessage;
|
|
1253
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1254
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it('should filter content with groupId metadata (parallel execution)', () => {
|
|
1258
|
+
const payload: TPayload = [
|
|
1259
|
+
{ role: 'user', content: 'Analyze this' },
|
|
1260
|
+
{
|
|
1261
|
+
role: 'assistant',
|
|
1262
|
+
content: [
|
|
1263
|
+
{ type: ContentTypes.TEXT, text: 'Creative analysis' },
|
|
1264
|
+
{ type: ContentTypes.TEXT, text: 'Practical analysis' },
|
|
1265
|
+
],
|
|
1266
|
+
},
|
|
1267
|
+
];
|
|
1268
|
+
|
|
1269
|
+
const contentMetadataMap = new Map([
|
|
1270
|
+
[0, { agentId: 'creative_analyst', groupId: 1 }],
|
|
1271
|
+
[1, { agentId: 'practical_analyst', groupId: 1 }],
|
|
1272
|
+
]);
|
|
1273
|
+
|
|
1274
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1275
|
+
targetAgentId: 'creative_analyst',
|
|
1276
|
+
contentMetadataMap,
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
expect(result.messages).toHaveLength(2);
|
|
1280
|
+
const aiMessage = result.messages[1] as AIMessage;
|
|
1281
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1282
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(1);
|
|
1283
|
+
expect((aiMessage.content as Array<{ text: string }>)[0].text).toBe(
|
|
1284
|
+
'Creative analysis'
|
|
1285
|
+
);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it('should not affect non-assistant messages when filtering', () => {
|
|
1289
|
+
const payload: TPayload = [
|
|
1290
|
+
{ role: 'user', content: 'Hello from user' },
|
|
1291
|
+
{ role: 'system', content: 'System message' },
|
|
1292
|
+
{
|
|
1293
|
+
role: 'assistant',
|
|
1294
|
+
content: [{ type: ContentTypes.TEXT, text: 'From agent_a' }],
|
|
1295
|
+
},
|
|
1296
|
+
];
|
|
1297
|
+
|
|
1298
|
+
const contentMetadataMap = new Map([[0, { agentId: 'agent_a' }]]);
|
|
1299
|
+
|
|
1300
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1301
|
+
targetAgentId: 'agent_a',
|
|
1302
|
+
contentMetadataMap,
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
// All three messages should be present
|
|
1306
|
+
expect(result.messages).toHaveLength(3);
|
|
1307
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1308
|
+
expect(result.messages[1]).toBeInstanceOf(SystemMessage);
|
|
1309
|
+
expect(result.messages[2]).toBeInstanceOf(AIMessage);
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1144
1312
|
});
|
|
@@ -143,18 +143,49 @@ async function testSequentialAgentChain() {
|
|
|
143
143
|
|
|
144
144
|
// Track agent progression
|
|
145
145
|
let currentAgent = '';
|
|
146
|
+
const startTime = Date.now();
|
|
147
|
+
let messageCount = 0;
|
|
146
148
|
|
|
147
|
-
// Create custom handlers
|
|
149
|
+
// Create custom handlers with extensive metadata logging
|
|
148
150
|
const customHandlers = {
|
|
149
151
|
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
150
|
-
[GraphEvents.CHAT_MODEL_END]:
|
|
152
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
153
|
+
handle: (
|
|
154
|
+
_event: string,
|
|
155
|
+
_data: t.StreamEventData,
|
|
156
|
+
metadata?: Record<string, unknown>
|
|
157
|
+
): void => {
|
|
158
|
+
console.log('\n====== CHAT_MODEL_END METADATA ======');
|
|
159
|
+
console.dir(metadata, { depth: null });
|
|
160
|
+
const elapsed = Date.now() - startTime;
|
|
161
|
+
console.log(`⏱️ COMPLETED at ${elapsed}ms`);
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
[GraphEvents.CHAT_MODEL_START]: {
|
|
165
|
+
handle: (
|
|
166
|
+
_event: string,
|
|
167
|
+
_data: t.StreamEventData,
|
|
168
|
+
metadata?: Record<string, unknown>
|
|
169
|
+
): void => {
|
|
170
|
+
console.log('\n====== CHAT_MODEL_START METADATA ======');
|
|
171
|
+
console.dir(metadata, { depth: null });
|
|
172
|
+
const elapsed = Date.now() - startTime;
|
|
173
|
+
console.log(`⏱️ STARTED at ${elapsed}ms`);
|
|
174
|
+
},
|
|
175
|
+
},
|
|
151
176
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
152
177
|
[GraphEvents.ON_RUN_STEP]: {
|
|
153
178
|
handle: (
|
|
154
179
|
event: GraphEvents.ON_RUN_STEP,
|
|
155
|
-
data: t.StreamEventData
|
|
180
|
+
data: t.StreamEventData,
|
|
181
|
+
metadata?: Record<string, unknown>
|
|
156
182
|
): void => {
|
|
157
183
|
const runStepData = data as any;
|
|
184
|
+
console.log('\n====== ON_RUN_STEP ======');
|
|
185
|
+
console.log('DATA:');
|
|
186
|
+
console.dir(data, { depth: null });
|
|
187
|
+
console.log('METADATA:');
|
|
188
|
+
console.dir(metadata, { depth: null });
|
|
158
189
|
if (runStepData?.name) {
|
|
159
190
|
currentAgent = runStepData.name;
|
|
160
191
|
console.log(`\n→ ${currentAgent} is processing...`);
|
|
@@ -165,9 +196,15 @@ async function testSequentialAgentChain() {
|
|
|
165
196
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
166
197
|
handle: (
|
|
167
198
|
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
168
|
-
data: t.StreamEventData
|
|
199
|
+
data: t.StreamEventData,
|
|
200
|
+
metadata?: Record<string, unknown>
|
|
169
201
|
): void => {
|
|
170
202
|
const runStepData = data as any;
|
|
203
|
+
console.log('\n====== ON_RUN_STEP_COMPLETED ======');
|
|
204
|
+
console.log('DATA:');
|
|
205
|
+
console.dir(data, { depth: null });
|
|
206
|
+
console.log('METADATA:');
|
|
207
|
+
console.dir(metadata, { depth: null });
|
|
171
208
|
if (runStepData?.name) {
|
|
172
209
|
console.log(`✓ ${runStepData.name} completed`);
|
|
173
210
|
}
|
|
@@ -180,16 +217,32 @@ async function testSequentialAgentChain() {
|
|
|
180
217
|
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
181
218
|
handle: (
|
|
182
219
|
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
183
|
-
data: t.StreamEventData
|
|
220
|
+
data: t.StreamEventData,
|
|
221
|
+
metadata?: Record<string, unknown>
|
|
184
222
|
): void => {
|
|
223
|
+
console.log('\n====== ON_RUN_STEP_DELTA ======');
|
|
224
|
+
console.log('DATA:');
|
|
225
|
+
console.dir(data, { depth: null });
|
|
226
|
+
console.log('METADATA:');
|
|
227
|
+
console.dir(metadata, { depth: null });
|
|
185
228
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
186
229
|
},
|
|
187
230
|
},
|
|
188
231
|
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
189
232
|
handle: (
|
|
190
233
|
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
191
|
-
data: t.StreamEventData
|
|
234
|
+
data: t.StreamEventData,
|
|
235
|
+
metadata?: Record<string, unknown>
|
|
192
236
|
): void => {
|
|
237
|
+
messageCount++;
|
|
238
|
+
// Only log first few message deltas to avoid spam
|
|
239
|
+
if (messageCount <= 3) {
|
|
240
|
+
console.log('\n====== ON_MESSAGE_DELTA ======');
|
|
241
|
+
console.log('DATA:');
|
|
242
|
+
console.dir(data, { depth: null });
|
|
243
|
+
console.log('METADATA:');
|
|
244
|
+
console.dir(metadata, { depth: null });
|
|
245
|
+
}
|
|
193
246
|
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
194
247
|
},
|
|
195
248
|
},
|
|
@@ -25,7 +25,8 @@ async function testParallelFromStart() {
|
|
|
25
25
|
console.log('Testing Parallel From Start Multi-Agent System...\n');
|
|
26
26
|
|
|
27
27
|
// Set up content aggregator
|
|
28
|
-
const { contentParts, aggregateContent } =
|
|
28
|
+
const { contentParts, aggregateContent, contentMetadataMap } =
|
|
29
|
+
createContentAggregator();
|
|
29
30
|
|
|
30
31
|
// Define two agents - both have NO incoming edges, so they run in parallel from the start
|
|
31
32
|
const agents: t.AgentInputs[] = [
|
|
@@ -58,7 +59,7 @@ async function testParallelFromStart() {
|
|
|
58
59
|
const agentTimings: Record<string, { start?: number; end?: number }> = {};
|
|
59
60
|
const startTime = Date.now();
|
|
60
61
|
|
|
61
|
-
// Create custom handlers
|
|
62
|
+
// Create custom handlers with extensive metadata logging
|
|
62
63
|
const customHandlers = {
|
|
63
64
|
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
64
65
|
[GraphEvents.CHAT_MODEL_END]: {
|
|
@@ -67,6 +68,8 @@ async function testParallelFromStart() {
|
|
|
67
68
|
_data: t.StreamEventData,
|
|
68
69
|
metadata?: Record<string, unknown>
|
|
69
70
|
): void => {
|
|
71
|
+
console.log('\n====== CHAT_MODEL_END METADATA ======');
|
|
72
|
+
console.dir(metadata, { depth: null });
|
|
70
73
|
const nodeName = metadata?.langgraph_node as string;
|
|
71
74
|
if (nodeName) {
|
|
72
75
|
const elapsed = Date.now() - startTime;
|
|
@@ -82,6 +85,8 @@ async function testParallelFromStart() {
|
|
|
82
85
|
_data: t.StreamEventData,
|
|
83
86
|
metadata?: Record<string, unknown>
|
|
84
87
|
): void => {
|
|
88
|
+
console.log('\n====== CHAT_MODEL_START METADATA ======');
|
|
89
|
+
console.dir(metadata, { depth: null });
|
|
85
90
|
const nodeName = metadata?.langgraph_node as string;
|
|
86
91
|
if (nodeName) {
|
|
87
92
|
const elapsed = Date.now() - startTime;
|
|
@@ -96,8 +101,14 @@ async function testParallelFromStart() {
|
|
|
96
101
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
97
102
|
handle: (
|
|
98
103
|
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
99
|
-
data: t.StreamEventData
|
|
104
|
+
data: t.StreamEventData,
|
|
105
|
+
metadata?: Record<string, unknown>
|
|
100
106
|
): void => {
|
|
107
|
+
console.log('\n====== ON_RUN_STEP_COMPLETED ======');
|
|
108
|
+
console.log('DATA:');
|
|
109
|
+
console.dir(data, { depth: null });
|
|
110
|
+
console.log('METADATA:');
|
|
111
|
+
console.dir(metadata, { depth: null });
|
|
101
112
|
aggregateContent({
|
|
102
113
|
event,
|
|
103
114
|
data: data as unknown as { result: t.ToolEndEvent },
|
|
@@ -107,25 +118,43 @@ async function testParallelFromStart() {
|
|
|
107
118
|
[GraphEvents.ON_RUN_STEP]: {
|
|
108
119
|
handle: (
|
|
109
120
|
event: GraphEvents.ON_RUN_STEP,
|
|
110
|
-
data: t.StreamEventData
|
|
121
|
+
data: t.StreamEventData,
|
|
122
|
+
metadata?: Record<string, unknown>
|
|
111
123
|
): void => {
|
|
124
|
+
console.log('\n====== ON_RUN_STEP ======');
|
|
125
|
+
console.log('DATA:');
|
|
126
|
+
console.dir(data, { depth: null });
|
|
127
|
+
console.log('METADATA:');
|
|
128
|
+
console.dir(metadata, { depth: null });
|
|
112
129
|
aggregateContent({ event, data: data as t.RunStep });
|
|
113
130
|
},
|
|
114
131
|
},
|
|
115
132
|
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
116
133
|
handle: (
|
|
117
134
|
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
118
|
-
data: t.StreamEventData
|
|
135
|
+
data: t.StreamEventData,
|
|
136
|
+
metadata?: Record<string, unknown>
|
|
119
137
|
): void => {
|
|
138
|
+
console.log('\n====== ON_RUN_STEP_DELTA ======');
|
|
139
|
+
console.log('DATA:');
|
|
140
|
+
console.dir(data, { depth: null });
|
|
141
|
+
console.log('METADATA:');
|
|
142
|
+
console.dir(metadata, { depth: null });
|
|
120
143
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
121
144
|
},
|
|
122
145
|
},
|
|
123
146
|
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
124
147
|
handle: (
|
|
125
148
|
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
126
|
-
data: t.StreamEventData
|
|
149
|
+
data: t.StreamEventData,
|
|
150
|
+
metadata?: Record<string, unknown>
|
|
127
151
|
): void => {
|
|
152
|
+
// Only log first delta per agent to avoid spam
|
|
153
|
+
console.log('\n====== ON_MESSAGE_DELTA ======');
|
|
154
|
+
console.log('DATA:');
|
|
128
155
|
console.dir(data, { depth: null });
|
|
156
|
+
console.log('METADATA:');
|
|
157
|
+
console.dir(metadata, { depth: null });
|
|
129
158
|
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
130
159
|
},
|
|
131
160
|
},
|
|
@@ -221,7 +250,11 @@ async function testParallelFromStart() {
|
|
|
221
250
|
console.log('====================================\n');
|
|
222
251
|
|
|
223
252
|
console.log('Final content parts:', contentParts.length, 'parts');
|
|
253
|
+
console.log('\n=== Content Parts (clean, no metadata) ===');
|
|
224
254
|
console.dir(contentParts, { depth: null });
|
|
255
|
+
console.log('\n=== Content Metadata Map (separate from content) ===');
|
|
256
|
+
console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
|
|
257
|
+
|
|
225
258
|
await sleep(3000);
|
|
226
259
|
} catch (error) {
|
|
227
260
|
console.error('Error in parallel-from-start multi-agent test:', error);
|
|
@@ -200,18 +200,50 @@ async function testParallelMultiAgent() {
|
|
|
200
200
|
|
|
201
201
|
// Track which agents are active
|
|
202
202
|
const activeAgents = new Set<string>();
|
|
203
|
+
const startTime = Date.now();
|
|
204
|
+
let messageCount = 0;
|
|
203
205
|
|
|
204
|
-
// Create custom handlers
|
|
206
|
+
// Create custom handlers with extensive metadata logging
|
|
205
207
|
const customHandlers = {
|
|
206
208
|
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
207
|
-
[GraphEvents.CHAT_MODEL_END]:
|
|
209
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
210
|
+
handle: (
|
|
211
|
+
_event: string,
|
|
212
|
+
_data: t.StreamEventData,
|
|
213
|
+
metadata?: Record<string, unknown>
|
|
214
|
+
): void => {
|
|
215
|
+
console.log('\n====== CHAT_MODEL_END METADATA ======');
|
|
216
|
+
console.dir(metadata, { depth: null });
|
|
217
|
+
const elapsed = Date.now() - startTime;
|
|
218
|
+
const nodeName = metadata?.langgraph_node as string;
|
|
219
|
+
console.log(`⏱️ [${nodeName || 'unknown'}] COMPLETED at ${elapsed}ms`);
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
[GraphEvents.CHAT_MODEL_START]: {
|
|
223
|
+
handle: (
|
|
224
|
+
_event: string,
|
|
225
|
+
_data: t.StreamEventData,
|
|
226
|
+
metadata?: Record<string, unknown>
|
|
227
|
+
): void => {
|
|
228
|
+
console.log('\n====== CHAT_MODEL_START METADATA ======');
|
|
229
|
+
console.dir(metadata, { depth: null });
|
|
230
|
+
const elapsed = Date.now() - startTime;
|
|
231
|
+
const nodeName = metadata?.langgraph_node as string;
|
|
232
|
+
console.log(`⏱️ [${nodeName || 'unknown'}] STARTED at ${elapsed}ms`);
|
|
233
|
+
},
|
|
234
|
+
},
|
|
208
235
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
209
236
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
210
237
|
handle: (
|
|
211
238
|
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
212
|
-
data: t.StreamEventData
|
|
239
|
+
data: t.StreamEventData,
|
|
240
|
+
metadata?: Record<string, unknown>
|
|
213
241
|
): void => {
|
|
214
|
-
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
242
|
+
console.log('\n====== ON_RUN_STEP_COMPLETED ======');
|
|
243
|
+
console.log('DATA:');
|
|
244
|
+
console.dir(data, { depth: null });
|
|
245
|
+
console.log('METADATA:');
|
|
246
|
+
console.dir(metadata, { depth: null });
|
|
215
247
|
const runStepData = data as any;
|
|
216
248
|
if (runStepData?.name) {
|
|
217
249
|
activeAgents.delete(runStepData.name);
|
|
@@ -226,9 +258,14 @@ async function testParallelMultiAgent() {
|
|
|
226
258
|
[GraphEvents.ON_RUN_STEP]: {
|
|
227
259
|
handle: (
|
|
228
260
|
event: GraphEvents.ON_RUN_STEP,
|
|
229
|
-
data: t.StreamEventData
|
|
261
|
+
data: t.StreamEventData,
|
|
262
|
+
metadata?: Record<string, unknown>
|
|
230
263
|
): void => {
|
|
231
|
-
console.log('====== ON_RUN_STEP ======');
|
|
264
|
+
console.log('\n====== ON_RUN_STEP ======');
|
|
265
|
+
console.log('DATA:');
|
|
266
|
+
console.dir(data, { depth: null });
|
|
267
|
+
console.log('METADATA:');
|
|
268
|
+
console.dir(metadata, { depth: null });
|
|
232
269
|
const runStepData = data as any;
|
|
233
270
|
if (runStepData?.name) {
|
|
234
271
|
activeAgents.add(runStepData.name);
|
|
@@ -240,18 +277,32 @@ async function testParallelMultiAgent() {
|
|
|
240
277
|
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
241
278
|
handle: (
|
|
242
279
|
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
243
|
-
data: t.StreamEventData
|
|
280
|
+
data: t.StreamEventData,
|
|
281
|
+
metadata?: Record<string, unknown>
|
|
244
282
|
): void => {
|
|
283
|
+
console.log('\n====== ON_RUN_STEP_DELTA ======');
|
|
284
|
+
console.log('DATA:');
|
|
285
|
+
console.dir(data, { depth: null });
|
|
286
|
+
console.log('METADATA:');
|
|
287
|
+
console.dir(metadata, { depth: null });
|
|
245
288
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
246
289
|
},
|
|
247
290
|
},
|
|
248
291
|
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
249
292
|
handle: (
|
|
250
293
|
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
251
|
-
data: t.StreamEventData
|
|
294
|
+
data: t.StreamEventData,
|
|
295
|
+
metadata?: Record<string, unknown>
|
|
252
296
|
): void => {
|
|
253
|
-
|
|
254
|
-
|
|
297
|
+
messageCount++;
|
|
298
|
+
// Only log first few message deltas per agent to avoid spam
|
|
299
|
+
if (messageCount <= 5) {
|
|
300
|
+
console.log('\n====== ON_MESSAGE_DELTA ======');
|
|
301
|
+
console.log('DATA:');
|
|
302
|
+
console.dir(data, { depth: null });
|
|
303
|
+
console.log('METADATA:');
|
|
304
|
+
console.dir(metadata, { depth: null });
|
|
305
|
+
}
|
|
255
306
|
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
256
307
|
},
|
|
257
308
|
},
|
|
@@ -22,7 +22,8 @@ async function testSequentialMultiAgent() {
|
|
|
22
22
|
console.log('Testing Sequential Multi-Agent System (A → B → C)...\n');
|
|
23
23
|
|
|
24
24
|
// Set up content aggregator
|
|
25
|
-
const { contentParts, aggregateContent } =
|
|
25
|
+
const { contentParts, aggregateContent, contentMetadataMap } =
|
|
26
|
+
createContentAggregator();
|
|
26
27
|
|
|
27
28
|
// Define three simple agents
|
|
28
29
|
const agents: t.AgentInputs[] = [
|
|
@@ -194,6 +195,10 @@ async function testSequentialMultiAgent() {
|
|
|
194
195
|
console.log('\n\n=== Final Output ===');
|
|
195
196
|
console.log('Sequential flow completed successfully!');
|
|
196
197
|
console.log(`Total content parts: ${contentParts.length}`);
|
|
198
|
+
console.log('\n=== Content Parts (clean, no metadata) ===');
|
|
199
|
+
console.dir(contentParts, { depth: null });
|
|
200
|
+
console.log('\n=== Content Metadata Map (separate from content) ===');
|
|
201
|
+
console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
|
|
197
202
|
|
|
198
203
|
// Display the sequential responses
|
|
199
204
|
const aiMessages = conversationHistory.filter(
|