const { logger } = require('@librechat/data-schemas'); const { createContentAggregator } = require('@librechat/agents'); const { validateAgentModel, getCustomEndpointConfig, createSequentialChainEdges, } = require('@librechat/api'); const { Constants, EModelEndpoint, isAgentsEndpoint, getResponseSender, } = require('librechat-data-provider'); const { createToolEndCallback, getDefaultHandlers, } = require('~/server/controllers/agents/callbacks'); const { initializeAgent } = require('~/server/services/Endpoints/agents/agent'); const { getModelsConfig } = require('~/server/controllers/ModelController'); const { loadAgentTools } = require('~/server/services/ToolService'); const AgentClient = require('~/server/controllers/agents/client'); const { getAgent } = require('~/models/Agent'); const { logViolation } = require('~/cache'); /** * @param {AbortSignal} signal */ function createToolLoader(signal) { /** * @param {object} params * @param {ServerRequest} params.req * @param {ServerResponse} params.res * @param {string} params.agentId * @param {string[]} params.tools * @param {string} params.provider * @param {string} params.model * @param {AgentToolResources} params.tool_resources * @returns {Promise<{ * tools: StructuredTool[], * toolContextMap: Record, * userMCPAuthMap?: Record> * } | undefined>} */ return async function loadTools({ req, res, agentId, tools, provider, model, tool_resources }) { const agent = { id: agentId, tools, provider, model }; try { return await loadAgentTools({ req, res, agent, signal, tool_resources, }); } catch (error) { logger.error('Error loading tools for agent ' + agentId, error); } }; } const initializeClient = async ({ req, res, signal, endpointOption }) => { if (!endpointOption) { throw new Error('Endpoint option not provided'); } const appConfig = req.config; // TODO: use endpointOption to determine options/modelOptions /** @type {Array} */ const collectedUsage = []; /** @type {ArtifactPromises} */ const artifactPromises = []; const { contentParts, aggregateContent } = createContentAggregator(); const toolEndCallback = createToolEndCallback({ req, res, artifactPromises }); const eventHandlers = getDefaultHandlers({ res, aggregateContent, toolEndCallback, collectedUsage, }); if (!endpointOption.agent) { throw new Error('No agent promise provided'); } const primaryAgent = await endpointOption.agent; delete endpointOption.agent; if (!primaryAgent) { throw new Error('Agent not found'); } const modelsConfig = await getModelsConfig(req); const validationResult = await validateAgentModel({ req, res, modelsConfig, logViolation, agent: primaryAgent, }); if (!validationResult.isValid) { throw new Error(validationResult.error?.message); } const agentConfigs = new Map(); const allowedProviders = new Set(appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders); const loadTools = createToolLoader(signal); /** @type {Array} */ const requestFiles = req.body.files ?? []; /** @type {string} */ const conversationId = req.body.conversationId; const primaryConfig = await initializeAgent({ req, res, loadTools, requestFiles, conversationId, agent: primaryAgent, endpointOption, allowedProviders, isInitialAgent: true, }); const agent_ids = primaryConfig.agent_ids; let userMCPAuthMap = primaryConfig.userMCPAuthMap; async function processAgent(agentId) { const agent = await getAgent({ id: agentId }); if (!agent) { throw new Error(`Agent ${agentId} not found`); } const validationResult = await validateAgentModel({ req, res, agent, modelsConfig, logViolation, }); if (!validationResult.isValid) { throw new Error(validationResult.error?.message); } const config = await initializeAgent({ req, res, agent, loadTools, requestFiles, conversationId, endpointOption, allowedProviders, }); if (userMCPAuthMap != null) { Object.assign(userMCPAuthMap, config.userMCPAuthMap ?? {}); } else { userMCPAuthMap = config.userMCPAuthMap; } agentConfigs.set(agentId, config); } 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) { if (checkAgentInit(to)) { continue; } await processAgent(to); } } else if (typeof edge.to === 'string' && checkAgentInit(edge.to)) { continue; } else if (typeof edge.to === 'string') { await processAgent(edge.to); } if (Array.isArray(edge.from)) { for (const from of edge.from) { if (checkAgentInit(from)) { continue; } await processAgent(from); } } else if (typeof edge.from === 'string' && checkAgentInit(edge.from)) { continue; } else if (typeof edge.from === 'string') { await processAgent(edge.from); } } } /** @deprecated Agent Chain */ 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), '{convo}'); edges = edges ? edges.concat(chain) : chain; } primaryConfig.edges = edges; let endpointConfig = appConfig.endpoints?.[primaryConfig.endpoint]; if (!isAgentsEndpoint(primaryConfig.endpoint) && !endpointConfig) { try { endpointConfig = getCustomEndpointConfig({ endpoint: primaryConfig.endpoint, appConfig, }); } catch (err) { logger.error( '[api/server/controllers/agents/client.js #titleConvo] Error getting custom endpoint config', err, ); } } const sender = primaryAgent.name ?? getResponseSender({ ...endpointOption, model: endpointOption.model_parameters.model, modelDisplayLabel: endpointConfig?.modelDisplayLabel, modelLabel: endpointOption.model_parameters.modelLabel, }); const client = new AgentClient({ req, res, sender, contentParts, agentConfigs, eventHandlers, collectedUsage, aggregateContent, artifactPromises, agent: primaryConfig, spec: endpointOption.spec, iconURL: endpointOption.iconURL, attachments: primaryConfig.attachments, endpointType: endpointOption.endpointType, resendFiles: primaryConfig.resendFiles ?? true, maxContextTokens: primaryConfig.maxContextTokens, endpoint: primaryConfig.id === Constants.EPHEMERAL_AGENT_ID ? primaryConfig.endpoint : EModelEndpoint.agents, }); return { client, userMCPAuthMap }; }; module.exports = { initializeClient };