| 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'); |
|
|
| |
| |
| |
| function createToolLoader(signal) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
|
|
| |
| |
| const collectedUsage = []; |
| |
| 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); |
| |
| const requestFiles = req.body.files ?? []; |
| |
| 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); |
| } |
| } |
| } |
|
|
| |
| 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 }; |
|
|