@librechat/agents 3.0.0-rc1 → 3.0.0-rc3

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.
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { config } from 'dotenv';
4
+ config();
5
+
6
+ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
7
+ import { Run } from '@/run';
8
+ import { Providers, GraphEvents } from '@/common';
9
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
10
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
11
+ import type * as t from '@/types';
12
+
13
+ const conversationHistory: BaseMessage[] = [];
14
+
15
+ /**
16
+ * Test supervisor-based multi-agent system using a single edge with multiple destinations
17
+ *
18
+ * Instead of creating 5 separate edges, we use one edge with an array of destinations
19
+ * This should create handoff tools for all 5 specialists from a single edge definition
20
+ */
21
+ async function testSupervisorListHandoff() {
22
+ console.log('Testing Supervisor with List-Based Handoff Edge...\n');
23
+
24
+ // Set up content aggregator
25
+ const { contentParts, aggregateContent } = createContentAggregator();
26
+
27
+ // Track which specialist role was selected
28
+ let selectedRole = '';
29
+
30
+ // Create custom handlers
31
+ const customHandlers = {
32
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
33
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
34
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
35
+ [GraphEvents.ON_RUN_STEP]: {
36
+ handle: (
37
+ event: GraphEvents.ON_RUN_STEP,
38
+ data: t.StreamEventData
39
+ ): void => {
40
+ const runStepData = data as any;
41
+ if (runStepData?.name) {
42
+ console.log(`\n[${runStepData.name}] Processing...`);
43
+ }
44
+ aggregateContent({ event, data: data as t.RunStep });
45
+ },
46
+ },
47
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
48
+ handle: (
49
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
50
+ data: t.StreamEventData
51
+ ): void => {
52
+ aggregateContent({
53
+ event,
54
+ data: data as unknown as { result: t.ToolEndEvent },
55
+ });
56
+ },
57
+ },
58
+ [GraphEvents.ON_MESSAGE_DELTA]: {
59
+ handle: (
60
+ event: GraphEvents.ON_MESSAGE_DELTA,
61
+ data: t.StreamEventData
62
+ ): void => {
63
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
64
+ },
65
+ },
66
+ [GraphEvents.TOOL_START]: {
67
+ handle: (
68
+ _event: string,
69
+ data: t.StreamEventData,
70
+ metadata?: Record<string, unknown>
71
+ ): void => {
72
+ const toolData = data as any;
73
+ if (toolData?.name?.includes('transfer_to_')) {
74
+ const specialist = toolData.name.replace('transfer_to_', '');
75
+ console.log(`\nšŸ”€ Transferring to ${specialist}...`);
76
+ selectedRole = specialist;
77
+ }
78
+ },
79
+ },
80
+ };
81
+
82
+ // Function to create the graph with a single edge to multiple specialists
83
+ function createSupervisorGraphWithListEdge(): t.RunConfig {
84
+ console.log(`\nCreating graph with supervisor and 5 specialist agents.`);
85
+ console.log(
86
+ 'Using a SINGLE edge with multiple destinations (list-based handoff).\n'
87
+ );
88
+
89
+ // Define the adaptive specialist configuration that will be reused
90
+ const specialistConfig = {
91
+ provider: Providers.ANTHROPIC,
92
+ clientOptions: {
93
+ modelName: 'claude-3-5-sonnet-latest',
94
+ apiKey: process.env.ANTHROPIC_API_KEY,
95
+ },
96
+ instructions: `You are an Adaptive Specialist. Your agent ID indicates your role:
97
+
98
+ - data_analyst: Focus on statistical analysis, metrics, ML evaluation, A/B testing
99
+ - security_expert: Focus on cybersecurity, vulnerability assessment, compliance
100
+ - product_designer: Focus on UX/UI design, user research, accessibility
101
+ - devops_engineer: Focus on CI/CD, infrastructure, cloud platforms, monitoring
102
+ - legal_advisor: Focus on licensing, privacy laws, contracts, regulatory compliance
103
+
104
+ The supervisor will provide specific instructions. Follow them while maintaining your expert perspective.`,
105
+ maxContextTokens: 8000,
106
+ };
107
+
108
+ // Create the graph with supervisor and all 5 specialists
109
+ const agents: t.AgentInputs[] = [
110
+ {
111
+ agentId: 'supervisor',
112
+ provider: Providers.ANTHROPIC,
113
+ clientOptions: {
114
+ modelName: 'claude-3-5-sonnet-latest',
115
+ apiKey: process.env.ANTHROPIC_API_KEY,
116
+ },
117
+ instructions: `You are a Task Supervisor with access to 5 specialist agents:
118
+ 1. transfer_to_data_analyst - For statistical analysis and metrics
119
+ 2. transfer_to_security_expert - For cybersecurity and vulnerability assessment
120
+ 3. transfer_to_product_designer - For UX/UI design
121
+ 4. transfer_to_devops_engineer - For infrastructure and deployment
122
+ 5. transfer_to_legal_advisor - For compliance and licensing
123
+
124
+ Your role is to:
125
+ 1. Analyze the incoming request
126
+ 2. Decide which specialist is best suited
127
+ 3. Use the appropriate transfer tool (e.g., transfer_to_data_analyst)
128
+ 4. Provide specific instructions to guide their work
129
+
130
+ Be specific about what you need from the specialist.`,
131
+ maxContextTokens: 8000,
132
+ },
133
+ // Include all 5 specialists with the same adaptive configuration
134
+ {
135
+ agentId: 'data_analyst',
136
+ ...specialistConfig,
137
+ },
138
+ {
139
+ agentId: 'security_expert',
140
+ ...specialistConfig,
141
+ },
142
+ {
143
+ agentId: 'product_designer',
144
+ ...specialistConfig,
145
+ },
146
+ {
147
+ agentId: 'devops_engineer',
148
+ ...specialistConfig,
149
+ },
150
+ {
151
+ agentId: 'legal_advisor',
152
+ ...specialistConfig,
153
+ },
154
+ ];
155
+
156
+ // Create a SINGLE edge from supervisor to ALL 5 specialists using a list
157
+ const edges: t.GraphEdge[] = [
158
+ {
159
+ from: 'supervisor',
160
+ to: [
161
+ 'data_analyst',
162
+ 'security_expert',
163
+ 'product_designer',
164
+ 'devops_engineer',
165
+ 'legal_advisor',
166
+ ],
167
+ description:
168
+ 'Transfer to appropriate specialist based on task requirements',
169
+ edgeType: 'handoff',
170
+ },
171
+ ];
172
+
173
+ return {
174
+ runId: `supervisor-list-handoff-${Date.now()}`,
175
+ graphConfig: {
176
+ type: 'multi-agent',
177
+ agents,
178
+ edges,
179
+ },
180
+ customHandlers,
181
+ returnContent: true,
182
+ };
183
+ }
184
+
185
+ try {
186
+ // Test with different queries
187
+ const testQueries = [
188
+ // 'How can we analyze user engagement metrics to improve our product?',
189
+ // 'What security measures should we implement for our new API?',
190
+ // 'Can you help design a better onboarding flow for our mobile app?',
191
+ // 'We need to set up a CI/CD pipeline for our microservices.',
192
+ 'What are the legal implications of using GPL-licensed code in our product?',
193
+ ];
194
+
195
+ const config = {
196
+ configurable: {
197
+ thread_id: 'supervisor-list-handoff-1',
198
+ },
199
+ streamMode: 'values',
200
+ version: 'v2' as const,
201
+ };
202
+
203
+ for (const query of testQueries) {
204
+ console.log(`\n${'='.repeat(60)}`);
205
+ console.log(`USER QUERY: "${query}"`);
206
+ console.log('='.repeat(60));
207
+
208
+ // Reset conversation
209
+ conversationHistory.length = 0;
210
+ conversationHistory.push(new HumanMessage(query));
211
+
212
+ // Create graph with supervisor having a single edge to multiple specialists
213
+ const runConfig = createSupervisorGraphWithListEdge();
214
+ const run = await Run.create(runConfig);
215
+
216
+ console.log('Processing request...');
217
+
218
+ // Process with streaming
219
+ const inputs = {
220
+ messages: conversationHistory,
221
+ };
222
+
223
+ const finalContentParts = await run.processStream(inputs, config);
224
+ const finalMessages = run.getRunMessages();
225
+
226
+ if (finalMessages) {
227
+ conversationHistory.push(...finalMessages);
228
+ }
229
+
230
+ // Show summary
231
+ console.log(`\n${'─'.repeat(60)}`);
232
+ console.log(`Graph structure:`);
233
+ console.log(`- Agents: 6 total (supervisor + 5 specialists)`);
234
+ console.log(`- Edges: 1 edge with multiple destinations`);
235
+ console.log(
236
+ `- Edge type: handoff (creates individual tools for each destination)`
237
+ );
238
+ console.log(
239
+ `- Result: Supervisor has 5 handoff tools from a single edge`
240
+ );
241
+ console.log('─'.repeat(60));
242
+ }
243
+
244
+ // Final summary
245
+ console.log(`\n${'='.repeat(60)}`);
246
+ console.log('TEST COMPLETE');
247
+ console.log('='.repeat(60));
248
+ console.log('\nThis test demonstrates that a single edge with multiple');
249
+ console.log('destinations in the "to" field creates individual handoff');
250
+ console.log('tools for each destination agent, achieving the same result');
251
+ console.log('as creating separate edges for each specialist.');
252
+ } catch (error) {
253
+ console.error('Error in supervisor list handoff test:', error);
254
+ }
255
+ }
256
+
257
+ // Run the test
258
+ testSupervisorListHandoff();
@@ -66,6 +66,10 @@ export type BaseGraphState = {
66
66
  messages: BaseMessage[];
67
67
  };
68
68
 
69
+ export type MultiAgentGraphState = BaseGraphState & {
70
+ agentMessages?: BaseMessage[];
71
+ };
72
+
69
73
  export type IState = BaseGraphState;
70
74
 
71
75
  export interface EventHandler {
@@ -116,6 +120,27 @@ export type CompiledStateWorkflow = CompiledStateGraph<
116
120
  StateDefinition
117
121
  >;
118
122
 
123
+ export type CompiledMultiAgentWorkflow = CompiledStateGraph<
124
+ StateType<{
125
+ messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
126
+ agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
127
+ }>,
128
+ UpdateType<{
129
+ messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
130
+ agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
131
+ }>,
132
+ string,
133
+ {
134
+ messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
135
+ agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
136
+ },
137
+ {
138
+ messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
139
+ agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
140
+ },
141
+ StateDefinition
142
+ >;
143
+
119
144
  export type CompiledAgentWorfklow = CompiledStateGraph<
120
145
  {
121
146
  messages: BaseMessage[];
@@ -290,19 +315,37 @@ export type StandardGraphInput = {
290
315
  };
291
316
 
292
317
  export type GraphEdge = {
293
- /** Use a list for multiple sources */
318
+ /** Agent ID, use a list for multiple sources */
294
319
  from: string | string[];
295
- /** Use a list for multiple destinations */
320
+ /** Agent ID, use a list for multiple destinations */
296
321
  to: string | string[];
297
322
  description?: string;
298
323
  /** Can return boolean or specific destination(s) */
299
324
  condition?: (state: BaseGraphState) => boolean | string | string[];
300
325
  /** 'handoff' creates tools for dynamic routing, 'direct' creates direct edges, which also allow parallel execution */
301
326
  edgeType?: 'handoff' | 'direct';
302
- /** Optional prompt to add when transitioning through this edge */
303
- promptInstructions?:
327
+ /**
328
+ * For direct edges: Optional prompt to add when transitioning through this edge.
329
+ * String prompts can include variables like {results} which will be replaced with
330
+ * messages from startIndex onwards. When {results} is used, excludeResults defaults to true.
331
+ *
332
+ * For handoff edges: Description for the input parameter that the handoff tool accepts,
333
+ * allowing the supervisor to pass specific instructions/context to the transferred agent.
334
+ */
335
+ prompt?:
304
336
  | string
305
- | ((messages: BaseMessage[]) => string | undefined);
337
+ | ((messages: BaseMessage[], runStartIndex: number) => string | undefined);
338
+ /**
339
+ * When true, excludes messages from startIndex when adding prompt.
340
+ * Automatically set to true when {results} variable is used in prompt.
341
+ */
342
+ excludeResults?: boolean;
343
+ /**
344
+ * For handoff edges: Customizes the parameter name for the handoff input.
345
+ * Defaults to "instructions" if not specified.
346
+ * Only applies when prompt is provided for handoff edges.
347
+ */
348
+ promptKey?: string;
306
349
  };
307
350
 
308
351
  export type MultiAgentGraphInput = StandardGraphInput & {
package/src/types/run.ts CHANGED
@@ -103,9 +103,14 @@ export type MultiAgentGraphConfig = {
103
103
  edges: g.GraphEdge[];
104
104
  };
105
105
 
106
+ export type StandardGraphConfig = Omit<
107
+ MultiAgentGraphConfig,
108
+ 'edges' | 'type'
109
+ > & { type?: 'standard'; signal?: AbortSignal };
110
+
106
111
  export type RunConfig = {
107
112
  runId: string;
108
- graphConfig: LegacyGraphConfig | MultiAgentGraphConfig;
113
+ graphConfig: LegacyGraphConfig | StandardGraphConfig | MultiAgentGraphConfig;
109
114
  customHandlers?: Record<string, g.EventHandler>;
110
115
  returnContent?: boolean;
111
116
  tokenCounter?: TokenCounter;