diff --git a/web/src/components/metadata-filter/index.tsx b/web/src/components/metadata-filter/index.tsx new file mode 100644 index 000000000..5559c32fb --- /dev/null +++ b/web/src/components/metadata-filter/index.tsx @@ -0,0 +1,72 @@ +import { DatasetMetadata } from '@/constants/chat'; +import { useTranslate } from '@/hooks/common-hooks'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { z } from 'zod'; +import { SelectWithSearch } from '../originui/select-with-search'; +import { RAGFlowFormItem } from '../ragflow-form'; +import { MetadataFilterConditions } from './metadata-filter-conditions'; + +type MetadataFilterProps = { + prefix?: string; +}; + +export const MetadataFilterSchema = { + meta_data_filter: z + .object({ + method: z.string().optional(), + manual: z + .array( + z.object({ + key: z.string(), + op: z.string(), + value: z.string(), + }), + ) + .optional(), + }) + .optional(), +}; + +export function MetadataFilter({ prefix = '' }: MetadataFilterProps) { + const { t } = useTranslate('chat'); + const form = useFormContext(); + + const methodName = prefix + 'meta_data_filter.method'; + + const kbIds: string[] = useWatch({ + control: form.control, + name: prefix + 'kb_ids', + }); + const metadata = useWatch({ + control: form.control, + name: methodName, + }); + const hasKnowledge = Array.isArray(kbIds) && kbIds.length > 0; + + const MetadataOptions = Object.values(DatasetMetadata).map((x) => { + return { + value: x, + label: t(`meta.${x}`), + }; + }); + + return ( + <> + {hasKnowledge && ( + + + + )} + {hasKnowledge && metadata === DatasetMetadata.Manual && ( + + )} + + ); +} diff --git a/web/src/components/metadata-filter/metadata-filter-conditions.tsx b/web/src/components/metadata-filter/metadata-filter-conditions.tsx new file mode 100644 index 000000000..7aee8c1d8 --- /dev/null +++ b/web/src/components/metadata-filter/metadata-filter-conditions.tsx @@ -0,0 +1,135 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Separator } from '@/components/ui/separator'; +import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request'; +import { SwitchOperatorOptions } from '@/pages/agent/constant'; +import { useBuildSwitchOperatorOptions } from '@/pages/agent/form/switch-form'; +import { Plus, X } from 'lucide-react'; +import { useCallback } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +export function MetadataFilterConditions({ + kbIds, + prefix = '', +}: { + kbIds: string[]; + prefix?: string; +}) { + const { t } = useTranslation(); + const form = useFormContext(); + const name = prefix + 'meta_data_filter.manual'; + const metadata = useFetchKnowledgeMetadata(kbIds); + + const switchOperatorOptions = useBuildSwitchOperatorOptions(); + + const { fields, remove, append } = useFieldArray({ + name, + control: form.control, + }); + + const add = useCallback( + (key: string) => () => { + append({ + key, + value: '', + op: SwitchOperatorOptions[0].value, + }); + }, + [append], + ); + + return ( +
+
+ {t('chat.conditions')} + + + + + + {Object.keys(metadata.data).map((key, idx) => { + return ( + + {key} + + ); + })} + + +
+
+ {fields.map((field, index) => { + const typeField = `${name}.${index}.key`; + return ( +
+ ( + + + + + + + )} + /> + + ( + + + + + + + )} + /> + + ( + + + + + + + )} + /> + +
+ ); + })} +
+
+ ); +} diff --git a/web/src/constants/chat.ts b/web/src/constants/chat.ts index d6777fd72..30da3faff 100644 --- a/web/src/constants/chat.ts +++ b/web/src/constants/chat.ts @@ -32,3 +32,9 @@ export enum ChatSearchParams { } export const EmptyConversationId = 'empty'; + +export enum DatasetMetadata { + Disabled = 'disabled', + Automatic = 'automatic', + Manual = 'manual', +} diff --git a/web/src/layouts/next-header.tsx b/web/src/layouts/next-header.tsx index 73fbf41ae..a75054d14 100644 --- a/web/src/layouts/next-header.tsx +++ b/web/src/layouts/next-header.tsx @@ -75,7 +75,7 @@ export function Header() { const tagsData = useMemo( () => [ { path: Routes.Home, name: t('header.home'), icon: House }, - { path: Routes.Datasets, name: t('header.knowledgeBase'), icon: Library }, + { path: Routes.Datasets, name: t('header.dataset'), icon: Library }, { path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText }, { path: Routes.Searches, name: t('header.search'), icon: Search }, { path: Routes.Agents, name: t('header.flow'), icon: Cpu }, diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 72d43f655..348e63bb0 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -81,6 +81,7 @@ export default { flow: 'Agent', search: 'Search', welcome: 'Welcome to', + dataset: 'Dataset', }, knowledgeList: { welcome: 'Welcome back', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index da53c5209..b13339a0a 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -73,6 +73,7 @@ export default { flow: 'Agent', search: '搜索', welcome: '欢迎来到', + dataset: '数据集', }, knowledgeList: { welcome: '欢迎回来', diff --git a/web/src/pages/agent/form/exesql-form/use-submit-form.ts b/web/src/pages/agent/form/exesql-form/use-submit-form.ts index 6dba7fcc1..8be69c7b0 100644 --- a/web/src/pages/agent/form/exesql-form/use-submit-form.ts +++ b/web/src/pages/agent/form/exesql-form/use-submit-form.ts @@ -3,7 +3,6 @@ import { useCallback } from 'react'; import { z } from 'zod'; export const ExeSQLFormSchema = { - sql: z.string(), db_type: z.string().min(1), database: z.string().min(1), username: z.string().min(1), @@ -14,7 +13,7 @@ export const ExeSQLFormSchema = { }; export const FormSchema = z.object({ - query: z.string().optional(), + sql: z.string().optional(), ...ExeSQLFormSchema, }); diff --git a/web/src/pages/agent/form/tool-form/exesql-form/index.tsx b/web/src/pages/agent/form/tool-form/exesql-form/index.tsx index 55721ef79..0dc43ca5e 100644 --- a/web/src/pages/agent/form/tool-form/exesql-form/index.tsx +++ b/web/src/pages/agent/form/tool-form/exesql-form/index.tsx @@ -25,11 +25,13 @@ const ExeSQLForm = () => { defaultValues: defaultValues as FormType, }); + const onError = (error: any) => console.log(error); + useWatchFormChange(form); return (
- + diff --git a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx index 6aa9177c4..0eb968472 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx @@ -2,8 +2,7 @@ import { FileUploader } from '@/components/file-uploader'; import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { MetadataFilter } from '@/components/metadata-filter'; import { SwitchFormField } from '@/components/switch-fom-field'; import { TavilyFormField } from '@/components/tavily-form-field'; import { @@ -16,26 +15,11 @@ import { import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { useTranslate } from '@/hooks/common-hooks'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { DatasetMetadata } from '../../constants'; -import { MetadataFilterConditions } from './metadata-filter-conditions'; +import { useFormContext } from 'react-hook-form'; export default function ChatBasicSetting() { const { t } = useTranslate('chat'); const form = useFormContext(); - const kbIds: string[] = useWatch({ control: form.control, name: 'kb_ids' }); - const metadata = useWatch({ - control: form.control, - name: 'meta_data_filter.method', - }); - const hasKnowledge = Array.isArray(kbIds) && kbIds.length > 0; - - const MetadataOptions = Object.values(DatasetMetadata).map((x) => { - return { - value: x, - label: t(`meta.${x}`), - }; - }); return (
@@ -125,18 +109,7 @@ export default function ChatBasicSetting() { > - {hasKnowledge && ( - - - - )} - {hasKnowledge && metadata === DatasetMetadata.Manual && ( - - )} +
); } diff --git a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx index 1fc244445..17d68c4a5 100644 --- a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx +++ b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx @@ -2,6 +2,7 @@ import { LlmSettingEnabledSchema, LlmSettingFieldSchema, } from '@/components/llm-setting-items/next'; +import { MetadataFilterSchema } from '@/components/metadata-filter'; import { rerankFormSchema } from '@/components/rerank'; import { vectorSimilarityWeightSchema } from '@/components/similarity-slider'; import { topnSchema } from '@/components/top-n-item'; @@ -46,20 +47,7 @@ export function useChatSettingSchema() { llm_id: z.string().optional(), ...vectorSimilarityWeightSchema, ...topnSchema, - meta_data_filter: z - .object({ - method: z.string().optional(), - manual: z - .array( - z.object({ - key: z.string(), - op: z.string(), - value: z.string(), - }), - ) - .optional(), - }) - .optional(), + ...MetadataFilterSchema, }); return formSchema; diff --git a/web/src/pages/next-chats/hooks/use-build-form-refs.ts b/web/src/pages/next-chats/hooks/use-build-form-refs.ts index c983c5d95..89ae46d2a 100644 --- a/web/src/pages/next-chats/hooks/use-build-form-refs.ts +++ b/web/src/pages/next-chats/hooks/use-build-form-refs.ts @@ -1,3 +1,4 @@ +import { removeUselessFieldsFromValues } from '@/utils/form'; import { isEmpty } from 'lodash'; import { useCallback, useEffect, useRef } from 'react'; @@ -23,7 +24,7 @@ export function useBuildFormRefs(chatBoxIds: string[]) { ? formRefs.current[chatBoxId].getFormData() : {}; - return llmConfig; + return removeUselessFieldsFromValues(llmConfig, ''); }, [formRefs], ); diff --git a/web/src/pages/next-chats/hooks/use-rename-chat.ts b/web/src/pages/next-chats/hooks/use-rename-chat.ts index 19a6b12c5..a0d87cb77 100644 --- a/web/src/pages/next-chats/hooks/use-rename-chat.ts +++ b/web/src/pages/next-chats/hooks/use-rename-chat.ts @@ -2,31 +2,8 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useSetDialog } from '@/hooks/use-chat-request'; import { IDialog } from '@/interfaces/database/chat'; import { isEmpty } from 'lodash'; -import { useCallback, useState } from 'react'; - -const InitialData = { - name: '', - icon: '', - language: 'English', - prompt_config: { - empty_response: '', - prologue: '你好! 我是你的助理,有什么可以帮到你的吗?', - quote: true, - keyword: false, - tts: false, - system: - '你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。\n 以下是知识库:\n {knowledge}\n 以上是知识库。', - refine_multiturn: false, - use_kg: false, - reasoning: false, - parameters: [{ key: 'knowledge', optional: false }], - }, - llm_id: '', - llm_setting: {}, - similarity_threshold: 0.2, - vector_similarity_weight: 0.30000000000000004, - top_n: 8, -}; +import { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; export const useRenameChat = () => { const [chat, setChat] = useState({} as IDialog); @@ -36,6 +13,33 @@ export const useRenameChat = () => { showModal: showChatRenameModal, } = useSetModalState(); const { setDialog, loading } = useSetDialog(); + const { t } = useTranslation(); + + const InitialData = useMemo( + () => ({ + name: '', + icon: '', + language: 'English', + prompt_config: { + empty_response: '', + prologue: t('chat.setAnOpenerInitial'), + quote: true, + keyword: false, + tts: false, + system: t('chat.systemInitialValue'), + refine_multiturn: false, + use_kg: false, + reasoning: false, + parameters: [{ key: 'knowledge', optional: false }], + }, + llm_id: '', + llm_setting: {}, + similarity_threshold: 0.2, + vector_similarity_weight: 0.30000000000000004, + top_n: 8, + }), + [t], + ); const onChatRenameOk = useCallback( async (name: string) => { @@ -49,7 +53,7 @@ export const useRenameChat = () => { hideChatRenameModal(); } }, - [setDialog, chat, hideChatRenameModal], + [chat, InitialData, setDialog, hideChatRenameModal], ); const handleShowChatRenameModal = useCallback( diff --git a/web/src/pages/next-search/search-setting.tsx b/web/src/pages/next-search/search-setting.tsx index 5e335e438..6a93273f3 100644 --- a/web/src/pages/next-search/search-setting.tsx +++ b/web/src/pages/next-search/search-setting.tsx @@ -1,5 +1,9 @@ // src/pages/next-search/search-setting.tsx +import { + MetadataFilter, + MetadataFilterSchema, +} from '@/components/metadata-filter'; import { Input } from '@/components/originui/input'; import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { Button } from '@/components/ui/button'; @@ -76,6 +80,7 @@ const SearchSettingFormSchema = z llm_setting: z.object(LlmSettingSchema), related_search: z.boolean(), query_mindmap: z.boolean(), + ...MetadataFilterSchema, }), }) .superRefine((data, ctx) => { @@ -165,6 +170,7 @@ const SearchSetting: React.FC = ({ 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]); @@ -346,7 +352,6 @@ const SearchSetting: React.FC = ({ )} /> - {/* Avatar */} = ({ )} /> - {/* Description */} = ({ )} /> - {/* Datasets */} = ({ )} /> + = ({ )} /> - {/* Rerank Model */} = ({ /> )} - {/* AI Summary */} = ({ )} /> - {aiSummaryDisabled && ( )} - {/* Feature Controls */} {/* = ({ )} /> */} - = ({ )} /> -