@librechat/agents 3.0.0-rc5 → 3.0.0-rc7

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.
@@ -36,10 +36,10 @@ async function testSupervisorMultiAgent() {
36
36
  // Define configurations for all possible specialists
37
37
  const specialistConfigs = {
38
38
  data_analyst: {
39
- provider: Providers.ANTHROPIC,
39
+ provider: Providers.OPENAI,
40
40
  clientOptions: {
41
- modelName: 'claude-3-5-sonnet-latest',
42
- apiKey: process.env.ANTHROPIC_API_KEY,
41
+ modelName: 'gpt-4.1',
42
+ apiKey: process.env.OPENAI_API_KEY,
43
43
  },
44
44
  instructions: `You are a Data Analyst specialist. Your expertise includes:
45
45
  - Statistical analysis and data visualization
@@ -52,10 +52,10 @@ async function testSupervisorMultiAgent() {
52
52
  maxContextTokens: 8000,
53
53
  },
54
54
  security_expert: {
55
- provider: Providers.ANTHROPIC,
55
+ provider: Providers.OPENAI,
56
56
  clientOptions: {
57
- modelName: 'claude-3-5-sonnet-latest',
58
- apiKey: process.env.ANTHROPIC_API_KEY,
57
+ modelName: 'gpt-4.1',
58
+ apiKey: process.env.OPENAI_API_KEY,
59
59
  },
60
60
  instructions: `You are a Security Expert. Your expertise includes:
61
61
  - Cybersecurity best practices
@@ -68,10 +68,10 @@ async function testSupervisorMultiAgent() {
68
68
  maxContextTokens: 8000,
69
69
  },
70
70
  product_designer: {
71
- provider: Providers.ANTHROPIC,
71
+ provider: Providers.OPENAI,
72
72
  clientOptions: {
73
- modelName: 'claude-3-5-sonnet-latest',
74
- apiKey: process.env.ANTHROPIC_API_KEY,
73
+ modelName: 'gpt-4.1',
74
+ apiKey: process.env.OPENAI_API_KEY,
75
75
  },
76
76
  instructions: `You are a Product Designer. Your expertise includes:
77
77
  - User experience (UX) design principles
@@ -84,10 +84,10 @@ async function testSupervisorMultiAgent() {
84
84
  maxContextTokens: 8000,
85
85
  },
86
86
  devops_engineer: {
87
- provider: Providers.ANTHROPIC,
87
+ provider: Providers.OPENAI,
88
88
  clientOptions: {
89
- modelName: 'claude-3-5-sonnet-latest',
90
- apiKey: process.env.ANTHROPIC_API_KEY,
89
+ modelName: 'gpt-4.1',
90
+ apiKey: process.env.OPENAI_API_KEY,
91
91
  },
92
92
  instructions: `You are a DevOps Engineer. Your expertise includes:
93
93
  - CI/CD pipeline design and optimization
@@ -100,10 +100,10 @@ async function testSupervisorMultiAgent() {
100
100
  maxContextTokens: 8000,
101
101
  },
102
102
  legal_advisor: {
103
- provider: Providers.ANTHROPIC,
103
+ provider: Providers.OPENAI,
104
104
  clientOptions: {
105
- modelName: 'claude-3-5-sonnet-latest',
106
- apiKey: process.env.ANTHROPIC_API_KEY,
105
+ modelName: 'gpt-4.1',
106
+ apiKey: process.env.OPENAI_API_KEY,
107
107
  },
108
108
  instructions: `You are a Legal Advisor specializing in technology. Your expertise includes:
109
109
  - Software licensing and open source compliance
@@ -154,6 +154,7 @@ async function testSupervisorMultiAgent() {
154
154
  event: GraphEvents.ON_MESSAGE_DELTA,
155
155
  data: t.StreamEventData
156
156
  ): void => {
157
+ console.dir(data, { depth: null });
157
158
  aggregateContent({ event, data: data as t.MessageDeltaEvent });
158
159
  },
159
160
  },
@@ -180,10 +181,10 @@ async function testSupervisorMultiAgent() {
180
181
 
181
182
  // Define the adaptive specialist configuration that will be reused
182
183
  const specialistConfig = {
183
- provider: Providers.ANTHROPIC,
184
+ provider: Providers.OPENAI,
184
185
  clientOptions: {
185
- modelName: 'claude-3-5-sonnet-latest',
186
- apiKey: process.env.ANTHROPIC_API_KEY,
186
+ modelName: 'gpt-4.1',
187
+ apiKey: process.env.OPENAI_API_KEY,
187
188
  },
188
189
  instructions: `You are an Adaptive Specialist. Your agent ID indicates your role:
189
190
 
@@ -202,10 +203,10 @@ async function testSupervisorMultiAgent() {
202
203
  const agents: t.AgentInputs[] = [
203
204
  {
204
205
  agentId: 'supervisor',
205
- provider: Providers.ANTHROPIC,
206
+ provider: Providers.OPENAI,
206
207
  clientOptions: {
207
- modelName: 'claude-3-5-sonnet-latest',
208
- apiKey: process.env.ANTHROPIC_API_KEY,
208
+ modelName: 'gpt-4.1-mini',
209
+ apiKey: process.env.OPENAI_API_KEY,
209
210
  },
210
211
  instructions: `You are a Task Supervisor with access to 5 specialist agents:
211
212
  1. transfer_to_data_analyst - For statistical analysis and metrics
@@ -298,10 +299,10 @@ async function testSupervisorMultiAgent() {
298
299
  // Test with different queries
299
300
  const testQueries = [
300
301
  'How can we analyze user engagement metrics to improve our product?',
301
- 'What security measures should we implement for our new API?',
302
- 'Can you help design a better onboarding flow for our mobile app?',
303
- 'We need to set up a CI/CD pipeline for our microservices.',
304
- 'What are the legal implications of using GPL-licensed code in our product?',
302
+ // 'What security measures should we implement for our new API?',
303
+ // 'Can you help design a better onboarding flow for our mobile app?',
304
+ // 'We need to set up a CI/CD pipeline for our microservices.',
305
+ // 'What are the legal implications of using GPL-licensed code in our product?',
305
306
  ];
306
307
 
307
308
  const config = {
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env bun
2
-
3
1
  import { config } from 'dotenv';
4
2
  config();
5
3
 
6
4
  import { HumanMessage } from '@langchain/core/messages';
7
5
  import { Run } from '@/run';
8
- import { Providers } from '@/common';
6
+ import { Providers, GraphEvents } from '@/common';
7
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
8
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
9
9
  import type * as t from '@/types';
10
10
 
11
11
  /**
@@ -14,8 +14,68 @@ import type * as t from '@/types';
14
14
  */
15
15
  async function testHandoffInput() {
16
16
  console.log('Testing Handoff Input Feature...\n');
17
+ // Set up content aggregator
18
+ const { contentParts, aggregateContent } = createContentAggregator();
19
+
20
+ // Track which specialist role was selected
21
+ let selectedRole = '';
22
+ let roleInstructions = '';
23
+
24
+ // Create custom handlers
25
+ const customHandlers = {
26
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
27
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
28
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
29
+ [GraphEvents.ON_RUN_STEP]: {
30
+ handle: (
31
+ event: GraphEvents.ON_RUN_STEP,
32
+ data: t.StreamEventData
33
+ ): void => {
34
+ const runStepData = data as any;
35
+ if (runStepData?.name) {
36
+ console.log(`\n[${runStepData.name}] Processing...`);
37
+ }
38
+ aggregateContent({ event, data: data as t.RunStep });
39
+ },
40
+ },
41
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
42
+ handle: (
43
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
44
+ data: t.StreamEventData
45
+ ): void => {
46
+ aggregateContent({
47
+ event,
48
+ data: data as unknown as { result: t.ToolEndEvent },
49
+ });
50
+ },
51
+ },
52
+ [GraphEvents.ON_MESSAGE_DELTA]: {
53
+ handle: (
54
+ event: GraphEvents.ON_MESSAGE_DELTA,
55
+ data: t.StreamEventData
56
+ ): void => {
57
+ console.dir(data, { depth: null });
58
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
59
+ },
60
+ },
61
+ [GraphEvents.TOOL_START]: {
62
+ handle: (
63
+ _event: string,
64
+ data: t.StreamEventData,
65
+ metadata?: Record<string, unknown>
66
+ ): void => {
67
+ const toolData = data as any;
68
+ if (toolData?.name?.includes('transfer_to_')) {
69
+ const specialist = toolData.name.replace('transfer_to_', '');
70
+ console.log(`\nšŸ”€ Transferring to ${specialist}...`);
71
+ selectedRole = specialist;
72
+ }
73
+ },
74
+ },
75
+ };
17
76
 
18
77
  const runConfig: t.RunConfig = {
78
+ customHandlers,
19
79
  runId: `test-handoff-input-${Date.now()}`,
20
80
  graphConfig: {
21
81
  type: 'multi-agent',
@@ -76,7 +136,7 @@ async function testHandoffInput() {
76
136
 
77
137
  // Test queries that should result in different handoffs with specific instructions
78
138
  const testQueries = [
79
- 'Analyze our Q4 sales data and identify the top 3 performing products',
139
+ // 'Analyze our Q4 sales data and identify the top 3 performing products',
80
140
  'Write a blog post about the benefits of remote work for software developers',
81
141
  ];
82
142
 
@@ -97,7 +157,7 @@ async function testHandoffInput() {
97
157
  messages: [new HumanMessage(query)],
98
158
  };
99
159
 
100
- await run.processStream(inputs, config);
160
+ const finalContentParts = await run.processStream(inputs, config);
101
161
 
102
162
  console.log(`\n${'─'.repeat(60)}`);
103
163
  console.log('Notice how the supervisor passes specific instructions');
@@ -5,8 +5,8 @@ config();
5
5
 
6
6
  import { HumanMessage, BaseMessage } from '@langchain/core/messages';
7
7
  import { Run } from '@/run';
8
- import { Providers, GraphEvents } from '@/common';
9
8
  import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
9
+ import { Providers, GraphEvents, Constants } from '@/common';
10
10
  import { ToolEndHandler, ModelEndHandler } from '@/events';
11
11
  import type * as t from '@/types';
12
12
 
@@ -70,8 +70,11 @@ async function testSupervisorListHandoff() {
70
70
  metadata?: Record<string, unknown>
71
71
  ): void => {
72
72
  const toolData = data as any;
73
- if (toolData?.name?.includes('transfer_to_')) {
74
- const specialist = toolData.name.replace('transfer_to_', '');
73
+ if (toolData?.name?.startsWith(Constants.LC_TRANSFER_TO_)) {
74
+ const specialist = toolData.name.replace(
75
+ Constants.LC_TRANSFER_TO_,
76
+ ''
77
+ );
75
78
  console.log(`\nšŸ”€ Transferring to ${specialist}...`);
76
79
  selectedRole = specialist;
77
80
  }
package/src/stream.ts CHANGED
@@ -153,7 +153,10 @@ export class ChatModelStreamHandler implements t.EventHandler {
153
153
  if (
154
154
  chunk.tool_calls &&
155
155
  chunk.tool_calls.length > 0 &&
156
- chunk.tool_calls.every((tc) => tc.id != null && tc.id !== '')
156
+ chunk.tool_calls.every(
157
+ (tc) =>
158
+ tc.id != null && tc.id !== '' && tc.name != null && tc.name !== ''
159
+ )
157
160
  ) {
158
161
  hasToolCalls = true;
159
162
  await handleToolCalls(chunk.tool_calls, metadata, graph);