@librechat/agents 3.0.0-rc8 → 3.0.0
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 +6 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +23 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +108 -17
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +21 -0
- package/dist/cjs/instrumentation.cjs.map +1 -0
- 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 +18 -3
- 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 +8 -2
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +49 -0
- package/dist/cjs/messages/cache.cjs.map +1 -0
- package/dist/cjs/messages/content.cjs +53 -0
- package/dist/cjs/messages/content.cjs.map +1 -0
- package/dist/cjs/messages/core.cjs +5 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +50 -59
- 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 +57 -5
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +7 -0
- 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/cjs/utils/misc.cjs +8 -1
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/title.cjs +54 -25
- package/dist/cjs/utils/title.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +6 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +23 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +108 -17
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +19 -0
- package/dist/esm/instrumentation.mjs.map +1 -0
- 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 +18 -3
- 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 +5 -2
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +47 -0
- package/dist/esm/messages/cache.mjs.map +1 -0
- package/dist/esm/messages/content.mjs +51 -0
- package/dist/esm/messages/content.mjs.map +1 -0
- package/dist/esm/messages/core.mjs +5 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +50 -58
- 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 +57 -5
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +7 -0
- 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/esm/utils/misc.mjs +8 -2
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/title.mjs +54 -25
- package/dist/esm/utils/title.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +4 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +11 -1
- 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 +4 -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/cache.d.ts +8 -0
- package/dist/types/messages/content.d.ts +7 -0
- package/dist/types/messages/format.d.ts +22 -25
- package/dist/types/messages/index.d.ts +2 -0
- package/dist/types/run.d.ts +2 -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 +3 -1
- package/dist/types/types/messages.d.ts +4 -0
- package/dist/types/utils/handlers.d.ts +34 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/misc.d.ts +1 -0
- package/package.json +8 -2
- package/src/agents/AgentContext.ts +8 -0
- package/src/graphs/Graph.ts +31 -2
- package/src/graphs/MultiAgentGraph.ts +125 -18
- package/src/instrumentation.ts +22 -0
- package/src/llm/anthropic/index.ts +23 -2
- package/src/llm/anthropic/llm.spec.ts +1 -1
- 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 +17 -4
- 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/cache.test.ts +262 -0
- package/src/messages/cache.ts +56 -0
- package/src/messages/content.test.ts +362 -0
- package/src/messages/content.ts +63 -0
- package/src/messages/core.ts +5 -2
- package/src/messages/format.ts +65 -71
- package/src/messages/formatMessage.test.ts +418 -2
- package/src/messages/index.ts +2 -0
- package/src/messages/prune.ts +51 -0
- package/src/run.ts +82 -10
- package/src/scripts/ant_web_search.ts +1 -1
- package/src/scripts/handoff-test.ts +1 -1
- package/src/scripts/multi-agent-chain.ts +278 -0
- package/src/scripts/multi-agent-conditional.ts +4 -4
- 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 +10 -8
- package/src/scripts/multi-agent-sequence.ts +3 -3
- package/src/scripts/multi-agent-supervisor.ts +5 -3
- package/src/scripts/multi-agent-test.ts +2 -2
- package/src/scripts/search.ts +5 -1
- package/src/scripts/simple.ts +8 -0
- package/src/scripts/test-custom-prompt-key.ts +4 -4
- package/src/scripts/test-handoff-input.ts +3 -3
- package/src/scripts/test-multi-agent-list-handoff.ts +2 -2
- package/src/scripts/tools.ts +4 -1
- package/src/specs/agent-handoffs.test.ts +889 -0
- package/src/stream.ts +9 -2
- 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 +6 -1
- package/src/types/messages.ts +4 -0
- package/src/utils/handlers.ts +107 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/llmConfig.ts +35 -1
- package/src/utils/misc.ts +33 -21
- package/src/utils/title.ts +80 -40
- 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-supervisor.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/test-custom-prompt-key.d.ts +0 -2
- package/dist/types/scripts/test-handoff-input.d.ts +0 -1
- package/dist/types/scripts/test-multi-agent-list-handoff.d.ts +0 -2
- package/dist/types/scripts/test-tools-before-handoff.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
- /package/dist/types/{scripts/abort.d.ts → instrumentation.d.ts} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { tool } from '@langchain/core/tools';
|
|
3
|
+
import { PromptTemplate } from '@langchain/core/prompts';
|
|
3
4
|
import {
|
|
4
5
|
ToolMessage,
|
|
5
6
|
HumanMessage,
|
|
6
7
|
getBufferString,
|
|
7
8
|
} from '@langchain/core/messages';
|
|
8
|
-
import { ChatPromptTemplate } from '@langchain/core/prompts';
|
|
9
9
|
import {
|
|
10
10
|
END,
|
|
11
11
|
START,
|
|
@@ -23,7 +23,17 @@ import { Constants } from '@/common';
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
|
|
26
|
-
* 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.
|
|
27
37
|
*/
|
|
28
38
|
export class MultiAgentGraph extends StandardGraph {
|
|
29
39
|
private edges: t.GraphEdge[];
|
|
@@ -328,41 +338,123 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
328
338
|
}
|
|
329
339
|
}
|
|
330
340
|
|
|
331
|
-
/**
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
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
|
+
]);
|
|
335
351
|
if (handoffDestinations.size > 0 || directDestinations.size === 0) {
|
|
336
|
-
|
|
352
|
+
allDestinations.add(END);
|
|
337
353
|
}
|
|
338
354
|
|
|
339
355
|
/** Agent subgraph (includes agent + tools) */
|
|
340
356
|
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
341
357
|
|
|
342
|
-
/** Wrapper function that handles agentMessages channel */
|
|
358
|
+
/** Wrapper function that handles agentMessages channel and conditional routing */
|
|
343
359
|
const agentWrapper = async (
|
|
344
360
|
state: t.MultiAgentGraphState
|
|
345
|
-
): Promise<t.MultiAgentGraphState> => {
|
|
361
|
+
): Promise<t.MultiAgentGraphState | Command> => {
|
|
362
|
+
let result: t.MultiAgentGraphState;
|
|
363
|
+
|
|
346
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
|
+
|
|
347
397
|
/** Temporary state with messages replaced by `agentMessages` */
|
|
348
398
|
const transformedState: t.MultiAgentGraphState = {
|
|
349
399
|
...state,
|
|
350
400
|
messages: state.agentMessages,
|
|
351
401
|
};
|
|
352
|
-
|
|
353
|
-
|
|
402
|
+
result = await agentSubgraph.invoke(transformedState);
|
|
403
|
+
result = {
|
|
354
404
|
...result,
|
|
355
405
|
/** Clear agentMessages for next agent */
|
|
356
406
|
agentMessages: [],
|
|
357
407
|
};
|
|
358
408
|
} else {
|
|
359
|
-
|
|
409
|
+
result = await agentSubgraph.invoke(state);
|
|
360
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;
|
|
361
453
|
};
|
|
362
454
|
|
|
363
455
|
/** Wrapped agent as a node with its possible destinations */
|
|
364
456
|
builder.addNode(agentId, agentWrapper, {
|
|
365
|
-
ends: Array.from(
|
|
457
|
+
ends: Array.from(allDestinations),
|
|
366
458
|
});
|
|
367
459
|
}
|
|
368
460
|
|
|
@@ -416,16 +508,16 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
416
508
|
let effectiveExcludeResults = excludeResults;
|
|
417
509
|
|
|
418
510
|
if (typeof prompt === 'function') {
|
|
419
|
-
promptText = prompt(state.messages, this.startIndex);
|
|
511
|
+
promptText = await prompt(state.messages, this.startIndex);
|
|
420
512
|
} else if (prompt != null) {
|
|
421
513
|
if (prompt.includes('{results}')) {
|
|
422
514
|
const resultsMessages = state.messages.slice(this.startIndex);
|
|
423
515
|
const resultsString = getBufferString(resultsMessages);
|
|
424
|
-
const promptTemplate =
|
|
425
|
-
const
|
|
516
|
+
const promptTemplate = PromptTemplate.fromTemplate(prompt);
|
|
517
|
+
const result = await promptTemplate.invoke({
|
|
426
518
|
results: resultsString,
|
|
427
519
|
});
|
|
428
|
-
promptText =
|
|
520
|
+
promptText = result.value;
|
|
429
521
|
effectiveExcludeResults =
|
|
430
522
|
excludeResults !== false && promptText !== '';
|
|
431
523
|
} else {
|
|
@@ -474,10 +566,25 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
474
566
|
/** @ts-ignore */
|
|
475
567
|
builder.addEdge(wrapperNodeId, destination);
|
|
476
568
|
} else {
|
|
477
|
-
/** No prompt instructions, add direct edges */
|
|
569
|
+
/** No prompt instructions, add direct edges (skip if source uses Command routing) */
|
|
478
570
|
for (const edge of edges) {
|
|
479
571
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
480
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
|
+
|
|
481
588
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
482
589
|
/** @ts-ignore */
|
|
483
590
|
builder.addEdge(source, destination);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
2
|
+
import { LangfuseSpanProcessor } from '@langfuse/otel';
|
|
3
|
+
import { isPresent } from '@/utils/misc';
|
|
4
|
+
|
|
5
|
+
if (
|
|
6
|
+
isPresent(process.env.LANGFUSE_SECRET_KEY) &&
|
|
7
|
+
isPresent(process.env.LANGFUSE_PUBLIC_KEY) &&
|
|
8
|
+
isPresent(process.env.LANGFUSE_BASE_URL)
|
|
9
|
+
) {
|
|
10
|
+
const langfuseSpanProcessor = new LangfuseSpanProcessor({
|
|
11
|
+
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
|
|
12
|
+
secretKey: process.env.LANGFUSE_SECRET_KEY,
|
|
13
|
+
baseUrl: process.env.LANGFUSE_BASE_URL,
|
|
14
|
+
environment: process.env.NODE_ENV ?? 'development',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const sdk = new NodeSDK({
|
|
18
|
+
spanProcessors: [langfuseSpanProcessor],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
sdk.start();
|
|
22
|
+
}
|
|
@@ -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,
|
|
@@ -58,7 +58,7 @@ async function invoke(
|
|
|
58
58
|
const extendedThinkingModelName = 'claude-3-7-sonnet-20250219';
|
|
59
59
|
|
|
60
60
|
// use this for tests involving citations
|
|
61
|
-
const citationsModelName = 'claude-
|
|
61
|
+
const citationsModelName = 'claude-sonnet-4-5-20250929';
|
|
62
62
|
|
|
63
63
|
// use this for tests involving PDF documents
|
|
64
64
|
const pdfModelName = 'claude-3-5-haiku-20241022';
|
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'],
|
package/src/llm/openai/index.ts
CHANGED
|
@@ -210,6 +210,9 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
|
210
210
|
public get exposedClient(): CustomOpenAIClient {
|
|
211
211
|
return this.client;
|
|
212
212
|
}
|
|
213
|
+
static lc_name(): string {
|
|
214
|
+
return 'LibreChatOpenAI';
|
|
215
|
+
}
|
|
213
216
|
protected _getClientOptions(
|
|
214
217
|
options?: OpenAICoreRequestOptions
|
|
215
218
|
): OpenAICoreRequestOptions {
|
|
@@ -245,10 +248,6 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
|
245
248
|
getReasoningParams(
|
|
246
249
|
options?: this['ParsedCallOptions']
|
|
247
250
|
): OpenAIClient.Reasoning | undefined {
|
|
248
|
-
if (!isReasoningModel(this.model)) {
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
251
|
// apply options in reverse order of importance -- newer options supersede older options
|
|
253
252
|
let reasoning: OpenAIClient.Reasoning | undefined;
|
|
254
253
|
if (this.reasoning !== undefined) {
|
|
@@ -360,6 +359,10 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
|
360
359
|
} else if ('reasoning' in delta) {
|
|
361
360
|
chunk.additional_kwargs.reasoning_content = delta.reasoning;
|
|
362
361
|
}
|
|
362
|
+
if ('provider_specific_fields' in delta) {
|
|
363
|
+
chunk.additional_kwargs.provider_specific_fields =
|
|
364
|
+
delta.provider_specific_fields;
|
|
365
|
+
}
|
|
363
366
|
defaultRole = delta.role ?? defaultRole;
|
|
364
367
|
const newTokenIndices = {
|
|
365
368
|
prompt: options.promptIndex ?? 0,
|
|
@@ -463,6 +466,9 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
|
|
|
463
466
|
public get exposedClient(): CustomOpenAIClient {
|
|
464
467
|
return this.client;
|
|
465
468
|
}
|
|
469
|
+
static lc_name(): 'LibreChatAzureOpenAI' {
|
|
470
|
+
return 'LibreChatAzureOpenAI';
|
|
471
|
+
}
|
|
466
472
|
/**
|
|
467
473
|
* Returns backwards compatible reasoning parameters from constructor params and call options
|
|
468
474
|
* @internal
|
|
@@ -607,6 +613,9 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
|
|
|
607
613
|
public get exposedClient(): CustomOpenAIClient {
|
|
608
614
|
return this.client;
|
|
609
615
|
}
|
|
616
|
+
static lc_name(): 'LibreChatDeepSeek' {
|
|
617
|
+
return 'LibreChatDeepSeek';
|
|
618
|
+
}
|
|
610
619
|
protected _getClientOptions(
|
|
611
620
|
options?: OpenAICoreRequestOptions
|
|
612
621
|
): OpenAICoreRequestOptions {
|
|
@@ -679,6 +688,10 @@ export class ChatXAI extends OriginalChatXAI {
|
|
|
679
688
|
}
|
|
680
689
|
}
|
|
681
690
|
|
|
691
|
+
static lc_name(): 'LibreChatXAI' {
|
|
692
|
+
return 'LibreChatXAI';
|
|
693
|
+
}
|
|
694
|
+
|
|
682
695
|
public get exposedClient(): CustomOpenAIClient {
|
|
683
696
|
return this.client;
|
|
684
697
|
}
|
|
@@ -298,10 +298,16 @@ export function _convertMessagesToOpenAIParams(
|
|
|
298
298
|
role = 'developer';
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
let hasAnthropicThinkingBlock: boolean = false;
|
|
302
|
+
|
|
301
303
|
const content =
|
|
302
304
|
typeof message.content === 'string'
|
|
303
305
|
? message.content
|
|
304
306
|
: message.content.map((m) => {
|
|
307
|
+
if ('type' in m && m.type === 'thinking') {
|
|
308
|
+
hasAnthropicThinkingBlock = true;
|
|
309
|
+
return m;
|
|
310
|
+
}
|
|
305
311
|
if (isDataContentBlock(m)) {
|
|
306
312
|
return convertToProviderContentBlock(
|
|
307
313
|
m,
|
|
@@ -326,7 +332,7 @@ export function _convertMessagesToOpenAIParams(
|
|
|
326
332
|
completionParam.tool_calls = message.tool_calls.map(
|
|
327
333
|
convertLangChainToolCallToOpenAI
|
|
328
334
|
);
|
|
329
|
-
completionParam.content = '';
|
|
335
|
+
completionParam.content = hasAnthropicThinkingBlock ? content : '';
|
|
330
336
|
} else {
|
|
331
337
|
if (message.additional_kwargs.tool_calls != null) {
|
|
332
338
|
completionParam.tool_calls = message.additional_kwargs.tool_calls;
|
|
@@ -1,27 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ChatOpenAI } from '@/llm/openai';
|
|
2
2
|
import type {
|
|
3
|
-
AIMessageChunk,
|
|
4
|
-
HumanMessageChunk,
|
|
5
|
-
SystemMessageChunk,
|
|
6
3
|
FunctionMessageChunk,
|
|
4
|
+
SystemMessageChunk,
|
|
5
|
+
HumanMessageChunk,
|
|
7
6
|
ToolMessageChunk,
|
|
8
7
|
ChatMessageChunk,
|
|
8
|
+
AIMessageChunk,
|
|
9
9
|
} from '@langchain/core/messages';
|
|
10
|
-
import {
|
|
10
|
+
import type {
|
|
11
|
+
ChatOpenAICallOptions,
|
|
12
|
+
OpenAIChatInput,
|
|
13
|
+
OpenAIClient,
|
|
14
|
+
} from '@langchain/openai';
|
|
11
15
|
|
|
12
16
|
export interface ChatOpenRouterCallOptions extends ChatOpenAICallOptions {
|
|
13
17
|
include_reasoning?: boolean;
|
|
18
|
+
modelKwargs?: OpenAIChatInput['modelKwargs'];
|
|
14
19
|
}
|
|
15
20
|
export class ChatOpenRouter extends ChatOpenAI {
|
|
16
21
|
constructor(_fields: Partial<ChatOpenRouterCallOptions>) {
|
|
17
|
-
const { include_reasoning, ...fields } = _fields;
|
|
22
|
+
const { include_reasoning, modelKwargs = {}, ...fields } = _fields;
|
|
18
23
|
super({
|
|
19
24
|
...fields,
|
|
20
25
|
modelKwargs: {
|
|
26
|
+
...modelKwargs,
|
|
21
27
|
include_reasoning,
|
|
22
28
|
},
|
|
23
29
|
});
|
|
24
30
|
}
|
|
31
|
+
static lc_name(): 'LibreChatOpenRouter' {
|
|
32
|
+
return 'LibreChatOpenRouter';
|
|
33
|
+
}
|
|
25
34
|
protected override _convertOpenAIDeltaToBaseMessageChunk(
|
|
26
35
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
36
|
delta: Record<string, any>,
|
|
@@ -313,8 +313,8 @@ export class ChatVertexAI extends ChatGoogle {
|
|
|
313
313
|
lc_namespace = ['langchain', 'chat_models', 'vertexai'];
|
|
314
314
|
dynamicThinkingBudget = false;
|
|
315
315
|
|
|
316
|
-
static lc_name(): '
|
|
317
|
-
return '
|
|
316
|
+
static lc_name(): 'LibreChatVertexAI' {
|
|
317
|
+
return 'LibreChatVertexAI';
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
constructor(fields?: VertexAIClientOptions) {
|