@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.
- package/dist/cjs/graphs/MultiAgentGraph.cjs +132 -32
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/run.cjs +26 -13
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +133 -33
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/run.mjs +26 -13
- package/dist/esm/run.mjs.map +1 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +1 -1
- package/dist/types/scripts/multi-agent-supervisor.d.ts +1 -0
- package/dist/types/scripts/test-custom-prompt-key.d.ts +2 -0
- package/dist/types/scripts/test-handoff-input.d.ts +2 -0
- package/dist/types/scripts/test-multi-agent-list-handoff.d.ts +2 -0
- package/dist/types/types/graph.d.ts +38 -4
- package/dist/types/types/run.d.ts +5 -1
- package/package.json +5 -1
- package/src/graphs/MultiAgentGraph.ts +154 -37
- package/src/run.ts +35 -24
- package/src/scripts/multi-agent-parallel.ts +27 -23
- package/src/scripts/multi-agent-supervisor.ts +361 -0
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +110 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +258 -0
- package/src/types/graph.ts +48 -5
- package/src/types/run.ts +6 -1
|
@@ -28,6 +28,9 @@ export type SystemCallbacks = {
|
|
|
28
28
|
export type BaseGraphState = {
|
|
29
29
|
messages: BaseMessage[];
|
|
30
30
|
};
|
|
31
|
+
export type MultiAgentGraphState = BaseGraphState & {
|
|
32
|
+
agentMessages?: BaseMessage[];
|
|
33
|
+
};
|
|
31
34
|
export type IState = BaseGraphState;
|
|
32
35
|
export interface EventHandler {
|
|
33
36
|
handle(event: string, data: StreamEventData | ModelEndData | RunStep | RunStepDeltaEvent | MessageDeltaEvent | ReasoningDeltaEvent | {
|
|
@@ -46,6 +49,19 @@ export type CompiledStateWorkflow = CompiledStateGraph<StateType<{
|
|
|
46
49
|
}, {
|
|
47
50
|
messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
48
51
|
}, StateDefinition>;
|
|
52
|
+
export type CompiledMultiAgentWorkflow = CompiledStateGraph<StateType<{
|
|
53
|
+
messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
54
|
+
agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
55
|
+
}>, UpdateType<{
|
|
56
|
+
messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
57
|
+
agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
58
|
+
}>, string, {
|
|
59
|
+
messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
60
|
+
agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
61
|
+
}, {
|
|
62
|
+
messages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
63
|
+
agentMessages: BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
64
|
+
}, StateDefinition>;
|
|
49
65
|
export type CompiledAgentWorfklow = CompiledStateGraph<{
|
|
50
66
|
messages: BaseMessage[];
|
|
51
67
|
}, {
|
|
@@ -185,17 +201,35 @@ export type StandardGraphInput = {
|
|
|
185
201
|
indexTokenCountMap?: Record<string, number>;
|
|
186
202
|
};
|
|
187
203
|
export type GraphEdge = {
|
|
188
|
-
/**
|
|
204
|
+
/** Agent ID, use a list for multiple sources */
|
|
189
205
|
from: string | string[];
|
|
190
|
-
/**
|
|
206
|
+
/** Agent ID, use a list for multiple destinations */
|
|
191
207
|
to: string | string[];
|
|
192
208
|
description?: string;
|
|
193
209
|
/** Can return boolean or specific destination(s) */
|
|
194
210
|
condition?: (state: BaseGraphState) => boolean | string | string[];
|
|
195
211
|
/** 'handoff' creates tools for dynamic routing, 'direct' creates direct edges, which also allow parallel execution */
|
|
196
212
|
edgeType?: 'handoff' | 'direct';
|
|
197
|
-
/**
|
|
198
|
-
|
|
213
|
+
/**
|
|
214
|
+
* For direct edges: Optional prompt to add when transitioning through this edge.
|
|
215
|
+
* String prompts can include variables like {results} which will be replaced with
|
|
216
|
+
* messages from startIndex onwards. When {results} is used, excludeResults defaults to true.
|
|
217
|
+
*
|
|
218
|
+
* For handoff edges: Description for the input parameter that the handoff tool accepts,
|
|
219
|
+
* allowing the supervisor to pass specific instructions/context to the transferred agent.
|
|
220
|
+
*/
|
|
221
|
+
prompt?: string | ((messages: BaseMessage[], runStartIndex: number) => string | undefined);
|
|
222
|
+
/**
|
|
223
|
+
* When true, excludes messages from startIndex when adding prompt.
|
|
224
|
+
* Automatically set to true when {results} variable is used in prompt.
|
|
225
|
+
*/
|
|
226
|
+
excludeResults?: boolean;
|
|
227
|
+
/**
|
|
228
|
+
* For handoff edges: Customizes the parameter name for the handoff input.
|
|
229
|
+
* Defaults to "instructions" if not specified.
|
|
230
|
+
* Only applies when prompt is provided for handoff edges.
|
|
231
|
+
*/
|
|
232
|
+
promptKey?: string;
|
|
199
233
|
};
|
|
200
234
|
export type MultiAgentGraphInput = StandardGraphInput & {
|
|
201
235
|
edges: GraphEdge[];
|
|
@@ -92,9 +92,13 @@ export type MultiAgentGraphConfig = {
|
|
|
92
92
|
agents: g.AgentInputs[];
|
|
93
93
|
edges: g.GraphEdge[];
|
|
94
94
|
};
|
|
95
|
+
export type StandardGraphConfig = Omit<MultiAgentGraphConfig, 'edges' | 'type'> & {
|
|
96
|
+
type?: 'standard';
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
};
|
|
95
99
|
export type RunConfig = {
|
|
96
100
|
runId: string;
|
|
97
|
-
graphConfig: LegacyGraphConfig | MultiAgentGraphConfig;
|
|
101
|
+
graphConfig: LegacyGraphConfig | StandardGraphConfig | MultiAgentGraphConfig;
|
|
98
102
|
customHandlers?: Record<string, g.EventHandler>;
|
|
99
103
|
returnContent?: boolean;
|
|
100
104
|
tokenCounter?: TokenCounter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@librechat/agents",
|
|
3
|
-
"version": "3.0.00-
|
|
3
|
+
"version": "3.0.00-rc3",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -60,6 +60,10 @@
|
|
|
60
60
|
"multi-agent-parallel": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-parallel.ts",
|
|
61
61
|
"multi-agent-sequence": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-sequence.ts",
|
|
62
62
|
"multi-agent-conditional": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-conditional.ts",
|
|
63
|
+
"multi-agent-supervisor": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-supervisor.ts",
|
|
64
|
+
"multi-agent-list-handoff": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-multi-agent-list-handoff.ts",
|
|
65
|
+
"test-handoff-input": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-handoff-input.ts",
|
|
66
|
+
"test-custom-prompt-key": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-custom-prompt-key.ts",
|
|
63
67
|
"script2": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/proto/example_test.ts",
|
|
64
68
|
"script3": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/proto/example_test_anthropic.ts",
|
|
65
69
|
"script4": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/cli4.ts --name 'Jo' --location 'New York, NY'",
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { tool } from '@langchain/core/tools';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ToolMessage,
|
|
5
|
+
HumanMessage,
|
|
6
|
+
getBufferString,
|
|
7
|
+
} from '@langchain/core/messages';
|
|
8
|
+
import { ChatPromptTemplate } from '@langchain/core/prompts';
|
|
4
9
|
import {
|
|
5
10
|
END,
|
|
6
11
|
START,
|
|
@@ -137,37 +142,52 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
137
142
|
const tools: t.GenericTool[] = [];
|
|
138
143
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
/** If there's a condition, create a single conditional handoff tool */
|
|
141
146
|
if (edge.condition != null) {
|
|
142
147
|
const toolName = 'conditional_transfer';
|
|
143
148
|
const toolDescription =
|
|
144
149
|
edge.description ?? 'Conditionally transfer control based on state';
|
|
145
150
|
|
|
151
|
+
/** Check if we have a prompt for handoff input */
|
|
152
|
+
const hasHandoffInput =
|
|
153
|
+
edge.prompt != null && typeof edge.prompt === 'string';
|
|
154
|
+
const handoffInputDescription = hasHandoffInput ? edge.prompt : undefined;
|
|
155
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
156
|
+
|
|
146
157
|
tools.push(
|
|
147
158
|
tool(
|
|
148
|
-
async (
|
|
159
|
+
async (input: Record<string, unknown>, config) => {
|
|
149
160
|
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
150
161
|
const toolCallId =
|
|
151
162
|
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
152
163
|
'unknown';
|
|
153
164
|
|
|
154
|
-
|
|
165
|
+
/** Evaluated condition */
|
|
155
166
|
const result = edge.condition!(state);
|
|
156
167
|
let destination: string;
|
|
157
168
|
|
|
158
169
|
if (typeof result === 'boolean') {
|
|
159
|
-
|
|
170
|
+
/** If true, use first destination; if false, don't transfer */
|
|
160
171
|
if (!result) return null;
|
|
161
172
|
destination = destinations[0];
|
|
162
173
|
} else if (typeof result === 'string') {
|
|
163
174
|
destination = result;
|
|
164
175
|
} else {
|
|
165
|
-
|
|
176
|
+
/** Array of destinations - for now, use the first */
|
|
166
177
|
destination = Array.isArray(result) ? result[0] : destinations[0];
|
|
167
178
|
}
|
|
168
179
|
|
|
180
|
+
let content = `Conditionally transferred to ${destination}`;
|
|
181
|
+
if (
|
|
182
|
+
hasHandoffInput &&
|
|
183
|
+
promptKey in input &&
|
|
184
|
+
input[promptKey] != null
|
|
185
|
+
) {
|
|
186
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
169
189
|
const toolMessage = new ToolMessage({
|
|
170
|
-
content
|
|
190
|
+
content,
|
|
171
191
|
name: toolName,
|
|
172
192
|
tool_call_id: toolCallId,
|
|
173
193
|
});
|
|
@@ -180,26 +200,51 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
180
200
|
},
|
|
181
201
|
{
|
|
182
202
|
name: toolName,
|
|
183
|
-
schema:
|
|
203
|
+
schema: hasHandoffInput
|
|
204
|
+
? z.object({
|
|
205
|
+
[promptKey]: z
|
|
206
|
+
.string()
|
|
207
|
+
.optional()
|
|
208
|
+
.describe(handoffInputDescription as string),
|
|
209
|
+
})
|
|
210
|
+
: z.object({}),
|
|
184
211
|
description: toolDescription,
|
|
185
212
|
}
|
|
186
213
|
)
|
|
187
214
|
);
|
|
188
215
|
} else {
|
|
189
|
-
|
|
216
|
+
/** Create individual tools for each destination */
|
|
190
217
|
for (const destination of destinations) {
|
|
191
218
|
const toolName = `transfer_to_${destination}`;
|
|
192
219
|
const toolDescription =
|
|
193
220
|
edge.description ?? `Transfer control to agent '${destination}'`;
|
|
194
221
|
|
|
222
|
+
/** Check if we have a prompt for handoff input */
|
|
223
|
+
const hasHandoffInput =
|
|
224
|
+
edge.prompt != null && typeof edge.prompt === 'string';
|
|
225
|
+
const handoffInputDescription = hasHandoffInput
|
|
226
|
+
? edge.prompt
|
|
227
|
+
: undefined;
|
|
228
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
229
|
+
|
|
195
230
|
tools.push(
|
|
196
231
|
tool(
|
|
197
|
-
async (
|
|
232
|
+
async (input: Record<string, unknown>, config) => {
|
|
198
233
|
const toolCallId =
|
|
199
234
|
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
200
235
|
'unknown';
|
|
236
|
+
|
|
237
|
+
let content = `Successfully transferred to ${destination}`;
|
|
238
|
+
if (
|
|
239
|
+
hasHandoffInput &&
|
|
240
|
+
promptKey in input &&
|
|
241
|
+
input[promptKey] != null
|
|
242
|
+
) {
|
|
243
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
201
246
|
const toolMessage = new ToolMessage({
|
|
202
|
-
content
|
|
247
|
+
content,
|
|
203
248
|
name: toolName,
|
|
204
249
|
tool_call_id: toolCallId,
|
|
205
250
|
});
|
|
@@ -214,7 +259,14 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
214
259
|
},
|
|
215
260
|
{
|
|
216
261
|
name: toolName,
|
|
217
|
-
schema:
|
|
262
|
+
schema: hasHandoffInput
|
|
263
|
+
? z.object({
|
|
264
|
+
[promptKey]: z
|
|
265
|
+
.string()
|
|
266
|
+
.optional()
|
|
267
|
+
.describe(handoffInputDescription as string),
|
|
268
|
+
})
|
|
269
|
+
: z.object({}),
|
|
218
270
|
description: toolDescription,
|
|
219
271
|
}
|
|
220
272
|
)
|
|
@@ -229,14 +281,14 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
229
281
|
* Create a complete agent subgraph (similar to createReactAgent)
|
|
230
282
|
*/
|
|
231
283
|
private createAgentSubgraph(agentId: string): t.CompiledAgentWorfklow {
|
|
232
|
-
|
|
284
|
+
/** This is essentially the same as `createAgentNode` from `StandardGraph` */
|
|
233
285
|
return this.createAgentNode(agentId);
|
|
234
286
|
}
|
|
235
287
|
|
|
236
288
|
/**
|
|
237
289
|
* Create the multi-agent workflow with dynamic handoffs
|
|
238
290
|
*/
|
|
239
|
-
override createWorkflow(): t.
|
|
291
|
+
override createWorkflow(): t.CompiledMultiAgentWorkflow {
|
|
240
292
|
const StateAnnotation = Annotation.Root({
|
|
241
293
|
messages: Annotation<BaseMessage[]>({
|
|
242
294
|
reducer: (a, b) => {
|
|
@@ -249,6 +301,12 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
249
301
|
},
|
|
250
302
|
default: () => [],
|
|
251
303
|
}),
|
|
304
|
+
/** Channel for passing filtered messages to agents when excludeResults is true */
|
|
305
|
+
agentMessages: Annotation<BaseMessage[]>({
|
|
306
|
+
/** Replaces state entirely */
|
|
307
|
+
reducer: (a, b) => b,
|
|
308
|
+
default: () => [],
|
|
309
|
+
}),
|
|
252
310
|
});
|
|
253
311
|
|
|
254
312
|
const builder = new StateGraph(StateAnnotation);
|
|
@@ -277,18 +335,40 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
277
335
|
}
|
|
278
336
|
}
|
|
279
337
|
|
|
280
|
-
|
|
281
|
-
|
|
338
|
+
/** If agent has handoff destinations, add END to possible ends
|
|
339
|
+
* If agent only has direct destinations, it naturally ends without explicit END
|
|
340
|
+
*/
|
|
282
341
|
const destinations = new Set([...handoffDestinations]);
|
|
283
342
|
if (handoffDestinations.size > 0 || directDestinations.size === 0) {
|
|
284
343
|
destinations.add(END);
|
|
285
344
|
}
|
|
286
345
|
|
|
287
|
-
|
|
346
|
+
/** Agent subgraph (includes agent + tools) */
|
|
288
347
|
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
289
348
|
|
|
290
|
-
|
|
291
|
-
|
|
349
|
+
/** Wrapper function that handles agentMessages channel */
|
|
350
|
+
const agentWrapper = async (
|
|
351
|
+
state: t.MultiAgentGraphState
|
|
352
|
+
): Promise<t.MultiAgentGraphState> => {
|
|
353
|
+
if (state.agentMessages != null && state.agentMessages.length > 0) {
|
|
354
|
+
/** Temporary state with messages replaced by `agentMessages` */
|
|
355
|
+
const transformedState: t.MultiAgentGraphState = {
|
|
356
|
+
...state,
|
|
357
|
+
messages: state.agentMessages,
|
|
358
|
+
};
|
|
359
|
+
const result = await agentSubgraph.invoke(transformedState);
|
|
360
|
+
return {
|
|
361
|
+
...result,
|
|
362
|
+
/** Clear agentMessages for next agent */
|
|
363
|
+
agentMessages: [],
|
|
364
|
+
};
|
|
365
|
+
} else {
|
|
366
|
+
return await agentSubgraph.invoke(state);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/** Wrapped agent as a node with its possible destinations */
|
|
371
|
+
builder.addNode(agentId, agentWrapper, {
|
|
292
372
|
ends: Array.from(destinations),
|
|
293
373
|
});
|
|
294
374
|
}
|
|
@@ -300,7 +380,8 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
300
380
|
builder.addEdge(START, startNode);
|
|
301
381
|
}
|
|
302
382
|
|
|
303
|
-
/**
|
|
383
|
+
/**
|
|
384
|
+
* Add direct edges for automatic transitions
|
|
304
385
|
* Group edges by destination to handle fan-in scenarios
|
|
305
386
|
*/
|
|
306
387
|
const edgesByDestination = new Map<string, t.GraphEdge[]>();
|
|
@@ -318,38 +399,74 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
318
399
|
for (const [destination, edges] of edgesByDestination) {
|
|
319
400
|
/** Checks if this is a fan-in scenario with prompt instructions */
|
|
320
401
|
const edgesWithPrompt = edges.filter(
|
|
321
|
-
(edge) =>
|
|
322
|
-
edge.promptInstructions != null && edge.promptInstructions !== ''
|
|
402
|
+
(edge) => edge.prompt != null && edge.prompt !== ''
|
|
323
403
|
);
|
|
324
404
|
|
|
325
405
|
if (edgesWithPrompt.length > 0) {
|
|
326
|
-
|
|
406
|
+
/**
|
|
407
|
+
* Single wrapper node for destination (Fan-in with prompt)
|
|
408
|
+
*/
|
|
327
409
|
const wrapperNodeId = `fan_in_${destination}_prompt`;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
410
|
+
/**
|
|
411
|
+
* First edge's `prompt`
|
|
412
|
+
* (they should all be the same for fan-in)
|
|
413
|
+
*/
|
|
414
|
+
const prompt = edgesWithPrompt[0].prompt;
|
|
415
|
+
/**
|
|
416
|
+
* First edge's `excludeResults` flag
|
|
417
|
+
* (they should all be the same for fan-in)
|
|
418
|
+
*/
|
|
419
|
+
const excludeResults = edgesWithPrompt[0].excludeResults;
|
|
331
420
|
|
|
332
421
|
builder.addNode(wrapperNodeId, async (state: t.BaseGraphState) => {
|
|
333
422
|
let promptText: string | undefined;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
423
|
+
let effectiveExcludeResults = excludeResults;
|
|
424
|
+
|
|
425
|
+
if (typeof prompt === 'function') {
|
|
426
|
+
promptText = prompt(state.messages, this.startIndex);
|
|
427
|
+
} else if (prompt != null) {
|
|
428
|
+
if (prompt.includes('{results}')) {
|
|
429
|
+
const resultsMessages = state.messages.slice(this.startIndex);
|
|
430
|
+
const resultsString = getBufferString(resultsMessages);
|
|
431
|
+
const promptTemplate = ChatPromptTemplate.fromTemplate(prompt);
|
|
432
|
+
const formattedPromptValue = await promptTemplate.invoke({
|
|
433
|
+
results: resultsString,
|
|
434
|
+
});
|
|
435
|
+
promptText = formattedPromptValue.messages[0].content.toString();
|
|
436
|
+
effectiveExcludeResults =
|
|
437
|
+
excludeResults !== false && promptText !== '';
|
|
438
|
+
} else {
|
|
439
|
+
promptText = prompt;
|
|
440
|
+
}
|
|
339
441
|
}
|
|
340
442
|
|
|
341
443
|
if (promptText != null && promptText !== '') {
|
|
342
|
-
|
|
444
|
+
if (
|
|
445
|
+
effectiveExcludeResults == null ||
|
|
446
|
+
effectiveExcludeResults === false
|
|
447
|
+
) {
|
|
448
|
+
return {
|
|
449
|
+
messages: [new HumanMessage(promptText)],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** When `excludeResults` is true, use agentMessages channel
|
|
454
|
+
* to pass filtered messages + prompt to the destination agent
|
|
455
|
+
*/
|
|
456
|
+
const filteredMessages = state.messages.slice(0, this.startIndex);
|
|
343
457
|
return {
|
|
344
|
-
messages: [
|
|
458
|
+
messages: [new HumanMessage(promptText)],
|
|
459
|
+
agentMessages: messagesStateReducer(filteredMessages, [
|
|
460
|
+
new HumanMessage(promptText),
|
|
461
|
+
]),
|
|
345
462
|
};
|
|
346
463
|
}
|
|
347
464
|
|
|
348
|
-
|
|
465
|
+
/** No prompt needed, return empty update */
|
|
349
466
|
return {};
|
|
350
467
|
});
|
|
351
468
|
|
|
352
|
-
|
|
469
|
+
/** Add edges from all sources to the wrapper, then wrapper to destination */
|
|
353
470
|
for (const edge of edges) {
|
|
354
471
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
355
472
|
for (const source of sources) {
|
|
@@ -359,12 +476,12 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
359
476
|
}
|
|
360
477
|
}
|
|
361
478
|
|
|
362
|
-
|
|
479
|
+
/** Single edge from wrapper to destination */
|
|
363
480
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
364
481
|
/** @ts-ignore */
|
|
365
482
|
builder.addEdge(wrapperNodeId, destination);
|
|
366
483
|
} else {
|
|
367
|
-
|
|
484
|
+
/** No prompt instructions, add direct edges */
|
|
368
485
|
for (const edge of edges) {
|
|
369
486
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
370
487
|
for (const source of sources) {
|
package/src/run.ts
CHANGED
|
@@ -73,7 +73,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
73
73
|
this.Graph.handlerRegistry = handlerRegistry;
|
|
74
74
|
}
|
|
75
75
|
} else {
|
|
76
|
-
|
|
76
|
+
/** Default to legacy graph for 'standard' or undefined type */
|
|
77
77
|
this.graphRunnable = this.createLegacyGraph(config.graphConfig);
|
|
78
78
|
if (this.Graph) {
|
|
79
79
|
this.Graph.compileOptions =
|
|
@@ -86,25 +86,38 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
private createLegacyGraph(
|
|
89
|
-
config: t.LegacyGraphConfig
|
|
89
|
+
config: t.LegacyGraphConfig | t.StandardGraphConfig
|
|
90
90
|
): t.CompiledStateWorkflow {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
let agentConfig: t.AgentInputs;
|
|
92
|
+
let signal: AbortSignal | undefined;
|
|
93
|
+
|
|
94
|
+
/** Check if this is a multi-agent style config (has agents array) */
|
|
95
|
+
if ('agents' in config && Array.isArray(config.agents)) {
|
|
96
|
+
if (config.agents.length === 0) {
|
|
97
|
+
throw new Error('At least one agent must be provided');
|
|
98
|
+
}
|
|
99
|
+
agentConfig = config.agents[0];
|
|
100
|
+
signal = config.signal;
|
|
101
|
+
} else {
|
|
102
|
+
/** Legacy path: build agent config from llmConfig */
|
|
103
|
+
const {
|
|
104
|
+
type: _type,
|
|
105
|
+
llmConfig,
|
|
106
|
+
signal: legacySignal,
|
|
107
|
+
tools = [],
|
|
108
|
+
...agentInputs
|
|
109
|
+
} = config as t.LegacyGraphConfig;
|
|
110
|
+
const { provider, ...clientOptions } = llmConfig;
|
|
111
|
+
|
|
112
|
+
agentConfig = {
|
|
113
|
+
...agentInputs,
|
|
114
|
+
tools,
|
|
115
|
+
provider,
|
|
116
|
+
clientOptions,
|
|
117
|
+
agentId: 'default',
|
|
118
|
+
};
|
|
119
|
+
signal = legacySignal;
|
|
120
|
+
}
|
|
108
121
|
|
|
109
122
|
const standardGraph = new StandardGraph({
|
|
110
123
|
signal,
|
|
@@ -113,10 +126,8 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
113
126
|
tokenCounter: this.tokenCounter,
|
|
114
127
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
115
128
|
});
|
|
116
|
-
|
|
117
|
-
standardGraph.compileOptions =
|
|
118
|
-
config as t.LegacyGraphConfig
|
|
119
|
-
).compileOptions;
|
|
129
|
+
/** Propagate compile options from graph config */
|
|
130
|
+
standardGraph.compileOptions = config.compileOptions;
|
|
120
131
|
this.Graph = standardGraph;
|
|
121
132
|
return standardGraph.createWorkflow();
|
|
122
133
|
}
|
|
@@ -145,7 +156,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
145
156
|
static async create<T extends t.BaseGraphState>(
|
|
146
157
|
config: t.RunConfig
|
|
147
158
|
): Promise<Run<T>> {
|
|
148
|
-
|
|
159
|
+
/** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is not */
|
|
149
160
|
if (config.indexTokenCountMap && !config.tokenCounter) {
|
|
150
161
|
config.tokenCounter = await createTokenCounter();
|
|
151
162
|
}
|
|
@@ -167,29 +167,33 @@ async function testParallelMultiAgent() {
|
|
|
167
167
|
description: 'Aggregate analysis results',
|
|
168
168
|
edgeType: 'direct', // Fan-in is also direct
|
|
169
169
|
// Add prompt when all analysts have provided input
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
170
|
+
// prompt: (messages, runStartIndex) => {
|
|
171
|
+
// // Check if we have analysis content from all three analysts
|
|
172
|
+
// // Look for the specific headers each analyst uses
|
|
173
|
+
// const aiMessages = messages.filter(
|
|
174
|
+
// (msg, index) => msg.getType() === 'ai' && index >= runStartIndex
|
|
175
|
+
// );
|
|
176
|
+
// const messageContent = aiMessages.map((msg) => msg.content).join('\n');
|
|
177
|
+
|
|
178
|
+
// const hasFinancialAnalysis = messageContent.includes(
|
|
179
|
+
// 'FINANCIAL ANALYSIS:'
|
|
180
|
+
// );
|
|
181
|
+
// const hasTechnicalAnalysis = messageContent.includes(
|
|
182
|
+
// 'TECHNICAL ANALYSIS:'
|
|
183
|
+
// );
|
|
184
|
+
// const hasMarketAnalysis = messageContent.includes('MARKET ANALYSIS:');
|
|
185
|
+
|
|
186
|
+
// console.log(
|
|
187
|
+
// `Checking for analyses - Financial: ${hasFinancialAnalysis}, Technical: ${hasTechnicalAnalysis}, Market: ${hasMarketAnalysis}`
|
|
188
|
+
// );
|
|
189
|
+
|
|
190
|
+
// if (hasFinancialAnalysis && hasTechnicalAnalysis && hasMarketAnalysis) {
|
|
191
|
+
// return 'Based on the comprehensive analyses from all three specialist teams above, please synthesize their insights into a cohesive executive summary. Focus on the key findings, common themes, and strategic implications across the financial, technical, and market perspectives.';
|
|
192
|
+
// }
|
|
193
|
+
// return undefined; // No prompt if we haven't received all analyst inputs
|
|
194
|
+
// },
|
|
195
|
+
prompt:
|
|
196
|
+
'Based on the comprehensive analyses from all three specialist teams below, please synthesize their insights into a cohesive executive summary. Focus on the key findings, common themes, and strategic implications across the financial, technical, and market perspectives.\n\n{results}',
|
|
193
197
|
},
|
|
194
198
|
];
|
|
195
199
|
|