diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index 7503b5eaf..2c279c59e 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -6,7 +6,7 @@ import { } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; // import ChatDrawer from '../chat/drawer'; -import FormDrawer from '../form-drawer'; +import FormDrawer from '../form-drawer/next'; import { useHandleDrop, useSelectCanvasData, diff --git a/web/src/pages/agent/form-drawer/index.tsx b/web/src/pages/agent/form-drawer/index.tsx index 40afa61f7..e48d69102 100644 --- a/web/src/pages/agent/form-drawer/index.tsx +++ b/web/src/pages/agent/form-drawer/index.tsx @@ -1,7 +1,6 @@ import { useTranslate } from '@/hooks/common-hooks'; import { IModalProps } from '@/interfaces/common'; -import { CloseOutlined } from '@ant-design/icons'; -import { Drawer, Flex, Form, Input } from 'antd'; +import { Flex, Form, Input } from 'antd'; import { get, isPlainObject, lowerFirst } from 'lodash'; import { Play } from 'lucide-react'; import { useEffect, useRef } from 'react'; @@ -30,7 +29,7 @@ import MessageForm from '../form/message-form'; import PubMedForm from '../form/pubmed-form'; import QWeatherForm from '../form/qweather-form'; import RelevantForm from '../form/relevant-form'; -import RetrievalForm from '../form/retrieval-form'; +import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; import SwitchForm from '../form/switch-form'; import TemplateForm from '../form/template-form'; @@ -42,15 +41,15 @@ import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; import OperatorIcon from '../operator-icon'; import { buildCategorizeListFromObject, - getDrawerWidth, needsSingleStepDebugging, } from '../utils'; -import SingleDebugDrawer from './single-debug-drawer'; +import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { FlowFormContext } from '../context'; import { RunTooltip } from '../flow-tooltip'; import IterationForm from '../form/iteration-from'; + import styles from './index.less'; interface IProps { @@ -144,72 +143,65 @@ const FormDrawer = ({ }, [visible, form, node?.data?.form, node?.id, node, operatorName]); return ( - - - - - - {node?.id === BeginId ? ( - {t(BeginId)} - ) : ( - - )} - + + + + + + + + + {node?.id === BeginId ? ( + {t(BeginId)} + ) : ( + + )} + - {needsSingleStepDebugging(operatorName) && ( - - - - )} - + {needsSingleStepDebugging(operatorName) && ( + + + + )} + {/* */} + + + {t(`${lowerFirst(operatorName)}Description`)} + - - {t(`${lowerFirst(operatorName)}Description`)} - - - } - placement="right" - onClose={hideModal} - open={visible} - getContainer={false} - mask={false} - width={getDrawerWidth()} - closeIcon={null} - rootClassName={styles.formDrawer} - > -
- {visible && ( - - - - )} -
- {singleDebugDrawerVisible && ( + +
+ {visible && ( + + + + )} +
+ + {/* {singleDebugDrawerVisible && ( - )} -
+ )} */} + ); }; diff --git a/web/src/pages/agent/form-drawer/next.tsx b/web/src/pages/agent/form-drawer/next.tsx new file mode 100644 index 000000000..35f236840 --- /dev/null +++ b/web/src/pages/agent/form-drawer/next.tsx @@ -0,0 +1,151 @@ +import { useTranslate } from '@/hooks/common-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { CloseOutlined } from '@ant-design/icons'; +import { Flex, Input } from 'antd'; +import { get, isPlainObject, lowerFirst } from 'lodash'; +import { Play } from 'lucide-react'; +import { useEffect, useRef } from 'react'; +import { BeginId, Operator, operatorMap } from '../constant'; +import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; +import OperatorIcon from '../operator-icon'; +import { + buildCategorizeListFromObject, + needsSingleStepDebugging, +} from '../utils'; +import SingleDebugDrawer from './single-debug-drawer'; + +import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { FlowFormContext } from '../context'; +import { RunTooltip } from '../flow-tooltip'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useFormConfigMap } from './use-form-config-map'; + +interface IProps { + node?: RAGFlowNodeType; + singleDebugDrawerVisible: IModalProps['visible']; + hideSingleDebugDrawer: IModalProps['hideModal']; + showSingleDebugDrawer: IModalProps['showModal']; +} + +const EmptyContent = () =>
; + +const FormDrawer = ({ + visible, + hideModal, + node, + singleDebugDrawerVisible, + hideSingleDebugDrawer, + showSingleDebugDrawer, +}: IModalProps & IProps) => { + const operatorName: Operator = node?.data.label as Operator; + + const FormConfigMap = useFormConfigMap(); + + const currentFormMap = FormConfigMap[operatorName]; + + const OperatorForm = currentFormMap.component ?? EmptyContent; + + const form = useForm({ + defaultValues: currentFormMap.defaultValues, + resolver: zodResolver(currentFormMap.schema), + }); + + const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ + id: node?.id, + data: node?.data, + }); + + const previousId = useRef(node?.id); + + const { t } = useTranslate('flow'); + + const { handleValuesChange } = useHandleFormValuesChange(node?.id); + + useEffect(() => { + if (visible) { + if (node?.id !== previousId.current) { + // form.resetFields(); + form.reset(); + form.clearErrors(); + } + + if (operatorName === Operator.Categorize) { + const items = buildCategorizeListFromObject( + get(node, 'data.form.category_description', {}), + ); + const formData = node?.data?.form; + if (isPlainObject(formData)) { + // form.setFieldsValue({ ...formData, items }); + form.reset({ ...formData, items }); + } + } else { + // form.setFieldsValue(node?.data?.form); + form.reset(node?.data?.form); + } + previousId.current = node?.id; + } + }, [visible, form, node?.data?.form, node?.id, node, operatorName]); + + return ( + + + + + + + + + {node?.id === BeginId ? ( + {t(BeginId)} + ) : ( + + )} + + + {needsSingleStepDebugging(operatorName) && ( + + + + )} + + + {t(`${lowerFirst(operatorName)}Description`)} + + +
+ {visible && ( + + + + )} +
+
+ {singleDebugDrawerVisible && ( + + )} +
+ ); +}; + +export default FormDrawer; diff --git a/web/src/pages/agent/form-drawer/use-form-config-map.tsx b/web/src/pages/agent/form-drawer/use-form-config-map.tsx new file mode 100644 index 000000000..240a6a761 --- /dev/null +++ b/web/src/pages/agent/form-drawer/use-form-config-map.tsx @@ -0,0 +1,256 @@ +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { Operator } from '../constant'; +import AkShareForm from '../form/akshare-form'; +import AnswerForm from '../form/answer-form'; +import ArXivForm from '../form/arxiv-form'; +import BaiduFanyiForm from '../form/baidu-fanyi-form'; +import BaiduForm from '../form/baidu-form'; +import BeginForm from '../form/begin-form'; +import BingForm from '../form/bing-form'; +import CategorizeForm from '../form/categorize-form'; +import CrawlerForm from '../form/crawler-form'; +import DeepLForm from '../form/deepl-form'; +import DuckDuckGoForm from '../form/duckduckgo-form'; +import EmailForm from '../form/email-form'; +import ExeSQLForm from '../form/exesql-form'; +import GenerateForm from '../form/generate-form'; +import GithubForm from '../form/github-form'; +import GoogleForm from '../form/google-form'; +import GoogleScholarForm from '../form/google-scholar-form'; +import InvokeForm from '../form/invoke-form'; +import IterationForm from '../form/iteration-from'; +import Jin10Form from '../form/jin10-form'; +import KeywordExtractForm from '../form/keyword-extract-form'; +import MessageForm from '../form/message-form'; +import PubMedForm from '../form/pubmed-form'; +import QWeatherForm from '../form/qweather-form'; +import RelevantForm from '../form/relevant-form'; +import RetrievalForm from '../form/retrieval-form/next'; +import RewriteQuestionForm from '../form/rewrite-question-form'; +import SwitchForm from '../form/switch-form'; +import TemplateForm from '../form/template-form'; +import TuShareForm from '../form/tushare-form'; +import WenCaiForm from '../form/wencai-form'; +import WikipediaForm from '../form/wikipedia-form'; +import YahooFinanceForm from '../form/yahoo-finance-form'; + +export function useFormConfigMap() { + const { t } = useTranslation(); + + const FormConfigMap = { + [Operator.Begin]: { + component: BeginForm, + defaultValues: {}, + schema: z.object({ + name: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + age: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + }), + }, + [Operator.Retrieval]: { + component: RetrievalForm, + defaultValues: { query: [] }, + schema: z.object({ + name: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + age: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + query: z.array( + z.object({ + type: z.string(), + }), + ), + }), + }, + [Operator.Generate]: { + component: GenerateForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Answer]: { + component: AnswerForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Categorize]: { + component: CategorizeForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Message]: { + component: MessageForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Relevant]: { + component: RelevantForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.RewriteQuestion]: { + component: RewriteQuestionForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Baidu]: { + component: BaiduForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.DuckDuckGo]: { + component: DuckDuckGoForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.KeywordExtract]: { + component: KeywordExtractForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Wikipedia]: { + component: WikipediaForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.PubMed]: { + component: PubMedForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.ArXiv]: { + component: ArXivForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Google]: { + component: GoogleForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Bing]: { + component: BingForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.GoogleScholar]: { + component: GoogleScholarForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.DeepL]: { + component: DeepLForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.GitHub]: { + component: GithubForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.BaiduFanyi]: { + component: BaiduFanyiForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.QWeather]: { + component: QWeatherForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.ExeSQL]: { + component: ExeSQLForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Switch]: { + component: SwitchForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.WenCai]: { + component: WenCaiForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.AkShare]: { + component: AkShareForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.YahooFinance]: { + component: YahooFinanceForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Jin10]: { + component: Jin10Form, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.TuShare]: { + component: TuShareForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Crawler]: { + component: CrawlerForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Invoke]: { + component: InvokeForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Concentrator]: { + component: () => <>, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Note]: { + component: () => <>, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Template]: { + component: TemplateForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Email]: { + component: EmailForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.Iteration]: { + component: IterationForm, + defaultValues: {}, + schema: z.object({}), + }, + [Operator.IterationStart]: { + component: () => <>, + defaultValues: {}, + schema: z.object({}), + }, + }; + + return FormConfigMap; +} diff --git a/web/src/pages/agent/form/components/next-dynamic-input-variable.tsx b/web/src/pages/agent/form/components/next-dynamic-input-variable.tsx new file mode 100644 index 000000000..0c0031c81 --- /dev/null +++ b/web/src/pages/agent/form/components/next-dynamic-input-variable.tsx @@ -0,0 +1,109 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { CircleMinus, Plus } from 'lucide-react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query'; + +interface IProps { + node?: RAGFlowNodeType; +} + +enum VariableType { + Reference = 'reference', + Input = 'input', +} + +const getVariableName = (type: string) => + type === VariableType.Reference ? 'component_id' : 'value'; + +export function DynamicVariableForm({ node }: IProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const { fields, remove, append } = useFieldArray({ + name: 'query', + control: form.control, + }); + + const valueOptions = useBuildComponentIdSelectOptions( + node?.id, + node?.parentId, + ); + + const options = [ + { value: VariableType.Reference, label: t('flow.reference') }, + { value: VariableType.Input, label: t('flow.text') }, + ]; + + return ( +
+ {fields.map((field, index) => { + const typeField = `query.${index}.type`; + const typeValue = form.watch(typeField); + return ( +
+ ( + + {/* City */} + + + + + + + )} + /> + ( + + {/* State */} + + + {typeValue === VariableType.Reference ? ( + + ) : ( + + )} + + + + )} + /> + remove(index)} + /> +
+ ); + })} + +
+ ); +} diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx new file mode 100644 index 000000000..c387feb38 --- /dev/null +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -0,0 +1,49 @@ +import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; +import { RerankFormFields } from '@/components/rerank'; +import { SimilaritySliderFormField } from '@/components/similarity-slider'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; +import { useTranslate } from '@/hooks/common-hooks'; +import { INextOperatorForm } from '../../interface'; +import { DynamicVariableForm } from '../components/next-dynamic-input-variable'; + +const RetrievalForm = ({ form, node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + return ( +
+ + + + + + ( + + {t('chat.emptyResponse')} + +