@librechat/agents 3.0.0-rc1 → 3.0.0-rc11
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/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +7 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +229 -44
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +21 -2
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +3 -0
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +13 -0
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/ollama/index.cjs +3 -0
- package/dist/cjs/llm/ollama/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +53 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +6 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +5 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs +1 -1
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +3 -1
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +5 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +52 -34
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +28 -0
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs +28 -15
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +1 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +2 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs +3 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +8 -6
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +5 -5
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs +132 -0
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +46 -9
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs +70 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -0
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +7 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +230 -45
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +21 -2
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +3 -0
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +13 -0
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/ollama/index.mjs +3 -0
- package/dist/esm/llm/ollama/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +53 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +6 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +5 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs +1 -1
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +5 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +52 -34
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +28 -0
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs +28 -15
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +2 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs +3 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +8 -6
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +5 -5
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs +129 -0
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +46 -9
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs +68 -0
- package/dist/esm/utils/handlers.mjs.map +1 -0
- package/dist/types/common/enum.d.ts +2 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +12 -2
- package/dist/types/llm/anthropic/index.d.ts +3 -0
- package/dist/types/llm/google/index.d.ts +1 -0
- package/dist/types/llm/ollama/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +14 -0
- package/dist/types/llm/openrouter/index.d.ts +4 -2
- package/dist/types/llm/vertexai/index.d.ts +1 -1
- package/dist/types/messages/format.d.ts +23 -20
- package/dist/types/run.d.ts +1 -1
- package/dist/types/tools/search/firecrawl.d.ts +2 -1
- package/dist/types/tools/search/rerankers.d.ts +4 -1
- package/dist/types/tools/search/search.d.ts +1 -2
- package/dist/types/tools/search/serper-scraper.d.ts +59 -0
- package/dist/types/tools/search/tool.d.ts +25 -4
- package/dist/types/tools/search/types.d.ts +31 -1
- package/dist/types/types/graph.d.ts +38 -4
- package/dist/types/types/llm.d.ts +1 -0
- package/dist/types/types/run.d.ts +5 -1
- package/dist/types/utils/handlers.d.ts +34 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/package.json +11 -3
- package/src/common/enum.ts +1 -0
- package/src/graphs/Graph.ts +8 -2
- package/src/graphs/MultiAgentGraph.ts +267 -50
- package/src/llm/anthropic/index.ts +23 -2
- package/src/llm/google/index.ts +4 -0
- package/src/llm/google/utils/common.ts +14 -0
- package/src/llm/ollama/index.ts +3 -0
- package/src/llm/openai/index.ts +60 -1
- package/src/llm/openai/utils/index.ts +7 -1
- package/src/llm/openrouter/index.ts +15 -6
- package/src/llm/vertexai/index.ts +2 -2
- package/src/messages/core.ts +5 -2
- package/src/messages/format.ts +67 -39
- package/src/messages/formatMessage.test.ts +418 -2
- package/src/messages/prune.ts +51 -0
- package/src/run.ts +38 -27
- package/src/scripts/multi-agent-chain.ts +278 -0
- package/src/scripts/multi-agent-document-review-chain.ts +197 -0
- package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
- package/src/scripts/multi-agent-parallel.ts +27 -23
- package/src/scripts/multi-agent-supervisor.ts +362 -0
- package/src/scripts/search.ts +5 -1
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +170 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
- package/src/scripts/test-tools-before-handoff.ts +233 -0
- package/src/scripts/tools.ts +4 -1
- package/src/stream.ts +4 -1
- package/src/tools/search/firecrawl.ts +5 -2
- package/src/tools/search/jina-reranker.test.ts +126 -0
- package/src/tools/search/rerankers.ts +11 -5
- package/src/tools/search/search.ts +6 -8
- package/src/tools/search/serper-scraper.ts +155 -0
- package/src/tools/search/tool.ts +49 -8
- package/src/tools/search/types.ts +46 -0
- package/src/types/graph.ts +51 -5
- package/src/types/llm.ts +1 -0
- package/src/types/run.ts +6 -1
- package/src/utils/handlers.ts +107 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/llmConfig.ts +35 -1
- package/dist/types/scripts/abort.d.ts +0 -1
- package/dist/types/scripts/ant_web_search.d.ts +0 -1
- package/dist/types/scripts/args.d.ts +0 -7
- package/dist/types/scripts/caching.d.ts +0 -1
- package/dist/types/scripts/cli.d.ts +0 -1
- package/dist/types/scripts/cli2.d.ts +0 -1
- package/dist/types/scripts/cli3.d.ts +0 -1
- package/dist/types/scripts/cli4.d.ts +0 -1
- package/dist/types/scripts/cli5.d.ts +0 -1
- package/dist/types/scripts/code_exec.d.ts +0 -1
- package/dist/types/scripts/code_exec_files.d.ts +0 -1
- package/dist/types/scripts/code_exec_simple.d.ts +0 -1
- package/dist/types/scripts/content.d.ts +0 -1
- package/dist/types/scripts/empty_input.d.ts +0 -1
- package/dist/types/scripts/handoff-test.d.ts +0 -1
- package/dist/types/scripts/image.d.ts +0 -1
- package/dist/types/scripts/memory.d.ts +0 -1
- package/dist/types/scripts/multi-agent-conditional.d.ts +0 -1
- package/dist/types/scripts/multi-agent-parallel.d.ts +0 -1
- package/dist/types/scripts/multi-agent-sequence.d.ts +0 -1
- package/dist/types/scripts/multi-agent-test.d.ts +0 -1
- package/dist/types/scripts/search.d.ts +0 -1
- package/dist/types/scripts/simple.d.ts +0 -1
- package/dist/types/scripts/stream.d.ts +0 -1
- package/dist/types/scripts/thinking.d.ts +0 -1
- package/dist/types/scripts/tools.d.ts +0 -1
- package/dist/types/specs/spec.utils.d.ts +0 -1
- package/src/scripts/multi-agent-example-output.md +0 -110
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { tool } from '@langchain/core/tools';
|
|
3
|
-
import {
|
|
3
|
+
import { PromptTemplate } from '@langchain/core/prompts';
|
|
4
|
+
import {
|
|
5
|
+
ToolMessage,
|
|
6
|
+
HumanMessage,
|
|
7
|
+
getBufferString,
|
|
8
|
+
} from '@langchain/core/messages';
|
|
4
9
|
import {
|
|
5
10
|
END,
|
|
6
11
|
START,
|
|
@@ -14,10 +19,21 @@ import type { ToolRunnableConfig } from '@langchain/core/tools';
|
|
|
14
19
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
15
20
|
import type * as t from '@/types';
|
|
16
21
|
import { StandardGraph } from './Graph';
|
|
22
|
+
import { Constants } from '@/common';
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
|
|
20
|
-
* with handoffs, fan-in/fan-out, and other composable patterns
|
|
26
|
+
* with handoffs, fan-in/fan-out, and other composable patterns.
|
|
27
|
+
*
|
|
28
|
+
* Key behavior:
|
|
29
|
+
* - Agents with ONLY handoff edges: Can dynamically route to any handoff destination
|
|
30
|
+
* - Agents with ONLY direct edges: Always follow their direct edges
|
|
31
|
+
* - Agents with BOTH: Use Command for exclusive routing (handoff OR direct, not both)
|
|
32
|
+
* - If handoff occurs: Only the handoff destination executes
|
|
33
|
+
* - If no handoff: Direct edges execute (potentially in parallel)
|
|
34
|
+
*
|
|
35
|
+
* This enables the common pattern where an agent either delegates (handoff)
|
|
36
|
+
* OR continues its workflow (direct edges), but not both simultaneously.
|
|
21
37
|
*/
|
|
22
38
|
export class MultiAgentGraph extends StandardGraph {
|
|
23
39
|
private edges: t.GraphEdge[];
|
|
@@ -119,14 +135,6 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
119
135
|
agentContext.tools = [];
|
|
120
136
|
}
|
|
121
137
|
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
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
|
@@ -137,37 +145,52 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
137
145
|
const tools: t.GenericTool[] = [];
|
|
138
146
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
139
147
|
|
|
140
|
-
|
|
148
|
+
/** If there's a condition, create a single conditional handoff tool */
|
|
141
149
|
if (edge.condition != null) {
|
|
142
150
|
const toolName = 'conditional_transfer';
|
|
143
151
|
const toolDescription =
|
|
144
152
|
edge.description ?? 'Conditionally transfer control based on state';
|
|
145
153
|
|
|
154
|
+
/** Check if we have a prompt for handoff input */
|
|
155
|
+
const hasHandoffInput =
|
|
156
|
+
edge.prompt != null && typeof edge.prompt === 'string';
|
|
157
|
+
const handoffInputDescription = hasHandoffInput ? edge.prompt : undefined;
|
|
158
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
159
|
+
|
|
146
160
|
tools.push(
|
|
147
161
|
tool(
|
|
148
|
-
async (
|
|
162
|
+
async (input: Record<string, unknown>, config) => {
|
|
149
163
|
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
150
164
|
const toolCallId =
|
|
151
165
|
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
152
166
|
'unknown';
|
|
153
167
|
|
|
154
|
-
|
|
168
|
+
/** Evaluated condition */
|
|
155
169
|
const result = edge.condition!(state);
|
|
156
170
|
let destination: string;
|
|
157
171
|
|
|
158
172
|
if (typeof result === 'boolean') {
|
|
159
|
-
|
|
173
|
+
/** If true, use first destination; if false, don't transfer */
|
|
160
174
|
if (!result) return null;
|
|
161
175
|
destination = destinations[0];
|
|
162
176
|
} else if (typeof result === 'string') {
|
|
163
177
|
destination = result;
|
|
164
178
|
} else {
|
|
165
|
-
|
|
179
|
+
/** Array of destinations - for now, use the first */
|
|
166
180
|
destination = Array.isArray(result) ? result[0] : destinations[0];
|
|
167
181
|
}
|
|
168
182
|
|
|
183
|
+
let content = `Conditionally transferred to ${destination}`;
|
|
184
|
+
if (
|
|
185
|
+
hasHandoffInput &&
|
|
186
|
+
promptKey in input &&
|
|
187
|
+
input[promptKey] != null
|
|
188
|
+
) {
|
|
189
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
169
192
|
const toolMessage = new ToolMessage({
|
|
170
|
-
content
|
|
193
|
+
content,
|
|
171
194
|
name: toolName,
|
|
172
195
|
tool_call_id: toolCallId,
|
|
173
196
|
});
|
|
@@ -180,26 +203,51 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
180
203
|
},
|
|
181
204
|
{
|
|
182
205
|
name: toolName,
|
|
183
|
-
schema:
|
|
206
|
+
schema: hasHandoffInput
|
|
207
|
+
? z.object({
|
|
208
|
+
[promptKey]: z
|
|
209
|
+
.string()
|
|
210
|
+
.optional()
|
|
211
|
+
.describe(handoffInputDescription as string),
|
|
212
|
+
})
|
|
213
|
+
: z.object({}),
|
|
184
214
|
description: toolDescription,
|
|
185
215
|
}
|
|
186
216
|
)
|
|
187
217
|
);
|
|
188
218
|
} else {
|
|
189
|
-
|
|
219
|
+
/** Create individual tools for each destination */
|
|
190
220
|
for (const destination of destinations) {
|
|
191
|
-
const toolName =
|
|
221
|
+
const toolName = `${Constants.LC_TRANSFER_TO_}${destination}`;
|
|
192
222
|
const toolDescription =
|
|
193
223
|
edge.description ?? `Transfer control to agent '${destination}'`;
|
|
194
224
|
|
|
225
|
+
/** Check if we have a prompt for handoff input */
|
|
226
|
+
const hasHandoffInput =
|
|
227
|
+
edge.prompt != null && typeof edge.prompt === 'string';
|
|
228
|
+
const handoffInputDescription = hasHandoffInput
|
|
229
|
+
? edge.prompt
|
|
230
|
+
: undefined;
|
|
231
|
+
const promptKey = edge.promptKey ?? 'instructions';
|
|
232
|
+
|
|
195
233
|
tools.push(
|
|
196
234
|
tool(
|
|
197
|
-
async (
|
|
235
|
+
async (input: Record<string, unknown>, config) => {
|
|
198
236
|
const toolCallId =
|
|
199
237
|
(config as ToolRunnableConfig | undefined)?.toolCall?.id ??
|
|
200
238
|
'unknown';
|
|
239
|
+
|
|
240
|
+
let content = `Successfully transferred to ${destination}`;
|
|
241
|
+
if (
|
|
242
|
+
hasHandoffInput &&
|
|
243
|
+
promptKey in input &&
|
|
244
|
+
input[promptKey] != null
|
|
245
|
+
) {
|
|
246
|
+
content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
201
249
|
const toolMessage = new ToolMessage({
|
|
202
|
-
content
|
|
250
|
+
content,
|
|
203
251
|
name: toolName,
|
|
204
252
|
tool_call_id: toolCallId,
|
|
205
253
|
});
|
|
@@ -214,7 +262,14 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
214
262
|
},
|
|
215
263
|
{
|
|
216
264
|
name: toolName,
|
|
217
|
-
schema:
|
|
265
|
+
schema: hasHandoffInput
|
|
266
|
+
? z.object({
|
|
267
|
+
[promptKey]: z
|
|
268
|
+
.string()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe(handoffInputDescription as string),
|
|
271
|
+
})
|
|
272
|
+
: z.object({}),
|
|
218
273
|
description: toolDescription,
|
|
219
274
|
}
|
|
220
275
|
)
|
|
@@ -229,14 +284,14 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
229
284
|
* Create a complete agent subgraph (similar to createReactAgent)
|
|
230
285
|
*/
|
|
231
286
|
private createAgentSubgraph(agentId: string): t.CompiledAgentWorfklow {
|
|
232
|
-
|
|
287
|
+
/** This is essentially the same as `createAgentNode` from `StandardGraph` */
|
|
233
288
|
return this.createAgentNode(agentId);
|
|
234
289
|
}
|
|
235
290
|
|
|
236
291
|
/**
|
|
237
292
|
* Create the multi-agent workflow with dynamic handoffs
|
|
238
293
|
*/
|
|
239
|
-
override createWorkflow(): t.
|
|
294
|
+
override createWorkflow(): t.CompiledMultiAgentWorkflow {
|
|
240
295
|
const StateAnnotation = Annotation.Root({
|
|
241
296
|
messages: Annotation<BaseMessage[]>({
|
|
242
297
|
reducer: (a, b) => {
|
|
@@ -249,6 +304,12 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
249
304
|
},
|
|
250
305
|
default: () => [],
|
|
251
306
|
}),
|
|
307
|
+
/** Channel for passing filtered messages to agents when excludeResults is true */
|
|
308
|
+
agentMessages: Annotation<BaseMessage[]>({
|
|
309
|
+
/** Replaces state entirely */
|
|
310
|
+
reducer: (a, b) => b,
|
|
311
|
+
default: () => [],
|
|
312
|
+
}),
|
|
252
313
|
});
|
|
253
314
|
|
|
254
315
|
const builder = new StateGraph(StateAnnotation);
|
|
@@ -277,19 +338,123 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
277
338
|
}
|
|
278
339
|
}
|
|
279
340
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
341
|
+
/** Check if this agent has BOTH handoff and direct edges */
|
|
342
|
+
const hasHandoffEdges = handoffDestinations.size > 0;
|
|
343
|
+
const hasDirectEdges = directDestinations.size > 0;
|
|
344
|
+
const needsCommandRouting = hasHandoffEdges && hasDirectEdges;
|
|
345
|
+
|
|
346
|
+
/** Collect all possible destinations for this agent */
|
|
347
|
+
const allDestinations = new Set([
|
|
348
|
+
...handoffDestinations,
|
|
349
|
+
...directDestinations,
|
|
350
|
+
]);
|
|
283
351
|
if (handoffDestinations.size > 0 || directDestinations.size === 0) {
|
|
284
|
-
|
|
352
|
+
allDestinations.add(END);
|
|
285
353
|
}
|
|
286
354
|
|
|
287
|
-
|
|
355
|
+
/** Agent subgraph (includes agent + tools) */
|
|
288
356
|
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
289
357
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
358
|
+
/** Wrapper function that handles agentMessages channel and conditional routing */
|
|
359
|
+
const agentWrapper = async (
|
|
360
|
+
state: t.MultiAgentGraphState
|
|
361
|
+
): Promise<t.MultiAgentGraphState | Command> => {
|
|
362
|
+
let result: t.MultiAgentGraphState;
|
|
363
|
+
|
|
364
|
+
if (state.agentMessages != null && state.agentMessages.length > 0) {
|
|
365
|
+
/**
|
|
366
|
+
* When using agentMessages (excludeResults=true), we need to update
|
|
367
|
+
* the token map to account for the new prompt message
|
|
368
|
+
*/
|
|
369
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
370
|
+
if (agentContext && agentContext.tokenCounter) {
|
|
371
|
+
// The agentMessages contains:
|
|
372
|
+
// 1. Filtered messages (0 to startIndex) - already have token counts
|
|
373
|
+
// 2. New prompt message - needs token counting
|
|
374
|
+
|
|
375
|
+
const freshTokenMap: Record<string, number> = {};
|
|
376
|
+
|
|
377
|
+
// Copy existing token counts for filtered messages (0 to startIndex)
|
|
378
|
+
for (let i = 0; i < this.startIndex; i++) {
|
|
379
|
+
const tokenCount = agentContext.indexTokenCountMap[i];
|
|
380
|
+
if (tokenCount !== undefined) {
|
|
381
|
+
freshTokenMap[i] = tokenCount;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Calculate tokens only for the new prompt message (last message)
|
|
386
|
+
const promptMessageIndex = state.agentMessages.length - 1;
|
|
387
|
+
if (promptMessageIndex >= this.startIndex) {
|
|
388
|
+
const promptMessage = state.agentMessages[promptMessageIndex];
|
|
389
|
+
freshTokenMap[promptMessageIndex] =
|
|
390
|
+
agentContext.tokenCounter(promptMessage);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Update the agent's token map with instructions added
|
|
394
|
+
agentContext.updateTokenMapWithInstructions(freshTokenMap);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Temporary state with messages replaced by `agentMessages` */
|
|
398
|
+
const transformedState: t.MultiAgentGraphState = {
|
|
399
|
+
...state,
|
|
400
|
+
messages: state.agentMessages,
|
|
401
|
+
};
|
|
402
|
+
result = await agentSubgraph.invoke(transformedState);
|
|
403
|
+
result = {
|
|
404
|
+
...result,
|
|
405
|
+
/** Clear agentMessages for next agent */
|
|
406
|
+
agentMessages: [],
|
|
407
|
+
};
|
|
408
|
+
} else {
|
|
409
|
+
result = await agentSubgraph.invoke(state);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** If agent has both handoff and direct edges, use Command for exclusive routing */
|
|
413
|
+
if (needsCommandRouting) {
|
|
414
|
+
/** Check if a handoff occurred */
|
|
415
|
+
const lastMessage = result.messages[
|
|
416
|
+
result.messages.length - 1
|
|
417
|
+
] as BaseMessage | null;
|
|
418
|
+
if (
|
|
419
|
+
lastMessage != null &&
|
|
420
|
+
lastMessage.getType() === 'tool' &&
|
|
421
|
+
typeof lastMessage.name === 'string' &&
|
|
422
|
+
lastMessage.name.startsWith(Constants.LC_TRANSFER_TO_)
|
|
423
|
+
) {
|
|
424
|
+
/** Handoff occurred - extract destination and navigate there exclusively */
|
|
425
|
+
const handoffDest = lastMessage.name.replace(
|
|
426
|
+
Constants.LC_TRANSFER_TO_,
|
|
427
|
+
''
|
|
428
|
+
);
|
|
429
|
+
return new Command({
|
|
430
|
+
update: result,
|
|
431
|
+
goto: handoffDest,
|
|
432
|
+
});
|
|
433
|
+
} else {
|
|
434
|
+
/** No handoff - proceed with direct edges */
|
|
435
|
+
const directDests = Array.from(directDestinations);
|
|
436
|
+
if (directDests.length === 1) {
|
|
437
|
+
return new Command({
|
|
438
|
+
update: result,
|
|
439
|
+
goto: directDests[0],
|
|
440
|
+
});
|
|
441
|
+
} else if (directDests.length > 1) {
|
|
442
|
+
/** Multiple direct destinations - they'll run in parallel */
|
|
443
|
+
return new Command({
|
|
444
|
+
update: result,
|
|
445
|
+
goto: directDests,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** No special routing needed - return state normally */
|
|
452
|
+
return result;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/** Wrapped agent as a node with its possible destinations */
|
|
456
|
+
builder.addNode(agentId, agentWrapper, {
|
|
457
|
+
ends: Array.from(allDestinations),
|
|
293
458
|
});
|
|
294
459
|
}
|
|
295
460
|
|
|
@@ -300,7 +465,8 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
300
465
|
builder.addEdge(START, startNode);
|
|
301
466
|
}
|
|
302
467
|
|
|
303
|
-
/**
|
|
468
|
+
/**
|
|
469
|
+
* Add direct edges for automatic transitions
|
|
304
470
|
* Group edges by destination to handle fan-in scenarios
|
|
305
471
|
*/
|
|
306
472
|
const edgesByDestination = new Map<string, t.GraphEdge[]>();
|
|
@@ -318,38 +484,74 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
318
484
|
for (const [destination, edges] of edgesByDestination) {
|
|
319
485
|
/** Checks if this is a fan-in scenario with prompt instructions */
|
|
320
486
|
const edgesWithPrompt = edges.filter(
|
|
321
|
-
(edge) =>
|
|
322
|
-
edge.promptInstructions != null && edge.promptInstructions !== ''
|
|
487
|
+
(edge) => edge.prompt != null && edge.prompt !== ''
|
|
323
488
|
);
|
|
324
489
|
|
|
325
490
|
if (edgesWithPrompt.length > 0) {
|
|
326
|
-
|
|
491
|
+
/**
|
|
492
|
+
* Single wrapper node for destination (Fan-in with prompt)
|
|
493
|
+
*/
|
|
327
494
|
const wrapperNodeId = `fan_in_${destination}_prompt`;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
495
|
+
/**
|
|
496
|
+
* First edge's `prompt`
|
|
497
|
+
* (they should all be the same for fan-in)
|
|
498
|
+
*/
|
|
499
|
+
const prompt = edgesWithPrompt[0].prompt;
|
|
500
|
+
/**
|
|
501
|
+
* First edge's `excludeResults` flag
|
|
502
|
+
* (they should all be the same for fan-in)
|
|
503
|
+
*/
|
|
504
|
+
const excludeResults = edgesWithPrompt[0].excludeResults;
|
|
331
505
|
|
|
332
506
|
builder.addNode(wrapperNodeId, async (state: t.BaseGraphState) => {
|
|
333
507
|
let promptText: string | undefined;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
508
|
+
let effectiveExcludeResults = excludeResults;
|
|
509
|
+
|
|
510
|
+
if (typeof prompt === 'function') {
|
|
511
|
+
promptText = await prompt(state.messages, this.startIndex);
|
|
512
|
+
} else if (prompt != null) {
|
|
513
|
+
if (prompt.includes('{results}')) {
|
|
514
|
+
const resultsMessages = state.messages.slice(this.startIndex);
|
|
515
|
+
const resultsString = getBufferString(resultsMessages);
|
|
516
|
+
const promptTemplate = PromptTemplate.fromTemplate(prompt);
|
|
517
|
+
const result = await promptTemplate.invoke({
|
|
518
|
+
results: resultsString,
|
|
519
|
+
});
|
|
520
|
+
promptText = result.value;
|
|
521
|
+
effectiveExcludeResults =
|
|
522
|
+
excludeResults !== false && promptText !== '';
|
|
523
|
+
} else {
|
|
524
|
+
promptText = prompt;
|
|
525
|
+
}
|
|
339
526
|
}
|
|
340
527
|
|
|
341
528
|
if (promptText != null && promptText !== '') {
|
|
342
|
-
|
|
529
|
+
if (
|
|
530
|
+
effectiveExcludeResults == null ||
|
|
531
|
+
effectiveExcludeResults === false
|
|
532
|
+
) {
|
|
533
|
+
return {
|
|
534
|
+
messages: [new HumanMessage(promptText)],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** When `excludeResults` is true, use agentMessages channel
|
|
539
|
+
* to pass filtered messages + prompt to the destination agent
|
|
540
|
+
*/
|
|
541
|
+
const filteredMessages = state.messages.slice(0, this.startIndex);
|
|
343
542
|
return {
|
|
344
|
-
messages: [
|
|
543
|
+
messages: [new HumanMessage(promptText)],
|
|
544
|
+
agentMessages: messagesStateReducer(filteredMessages, [
|
|
545
|
+
new HumanMessage(promptText),
|
|
546
|
+
]),
|
|
345
547
|
};
|
|
346
548
|
}
|
|
347
549
|
|
|
348
|
-
|
|
550
|
+
/** No prompt needed, return empty update */
|
|
349
551
|
return {};
|
|
350
552
|
});
|
|
351
553
|
|
|
352
|
-
|
|
554
|
+
/** Add edges from all sources to the wrapper, then wrapper to destination */
|
|
353
555
|
for (const edge of edges) {
|
|
354
556
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
355
557
|
for (const source of sources) {
|
|
@@ -359,15 +561,30 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
359
561
|
}
|
|
360
562
|
}
|
|
361
563
|
|
|
362
|
-
|
|
564
|
+
/** Single edge from wrapper to destination */
|
|
363
565
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
364
566
|
/** @ts-ignore */
|
|
365
567
|
builder.addEdge(wrapperNodeId, destination);
|
|
366
568
|
} else {
|
|
367
|
-
|
|
569
|
+
/** No prompt instructions, add direct edges (skip if source uses Command routing) */
|
|
368
570
|
for (const edge of edges) {
|
|
369
571
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
370
572
|
for (const source of sources) {
|
|
573
|
+
/** Check if this source node has both handoff and direct edges */
|
|
574
|
+
const sourceHandoffEdges = this.handoffEdges.filter((e) => {
|
|
575
|
+
const eSources = Array.isArray(e.from) ? e.from : [e.from];
|
|
576
|
+
return eSources.includes(source);
|
|
577
|
+
});
|
|
578
|
+
const sourceDirectEdges = this.directEdges.filter((e) => {
|
|
579
|
+
const eSources = Array.isArray(e.from) ? e.from : [e.from];
|
|
580
|
+
return eSources.includes(source);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
/** Skip adding edge if source uses Command routing (has both types) */
|
|
584
|
+
if (sourceHandoffEdges.length > 0 && sourceDirectEdges.length > 0) {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
|
|
371
588
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
372
589
|
/** @ts-ignore */
|
|
373
590
|
builder.addEdge(source, destination);
|
|
@@ -135,12 +135,18 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
135
135
|
private message_delta: AnthropicMessageDeltaEvent | undefined;
|
|
136
136
|
private tools_in_params?: boolean;
|
|
137
137
|
private emitted_usage?: boolean;
|
|
138
|
+
top_k: number | undefined;
|
|
138
139
|
constructor(fields?: CustomAnthropicInput) {
|
|
139
140
|
super(fields);
|
|
140
141
|
this.resetTokenEvents();
|
|
142
|
+
this.setDirectFields(fields);
|
|
141
143
|
this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
static lc_name(): 'LibreChatAnthropic' {
|
|
147
|
+
return 'LibreChatAnthropic';
|
|
148
|
+
}
|
|
149
|
+
|
|
144
150
|
/**
|
|
145
151
|
* Get the parameters used to invoke the model
|
|
146
152
|
*/
|
|
@@ -158,7 +164,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
158
164
|
| undefined = handleToolChoice(options?.tool_choice);
|
|
159
165
|
|
|
160
166
|
if (this.thinking.type === 'enabled') {
|
|
161
|
-
if (this.
|
|
167
|
+
if (this.top_k !== -1 && (this.top_k as number | undefined) != null) {
|
|
162
168
|
throw new Error('topK is not supported when thinking is enabled');
|
|
163
169
|
}
|
|
164
170
|
if (this.topP !== -1 && (this.topP as number | undefined) != null) {
|
|
@@ -187,7 +193,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
187
193
|
return {
|
|
188
194
|
model: this.model,
|
|
189
195
|
temperature: this.temperature,
|
|
190
|
-
top_k: this.
|
|
196
|
+
top_k: this.top_k,
|
|
191
197
|
top_p: this.topP,
|
|
192
198
|
stop_sequences: options?.stop ?? this.stopSequences,
|
|
193
199
|
stream: this.streaming,
|
|
@@ -244,6 +250,21 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
244
250
|
this.tools_in_params = undefined;
|
|
245
251
|
}
|
|
246
252
|
|
|
253
|
+
setDirectFields(fields?: CustomAnthropicInput): void {
|
|
254
|
+
this.temperature = fields?.temperature ?? undefined;
|
|
255
|
+
this.topP = fields?.topP ?? undefined;
|
|
256
|
+
this.top_k = fields?.topK;
|
|
257
|
+
if (this.temperature === -1 || this.temperature === 1) {
|
|
258
|
+
this.temperature = undefined;
|
|
259
|
+
}
|
|
260
|
+
if (this.topP === -1) {
|
|
261
|
+
this.topP = undefined;
|
|
262
|
+
}
|
|
263
|
+
if (this.top_k === -1) {
|
|
264
|
+
this.top_k = undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
247
268
|
private createGenerationChunk({
|
|
248
269
|
token,
|
|
249
270
|
chunk,
|
package/src/llm/google/index.ts
CHANGED
|
@@ -107,6 +107,10 @@ export class CustomChatGoogleGenerativeAI extends ChatGoogleGenerativeAI {
|
|
|
107
107
|
this.streamUsage = fields.streamUsage ?? this.streamUsage;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
static lc_name(): 'LibreChatGoogleGenerativeAI' {
|
|
111
|
+
return 'LibreChatGoogleGenerativeAI';
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
invocationParams(
|
|
111
115
|
options?: this['ParsedCallOptions']
|
|
112
116
|
): Omit<GenerateContentRequest, 'contents'> {
|
|
@@ -301,6 +301,20 @@ function _convertLangChainContentToPart(
|
|
|
301
301
|
mimeType,
|
|
302
302
|
},
|
|
303
303
|
};
|
|
304
|
+
} else if (
|
|
305
|
+
content.type === 'document' ||
|
|
306
|
+
content.type === 'audio' ||
|
|
307
|
+
content.type === 'video'
|
|
308
|
+
) {
|
|
309
|
+
if (!isMultimodalModel) {
|
|
310
|
+
throw new Error(`This model does not support ${content.type}s`);
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
inlineData: {
|
|
314
|
+
data: content.data,
|
|
315
|
+
mimeType: content.mimeType,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
304
318
|
} else if (content.type === 'media') {
|
|
305
319
|
return messageContentMedia(content);
|
|
306
320
|
} else if (content.type === 'tool_use') {
|
package/src/llm/ollama/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
} from './utils';
|
|
14
14
|
|
|
15
15
|
export class ChatOllama extends BaseChatOllama {
|
|
16
|
+
static lc_name(): 'LibreChatOllama' {
|
|
17
|
+
return 'LibreChatOllama';
|
|
18
|
+
}
|
|
16
19
|
async *_streamResponseChunks(
|
|
17
20
|
messages: BaseMessage[],
|
|
18
21
|
options: this['ParsedCallOptions'],
|