| | import { EarthIcon } from 'lucide-react'; |
| | import { ControlCombobox } from '@librechat/client'; |
| | import { useCallback, useEffect, useRef } from 'react'; |
| | import { useFormContext, Controller } from 'react-hook-form'; |
| | import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider'; |
| | import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query'; |
| | import type { Agent, AgentCreateParams } from 'librechat-data-provider'; |
| | import type { TAgentCapabilities, AgentForm } from '~/common'; |
| | import { cn, createProviderOption, processAgentOption, getDefaultAgentFormValues } from '~/utils'; |
| | import { useLocalize, useAgentDefaultPermissionLevel } from '~/hooks'; |
| | import { useListAgentsQuery } from '~/data-provider'; |
| |
|
| | const keys = new Set(Object.keys(defaultAgentFormValues)); |
| |
|
| | export default function AgentSelect({ |
| | agentQuery, |
| | selectedAgentId = null, |
| | setCurrentAgentId, |
| | createMutation, |
| | }: { |
| | selectedAgentId: string | null; |
| | agentQuery: QueryObserverResult<Agent>; |
| | setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>; |
| | createMutation: UseMutationResult<Agent, Error, AgentCreateParams>; |
| | }) { |
| | const localize = useLocalize(); |
| | const lastSelectedAgent = useRef<string | null>(null); |
| | const { control, reset } = useFormContext(); |
| | const permissionLevel = useAgentDefaultPermissionLevel(); |
| |
|
| | const { data: agents = null } = useListAgentsQuery( |
| | { requiredPermission: permissionLevel }, |
| | { |
| | select: (res) => |
| | res.data.map((agent) => |
| | processAgentOption({ |
| | agent: { |
| | ...agent, |
| | name: agent.name || agent.id, |
| | }, |
| | }), |
| | ), |
| | }, |
| | ); |
| |
|
| | const resetAgentForm = useCallback( |
| | (fullAgent: Agent) => { |
| | const isGlobal = fullAgent.isPublic ?? false; |
| | const update = { |
| | ...fullAgent, |
| | provider: createProviderOption(fullAgent.provider), |
| | label: fullAgent.name ?? '', |
| | value: fullAgent.id || '', |
| | icon: isGlobal ? <EarthIcon className={'icon-lg text-green-400'} /> : null, |
| | }; |
| |
|
| | const capabilities: TAgentCapabilities = { |
| | [AgentCapabilities.web_search]: false, |
| | [AgentCapabilities.file_search]: false, |
| | [AgentCapabilities.execute_code]: false, |
| | [AgentCapabilities.end_after_tools]: false, |
| | [AgentCapabilities.hide_sequential_outputs]: false, |
| | }; |
| |
|
| | const agentTools: string[] = []; |
| | (fullAgent.tools ?? []).forEach((tool) => { |
| | if (capabilities[tool] !== undefined) { |
| | capabilities[tool] = true; |
| | return; |
| | } |
| |
|
| | agentTools.push(tool); |
| | }); |
| |
|
| | const formValues: Partial<AgentForm & TAgentCapabilities> = { |
| | ...capabilities, |
| | agent: update, |
| | model: update.model, |
| | tools: agentTools, |
| | |
| | category: fullAgent.category || 'general', |
| | |
| | support_contact: fullAgent.support_contact, |
| | avatar_file: null, |
| | avatar_preview: fullAgent.avatar?.filepath ?? '', |
| | avatar_action: null, |
| | }; |
| |
|
| | Object.entries(fullAgent).forEach(([name, value]) => { |
| | if (name === 'model_parameters') { |
| | formValues[name] = value; |
| | return; |
| | } |
| |
|
| | if (capabilities[name] !== undefined) { |
| | formValues[name] = value; |
| | return; |
| | } |
| |
|
| | if ( |
| | name === 'agent_ids' && |
| | Array.isArray(value) && |
| | value.every((item) => typeof item === 'string') |
| | ) { |
| | formValues[name] = value; |
| | return; |
| | } |
| |
|
| | if (name === 'edges' && Array.isArray(value)) { |
| | formValues[name] = value; |
| | return; |
| | } |
| |
|
| | if (!keys.has(name)) { |
| | return; |
| | } |
| |
|
| | if (name === 'recursion_limit' && typeof value === 'number') { |
| | formValues[name] = value; |
| | return; |
| | } |
| |
|
| | if (typeof value !== 'number' && typeof value !== 'object') { |
| | formValues[name] = value; |
| | } |
| | }); |
| |
|
| | reset(formValues); |
| | }, |
| | [reset], |
| | ); |
| |
|
| | const onSelect = useCallback( |
| | (selectedId: string) => { |
| | const agentExists = !!(selectedId |
| | ? (agents ?? []).find((agent) => agent.id === selectedId) |
| | : undefined); |
| |
|
| | createMutation.reset(); |
| | if (!agentExists) { |
| | setCurrentAgentId(undefined); |
| | return reset(getDefaultAgentFormValues()); |
| | } |
| |
|
| | setCurrentAgentId(selectedId); |
| | const agent = agentQuery.data; |
| | if (!agent) { |
| | console.warn('Agent not found'); |
| | return; |
| | } |
| |
|
| | resetAgentForm(agent); |
| | }, |
| | [agents, createMutation, setCurrentAgentId, agentQuery.data, resetAgentForm, reset], |
| | ); |
| |
|
| | useEffect(() => { |
| | if (agentQuery.data && agentQuery.isSuccess) { |
| | resetAgentForm(agentQuery.data); |
| | } |
| | }, [agentQuery.data, agentQuery.isSuccess, resetAgentForm]); |
| |
|
| | useEffect(() => { |
| | let timerId: NodeJS.Timeout | null = null; |
| |
|
| | if (selectedAgentId === lastSelectedAgent.current) { |
| | return; |
| | } |
| |
|
| | if (selectedAgentId != null && selectedAgentId !== '' && agents) { |
| | timerId = setTimeout(() => { |
| | lastSelectedAgent.current = selectedAgentId; |
| | onSelect(selectedAgentId); |
| | }, 5); |
| | } |
| |
|
| | return () => { |
| | if (timerId) { |
| | clearTimeout(timerId); |
| | } |
| | }; |
| | }, [selectedAgentId, agents, onSelect]); |
| |
|
| | const createAgent = localize('com_ui_create') + ' ' + localize('com_ui_agent'); |
| |
|
| | return ( |
| | <Controller |
| | name="agent" |
| | control={control} |
| | render={({ field }) => ( |
| | <ControlCombobox |
| | containerClassName="px-0" |
| | selectedValue={(field?.value?.value ?? '') + ''} |
| | displayValue={field?.value?.label ?? ''} |
| | selectPlaceholder={field?.value?.value ?? createAgent} |
| | iconSide="right" |
| | searchPlaceholder={localize('com_agents_search_name')} |
| | SelectIcon={field?.value?.icon} |
| | setValue={onSelect} |
| | items={ |
| | agents?.map((agent) => ({ |
| | label: agent.name ?? '', |
| | value: agent.id ?? '', |
| | icon: agent.icon, |
| | })) ?? [ |
| | { |
| | label: 'Loading...', |
| | value: '', |
| | }, |
| | ] |
| | } |
| | className={cn( |
| | 'z-50 flex h-[40px] w-full flex-none items-center justify-center truncate rounded-md bg-transparent font-bold', |
| | )} |
| | ariaLabel={localize('com_ui_agent')} |
| | isCollapsed={false} |
| | showCarat={true} |
| | /> |
| | )} |
| | /> |
| | ); |
| | } |
| |
|