// src/pages/next-search/search-setting.tsx import { AvatarUpload } from '@/components/avatar-upload'; import { MetadataFilter, MetadataFilterSchema, } from '@/components/metadata-filter'; import { Button } from '@/components/ui/button'; import { SingleFormSlider } from '@/components/ui/dual-range-slider'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { MultiSelect, MultiSelectOptionType, } from '@/components/ui/multi-select'; import { RAGFlowSelect } from '@/components/ui/select'; import { Spin } from '@/components/ui/spin'; import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useComposeLlmOptionsByModelTypes, useSelectLlmOptionsByModelType, } from '@/hooks/llm-hooks'; import { useFetchTenantInfo } from '@/hooks/user-setting-hooks'; import { IKnowledge } from '@/interfaces/database/knowledge'; import { cn } from '@/lib/utils'; import { zodResolver } from '@hookform/resolvers/zod'; import { X } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { LlmModelType } from '../dataset/dataset/constant'; import { ISearchAppDetailProps, IUpdateSearchProps, IllmSettingProps, useUpdateSearch, } from '../next-searches/hooks'; import { LlmSettingFieldItems, LlmSettingSchema, } from './search-setting-aisummery-config'; interface SearchSettingProps { open: boolean; setOpen: (open: boolean) => void; className?: string; data: ISearchAppDetailProps; } const SearchSettingFormSchema = z .object({ search_id: z.string().optional(), name: z.string().min(1, 'Name is required'), avatar: z.string().optional(), description: z.string().optional(), search_config: z.object({ kb_ids: z.array(z.string()).min(1, 'At least one dataset is required'), vector_similarity_weight: z.number().min(0).max(1), web_search: z.boolean(), similarity_threshold: z.number(), use_kg: z.boolean(), rerank_id: z.string(), use_rerank: z.boolean(), top_k: z.number(), summary: z.boolean(), llm_setting: z.object(LlmSettingSchema), related_search: z.boolean(), query_mindmap: z.boolean(), ...MetadataFilterSchema, }), }) .superRefine((data, ctx) => { if (data.search_config.use_rerank && !data.search_config.rerank_id) { ctx.addIssue({ path: ['search_config', 'rerank_id'], message: 'Rerank model is required when rerank is enabled', code: z.ZodIssueCode.custom, }); } if (data.search_config.summary && !data.search_config.llm_setting?.llm_id) { ctx.addIssue({ path: ['search_config', 'llm_setting', 'llm_id'], message: 'Model is required when AI Summary is enabled', code: z.ZodIssueCode.custom, }); } }); type SearchSettingFormData = z.infer; const SearchSetting: React.FC = ({ open = false, setOpen, className, data, }) => { const [width0, setWidth0] = useState('w-[440px]'); const { search_config } = data || {}; const { llm_setting } = search_config || {}; const formMethods = useForm({ resolver: zodResolver(SearchSettingFormSchema), }); const [datasetList, setDatasetList] = useState([]); const [datasetSelectEmbdId, setDatasetSelectEmbdId] = useState(''); const { t } = useTranslation(); const descriptionDefaultValue = t('search.descriptionValue'); const resetForm = useCallback(() => { formMethods.reset({ search_id: data?.id, name: data?.name || '', avatar: data?.avatar || '', description: data?.description || descriptionDefaultValue, search_config: { kb_ids: search_config?.kb_ids || [], vector_similarity_weight: (search_config?.vector_similarity_weight ? 1 - search_config?.vector_similarity_weight : 0.3) || 0.3, web_search: search_config?.web_search || false, doc_ids: [], similarity_threshold: search_config?.similarity_threshold || 0.2, use_kg: false, rerank_id: search_config?.rerank_id || '', use_rerank: search_config?.rerank_id ? true : false, top_k: search_config?.top_k || 1024, summary: search_config?.summary || false, chat_id: search_config?.chat_id || '', llm_setting: { llm_id: search_config?.chat_id || '', parameter: llm_setting?.parameter, temperature: llm_setting?.temperature || 0, top_p: llm_setting?.top_p || 0, frequency_penalty: llm_setting?.frequency_penalty || 0, presence_penalty: llm_setting?.presence_penalty || 0, temperatureEnabled: llm_setting?.temperature ? true : false, topPEnabled: llm_setting?.top_p ? true : false, presencePenaltyEnabled: llm_setting?.presence_penalty ? true : false, frequencyPenaltyEnabled: llm_setting?.frequency_penalty ? true : false, }, chat_settingcross_languages: [], highlight: false, keyword: false, related_search: search_config?.related_search || false, query_mindmap: search_config?.query_mindmap || false, meta_data_filter: search_config?.meta_data_filter, }, }); }, [data, search_config, llm_setting, formMethods, descriptionDefaultValue]); useEffect(() => { resetForm(); }, [resetForm]); useEffect(() => { if (!open) { setTimeout(() => { setWidth0('w-0 hidden'); }, 500); } else { setWidth0('w-[440px]'); } }, [open]); const { list: datasetListOrigin } = useFetchKnowledgeList(); useEffect(() => { const datasetListMap = datasetListOrigin.map((item: IKnowledge) => { return { label: item.name, suffix: (
{item.embd_id}
), value: item.id, disabled: item.embd_id !== datasetSelectEmbdId && datasetSelectEmbdId !== '', }; }); setDatasetList(datasetListMap); }, [datasetListOrigin, datasetSelectEmbdId]); const handleDatasetSelectChange = ( value: string[], onChange: (value: string[]) => void, ) => { console.log(value); if (value.length) { const data = datasetListOrigin?.find((item) => item.id === value[0]); setDatasetSelectEmbdId(data?.embd_id ?? ''); } else { setDatasetSelectEmbdId(''); } formMethods.setValue('search_config.kb_ids', value); onChange?.(value); }; const allOptions = useSelectLlmOptionsByModelType(); const rerankModelOptions = useMemo(() => { return allOptions[LlmModelType.Rerank]; }, [allOptions]); const aiSummeryModelOptions = useComposeLlmOptionsByModelTypes([ LlmModelType.Chat, LlmModelType.Image2text, ]); const rerankModelDisabled = useWatch({ control: formMethods.control, name: 'search_config.use_rerank', }); const aiSummaryDisabled = useWatch({ control: formMethods.control, name: 'search_config.summary', }); const { updateSearch } = useUpdateSearch(); const [formSubmitLoading, setFormSubmitLoading] = useState(false); const { data: systemSetting } = useFetchTenantInfo(); const onSubmit = async ( formData: IUpdateSearchProps & { tenant_id: string }, ) => { try { setFormSubmitLoading(true); const { search_config, ...other_formdata } = formData; const { llm_setting, vector_similarity_weight, use_rerank, rerank_id, ...other_config } = search_config; const llmSetting = { // llm_id: llm_setting.llm_id, parameter: llm_setting.parameter, temperature: llm_setting.temperature, top_p: llm_setting.top_p, frequency_penalty: llm_setting.frequency_penalty, presence_penalty: llm_setting.presence_penalty, } as IllmSettingProps; await updateSearch({ ...other_formdata, search_config: { ...other_config, chat_id: llm_setting.llm_id, vector_similarity_weight: 1 - vector_similarity_weight, rerank_id: use_rerank ? rerank_id : '', llm_setting: { ...llmSetting }, }, tenant_id: systemSetting.tenant_id, }); setOpen(false); } catch (error) { console.error('Failed to update search:', error); } finally { setFormSubmitLoading(false); } }; return (
{t('search.searchSettings')}
setOpen(false)}>
{ console.log('Form submitted with data:', data); onSubmit(data as unknown as IUpdateSearchProps); }, (errors) => { console.log('Validation errors:', errors); }, )} className="space-y-6" > {/* Name */} ( * {t('search.name')} )} /> {/* Avatar */} ( {t('search.avatar')} )} /> {/* Description */} ( {t('search.description')}