From 6235ad21fe2ee86d755627863668d57c8e761eba Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 4 Sep 2025 21:25:13 -0400 Subject: [PATCH] feat: add `createSequentialChainEdges` function to add back agent chaining via multi-agents --- .../services/Endpoints/agents/initialize.js | 32 +++++++++---- packages/api/src/agents/chain.ts | 46 +++++++++++++++++++ packages/api/src/agents/index.ts | 1 + 3 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 packages/api/src/agents/chain.ts diff --git a/api/server/services/Endpoints/agents/initialize.js b/api/server/services/Endpoints/agents/initialize.js index e0ee5b862b..503bf8f117 100644 --- a/api/server/services/Endpoints/agents/initialize.js +++ b/api/server/services/Endpoints/agents/initialize.js @@ -1,6 +1,10 @@ const { logger } = require('@librechat/data-schemas'); const { createContentAggregator } = require('@librechat/agents'); -const { validateAgentModel, getCustomEndpointConfig } = require('@librechat/api'); +const { + validateAgentModel, + getCustomEndpointConfig, + createSequentialChainEdges, +} = require('@librechat/api'); const { Constants, EModelEndpoint, @@ -156,15 +160,9 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { agentConfigs.set(agentId, config); } - if (agent_ids?.length) { - for (const agentId of agent_ids) { - await processAgent(agentId); - } - } - - if ((primaryConfig.edges?.length ?? 0) > 0) { - const edges = primaryConfig.edges; - const checkAgentInit = (agentId) => agentId === primaryConfig.id || agentConfigs.has(agentId); + let edges = primaryConfig.edges; + const checkAgentInit = (agentId) => agentId === primaryConfig.id || agentConfigs.has(agentId); + if ((edges?.length ?? 0) > 0) { for (const edge of edges) { if (Array.isArray(edge.to)) { for (const to of edge.to) { @@ -194,6 +192,20 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { } } + if (agent_ids?.length) { + for (const agentId of agent_ids) { + if (checkAgentInit(agentId)) { + continue; + } + await processAgent(agentId); + } + + const chain = await createSequentialChainEdges([primaryConfig.id].concat(agent_ids)); + edges = edges ? edges.concat(chain) : chain; + } + + primaryConfig.edges = edges; + let endpointConfig = appConfig.endpoints?.[primaryConfig.endpoint]; if (!isAgentsEndpoint(primaryConfig.endpoint) && !endpointConfig) { try { diff --git a/packages/api/src/agents/chain.ts b/packages/api/src/agents/chain.ts new file mode 100644 index 0000000000..fe8377ade8 --- /dev/null +++ b/packages/api/src/agents/chain.ts @@ -0,0 +1,46 @@ +import { PromptTemplate } from '@langchain/core/prompts'; +import { BaseMessage, getBufferString } from '@langchain/core/messages'; +import type { GraphEdge } from '@librechat/agents'; + +const DEFAULT_PROMPT_TEMPLATE = `Based on the following conversation and analysis from previous agents, please provide your insights:\n\n{convo}\n\nPlease add your specific expertise and perspective to this discussion.`; + +/** + * Helper function to create sequential chain edges with buffer string prompts + * + * @param agentIds - Array of agent IDs in order of execution + * @param promptTemplate - Optional prompt template string; defaults to a predefined template if not provided + * @returns Array of edges configured for sequential chain with buffer prompts + */ +export async function createSequentialChainEdges( + agentIds: string[], + promptTemplate = DEFAULT_PROMPT_TEMPLATE, +): Promise { + const edges: GraphEdge[] = []; + + for (let i = 0; i < agentIds.length - 1; i++) { + const fromAgent = agentIds[i]; + const toAgent = agentIds[i + 1]; + + edges.push({ + from: fromAgent, + to: toAgent, + edgeType: 'direct', + // Use a prompt function to create the buffer string from all previous results + prompt: async (messages: BaseMessage[], startIndex: number) => { + /** Only the messages from this run (after startIndex) are passed in */ + const runMessages = messages.slice(startIndex); + const bufferString = getBufferString(runMessages); + const template = PromptTemplate.fromTemplate(promptTemplate); + const result = await template.invoke({ + convo: bufferString, + }); + return result.value; + }, + /** Critical: exclude previous results so only the prompt is passed */ + excludeResults: true, + description: `Sequential chain from ${fromAgent} to ${toAgent}`, + }); + } + + return edges; +} diff --git a/packages/api/src/agents/index.ts b/packages/api/src/agents/index.ts index 7cb6ce50b2..938573acb0 100644 --- a/packages/api/src/agents/index.ts +++ b/packages/api/src/agents/index.ts @@ -1,3 +1,4 @@ +export * from './chain'; export * from './config'; export * from './memory'; export * from './migration';