@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.
Files changed (38) hide show
  1. package/dist/cjs/graphs/Graph.cjs +28 -2
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +108 -0
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/messages/format.cjs +22 -1
  6. package/dist/cjs/messages/format.cjs.map +1 -1
  7. package/dist/cjs/stream.cjs +22 -11
  8. package/dist/cjs/stream.cjs.map +1 -1
  9. package/dist/esm/graphs/Graph.mjs +28 -2
  10. package/dist/esm/graphs/Graph.mjs.map +1 -1
  11. package/dist/esm/graphs/MultiAgentGraph.mjs +108 -0
  12. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  13. package/dist/esm/messages/format.mjs +22 -1
  14. package/dist/esm/messages/format.mjs.map +1 -1
  15. package/dist/esm/stream.mjs +22 -11
  16. package/dist/esm/stream.mjs.map +1 -1
  17. package/dist/types/graphs/Graph.d.ts +14 -0
  18. package/dist/types/graphs/MultiAgentGraph.d.ts +41 -0
  19. package/dist/types/messages/format.d.ts +8 -2
  20. package/dist/types/types/stream.d.ts +22 -2
  21. package/package.json +3 -2
  22. package/src/graphs/Graph.ts +30 -2
  23. package/src/graphs/MultiAgentGraph.ts +119 -0
  24. package/src/messages/format.ts +33 -3
  25. package/src/messages/formatAgentMessages.test.ts +168 -0
  26. package/src/scripts/multi-agent-chain.ts +59 -6
  27. package/src/scripts/multi-agent-parallel-start.ts +39 -6
  28. package/src/scripts/multi-agent-parallel.ts +61 -10
  29. package/src/scripts/multi-agent-sequence.ts +6 -1
  30. package/src/scripts/parallel-asymmetric-tools-test.ts +274 -0
  31. package/src/scripts/parallel-full-metadata-test.ts +240 -0
  32. package/src/scripts/parallel-tools-test.ts +340 -0
  33. package/src/scripts/sequential-full-metadata-test.ts +197 -0
  34. package/src/scripts/single-agent-metadata-test.ts +198 -0
  35. package/src/scripts/test-thinking-handoff.ts +8 -0
  36. package/src/scripts/tools.ts +31 -11
  37. package/src/stream.ts +25 -14
  38. package/src/types/stream.ts +23 -4
@@ -0,0 +1,198 @@
1
+ import { config } from 'dotenv';
2
+ config();
3
+
4
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import type * as t from '@/types';
7
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
8
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
9
+ import { Providers, GraphEvents } from '@/common';
10
+ import { sleep } from '@/utils/run';
11
+ import { Run } from '@/run';
12
+
13
+ const conversationHistory: BaseMessage[] = [];
14
+
15
+ /**
16
+ * Single agent test with extensive metadata logging
17
+ * Compare with multi-agent-parallel-start.ts to see metadata differences
18
+ */
19
+ async function testSingleAgent() {
20
+ console.log('Testing Single Agent with Metadata Logging...\n');
21
+
22
+ // Set up content aggregator
23
+ const { contentParts, aggregateContent, contentMetadataMap } =
24
+ createContentAggregator();
25
+
26
+ const startTime = Date.now();
27
+
28
+ // Create custom handlers with extensive metadata logging
29
+ const customHandlers = {
30
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
31
+ [GraphEvents.CHAT_MODEL_END]: {
32
+ handle: (
33
+ _event: string,
34
+ _data: t.StreamEventData,
35
+ metadata?: Record<string, unknown>
36
+ ): void => {
37
+ console.log('\n====== CHAT_MODEL_END METADATA ======');
38
+ console.dir(metadata, { depth: null });
39
+ const elapsed = Date.now() - startTime;
40
+ console.log(`⏱️ COMPLETED at ${elapsed}ms`);
41
+ },
42
+ },
43
+ [GraphEvents.CHAT_MODEL_START]: {
44
+ handle: (
45
+ _event: string,
46
+ _data: t.StreamEventData,
47
+ metadata?: Record<string, unknown>
48
+ ): void => {
49
+ console.log('\n====== CHAT_MODEL_START METADATA ======');
50
+ console.dir(metadata, { depth: null });
51
+ const elapsed = Date.now() - startTime;
52
+ console.log(`⏱️ STARTED at ${elapsed}ms`);
53
+ },
54
+ },
55
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
56
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
57
+ handle: (
58
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
59
+ data: t.StreamEventData,
60
+ metadata?: Record<string, unknown>
61
+ ): void => {
62
+ console.log('\n====== ON_RUN_STEP_COMPLETED ======');
63
+ console.log('DATA:');
64
+ console.dir(data, { depth: null });
65
+ console.log('METADATA:');
66
+ console.dir(metadata, { depth: null });
67
+ aggregateContent({
68
+ event,
69
+ data: data as unknown as { result: t.ToolEndEvent },
70
+ });
71
+ },
72
+ },
73
+ [GraphEvents.ON_RUN_STEP]: {
74
+ handle: (
75
+ event: GraphEvents.ON_RUN_STEP,
76
+ data: t.StreamEventData,
77
+ metadata?: Record<string, unknown>
78
+ ): void => {
79
+ console.log('\n====== ON_RUN_STEP ======');
80
+ console.log('DATA:');
81
+ console.dir(data, { depth: null });
82
+ console.log('METADATA:');
83
+ console.dir(metadata, { depth: null });
84
+ aggregateContent({ event, data: data as t.RunStep });
85
+ },
86
+ },
87
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
88
+ handle: (
89
+ event: GraphEvents.ON_RUN_STEP_DELTA,
90
+ data: t.StreamEventData,
91
+ metadata?: Record<string, unknown>
92
+ ): void => {
93
+ console.log('\n====== ON_RUN_STEP_DELTA ======');
94
+ console.log('DATA:');
95
+ console.dir(data, { depth: null });
96
+ console.log('METADATA:');
97
+ console.dir(metadata, { depth: null });
98
+ aggregateContent({ event, data: data as t.RunStepDeltaEvent });
99
+ },
100
+ },
101
+ [GraphEvents.ON_MESSAGE_DELTA]: {
102
+ handle: (
103
+ event: GraphEvents.ON_MESSAGE_DELTA,
104
+ data: t.StreamEventData,
105
+ metadata?: Record<string, unknown>
106
+ ): void => {
107
+ console.log('\n====== ON_MESSAGE_DELTA ======');
108
+ console.log('DATA:');
109
+ console.dir(data, { depth: null });
110
+ console.log('METADATA:');
111
+ console.dir(metadata, { depth: null });
112
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
113
+ },
114
+ },
115
+ };
116
+
117
+ // Create single-agent run configuration (standard graph, not multi-agent)
118
+ const runConfig: t.RunConfig = {
119
+ runId: `single-agent-${Date.now()}`,
120
+ graphConfig: {
121
+ type: 'standard',
122
+ llmConfig: {
123
+ provider: Providers.ANTHROPIC,
124
+ modelName: 'claude-haiku-4-5',
125
+ apiKey: process.env.ANTHROPIC_API_KEY,
126
+ },
127
+ instructions: `You are a helpful AI assistant. Keep your response concise (50-100 words).`,
128
+ },
129
+ customHandlers,
130
+ returnContent: true,
131
+ };
132
+
133
+ try {
134
+ // Create and execute the run
135
+ const run = await Run.create(runConfig);
136
+
137
+ // Debug: Log the graph structure
138
+ console.log('=== DEBUG: Graph Structure ===');
139
+ const graph = (run as any).Graph;
140
+ console.log('Graph exists:', !!graph);
141
+ if (graph) {
142
+ console.log('Graph type:', graph.constructor.name);
143
+ console.log('AgentContexts exists:', !!graph.agentContexts);
144
+ if (graph.agentContexts) {
145
+ console.log('AgentContexts size:', graph.agentContexts.size);
146
+ for (const [agentId, context] of graph.agentContexts) {
147
+ console.log(`\nAgent: ${agentId}`);
148
+ console.log(
149
+ `Tools: ${context.tools?.map((t: any) => t.name || 'unnamed').join(', ') || 'none'}`
150
+ );
151
+ }
152
+ }
153
+ }
154
+ console.log('=== END DEBUG ===\n');
155
+
156
+ const userMessage = `What are the best approaches to learning a new programming language?`;
157
+ conversationHistory.push(new HumanMessage(userMessage));
158
+
159
+ console.log('Invoking single-agent graph...\n');
160
+
161
+ const config = {
162
+ configurable: {
163
+ thread_id: 'single-agent-conversation-1',
164
+ },
165
+ streamMode: 'values',
166
+ version: 'v2' as const,
167
+ };
168
+
169
+ // Process with streaming
170
+ const inputs = {
171
+ messages: conversationHistory,
172
+ };
173
+
174
+ const finalContentParts = await run.processStream(inputs, config);
175
+ const finalMessages = run.getRunMessages();
176
+
177
+ if (finalMessages) {
178
+ conversationHistory.push(...finalMessages);
179
+ }
180
+
181
+ console.log('\n\n========== SUMMARY ==========');
182
+ console.log('Final content parts:', contentParts.length, 'parts');
183
+ console.log('\n=== Content Parts (clean, no metadata) ===');
184
+ console.dir(contentParts, { depth: null });
185
+ console.log(
186
+ '\n=== Content Metadata Map (should be empty for single-agent) ==='
187
+ );
188
+ console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
189
+ console.log('====================================\n');
190
+
191
+ await sleep(3000);
192
+ } catch (error) {
193
+ console.error('Error in single-agent test:', error);
194
+ }
195
+ }
196
+
197
+ // Run the test
198
+ testSingleAgent();
@@ -32,6 +32,14 @@ async function testThinkingHandoff() {
32
32
  }
33
33
  },
34
34
  },
35
+ [GraphEvents.ON_RUN_STEP]: {
36
+ handle: (_event: string, data: t.StreamEventData): void => {
37
+ const runStep = data as t.RunStep;
38
+ console.log(
39
+ `\n📍 ON_RUN_STEP: agentId=${runStep.agentId}, groupId=${runStep.groupId}`
40
+ );
41
+ },
42
+ },
35
43
  };
36
44
 
37
45
  // Create the graph configuration
@@ -21,15 +21,36 @@ async function testStandardStreaming(): Promise<void> {
21
21
  [GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
22
22
  return true;
23
23
  }),
24
- [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
24
+ [GraphEvents.CHAT_MODEL_END]: {
25
+ handle: (
26
+ _event: string,
27
+ _data: t.StreamEventData,
28
+ metadata?: Record<string, unknown>
29
+ ): void => {
30
+ console.log('\n====== CHAT_MODEL_END METADATA ======');
31
+ console.dir(metadata, { depth: null });
32
+ },
33
+ },
34
+ [GraphEvents.CHAT_MODEL_START]: {
35
+ handle: (
36
+ _event: string,
37
+ _data: t.StreamEventData,
38
+ metadata?: Record<string, unknown>
39
+ ): void => {
40
+ console.log('\n====== CHAT_MODEL_START METADATA ======');
41
+ console.dir(metadata, { depth: null });
42
+ },
43
+ },
25
44
  [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
26
45
  [GraphEvents.ON_RUN_STEP_COMPLETED]: {
27
46
  handle: (
28
47
  event: GraphEvents.ON_RUN_STEP_COMPLETED,
29
- data: t.StreamEventData
48
+ data: t.StreamEventData,
49
+ metadata?: Record<string, unknown>
30
50
  ): void => {
31
51
  console.log('====== ON_RUN_STEP_COMPLETED ======');
32
- console.dir(data, { depth: null });
52
+ console.log('METADATA:');
53
+ console.dir(metadata, { depth: null });
33
54
  aggregateContent({
34
55
  event,
35
56
  data: data as unknown as { result: t.ToolEndEvent },
@@ -39,10 +60,14 @@ async function testStandardStreaming(): Promise<void> {
39
60
  [GraphEvents.ON_RUN_STEP]: {
40
61
  handle: (
41
62
  event: GraphEvents.ON_RUN_STEP,
42
- data: t.StreamEventData
63
+ data: t.StreamEventData,
64
+ metadata?: Record<string, unknown>
43
65
  ): void => {
44
66
  console.log('====== ON_RUN_STEP ======');
67
+ console.log('DATA:');
45
68
  console.dir(data, { depth: null });
69
+ console.log('METADATA:');
70
+ console.dir(metadata, { depth: null });
46
71
  aggregateContent({ event, data: data as t.RunStep });
47
72
  },
48
73
  },
@@ -51,8 +76,6 @@ async function testStandardStreaming(): Promise<void> {
51
76
  event: GraphEvents.ON_RUN_STEP_DELTA,
52
77
  data: t.StreamEventData
53
78
  ): void => {
54
- console.log('====== ON_RUN_STEP_DELTA ======');
55
- console.dir(data, { depth: null });
56
79
  aggregateContent({ event, data: data as t.RunStepDeltaEvent });
57
80
  },
58
81
  },
@@ -61,8 +84,6 @@ async function testStandardStreaming(): Promise<void> {
61
84
  event: GraphEvents.ON_MESSAGE_DELTA,
62
85
  data: t.StreamEventData
63
86
  ): void => {
64
- console.log('====== ON_MESSAGE_DELTA ======');
65
- console.dir(data, { depth: null });
66
87
  aggregateContent({ event, data: data as t.MessageDeltaEvent });
67
88
  },
68
89
  },
@@ -71,8 +92,6 @@ async function testStandardStreaming(): Promise<void> {
71
92
  event: GraphEvents.ON_REASONING_DELTA,
72
93
  data: t.StreamEventData
73
94
  ) => {
74
- console.log('====== ON_REASONING_DELTA ======');
75
- console.dir(data, { depth: null });
76
95
  aggregateContent({ event, data: data as t.ReasoningDeltaEvent });
77
96
  },
78
97
  },
@@ -83,7 +102,8 @@ async function testStandardStreaming(): Promise<void> {
83
102
  metadata?: Record<string, unknown>
84
103
  ): void => {
85
104
  console.log('====== TOOL_START ======');
86
- console.dir(data, { depth: null });
105
+ console.log('METADATA:');
106
+ console.dir(metadata, { depth: null });
87
107
  },
88
108
  },
89
109
  };
package/src/stream.ts CHANGED
@@ -442,8 +442,11 @@ export function createContentAggregator(): t.ContentAggregatorResult {
442
442
  const contentParts: Array<t.MessageContentComplex | undefined> = [];
443
443
  const stepMap = new Map<string, t.RunStep>();
444
444
  const toolCallIdMap = new Map<string, string>();
445
- // Track agentId for each content index (for parallel agent attribution)
446
- const contentAgentMap = new Map<number, string>();
445
+ // Track agentId and groupId for each content index (applied to content parts)
446
+ const contentMetaMap = new Map<
447
+ number,
448
+ { agentId?: string; groupId?: number }
449
+ >();
447
450
 
448
451
  const updateContent = (
449
452
  index: number,
@@ -580,14 +583,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
580
583
  tool_call: newToolCall,
581
584
  };
582
585
  }
583
-
584
- // Apply agentId to content part for parallel execution attribution
585
- const agentId = contentAgentMap.get(index);
586
- if (agentId !== undefined) {
587
- (
588
- contentParts[index] as t.MessageContentComplex & { agentId?: string }
589
- ).agentId = agentId;
590
- }
591
586
  };
592
587
 
593
588
  const aggregateContent = ({
@@ -606,9 +601,20 @@ export function createContentAggregator(): t.ContentAggregatorResult {
606
601
  const runStep = data as t.RunStep;
607
602
  stepMap.set(runStep.id, runStep);
608
603
 
609
- // Track agentId for this content index (for parallel execution)
610
- if (runStep.agentId != null && runStep.agentId !== '') {
611
- contentAgentMap.set(runStep.index, runStep.agentId);
604
+ // Track agentId (MultiAgentGraph) and groupId (parallel execution) separately
605
+ // - agentId: present for all MultiAgentGraph runs (enables agent labels in UI)
606
+ // - groupId: present only for parallel execution (enables column rendering)
607
+ const hasAgentId = runStep.agentId != null && runStep.agentId !== '';
608
+ const hasGroupId = runStep.groupId != null;
609
+ if (hasAgentId || hasGroupId) {
610
+ const existingMeta = contentMetaMap.get(runStep.index) ?? {};
611
+ if (hasAgentId) {
612
+ existingMeta.agentId = runStep.agentId;
613
+ }
614
+ if (hasGroupId) {
615
+ existingMeta.groupId = runStep.groupId;
616
+ }
617
+ contentMetaMap.set(runStep.index, existingMeta);
612
618
  }
613
619
 
614
620
  // Store tool call IDs if present
@@ -721,5 +727,10 @@ export function createContentAggregator(): t.ContentAggregatorResult {
721
727
  }
722
728
  };
723
729
 
724
- return { contentParts, aggregateContent, stepMap, contentAgentMap };
730
+ return {
731
+ contentParts,
732
+ aggregateContent,
733
+ stepMap,
734
+ contentMetadataMap: contentMetaMap,
735
+ };
725
736
  }
@@ -66,6 +66,17 @@ export type RunStep = {
66
66
  id: string; // #new
67
67
  runId?: string; // #new
68
68
  agentId?: string; // #new - tracks which agent this step belongs to
69
+ /**
70
+ * Group ID - incrementing number (1, 2, 3...) reflecting execution order.
71
+ * Agents with the same groupId run in parallel and should be rendered together.
72
+ * undefined means the agent runs sequentially (not part of any parallel group).
73
+ *
74
+ * Example for: researcher -> [analyst1, analyst2, analyst3] -> summarizer
75
+ * - researcher: undefined (sequential)
76
+ * - analyst1, analyst2, analyst3: 1 (first parallel group)
77
+ * - summarizer: undefined (sequential)
78
+ */
79
+ groupId?: number; // #new
69
80
  index: number; // #new
70
81
  stepIndex?: number; // #new
71
82
  stepDetails: StepDetails;
@@ -332,8 +343,6 @@ export type MessageContentComplex = (
332
343
  })
333
344
  ) & {
334
345
  tool_call_ids?: string[];
335
- // Optional agentId for parallel execution attribution
336
- agentId?: string;
337
346
  };
338
347
 
339
348
  export interface TMessage {
@@ -397,10 +406,20 @@ export type ContentAggregator = ({
397
406
  result: ToolEndEvent;
398
407
  };
399
408
  }) => void;
409
+ /**
410
+ * Metadata for content parts in multi-agent runs.
411
+ * - agentId: present for all MultiAgentGraph runs (enables agent labels in UI)
412
+ * - groupId: present only for parallel execution (enables column rendering)
413
+ */
414
+ export type ContentMetadata = {
415
+ agentId?: string;
416
+ groupId?: number;
417
+ };
418
+
400
419
  export type ContentAggregatorResult = {
401
420
  stepMap: Map<string, RunStep | undefined>;
402
421
  contentParts: Array<MessageContentComplex | undefined>;
422
+ /** Map of content index to metadata (agentId, groupId). Only populated for MultiAgentGraph runs. */
423
+ contentMetadataMap: Map<number, ContentMetadata>;
403
424
  aggregateContent: ContentAggregator;
404
- // Maps content index to agentId for parallel execution attribution
405
- contentAgentMap: Map<number, string>;
406
425
  };