import { useState, useMemo, useEffect } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { useForm, FormProvider, Controller, useWatch } from 'react-hook-form'; import { useGetModelsQuery, useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { Tools, QueryKeys, Capabilities, EModelEndpoint, actionDelimiter, ImageVisionTool, defaultAssistantFormValues, } from 'librechat-data-provider'; import type { AssistantForm, AssistantPanelProps } from '~/common'; import type { FunctionTool, TPlugin, TEndpointsConfig } from 'librechat-data-provider'; import { useCreateAssistantMutation, useUpdateAssistantMutation } from '~/data-provider'; import { SelectDropDown, Checkbox, QuestionMark } from '~/components/ui'; import { useAssistantsMapContext, useToastContext } from '~/Providers'; import { useSelectAssistant, useLocalize } from '~/hooks'; import { ToolSelectDialog } from '~/components/Tools'; import AssistantAvatar from './AssistantAvatar'; import AssistantSelect from './AssistantSelect'; import AssistantAction from './AssistantAction'; import ContextButton from './ContextButton'; import AssistantTool from './AssistantTool'; import { Spinner } from '~/components/svg'; import { cn, cardStyle } from '~/utils/'; import Knowledge from './Knowledge'; import { Panel } from '~/common'; const labelClass = 'mb-2 block text-xs font-bold text-gray-700 dark:text-gray-400'; const inputClass = 'focus:shadow-outline w-full appearance-none rounded-md border px-3 py-2 text-sm leading-tight text-gray-700 dark:text-white shadow focus:border-green-500 focus:outline-none focus:ring-0 dark:bg-gray-800 dark:border-gray-700/80'; export default function AssistantPanel({ // index = 0, setAction, actions = [], setActivePanel, assistant_id: current_assistant_id, setCurrentAssistantId, }: AssistantPanelProps) { const queryClient = useQueryClient(); const modelsQuery = useGetModelsQuery(); const assistantMap = useAssistantsMapContext(); const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery(); const allTools = queryClient.getQueryData([QueryKeys.tools]) ?? []; const { onSelect: onSelectAssistant } = useSelectAssistant(); const { showToast } = useToastContext(); const localize = useLocalize(); const methods = useForm({ defaultValues: defaultAssistantFormValues, }); const [showToolDialog, setShowToolDialog] = useState(false); const { control, handleSubmit, reset, setValue, getValues } = methods; const assistant = useWatch({ control, name: 'assistant' }); const functions = useWatch({ control, name: 'functions' }); const assistant_id = useWatch({ control, name: 'id' }); const model = useWatch({ control, name: 'model' }); const activeModel = useMemo(() => { return assistantMap?.[assistant_id]?.model; }, [assistantMap, assistant_id]); const assistants = useMemo(() => endpointsConfig?.[EModelEndpoint.assistants], [endpointsConfig]); const retrievalModels = useMemo(() => new Set(assistants?.retrievalModels ?? []), [assistants]); const toolsEnabled = useMemo( () => assistants?.capabilities?.includes(Capabilities.tools), [assistants], ); const actionsEnabled = useMemo( () => assistants?.capabilities?.includes(Capabilities.actions), [assistants], ); const retrievalEnabled = useMemo( () => assistants?.capabilities?.includes(Capabilities.retrieval), [assistants], ); const codeEnabled = useMemo( () => assistants?.capabilities?.includes(Capabilities.code_interpreter), [assistants], ); const imageVisionEnabled = useMemo( () => assistants?.capabilities?.includes(Capabilities.image_vision), [assistants], ); useEffect(() => { if (model && !retrievalModels.has(model)) { setValue(Capabilities.retrieval, false); } }, [model, setValue, retrievalModels]); /* Mutations */ const update = useUpdateAssistantMutation({ onSuccess: (data) => { showToast({ message: `${localize('com_assistants_update_success')} ${ data.name ?? localize('com_ui_assistant') }`, }); }, onError: (err) => { const error = err as Error; showToast({ message: `${localize('com_assistants_update_error')}${ error?.message ? ` ${localize('com_ui_error')}: ${error?.message}` : '' }`, status: 'error', }); }, }); const create = useCreateAssistantMutation({ onSuccess: (data) => { setCurrentAssistantId(data.id); showToast({ message: `${localize('com_assistants_create_success')} ${ data.name ?? localize('com_ui_assistant') }`, }); }, onError: (err) => { const error = err as Error; showToast({ message: `${localize('com_assistants_create_error')}${ error?.message ? ` ${localize('com_ui_error')}: ${error?.message}` : '' }`, status: 'error', }); }, }); const files = useMemo(() => { if (typeof assistant === 'string') { return []; } return assistant.files; }, [assistant]); const onSubmit = (data: AssistantForm) => { const tools: Array = [...functions].map((functionName) => { if (!functionName.includes(actionDelimiter)) { return functionName; } else { const assistant = assistantMap?.[assistant_id]; const tool = assistant?.tools?.find((tool) => tool.function?.name === functionName); if (assistant && tool) { return tool; } } return functionName; }); console.log(data); if (data.code_interpreter) { tools.push({ type: Tools.code_interpreter }); } if (data.retrieval) { tools.push({ type: Tools.retrieval }); } if (data.image_vision) { tools.push(ImageVisionTool); } const { name, description, instructions, model, // file_ids, // TODO: add file handling here } = data; if (assistant_id) { update.mutate({ assistant_id, data: { name, description, instructions, model, tools, }, }); return; } create.mutate({ name, description, instructions, model, tools, }); }; return (
( )} /> {/* Select Button */} {assistant_id && ( )}
{/* Avatar & Name */}
( )} /> (

{field.value ?? ''}

)} />
{/* Description */}
( )} />
{/* Instructions */}
(