### What problem does this PR solve? Feat: Render DynamicCategorize with shadcn-ui. #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.1
| @@ -0,0 +1,31 @@ | |||
| import { | |||
| FormControl, | |||
| FormField, | |||
| FormItem, | |||
| FormLabel, | |||
| FormMessage, | |||
| } from '@/components/ui/form'; | |||
| import { useFormContext } from 'react-hook-form'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { NextLLMSelect } from './llm-select'; | |||
| export function LargeModelFormField() { | |||
| const form = useFormContext(); | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <FormField | |||
| control={form.control} | |||
| name="llm_id" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel tooltip={t('chat.modelTip')}>{t('chat.model')}</FormLabel> | |||
| <FormControl> | |||
| <NextLLMSelect {...field} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| ); | |||
| } | |||
| @@ -1,208 +0,0 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { Flex, Form, 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 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 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'; | |||
| import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; | |||
| import OperatorIcon from '../operator-icon'; | |||
| import { | |||
| buildCategorizeListFromObject, | |||
| needsSingleStepDebugging, | |||
| } from '../utils'; | |||
| 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 { | |||
| node?: RAGFlowNodeType; | |||
| singleDebugDrawerVisible: IModalProps<any>['visible']; | |||
| hideSingleDebugDrawer: IModalProps<any>['hideModal']; | |||
| showSingleDebugDrawer: IModalProps<any>['showModal']; | |||
| } | |||
| const FormMap = { | |||
| [Operator.Begin]: BeginForm, | |||
| [Operator.Retrieval]: RetrievalForm, | |||
| [Operator.Generate]: GenerateForm, | |||
| [Operator.Answer]: AnswerForm, | |||
| [Operator.Categorize]: CategorizeForm, | |||
| [Operator.Message]: MessageForm, | |||
| [Operator.Relevant]: RelevantForm, | |||
| [Operator.RewriteQuestion]: RewriteQuestionForm, | |||
| [Operator.Baidu]: BaiduForm, | |||
| [Operator.DuckDuckGo]: DuckDuckGoForm, | |||
| [Operator.KeywordExtract]: KeywordExtractForm, | |||
| [Operator.Wikipedia]: WikipediaForm, | |||
| [Operator.PubMed]: PubMedForm, | |||
| [Operator.ArXiv]: ArXivForm, | |||
| [Operator.Google]: GoogleForm, | |||
| [Operator.Bing]: BingForm, | |||
| [Operator.GoogleScholar]: GoogleScholarForm, | |||
| [Operator.DeepL]: DeepLForm, | |||
| [Operator.GitHub]: GithubForm, | |||
| [Operator.BaiduFanyi]: BaiduFanyiForm, | |||
| [Operator.QWeather]: QWeatherForm, | |||
| [Operator.ExeSQL]: ExeSQLForm, | |||
| [Operator.Switch]: SwitchForm, | |||
| [Operator.WenCai]: WenCaiForm, | |||
| [Operator.AkShare]: AkShareForm, | |||
| [Operator.YahooFinance]: YahooFinanceForm, | |||
| [Operator.Jin10]: Jin10Form, | |||
| [Operator.TuShare]: TuShareForm, | |||
| [Operator.Crawler]: CrawlerForm, | |||
| [Operator.Invoke]: InvokeForm, | |||
| [Operator.Concentrator]: () => <></>, | |||
| [Operator.Note]: () => <></>, | |||
| [Operator.Template]: TemplateForm, | |||
| [Operator.Email]: EmailForm, | |||
| [Operator.Iteration]: IterationForm, | |||
| [Operator.IterationStart]: () => <></>, | |||
| }; | |||
| const EmptyContent = () => <div></div>; | |||
| const FormDrawer = ({ | |||
| visible, | |||
| hideModal, | |||
| node, | |||
| singleDebugDrawerVisible, | |||
| hideSingleDebugDrawer, | |||
| showSingleDebugDrawer, | |||
| }: IModalProps<any> & IProps) => { | |||
| const operatorName: Operator = node?.data.label as Operator; | |||
| const OperatorForm = FormMap[operatorName] ?? EmptyContent; | |||
| const [form] = Form.useForm(); | |||
| const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ | |||
| id: node?.id, | |||
| data: node?.data, | |||
| }); | |||
| const previousId = useRef<string | undefined>(node?.id); | |||
| const { t } = useTranslate('flow'); | |||
| const { handleValuesChange } = useHandleFormValuesChange(node?.id); | |||
| useEffect(() => { | |||
| if (visible) { | |||
| if (node?.id !== previousId.current) { | |||
| form.resetFields(); | |||
| } | |||
| 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 }); | |||
| } | |||
| } else { | |||
| form.setFieldsValue(node?.data?.form); | |||
| } | |||
| previousId.current = node?.id; | |||
| } | |||
| }, [visible, form, node?.data?.form, node?.id, node, operatorName]); | |||
| return ( | |||
| <Sheet onOpenChange={hideModal} open={visible}> | |||
| <SheetContent> | |||
| <SheetHeader> | |||
| <Flex vertical> | |||
| <Flex gap={'middle'} align="center"> | |||
| <OperatorIcon | |||
| name={operatorName} | |||
| color={operatorMap[operatorName]?.color} | |||
| ></OperatorIcon> | |||
| <Flex align="center" gap={'small'} flex={1}> | |||
| <label htmlFor="" className={styles.title}> | |||
| {t('title')} | |||
| </label> | |||
| {node?.id === BeginId ? ( | |||
| <span>{t(BeginId)}</span> | |||
| ) : ( | |||
| <Input | |||
| value={name} | |||
| onBlur={handleNameBlur} | |||
| onChange={handleNameChange} | |||
| ></Input> | |||
| )} | |||
| </Flex> | |||
| {needsSingleStepDebugging(operatorName) && ( | |||
| <RunTooltip> | |||
| <Play | |||
| className="size-5 cursor-pointer" | |||
| onClick={showSingleDebugDrawer} | |||
| /> | |||
| </RunTooltip> | |||
| )} | |||
| {/* <CloseOutlined onClick={hideModal} /> */} | |||
| </Flex> | |||
| <span className={styles.operatorDescription}> | |||
| {t(`${lowerFirst(operatorName)}Description`)} | |||
| </span> | |||
| </Flex> | |||
| </SheetHeader> | |||
| <section className={styles.formWrapper}> | |||
| {visible && ( | |||
| <FlowFormContext.Provider value={node}> | |||
| <OperatorForm | |||
| onValuesChange={handleValuesChange} | |||
| form={form} | |||
| node={node} | |||
| ></OperatorForm> | |||
| </FlowFormContext.Provider> | |||
| )} | |||
| </section> | |||
| </SheetContent> | |||
| {/* {singleDebugDrawerVisible && ( | |||
| <SingleDebugDrawer | |||
| visible={singleDebugDrawerVisible} | |||
| hideModal={hideSingleDebugDrawer} | |||
| componentId={node?.id} | |||
| ></SingleDebugDrawer> | |||
| )} */} | |||
| </Sheet> | |||
| ); | |||
| }; | |||
| export default FormDrawer; | |||
| @@ -8,6 +8,7 @@ import { | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { RAGFlowNodeType } from '@/interfaces/database/flow'; | |||
| import { cn } from '@/lib/utils'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { get, isPlainObject, lowerFirst } from 'lodash'; | |||
| import { Play, X } from 'lucide-react'; | |||
| @@ -94,9 +95,9 @@ const FormSheet = ({ | |||
| return ( | |||
| <Sheet open={visible} modal={false}> | |||
| <SheetTitle className="hidden"></SheetTitle> | |||
| <SheetContent className="bg-white top-20" closeIcon={false}> | |||
| <SheetContent className={cn('bg-white top-20 p-0')} closeIcon={false}> | |||
| <SheetHeader> | |||
| <section className="flex-col border-b pb-2"> | |||
| <section className="flex-col border-b py-2 px-5"> | |||
| <div className="flex items-center gap-2 pb-3"> | |||
| <OperatorIcon | |||
| name={operatorName} | |||
| @@ -99,8 +99,15 @@ export function useFormConfigMap() { | |||
| }, | |||
| [Operator.Categorize]: { | |||
| component: CategorizeForm, | |||
| defaultValues: {}, | |||
| schema: z.object({}), | |||
| defaultValues: { message_history_window_size: 1 }, | |||
| schema: z.object({ | |||
| message_history_window_size: z.number(), | |||
| items: z.array( | |||
| z.object({ | |||
| name: z.string().min(1, t('flow.nameMessage')).trim(), | |||
| }), | |||
| ), | |||
| }), | |||
| }, | |||
| [Operator.Message]: { | |||
| component: MessageForm, | |||
| @@ -1,18 +1,25 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Collapsible, | |||
| CollapsibleContent, | |||
| CollapsibleTrigger, | |||
| } from '@/components/ui/collapsible'; | |||
| import { | |||
| FormControl, | |||
| FormField, | |||
| FormItem, | |||
| FormLabel, | |||
| FormMessage, | |||
| } from '@/components/ui/form'; | |||
| import { Input } from '@/components/ui/input'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { CloseOutlined, PlusOutlined } from '@ant-design/icons'; | |||
| import { PlusOutlined } from '@ant-design/icons'; | |||
| import { useUpdateNodeInternals } from '@xyflow/react'; | |||
| import { | |||
| Button, | |||
| Collapse, | |||
| Flex, | |||
| Form, | |||
| FormListFieldData, | |||
| Input, | |||
| Select, | |||
| } from 'antd'; | |||
| import { FormInstance } from 'antd/lib'; | |||
| import { humanId } from 'human-id'; | |||
| import humanId from 'human-id'; | |||
| import trim from 'lodash/trim'; | |||
| import { ChevronsUpDown, X } from 'lucide-react'; | |||
| import { | |||
| ChangeEventHandler, | |||
| FocusEventHandler, | |||
| @@ -20,11 +27,10 @@ import { | |||
| useEffect, | |||
| useState, | |||
| } from 'react'; | |||
| import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form'; | |||
| import { Operator } from '../../constant'; | |||
| import { useBuildFormSelectOptions } from '../../form-hooks'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| nodeId?: string; | |||
| } | |||
| @@ -33,20 +39,20 @@ interface INameInputProps { | |||
| value?: string; | |||
| onChange?: (value: string) => void; | |||
| otherNames?: string[]; | |||
| validate(errors: string[]): void; | |||
| validate(error?: string): void; | |||
| } | |||
| const getOtherFieldValues = ( | |||
| form: FormInstance, | |||
| form: UseFormReturn, | |||
| formListName: string = 'items', | |||
| field: FormListFieldData, | |||
| index: number, | |||
| latestField: string, | |||
| ) => | |||
| (form.getFieldValue([formListName]) ?? []) | |||
| (form.getValues(formListName) ?? []) | |||
| .map((x: any) => x[latestField]) | |||
| .filter( | |||
| (x: string) => | |||
| x !== form.getFieldValue([formListName, field.name, latestField]), | |||
| x !== form.getValues(`${formListName}.${index}.${latestField}`), | |||
| ); | |||
| const NameInput = ({ | |||
| @@ -61,15 +67,16 @@ const NameInput = ({ | |||
| const handleNameChange: ChangeEventHandler<HTMLInputElement> = useCallback( | |||
| (e) => { | |||
| const val = e.target.value; | |||
| setName(val); | |||
| const trimmedVal = trim(val); | |||
| // trigger validation | |||
| if (otherNames?.some((x) => x === val)) { | |||
| validate([t('nameRepeatedMsg')]); | |||
| } else if (trim(val) === '') { | |||
| validate([t('nameRequiredMsg')]); | |||
| if (otherNames?.some((x) => x === trimmedVal)) { | |||
| validate(t('nameRepeatedMsg')); | |||
| } else if (trimmedVal === '') { | |||
| validate(t('nameRequiredMsg')); | |||
| } else { | |||
| validate([]); | |||
| validate(''); | |||
| } | |||
| setName(val); | |||
| }, | |||
| [otherNames, validate, t], | |||
| ); | |||
| @@ -97,128 +104,161 @@ const NameInput = ({ | |||
| ); | |||
| }; | |||
| const FormSet = ({ nodeId, field }: IProps & { field: FormListFieldData }) => { | |||
| const form = Form.useFormInstance(); | |||
| const FormSet = ({ nodeId, index }: IProps & { index: number }) => { | |||
| const form = useFormContext(); | |||
| const { t } = useTranslate('flow'); | |||
| const buildCategorizeToOptions = useBuildFormSelectOptions( | |||
| Operator.Categorize, | |||
| nodeId, | |||
| ); | |||
| const buildFieldName = useCallback( | |||
| (name: string) => { | |||
| return `items.${index}.${name}`; | |||
| }, | |||
| [index], | |||
| ); | |||
| return ( | |||
| <section> | |||
| <Form.Item | |||
| label={t('categoryName')} | |||
| name={[field.name, 'name']} | |||
| validateTrigger={['onChange', 'onBlur']} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| whitespace: true, | |||
| message: t('nameMessage'), | |||
| }, | |||
| ]} | |||
| > | |||
| <NameInput | |||
| otherNames={getOtherFieldValues(form, 'items', field, 'name')} | |||
| validate={(errors: string[]) => | |||
| form.setFields([ | |||
| { | |||
| name: ['items', field.name, 'name'], | |||
| errors, | |||
| }, | |||
| ]) | |||
| } | |||
| ></NameInput> | |||
| </Form.Item> | |||
| <Form.Item label={t('description')} name={[field.name, 'description']}> | |||
| <Input.TextArea rows={3} /> | |||
| </Form.Item> | |||
| <Form.Item label={t('examples')} name={[field.name, 'examples']}> | |||
| <Input.TextArea rows={3} /> | |||
| </Form.Item> | |||
| <Form.Item label={t('nextStep')} name={[field.name, 'to']}> | |||
| <Select | |||
| allowClear | |||
| options={buildCategorizeToOptions( | |||
| getOtherFieldValues(form, 'items', field, 'to'), | |||
| )} | |||
| /> | |||
| </Form.Item> | |||
| <Form.Item hidden name={[field.name, 'index']}> | |||
| <Input /> | |||
| </Form.Item> | |||
| <section className="space-y-4"> | |||
| <FormField | |||
| control={form.control} | |||
| name={buildFieldName('name')} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('categoryName')}</FormLabel> | |||
| <FormControl> | |||
| <NameInput | |||
| {...field} | |||
| otherNames={getOtherFieldValues(form, 'items', index, 'name')} | |||
| validate={(error?: string) => { | |||
| const fieldName = buildFieldName('name'); | |||
| if (error) { | |||
| form.setError(fieldName, { message: error }); | |||
| } else { | |||
| form.clearErrors(fieldName); | |||
| } | |||
| }} | |||
| ></NameInput> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name={buildFieldName('description')} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('description')}</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field} rows={3} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name={buildFieldName('examples')} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('examples')}</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field} rows={3} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name={buildFieldName('to')} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('nextStep')}</FormLabel> | |||
| <FormControl> | |||
| <RAGFlowSelect | |||
| {...field} | |||
| allowClear | |||
| options={buildCategorizeToOptions( | |||
| getOtherFieldValues(form, 'items', index, 'to'), | |||
| )} | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name="index" | |||
| render={({ field }) => ( | |||
| <FormItem className="hidden"> | |||
| <FormLabel>{t('examples')}</FormLabel> | |||
| <FormControl> | |||
| <Input {...field} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| </section> | |||
| ); | |||
| }; | |||
| const DynamicCategorize = ({ nodeId }: IProps) => { | |||
| const updateNodeInternals = useUpdateNodeInternals(); | |||
| const form = Form.useFormInstance(); | |||
| const form = useFormContext(); | |||
| const { t } = useTranslate('flow'); | |||
| const { fields, remove, append } = useFieldArray({ | |||
| name: 'items', | |||
| control: form.control, | |||
| }); | |||
| const handleAdd = () => { | |||
| append({ | |||
| name: humanId(), | |||
| }); | |||
| if (nodeId) updateNodeInternals(nodeId); | |||
| }; | |||
| return ( | |||
| <> | |||
| <Form.List name="items"> | |||
| {(fields, { add, remove }) => { | |||
| const handleAdd = () => { | |||
| const idx = form.getFieldValue([ | |||
| 'items', | |||
| fields.at(-1)?.name, | |||
| 'index', | |||
| ]); | |||
| add({ | |||
| name: humanId(), | |||
| index: fields.length === 0 ? 0 : idx + 1, | |||
| }); | |||
| if (nodeId) updateNodeInternals(nodeId); | |||
| }; | |||
| return ( | |||
| <Flex gap={18} vertical> | |||
| {fields.map((field) => ( | |||
| <Collapse | |||
| size="small" | |||
| key={field.key} | |||
| className={styles.caseCard} | |||
| items={[ | |||
| { | |||
| key: field.key, | |||
| label: ( | |||
| <div className="flex justify-between"> | |||
| <span> | |||
| {form.getFieldValue(['items', field.name, 'name'])} | |||
| </span> | |||
| <CloseOutlined | |||
| onClick={() => { | |||
| remove(field.name); | |||
| }} | |||
| /> | |||
| </div> | |||
| ), | |||
| children: ( | |||
| <FormSet nodeId={nodeId} field={field}></FormSet> | |||
| ), | |||
| }, | |||
| ]} | |||
| ></Collapse> | |||
| ))} | |||
| <Button | |||
| type="dashed" | |||
| onClick={handleAdd} | |||
| block | |||
| className={styles.addButton} | |||
| icon={<PlusOutlined />} | |||
| > | |||
| {t('addCategory')} | |||
| </Button> | |||
| </Flex> | |||
| ); | |||
| }} | |||
| </Form.List> | |||
| </> | |||
| <div className="flex flex-col gap-4 "> | |||
| {fields.map((field, index) => ( | |||
| <Collapsible key={field.id}> | |||
| <div className="flex items-center justify-between space-x-4"> | |||
| <h4 className="font-bold"> | |||
| {form.getValues(`items.${index}.name`)} | |||
| </h4> | |||
| <CollapsibleTrigger asChild> | |||
| <div className="flex gap-4"> | |||
| <Button | |||
| variant="ghost" | |||
| size="sm" | |||
| className="w-9 p-0" | |||
| onClick={() => remove(index)} | |||
| > | |||
| <X className="h-4 w-4" /> | |||
| </Button> | |||
| <Button variant="ghost" size="sm" className="w-9 p-0"> | |||
| <ChevronsUpDown className="h-4 w-4" /> | |||
| <span className="sr-only">Toggle</span> | |||
| </Button> | |||
| </div> | |||
| </CollapsibleTrigger> | |||
| </div> | |||
| <CollapsibleContent> | |||
| <FormSet nodeId={nodeId} index={index}></FormSet> | |||
| </CollapsibleContent> | |||
| </Collapsible> | |||
| ))} | |||
| <Button type={'button'} onClick={handleAdd}> | |||
| <PlusOutlined /> | |||
| {t('addCategory')} | |||
| </Button> | |||
| </div> | |||
| ); | |||
| }; | |||
| @@ -1,13 +0,0 @@ | |||
| @lightBackgroundColor: rgba(150, 150, 150, 0.07); | |||
| @darkBackgroundColor: rgba(150, 150, 150, 0.12); | |||
| .caseCard { | |||
| :global(.ant-collapse-content) { | |||
| background-color: @darkBackgroundColor; | |||
| } | |||
| } | |||
| .addButton { | |||
| color: rgb(22, 119, 255); | |||
| font-weight: 600; | |||
| } | |||
| @@ -1,41 +1,33 @@ | |||
| import LLMSelect from '@/components/llm-select'; | |||
| import MessageHistoryWindowSizeItem from '@/components/message-history-window-size-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form } from 'antd'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| import { LargeModelFormField } from '@/components/large-model-form-field'; | |||
| import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; | |||
| import { Form } from '@/components/ui/form'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; | |||
| import DynamicCategorize from './dynamic-categorize'; | |||
| import { useHandleFormValuesChange } from './hooks'; | |||
| const CategorizeForm = ({ form, onValuesChange, node }: IOperatorForm) => { | |||
| const { t } = useTranslate('flow'); | |||
| const { handleValuesChange } = useHandleFormValuesChange({ | |||
| form, | |||
| nodeId: node?.id, | |||
| onValuesChange, | |||
| }); | |||
| const CategorizeForm = ({ form, node }: INextOperatorForm) => { | |||
| // const { handleValuesChange } = useHandleFormValuesChange({ | |||
| // form, | |||
| // nodeId: node?.id, | |||
| // onValuesChange, | |||
| // }); | |||
| return ( | |||
| <Form | |||
| name="basic" | |||
| autoComplete="off" | |||
| form={form} | |||
| onValuesChange={handleValuesChange} | |||
| initialValues={{ items: [{}] }} | |||
| layout={'vertical'} | |||
| // onValuesChange={handleValuesChange} | |||
| {...form} | |||
| > | |||
| <DynamicInputVariable node={node}></DynamicInputVariable> | |||
| <Form.Item | |||
| name={'llm_id'} | |||
| label={t('model', { keyPrefix: 'chat' })} | |||
| tooltip={t('modelTip', { keyPrefix: 'chat' })} | |||
| <form | |||
| className="space-y-6 p-5 overflow-auto max-h-[76vh]" | |||
| onSubmit={(e) => { | |||
| e.preventDefault(); | |||
| }} | |||
| > | |||
| <LLMSelect></LLMSelect> | |||
| </Form.Item> | |||
| <MessageHistoryWindowSizeItem | |||
| initialValue={1} | |||
| ></MessageHistoryWindowSizeItem> | |||
| <DynamicCategorize nodeId={node?.id}></DynamicCategorize> | |||
| <DynamicInputVariable node={node}></DynamicInputVariable> | |||
| <LargeModelFormField></LargeModelFormField> | |||
| <MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField> | |||
| <DynamicCategorize nodeId={node?.id}></DynamicCategorize> | |||
| </form> | |||
| </Form> | |||
| ); | |||
| }; | |||
| @@ -1,16 +0,0 @@ | |||
| .dynamicDeleteButton { | |||
| position: relative; | |||
| top: 4px; | |||
| margin: 0 8px; | |||
| color: #999; | |||
| font-size: 24px; | |||
| cursor: pointer; | |||
| transition: all 0.3s; | |||
| &:hover { | |||
| color: #777; | |||
| } | |||
| &[disabled] { | |||
| cursor: not-allowed; | |||
| opacity: 0.5; | |||
| } | |||
| } | |||