import React, { useState, useMemo, useCallback } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { Controller, useWatch, useFormContext } from 'react-hook-form'; import { QueryKeys, SystemRoles, Permissions, EModelEndpoint, PermissionTypes, AgentCapabilities, } from 'librechat-data-provider'; import type { TConfig, TPlugin } from 'librechat-data-provider'; import type { AgentForm, AgentPanelProps } from '~/common'; import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils'; import { useCreateAgentMutation, useUpdateAgentMutation } from '~/data-provider'; import { useLocalize, useAuthContext, useHasAccess } from '~/hooks'; import { useToastContext, useFileMapContext } from '~/Providers'; import { icons } from '~/components/Chat/Menus/Endpoints/Icons'; import Action from '~/components/SidePanel/Builder/Action'; import { ToolSelectDialog } from '~/components/Tools'; import { processAgentOption } from '~/utils'; import AdminSettings from './AdminSettings'; import { Spinner } from '~/components/svg'; import DeleteButton from './DeleteButton'; import AgentAvatar from './AgentAvatar'; import FileSearch from './FileSearch'; import ShareAgent from './ShareAgent'; 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({ setAction, actions = [], agentsConfig, endpointsConfig, setActivePanel, setCurrentAgentId, }: AgentPanelProps & { agentsConfig?: TConfig | null }) { const { user } = useAuthContext(); const fileMap = useFileMapContext(); const queryClient = useQueryClient(); const allTools = queryClient.getQueryData([QueryKeys.tools]) ?? []; const { showToast } = useToastContext(); const localize = useLocalize(); const [showToolDialog, setShowToolDialog] = useState(false); const methods = useFormContext(); 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 hasAccessToShareAgents = useHasAccess({ permissionType: PermissionTypes.AGENTS, permission: Permissions.SHARED_GLOBAL, }); const toolsEnabled = useMemo( () => agentsConfig?.capabilities?.includes(AgentCapabilities.tools), [agentsConfig], ); const actionsEnabled = useMemo( () => agentsConfig?.capabilities?.includes(AgentCapabilities.actions), [agentsConfig], ); const fileSearchEnabled = useMemo( () => agentsConfig?.capabilities?.includes(AgentCapabilities.file_search) ?? false, [agentsConfig], ); const codeEnabled = useMemo( () => agentsConfig?.capabilities?.includes(AgentCapabilities.execute_code) ?? false, [agentsConfig], ); 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, }); return _agent.knowledge_files ?? []; }, [agent, agent_id, fileMap]); 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, }); return _agent.code_files ?? []; }, [agent, agent_id, fileMap]); /* Mutations */ const update = useUpdateAgentMutation({ onSuccess: (data) => { showToast({ message: `${localize('com_assistants_update_success')} ${ data.name ?? localize('com_ui_agent') }`, }); }, onError: (err) => { const error = err as Error; showToast({ message: `${localize('com_agents_update_error')}${ error.message ? ` ${localize('com_ui_error')}: ${error.message}` : '' }`, status: 'error', }); }, }); const create = useCreateAgentMutation({ onSuccess: (data) => { setCurrentAgentId(data.id); showToast({ message: `${localize('com_assistants_create_success ')} ${ data.name ?? localize('com_ui_agent') }`, }); }, onError: (err) => { const error = err as Error; showToast({ message: `${localize('com_agents_create_error')}${ error.message ? ` ${localize('com_ui_error')}: ${error.message}` : '' }`, status: 'error', }); }, }); 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 endpointType: EModelEndpoint | undefined; let endpointIconURL: string | undefined; let iconKey: string | undefined; let Icon: | React.ComponentType< React.SVGProps & { endpoint: string; endpointType: EModelEndpoint | undefined; iconURL: string | undefined; } > | 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 renderSaveButton = () => { if (create.isLoading || update.isLoading) { return