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, getIconKey, cn, } from '~/utils'; import { useFileMapContext, useAgentPanelContext } from '~/Providers'; import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities'; import AgentCategorySelector from './AgentCategorySelector'; import Action from '~/components/SidePanel/Builder/Action'; import { ToolSelectDialog } from '~/components/Tools'; 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 { useLocalize } from '~/hooks'; import FileSearch from './FileSearch'; import Artifacts from './Artifacts'; import AgentTool from './AgentTool'; import CodeForm from './Code/Form'; 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 { actions, setAction, agentsConfig, setActivePanel, endpointsConfig, groupedTools: allTools, } = useAgentPanelContext(); const { control } = 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 { ocrEnabled, codeEnabled, toolsEnabled, 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]; } // Determine what to show const selectedToolIds = tools ?? []; const visibleToolIds = new Set(selectedToolIds); // Check what group parent tools should be shown if any subtool is present Object.entries(allTools ?? {}).forEach(([toolId, toolObj]) => { if (toolObj.tools?.length) { // if any subtool of this group is selected, ensure group parent tool rendered if (toolObj.tools.some((st) => selectedToolIds.includes(st.tool_id))) { visibleToolIds.add(toolId); } } }); return ( <>
{/* Avatar & Name */}
( )} /> (

{field.value}

)} />
{/* Description */}
( )} />
{/* Category */}
{/* Instructions */} {/* Model and Provider */}
{(codeEnabled || fileSearchEnabled || artifactsEnabled || ocrEnabled || webSearchEnabled) && (
{/* Code Execution */} {codeEnabled && } {/* Web Search */} {webSearchEnabled && } {/* File Context (OCR) */} {ocrEnabled && } {/* Artifacts */} {artifactsEnabled && } {/* File Search */} {fileSearchEnabled && }
)} {/* Agent Tools & Actions */}
{/* // Render all visible IDs (including groups with subtools selected) */} {[...visibleToolIds].map((toolId, i) => { if (!allTools) return null; const tool = allTools[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) && ( )}
{/* MCP Section */} {/* */} {/* Support Contact (Optional) */}
{/* Support Contact Name */}
( <> {error && ( {error.message} )} )} />
{/* Support Contact Email */}
( <> {error && ( {error.message} )} )} />
); }