@librechat/agents 2.4.322 → 3.0.0-rc1
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/agents/AgentContext.cjs +218 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -0
- package/dist/cjs/common/enum.cjs +14 -5
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +10 -6
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +309 -212
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +322 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/index.cjs +54 -9
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +52 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +22 -2
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/tools.cjs +29 -0
- package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -0
- package/dist/cjs/llm/google/index.cjs +144 -0
- package/dist/cjs/llm/google/index.cjs.map +1 -0
- package/dist/cjs/llm/google/utils/common.cjs +477 -0
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -0
- package/dist/cjs/llm/ollama/index.cjs +67 -0
- package/dist/cjs/llm/ollama/index.cjs.map +1 -0
- package/dist/cjs/llm/ollama/utils.cjs +158 -0
- package/dist/cjs/llm/ollama/utils.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +389 -3
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +672 -0
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -0
- package/dist/cjs/llm/providers.cjs +15 -15
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs +14 -3
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs +330 -0
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -0
- package/dist/cjs/main.cjs +11 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/run.cjs +120 -81
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +85 -51
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +10 -4
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +119 -13
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/search/anthropic.cjs +40 -0
- package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
- package/dist/cjs/tools/search/firecrawl.cjs +55 -9
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs +6 -6
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +7 -29
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +86 -16
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +4 -2
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +1 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs +31 -0
- package/dist/cjs/utils/events.cjs.map +1 -0
- package/dist/cjs/utils/title.cjs +57 -21
- package/dist/cjs/utils/title.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +54 -7
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +216 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -0
- package/dist/esm/common/enum.mjs +15 -6
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +10 -6
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +311 -214
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +320 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
- package/dist/esm/llm/anthropic/index.mjs +54 -9
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
- package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
- package/dist/esm/llm/google/index.mjs +142 -0
- package/dist/esm/llm/google/index.mjs.map +1 -0
- package/dist/esm/llm/google/utils/common.mjs +471 -0
- package/dist/esm/llm/google/utils/common.mjs.map +1 -0
- package/dist/esm/llm/ollama/index.mjs +65 -0
- package/dist/esm/llm/ollama/index.mjs.map +1 -0
- package/dist/esm/llm/ollama/utils.mjs +155 -0
- package/dist/esm/llm/ollama/utils.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +388 -4
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +666 -0
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
- package/dist/esm/llm/providers.mjs +5 -5
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/text.mjs +14 -3
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs +328 -0
- package/dist/esm/llm/vertexai/index.mjs.map +1 -0
- package/dist/esm/main.mjs +6 -5
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/run.mjs +121 -83
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +87 -54
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +10 -4
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +119 -15
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/search/anthropic.mjs +37 -0
- package/dist/esm/tools/search/anthropic.mjs.map +1 -0
- package/dist/esm/tools/search/firecrawl.mjs +55 -9
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs +7 -7
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +7 -29
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +86 -16
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +4 -2
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +1 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/utils/events.mjs +29 -0
- package/dist/esm/utils/events.mjs.map +1 -0
- package/dist/esm/utils/title.mjs +57 -22
- package/dist/esm/utils/title.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +54 -8
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +91 -0
- package/dist/types/common/enum.d.ts +15 -6
- package/dist/types/events.d.ts +5 -4
- package/dist/types/graphs/Graph.d.ts +64 -67
- package/dist/types/graphs/MultiAgentGraph.d.ts +37 -0
- package/dist/types/graphs/index.d.ts +1 -0
- package/dist/types/llm/anthropic/index.d.ts +11 -0
- package/dist/types/llm/anthropic/types.d.ts +9 -3
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
- package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
- package/dist/types/llm/google/index.d.ts +13 -0
- package/dist/types/llm/google/types.d.ts +32 -0
- package/dist/types/llm/google/utils/common.d.ts +19 -0
- package/dist/types/llm/google/utils/tools.d.ts +10 -0
- package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
- package/dist/types/llm/ollama/index.d.ts +7 -0
- package/dist/types/llm/ollama/utils.d.ts +7 -0
- package/dist/types/llm/openai/index.d.ts +72 -3
- package/dist/types/llm/openai/types.d.ts +10 -0
- package/dist/types/llm/openai/utils/index.d.ts +20 -0
- package/dist/types/llm/text.d.ts +1 -1
- package/dist/types/llm/vertexai/index.d.ts +293 -0
- package/dist/types/messages/reducer.d.ts +9 -0
- package/dist/types/run.d.ts +19 -12
- package/dist/types/scripts/ant_web_search.d.ts +1 -0
- package/dist/types/scripts/args.d.ts +2 -1
- package/dist/types/scripts/handoff-test.d.ts +1 -0
- package/dist/types/scripts/multi-agent-conditional.d.ts +1 -0
- package/dist/types/scripts/multi-agent-parallel.d.ts +1 -0
- package/dist/types/scripts/multi-agent-sequence.d.ts +1 -0
- package/dist/types/scripts/multi-agent-test.d.ts +1 -0
- package/dist/types/stream.d.ts +10 -3
- package/dist/types/tools/CodeExecutor.d.ts +2 -2
- package/dist/types/tools/ToolNode.d.ts +1 -1
- package/dist/types/tools/handlers.d.ts +17 -4
- package/dist/types/tools/search/anthropic.d.ts +16 -0
- package/dist/types/tools/search/firecrawl.d.ts +15 -0
- package/dist/types/tools/search/rerankers.d.ts +0 -1
- package/dist/types/tools/search/types.d.ts +30 -9
- package/dist/types/types/graph.d.ts +95 -15
- package/dist/types/types/llm.d.ts +24 -10
- package/dist/types/types/run.d.ts +46 -8
- package/dist/types/types/stream.d.ts +16 -2
- package/dist/types/types/tools.d.ts +1 -1
- package/dist/types/utils/events.d.ts +6 -0
- package/dist/types/utils/title.d.ts +2 -1
- package/dist/types/utils/tokens.d.ts +24 -0
- package/package.json +33 -17
- package/src/agents/AgentContext.ts +315 -0
- package/src/common/enum.ts +14 -5
- package/src/events.ts +24 -13
- package/src/graphs/Graph.ts +495 -312
- package/src/graphs/MultiAgentGraph.ts +381 -0
- package/src/graphs/index.ts +2 -1
- package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
- package/src/llm/anthropic/index.ts +78 -13
- package/src/llm/anthropic/llm.spec.ts +491 -115
- package/src/llm/anthropic/types.ts +39 -3
- package/src/llm/anthropic/utils/message_inputs.ts +67 -11
- package/src/llm/anthropic/utils/message_outputs.ts +21 -2
- package/src/llm/anthropic/utils/output_parsers.ts +25 -6
- package/src/llm/anthropic/utils/tools.ts +29 -0
- package/src/llm/google/index.ts +218 -0
- package/src/llm/google/types.ts +43 -0
- package/src/llm/google/utils/common.ts +646 -0
- package/src/llm/google/utils/tools.ts +160 -0
- package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
- package/src/llm/ollama/index.ts +89 -0
- package/src/llm/ollama/utils.ts +193 -0
- package/src/llm/openai/index.ts +600 -14
- package/src/llm/openai/types.ts +24 -0
- package/src/llm/openai/utils/index.ts +912 -0
- package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
- package/src/llm/providers.ts +10 -9
- package/src/llm/text.ts +26 -7
- package/src/llm/vertexai/index.ts +360 -0
- package/src/messages/reducer.ts +80 -0
- package/src/run.ts +181 -112
- package/src/scripts/ant_web_search.ts +158 -0
- package/src/scripts/args.ts +12 -8
- package/src/scripts/cli4.ts +29 -21
- package/src/scripts/cli5.ts +29 -21
- package/src/scripts/code_exec.ts +54 -23
- package/src/scripts/code_exec_files.ts +48 -17
- package/src/scripts/code_exec_simple.ts +46 -27
- package/src/scripts/handoff-test.ts +135 -0
- package/src/scripts/image.ts +52 -20
- package/src/scripts/multi-agent-conditional.ts +220 -0
- package/src/scripts/multi-agent-example-output.md +110 -0
- package/src/scripts/multi-agent-parallel.ts +337 -0
- package/src/scripts/multi-agent-sequence.ts +212 -0
- package/src/scripts/multi-agent-test.ts +186 -0
- package/src/scripts/search.ts +1 -9
- package/src/scripts/simple.ts +25 -10
- package/src/scripts/tools.ts +48 -18
- package/src/specs/anthropic.simple.test.ts +150 -34
- package/src/specs/azure.simple.test.ts +325 -0
- package/src/specs/openai.simple.test.ts +140 -33
- package/src/specs/openrouter.simple.test.ts +107 -0
- package/src/specs/prune.test.ts +4 -9
- package/src/specs/reasoning.test.ts +80 -44
- package/src/specs/token-memoization.test.ts +39 -0
- package/src/stream.test.ts +94 -0
- package/src/stream.ts +139 -60
- package/src/tools/ToolNode.ts +21 -7
- package/src/tools/handlers.ts +192 -18
- package/src/tools/search/anthropic.ts +51 -0
- package/src/tools/search/firecrawl.ts +69 -20
- package/src/tools/search/format.ts +6 -8
- package/src/tools/search/rerankers.ts +7 -40
- package/src/tools/search/search.ts +97 -16
- package/src/tools/search/tool.ts +5 -2
- package/src/tools/search/types.ts +30 -10
- package/src/tools/search/utils.ts +1 -1
- package/src/types/graph.ts +272 -103
- package/src/types/llm.ts +25 -12
- package/src/types/run.ts +51 -13
- package/src/types/stream.ts +22 -1
- package/src/types/tools.ts +16 -10
- package/src/utils/events.ts +32 -0
- package/src/utils/llmConfig.ts +19 -7
- package/src/utils/title.ts +104 -30
- package/src/utils/tokens.ts +69 -10
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { tool } from '@langchain/core/tools';
|
|
3
|
+
import { ToolMessage, HumanMessage } from '@langchain/core/messages';
|
|
4
|
+
import {
|
|
5
|
+
END,
|
|
6
|
+
START,
|
|
7
|
+
Command,
|
|
8
|
+
StateGraph,
|
|
9
|
+
Annotation,
|
|
10
|
+
getCurrentTaskInput,
|
|
11
|
+
messagesStateReducer,
|
|
12
|
+
} from '@langchain/langgraph';
|
|
13
|
+
import type { ToolRunnableConfig } from '@langchain/core/tools';
|
|
14
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
15
|
+
import type * as t from '@/types';
|
|
16
|
+
import { StandardGraph } from './Graph';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
|
|
20
|
+
* with handoffs, fan-in/fan-out, and other composable patterns
|
|
21
|
+
*/
|
|
22
|
+
export class MultiAgentGraph extends StandardGraph {
|
|
23
|
+
private edges: t.GraphEdge[];
|
|
24
|
+
private startingNodes: Set<string> = new Set();
|
|
25
|
+
private directEdges: t.GraphEdge[] = [];
|
|
26
|
+
private handoffEdges: t.GraphEdge[] = [];
|
|
27
|
+
|
|
28
|
+
constructor(input: t.MultiAgentGraphInput) {
|
|
29
|
+
super(input);
|
|
30
|
+
this.edges = input.edges;
|
|
31
|
+
this.categorizeEdges();
|
|
32
|
+
this.analyzeGraph();
|
|
33
|
+
this.createHandoffTools();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Categorize edges into handoff and direct types
|
|
38
|
+
*/
|
|
39
|
+
private categorizeEdges(): void {
|
|
40
|
+
for (const edge of this.edges) {
|
|
41
|
+
// Default behavior: edges with conditions or explicit 'handoff' type are handoff edges
|
|
42
|
+
// Edges with explicit 'direct' type or multi-destination without conditions are direct edges
|
|
43
|
+
if (edge.edgeType === 'direct') {
|
|
44
|
+
this.directEdges.push(edge);
|
|
45
|
+
} else if (edge.edgeType === 'handoff' || edge.condition != null) {
|
|
46
|
+
this.handoffEdges.push(edge);
|
|
47
|
+
} else {
|
|
48
|
+
// Default: single-to-single edges are handoff, single-to-multiple are direct
|
|
49
|
+
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
50
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
51
|
+
|
|
52
|
+
if (sources.length === 1 && destinations.length > 1) {
|
|
53
|
+
// Fan-out pattern defaults to direct
|
|
54
|
+
this.directEdges.push(edge);
|
|
55
|
+
} else {
|
|
56
|
+
// Everything else defaults to handoff
|
|
57
|
+
this.handoffEdges.push(edge);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Analyze graph structure to determine starting nodes and connections
|
|
65
|
+
*/
|
|
66
|
+
private analyzeGraph(): void {
|
|
67
|
+
const hasIncomingEdge = new Set<string>();
|
|
68
|
+
|
|
69
|
+
// Track all nodes that have incoming edges
|
|
70
|
+
for (const edge of this.edges) {
|
|
71
|
+
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
72
|
+
destinations.forEach((dest) => hasIncomingEdge.add(dest));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Starting nodes are those without incoming edges
|
|
76
|
+
for (const agentId of this.agentContexts.keys()) {
|
|
77
|
+
if (!hasIncomingEdge.has(agentId)) {
|
|
78
|
+
this.startingNodes.add(agentId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// If no starting nodes found, use the first agent
|
|
83
|
+
if (this.startingNodes.size === 0 && this.agentContexts.size > 0) {
|
|
84
|
+
this.startingNodes.add(this.agentContexts.keys().next().value!);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create handoff tools for agents based on handoff edges only
|
|
90
|
+
*/
|
|
91
|
+
private createHandoffTools(): void {
|
|
92
|
+
// Group handoff edges by source agent(s)
|
|
93
|
+
const handoffsByAgent = new Map<string, t.GraphEdge[]>();
|
|
94
|
+
|
|
95
|
+
// Only process handoff edges for tool creation
|
|
96
|
+
for (const edge of this.handoffEdges) {
|
|
97
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
98
|
+
sources.forEach((source) => {
|
|
99
|
+
if (!handoffsByAgent.has(source)) {
|
|
100
|
+
handoffsByAgent.set(source, []);
|
|
101
|
+
}
|
|
102
|
+
handoffsByAgent.get(source)!.push(edge);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Create handoff tools for each agent
|
|
107
|
+
for (const [agentId, edges] of handoffsByAgent) {
|
|
108
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
109
|
+
if (!agentContext) continue;
|
|
110
|
+
|
|
111
|
+
// Create handoff tools for this agent's outgoing edges
|
|
112
|
+
const handoffTools: t.GenericTool[] = [];
|
|
113
|
+
for (const edge of edges) {
|
|
114
|
+
handoffTools.push(...this.createHandoffToolsForEdge(edge));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Add handoff tools to the agent's existing tools
|
|
118
|
+
if (!agentContext.tools) {
|
|
119
|
+
agentContext.tools = [];
|
|
120
|
+
}
|
|
121
|
+
agentContext.tools.push(...handoffTools);
|
|
122
|
+
|
|
123
|
+
// Update tool map
|
|
124
|
+
for (const tool of handoffTools) {
|
|
125
|
+
if (!agentContext.toolMap) {
|
|
126
|
+
agentContext.toolMap = new Map();
|
|
127
|
+
}
|
|
128
|
+
agentContext.toolMap.set(tool.name, tool);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create handoff tools for an edge (handles multiple destinations)
|
|
135
|
+
*/
|
|
136
|
+
private createHandoffToolsForEdge(edge: t.GraphEdge): t.GenericTool[] {
|
|
137
|
+
const tools: t.GenericTool[] = [];
|
|
138
|
+
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
139
|
+
|
|
140
|
+
// If there's a condition, create a single conditional handoff tool
|
|
141
|
+
if (edge.condition != null) {
|
|
142
|
+
const toolName = 'conditional_transfer';
|
|
143
|
+
const toolDescription =
|
|
144
|
+
edge.description ?? 'Conditionally transfer control based on state';
|
|
145
|
+
|
|
146
|
+
tools.push(
|
|
147
|
+
tool(
|
|
148
|
+
async (_, config) => {
|
|
149
|
+
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
150
|
+
const toolCallId =
|
|
151
|
+
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
152
|
+
'unknown';
|
|
153
|
+
|
|
154
|
+
// Evaluate condition
|
|
155
|
+
const result = edge.condition!(state);
|
|
156
|
+
let destination: string;
|
|
157
|
+
|
|
158
|
+
if (typeof result === 'boolean') {
|
|
159
|
+
// If true, use first destination; if false, don't transfer
|
|
160
|
+
if (!result) return null;
|
|
161
|
+
destination = destinations[0];
|
|
162
|
+
} else if (typeof result === 'string') {
|
|
163
|
+
destination = result;
|
|
164
|
+
} else {
|
|
165
|
+
// Array of destinations - for now, use the first
|
|
166
|
+
destination = Array.isArray(result) ? result[0] : destinations[0];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const toolMessage = new ToolMessage({
|
|
170
|
+
content: `Conditionally transferred to ${destination}`,
|
|
171
|
+
name: toolName,
|
|
172
|
+
tool_call_id: toolCallId,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return new Command({
|
|
176
|
+
goto: destination,
|
|
177
|
+
update: { messages: state.messages.concat(toolMessage) },
|
|
178
|
+
graph: Command.PARENT,
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: toolName,
|
|
183
|
+
schema: z.object({}),
|
|
184
|
+
description: toolDescription,
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
// Create individual tools for each destination
|
|
190
|
+
for (const destination of destinations) {
|
|
191
|
+
const toolName = `transfer_to_${destination}`;
|
|
192
|
+
const toolDescription =
|
|
193
|
+
edge.description ?? `Transfer control to agent '${destination}'`;
|
|
194
|
+
|
|
195
|
+
tools.push(
|
|
196
|
+
tool(
|
|
197
|
+
async (_, config) => {
|
|
198
|
+
const toolCallId =
|
|
199
|
+
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
200
|
+
'unknown';
|
|
201
|
+
const toolMessage = new ToolMessage({
|
|
202
|
+
content: `Successfully transferred to ${destination}`,
|
|
203
|
+
name: toolName,
|
|
204
|
+
tool_call_id: toolCallId,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
208
|
+
|
|
209
|
+
return new Command({
|
|
210
|
+
goto: destination,
|
|
211
|
+
update: { messages: state.messages.concat(toolMessage) },
|
|
212
|
+
graph: Command.PARENT,
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: toolName,
|
|
217
|
+
schema: z.object({}),
|
|
218
|
+
description: toolDescription,
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return tools;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Create a complete agent subgraph (similar to createReactAgent)
|
|
230
|
+
*/
|
|
231
|
+
private createAgentSubgraph(agentId: string): t.CompiledAgentWorfklow {
|
|
232
|
+
// This is essentially the same as createAgentNode from StandardGraph
|
|
233
|
+
return this.createAgentNode(agentId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create the multi-agent workflow with dynamic handoffs
|
|
238
|
+
*/
|
|
239
|
+
override createWorkflow(): t.CompiledStateWorkflow {
|
|
240
|
+
const StateAnnotation = Annotation.Root({
|
|
241
|
+
messages: Annotation<BaseMessage[]>({
|
|
242
|
+
reducer: (a, b) => {
|
|
243
|
+
if (!a.length) {
|
|
244
|
+
this.startIndex = a.length + b.length;
|
|
245
|
+
}
|
|
246
|
+
const result = messagesStateReducer(a, b);
|
|
247
|
+
this.messages = result;
|
|
248
|
+
return result;
|
|
249
|
+
},
|
|
250
|
+
default: () => [],
|
|
251
|
+
}),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const builder = new StateGraph(StateAnnotation);
|
|
255
|
+
|
|
256
|
+
// Add all agents as complete subgraphs
|
|
257
|
+
for (const [agentId] of this.agentContexts) {
|
|
258
|
+
// Get all possible destinations for this agent
|
|
259
|
+
const handoffDestinations = new Set<string>();
|
|
260
|
+
const directDestinations = new Set<string>();
|
|
261
|
+
|
|
262
|
+
// Check handoff edges for destinations
|
|
263
|
+
for (const edge of this.handoffEdges) {
|
|
264
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
265
|
+
if (sources.includes(agentId) === true) {
|
|
266
|
+
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
267
|
+
dests.forEach((dest) => handoffDestinations.add(dest));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Check direct edges for destinations
|
|
272
|
+
for (const edge of this.directEdges) {
|
|
273
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
274
|
+
if (sources.includes(agentId) === true) {
|
|
275
|
+
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
276
|
+
dests.forEach((dest) => directDestinations.add(dest));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// If agent has handoff destinations, add END to possible ends
|
|
281
|
+
// If agent only has direct destinations, it naturally ends without explicit END
|
|
282
|
+
const destinations = new Set([...handoffDestinations]);
|
|
283
|
+
if (handoffDestinations.size > 0 || directDestinations.size === 0) {
|
|
284
|
+
destinations.add(END);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Create the agent subgraph (includes agent + tools)
|
|
288
|
+
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
289
|
+
|
|
290
|
+
// Add the agent as a node with its possible destinations
|
|
291
|
+
builder.addNode(agentId, agentSubgraph, {
|
|
292
|
+
ends: Array.from(destinations),
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Add starting edges for all starting nodes
|
|
297
|
+
for (const startNode of this.startingNodes) {
|
|
298
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
299
|
+
/** @ts-ignore */
|
|
300
|
+
builder.addEdge(START, startNode);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Add direct edges for automatic transitions
|
|
304
|
+
* Group edges by destination to handle fan-in scenarios
|
|
305
|
+
*/
|
|
306
|
+
const edgesByDestination = new Map<string, t.GraphEdge[]>();
|
|
307
|
+
|
|
308
|
+
for (const edge of this.directEdges) {
|
|
309
|
+
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
310
|
+
for (const destination of destinations) {
|
|
311
|
+
if (!edgesByDestination.has(destination)) {
|
|
312
|
+
edgesByDestination.set(destination, []);
|
|
313
|
+
}
|
|
314
|
+
edgesByDestination.get(destination)!.push(edge);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (const [destination, edges] of edgesByDestination) {
|
|
319
|
+
/** Checks if this is a fan-in scenario with prompt instructions */
|
|
320
|
+
const edgesWithPrompt = edges.filter(
|
|
321
|
+
(edge) =>
|
|
322
|
+
edge.promptInstructions != null && edge.promptInstructions !== ''
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
if (edgesWithPrompt.length > 0) {
|
|
326
|
+
// Fan-in with prompt: create a single wrapper node for this destination
|
|
327
|
+
const wrapperNodeId = `fan_in_${destination}_prompt`;
|
|
328
|
+
|
|
329
|
+
// Use the first edge's prompt instructions (they should all be the same for fan-in)
|
|
330
|
+
const promptInstructions = edgesWithPrompt[0].promptInstructions;
|
|
331
|
+
|
|
332
|
+
builder.addNode(wrapperNodeId, async (state: t.BaseGraphState) => {
|
|
333
|
+
let promptText: string | undefined;
|
|
334
|
+
|
|
335
|
+
if (typeof promptInstructions === 'function') {
|
|
336
|
+
promptText = promptInstructions(state.messages);
|
|
337
|
+
} else {
|
|
338
|
+
promptText = promptInstructions;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (promptText != null && promptText !== '') {
|
|
342
|
+
// Return state with the prompt message added
|
|
343
|
+
return {
|
|
344
|
+
messages: [...state.messages, new HumanMessage(promptText)],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// No prompt needed, return empty update
|
|
349
|
+
return {};
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Add edges from all sources to the wrapper, then wrapper to destination
|
|
353
|
+
for (const edge of edges) {
|
|
354
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
355
|
+
for (const source of sources) {
|
|
356
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
357
|
+
/** @ts-ignore */
|
|
358
|
+
builder.addEdge(source, wrapperNodeId);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Single edge from wrapper to destination
|
|
363
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
364
|
+
/** @ts-ignore */
|
|
365
|
+
builder.addEdge(wrapperNodeId, destination);
|
|
366
|
+
} else {
|
|
367
|
+
// No prompt instructions, add direct edges
|
|
368
|
+
for (const edge of edges) {
|
|
369
|
+
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
370
|
+
for (const source of sources) {
|
|
371
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
372
|
+
/** @ts-ignore */
|
|
373
|
+
builder.addEdge(source, destination);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return builder.compile(this.compileOptions as unknown as never);
|
|
380
|
+
}
|
|
381
|
+
}
|
package/src/graphs/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from './Graph';
|
|
1
|
+
export * from './Graph';
|
|
2
|
+
export * from './MultiAgentGraph';
|
|
Binary file
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from '@langchain/core/messages';
|
|
10
10
|
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
11
11
|
import type { AnthropicInput } from '@langchain/anthropic';
|
|
12
|
+
import type { Anthropic } from '@anthropic-ai/sdk';
|
|
12
13
|
import type {
|
|
13
14
|
AnthropicMessageCreateParams,
|
|
14
15
|
AnthropicStreamingMessageCreateParams,
|
|
@@ -18,6 +19,7 @@ import type {
|
|
|
18
19
|
} from '@/llm/anthropic/types';
|
|
19
20
|
import { _makeMessageChunkFromAnthropicEvent } from './utils/message_outputs';
|
|
20
21
|
import { _convertMessagesToAnthropicPayload } from './utils/message_inputs';
|
|
22
|
+
import { handleToolChoice } from './utils/tools';
|
|
21
23
|
import { TextStream } from '@/llm/text';
|
|
22
24
|
|
|
23
25
|
function _toolsInParams(
|
|
@@ -120,6 +122,13 @@ export type CustomAnthropicInput = AnthropicInput & {
|
|
|
120
122
|
_lc_stream_delay?: number;
|
|
121
123
|
} & BaseChatModelParams;
|
|
122
124
|
|
|
125
|
+
/**
|
|
126
|
+
* A type representing additional parameters that can be passed to the
|
|
127
|
+
* Anthropic API.
|
|
128
|
+
*/
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
+
type Kwargs = Record<string, any>;
|
|
131
|
+
|
|
123
132
|
export class CustomAnthropic extends ChatAnthropicMessages {
|
|
124
133
|
_lc_stream_delay: number;
|
|
125
134
|
private message_start: AnthropicMessageStartEvent | undefined;
|
|
@@ -128,9 +137,68 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
128
137
|
private emitted_usage?: boolean;
|
|
129
138
|
constructor(fields?: CustomAnthropicInput) {
|
|
130
139
|
super(fields);
|
|
140
|
+
this.resetTokenEvents();
|
|
131
141
|
this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
|
|
132
142
|
}
|
|
133
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Get the parameters used to invoke the model
|
|
146
|
+
*/
|
|
147
|
+
override invocationParams(
|
|
148
|
+
options?: this['ParsedCallOptions']
|
|
149
|
+
): Omit<
|
|
150
|
+
AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams,
|
|
151
|
+
'messages'
|
|
152
|
+
> &
|
|
153
|
+
Kwargs {
|
|
154
|
+
const tool_choice:
|
|
155
|
+
| Anthropic.Messages.ToolChoiceAuto
|
|
156
|
+
| Anthropic.Messages.ToolChoiceAny
|
|
157
|
+
| Anthropic.Messages.ToolChoiceTool
|
|
158
|
+
| undefined = handleToolChoice(options?.tool_choice);
|
|
159
|
+
|
|
160
|
+
if (this.thinking.type === 'enabled') {
|
|
161
|
+
if (this.topK !== -1 && (this.topK as number | undefined) != null) {
|
|
162
|
+
throw new Error('topK is not supported when thinking is enabled');
|
|
163
|
+
}
|
|
164
|
+
if (this.topP !== -1 && (this.topP as number | undefined) != null) {
|
|
165
|
+
throw new Error('topP is not supported when thinking is enabled');
|
|
166
|
+
}
|
|
167
|
+
if (
|
|
168
|
+
this.temperature !== 1 &&
|
|
169
|
+
(this.temperature as number | undefined) != null
|
|
170
|
+
) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'temperature is not supported when thinking is enabled'
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
model: this.model,
|
|
178
|
+
stop_sequences: options?.stop ?? this.stopSequences,
|
|
179
|
+
stream: this.streaming,
|
|
180
|
+
max_tokens: this.maxTokens,
|
|
181
|
+
tools: this.formatStructuredToolToAnthropic(options?.tools),
|
|
182
|
+
tool_choice,
|
|
183
|
+
thinking: this.thinking,
|
|
184
|
+
...this.invocationKwargs,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
model: this.model,
|
|
189
|
+
temperature: this.temperature,
|
|
190
|
+
top_k: this.topK,
|
|
191
|
+
top_p: this.topP,
|
|
192
|
+
stop_sequences: options?.stop ?? this.stopSequences,
|
|
193
|
+
stream: this.streaming,
|
|
194
|
+
max_tokens: this.maxTokens,
|
|
195
|
+
tools: this.formatStructuredToolToAnthropic(options?.tools),
|
|
196
|
+
tool_choice,
|
|
197
|
+
thinking: this.thinking,
|
|
198
|
+
...this.invocationKwargs,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
134
202
|
/**
|
|
135
203
|
* Get stream usage as returned by this client's API response.
|
|
136
204
|
* @returns The stream usage object.
|
|
@@ -209,6 +277,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
209
277
|
options: this['ParsedCallOptions'],
|
|
210
278
|
runManager?: CallbackManagerForLLMRun
|
|
211
279
|
): AsyncGenerator<ChatGenerationChunk> {
|
|
280
|
+
this.resetTokenEvents();
|
|
212
281
|
const params = this.invocationParams(options);
|
|
213
282
|
const formattedMessages = _convertMessagesToAnthropicPayload(messages);
|
|
214
283
|
const payload = {
|
|
@@ -221,16 +290,9 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
221
290
|
!_documentsInParams(payload) &&
|
|
222
291
|
!_thinkingInParams(payload);
|
|
223
292
|
|
|
224
|
-
const stream = await this.createStreamWithRetry(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
...formattedMessages,
|
|
228
|
-
stream: true,
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
headers: options.headers,
|
|
232
|
-
}
|
|
233
|
-
);
|
|
293
|
+
const stream = await this.createStreamWithRetry(payload, {
|
|
294
|
+
headers: options.headers,
|
|
295
|
+
});
|
|
234
296
|
|
|
235
297
|
const shouldStreamUsage = this.streamUsage ?? options.streamUsage;
|
|
236
298
|
|
|
@@ -263,7 +325,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
263
325
|
if (
|
|
264
326
|
!tokenType ||
|
|
265
327
|
tokenType === 'input' ||
|
|
266
|
-
(token === '' && usageMetadata)
|
|
328
|
+
(token === '' && (usageMetadata != null || chunk.id != null))
|
|
267
329
|
) {
|
|
268
330
|
const generationChunk = this.createGenerationChunk({
|
|
269
331
|
token,
|
|
@@ -290,10 +352,13 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
290
352
|
maxChunkSize: 8,
|
|
291
353
|
});
|
|
292
354
|
|
|
293
|
-
const generator = textStream.generateText();
|
|
355
|
+
const generator = textStream.generateText(options.signal);
|
|
294
356
|
try {
|
|
295
357
|
let emittedUsage = false;
|
|
296
358
|
for await (const currentToken of generator) {
|
|
359
|
+
if ((options.signal as AbortSignal | undefined)?.aborted === true) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
297
362
|
const newChunk = cloneChunk(currentToken, tokenType, chunk);
|
|
298
363
|
|
|
299
364
|
const generationChunk = this.createGenerationChunk({
|
|
@@ -309,7 +374,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
309
374
|
yield generationChunk;
|
|
310
375
|
|
|
311
376
|
await runManager?.handleLLMNewToken(
|
|
312
|
-
|
|
377
|
+
currentToken,
|
|
313
378
|
undefined,
|
|
314
379
|
undefined,
|
|
315
380
|
undefined,
|