import React, { useState, useMemo, useCallback } from 'react'; import { useToastContext } from '@librechat/client'; import { EModelEndpoint } from 'librechat-data-provider'; import { Controller, useWatch, useFormContext } from 'react-hook-form'; import type { AgentForm, AgentPanelProps, IconComponentTypes } from '~/common'; import { removeFocusOutlines, processAgentOption, getEndpointField, defaultTextProps, validateEmail, getIconKey, cn, } from '~/utils'; import { ToolSelectDialog, MCPToolSelectDialog } from '~/components/Tools'; import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities'; import { useFileMapContext, useAgentPanelContext } from '~/Providers'; import AgentCategorySelector from './AgentCategorySelector'; import Action from '~/components/SidePanel/Builder/Action'; import { useLocalize, useVisibleTools } from '~/hooks'; import { useGetAgentFiles } from '~/data-provider'; import { icons } from '~/hooks/Endpoint/Icons'; import Instructions from './Instructions'; import AgentAvatar from './AgentAvatar'; import FileContext from './FileContext'; import SearchForm from './Search/Form'; import FileSearch from './FileSearch'; import Artifacts from './Artifacts'; import AgentTool from './AgentTool'; import CodeForm from './Code/Form'; import MCPTools from './MCPTools'; import { Panel } from '~/common'; const labelClass = 'mb-2 text-token-text-primary block font-medium'; const inputClass = cn( defaultTextProps, 'flex w-full px-3 py-2 border-border-light bg-surface-secondary focus-visible:ring-2 focus-visible:ring-ring-primary', removeFocusOutlines, ); export default function AgentConfig({ createMutation }: Pick) { const localize = useLocalize(); const fileMap = useFileMapContext(); const { showToast } = useToastContext(); const methods = useFormContext(); const [showToolDialog, setShowToolDialog] = useState(false); const [showMCPToolDialog, setShowMCPToolDialog] = useState(false); const { actions, setAction, regularTools, agentsConfig, startupConfig, mcpServersMap, setActivePanel, endpointsConfig, } = useAgentPanelContext(); const { control, formState: { errors }, } = methods; const provider = useWatch({ control, name: 'provider' }); const model = useWatch({ control, name: 'model' }); const agent = useWatch({ control, name: 'agent' }); const tools = useWatch({ control, name: 'tools' }); const agent_id = useWatch({ control, name: 'id' }); const { data: agentFiles = [] } = useGetAgentFiles(agent_id); const mergedFileMap = useMemo(() => { const newFileMap = { ...fileMap }; agentFiles.forEach((file) => { if (file.file_id) { newFileMap[file.file_id] = file; } }); return newFileMap; }, [fileMap, agentFiles]); const { codeEnabled, toolsEnabled, contextEnabled, actionsEnabled, artifactsEnabled, webSearchEnabled, fileSearchEnabled, } = useAgentCapabilities(agentsConfig?.capabilities); const context_files = useMemo(() => { if (typeof agent === 'string') { return []; } if (agent?.id !== agent_id) { return []; } if (agent.context_files) { return agent.context_files; } const _agent = processAgentOption({ agent, fileMap: mergedFileMap, }); return _agent.context_files ?? []; }, [agent, agent_id, mergedFileMap]); const knowledge_files = useMemo(() => { if (typeof agent === 'string') { return []; } if (agent?.id !== agent_id) { return []; } if (agent.knowledge_files) { return agent.knowledge_files; } const _agent = processAgentOption({ agent, fileMap: mergedFileMap, }); return _agent.knowledge_files ?? []; }, [agent, agent_id, mergedFileMap]); const code_files = useMemo(() => { if (typeof agent === 'string') { return []; } if (agent?.id !== agent_id) { return []; } if (agent.code_files) { return agent.code_files; } const _agent = processAgentOption({ agent, fileMap: mergedFileMap, }); return _agent.code_files ?? []; }, [agent, agent_id, mergedFileMap]); const handleAddActions = useCallback(() => { if (!agent_id) { showToast({ message: localize('com_assistants_actions_disabled'), status: 'warning', }); return; } setActivePanel(Panel.actions); }, [agent_id, setActivePanel, showToast, localize]); const providerValue = typeof provider === 'string' ? provider : provider?.value; let Icon: IconComponentTypes | null | undefined; let endpointType: EModelEndpoint | undefined; let endpointIconURL: string | undefined; let iconKey: string | undefined; if (providerValue !== undefined) { endpointType = getEndpointField(endpointsConfig, providerValue as string, 'type'); endpointIconURL = getEndpointField(endpointsConfig, providerValue as string, 'iconURL'); iconKey = getIconKey({ endpoint: providerValue as string, endpointsConfig, endpointType, endpointIconURL, }); Icon = icons[iconKey]; } const { toolIds, mcpServerNames } = useVisibleTools(tools, regularTools, mcpServersMap); return ( <>
{/* Avatar & Name */}
( <>
{errors.name ? errors.name.message : ' '}
)} /> (

{field.value}

)} />
{/* Description */}
( )} />
{/* Category */}
{/* Instructions */} {/* Model and Provider */}
{(codeEnabled || fileSearchEnabled || artifactsEnabled || contextEnabled || webSearchEnabled) && (
{/* Code Execution */} {codeEnabled && } {/* Web Search */} {webSearchEnabled && } {/* File Context */} {contextEnabled && } {/* Artifacts */} {artifactsEnabled && } {/* File Search */} {fileSearchEnabled && }
)} {/* MCP Section */} {startupConfig?.mcpServers != null && ( )} {/* Agent Tools & Actions */}
{/* Render all visible IDs */} {toolIds.map((toolId, i) => { const tool = regularTools?.find((t) => t.pluginKey === toolId); if (!tool) return null; return ( ); })}
{(actions ?? []) .filter((action) => action.agent_id === agent_id) .map((action, i) => ( { setAction(action); setActivePanel(Panel.actions); }} /> ))}
{(toolsEnabled ?? false) && ( )} {(actionsEnabled ?? false) && ( )}
{/* Support Contact (Optional) */}
{/* Support Contact Name */}
( <> {error && ( {error.message} )} )} />
{/* Support Contact Email */}
validateEmail(value ?? '', localize('com_ui_support_contact_email_invalid')), }} render={({ field, fieldState: { error } }) => ( <> {error && ( {error.message} )} )} />
{startupConfig?.mcpServers != null && ( )} ); }