### 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)
This commit is contained in:
parent
fb4b5b0a06
commit
850e218051
31
web/src/components/large-model-form-field.tsx
Normal file
31
web/src/components/large-model-form-field.tsx
Normal file
@ -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 { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { get, isPlainObject, lowerFirst } from 'lodash';
|
import { get, isPlainObject, lowerFirst } from 'lodash';
|
||||||
import { Play, X } from 'lucide-react';
|
import { Play, X } from 'lucide-react';
|
||||||
@ -94,9 +95,9 @@ const FormSheet = ({
|
|||||||
return (
|
return (
|
||||||
<Sheet open={visible} modal={false}>
|
<Sheet open={visible} modal={false}>
|
||||||
<SheetTitle className="hidden"></SheetTitle>
|
<SheetTitle className="hidden"></SheetTitle>
|
||||||
<SheetContent className="bg-white top-20" closeIcon={false}>
|
<SheetContent className={cn('bg-white top-20 p-0')} closeIcon={false}>
|
||||||
<SheetHeader>
|
<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">
|
<div className="flex items-center gap-2 pb-3">
|
||||||
<OperatorIcon
|
<OperatorIcon
|
||||||
name={operatorName}
|
name={operatorName}
|
||||||
|
|||||||
@ -99,8 +99,15 @@ export function useFormConfigMap() {
|
|||||||
},
|
},
|
||||||
[Operator.Categorize]: {
|
[Operator.Categorize]: {
|
||||||
component: CategorizeForm,
|
component: CategorizeForm,
|
||||||
defaultValues: {},
|
defaultValues: { message_history_window_size: 1 },
|
||||||
schema: z.object({}),
|
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]: {
|
[Operator.Message]: {
|
||||||
component: MessageForm,
|
component: MessageForm,
|
||||||
|
|||||||
@ -1,18 +1,25 @@
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { Button } from '@/components/ui/button';
|
||||||
import { CloseOutlined, PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { useUpdateNodeInternals } from '@xyflow/react';
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Collapsible,
|
||||||
Collapse,
|
CollapsibleContent,
|
||||||
Flex,
|
CollapsibleTrigger,
|
||||||
Form,
|
} from '@/components/ui/collapsible';
|
||||||
FormListFieldData,
|
import {
|
||||||
Input,
|
FormControl,
|
||||||
Select,
|
FormField,
|
||||||
} from 'antd';
|
FormItem,
|
||||||
import { FormInstance } from 'antd/lib';
|
FormLabel,
|
||||||
import { humanId } from 'human-id';
|
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 { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { useUpdateNodeInternals } from '@xyflow/react';
|
||||||
|
import humanId from 'human-id';
|
||||||
import trim from 'lodash/trim';
|
import trim from 'lodash/trim';
|
||||||
|
import { ChevronsUpDown, X } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
FocusEventHandler,
|
FocusEventHandler,
|
||||||
@ -20,11 +27,10 @@ import {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
import { Operator } from '../../constant';
|
import { Operator } from '../../constant';
|
||||||
import { useBuildFormSelectOptions } from '../../form-hooks';
|
import { useBuildFormSelectOptions } from '../../form-hooks';
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
nodeId?: string;
|
nodeId?: string;
|
||||||
}
|
}
|
||||||
@ -33,20 +39,20 @@ interface INameInputProps {
|
|||||||
value?: string;
|
value?: string;
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
otherNames?: string[];
|
otherNames?: string[];
|
||||||
validate(errors: string[]): void;
|
validate(error?: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOtherFieldValues = (
|
const getOtherFieldValues = (
|
||||||
form: FormInstance,
|
form: UseFormReturn,
|
||||||
formListName: string = 'items',
|
formListName: string = 'items',
|
||||||
field: FormListFieldData,
|
index: number,
|
||||||
latestField: string,
|
latestField: string,
|
||||||
) =>
|
) =>
|
||||||
(form.getFieldValue([formListName]) ?? [])
|
(form.getValues(formListName) ?? [])
|
||||||
.map((x: any) => x[latestField])
|
.map((x: any) => x[latestField])
|
||||||
.filter(
|
.filter(
|
||||||
(x: string) =>
|
(x: string) =>
|
||||||
x !== form.getFieldValue([formListName, field.name, latestField]),
|
x !== form.getValues(`${formListName}.${index}.${latestField}`),
|
||||||
);
|
);
|
||||||
|
|
||||||
const NameInput = ({
|
const NameInput = ({
|
||||||
@ -61,15 +67,16 @@ const NameInput = ({
|
|||||||
const handleNameChange: ChangeEventHandler<HTMLInputElement> = useCallback(
|
const handleNameChange: ChangeEventHandler<HTMLInputElement> = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
const val = e.target.value;
|
const val = e.target.value;
|
||||||
// trigger validation
|
|
||||||
if (otherNames?.some((x) => x === val)) {
|
|
||||||
validate([t('nameRepeatedMsg')]);
|
|
||||||
} else if (trim(val) === '') {
|
|
||||||
validate([t('nameRequiredMsg')]);
|
|
||||||
} else {
|
|
||||||
validate([]);
|
|
||||||
}
|
|
||||||
setName(val);
|
setName(val);
|
||||||
|
const trimmedVal = trim(val);
|
||||||
|
// trigger validation
|
||||||
|
if (otherNames?.some((x) => x === trimmedVal)) {
|
||||||
|
validate(t('nameRepeatedMsg'));
|
||||||
|
} else if (trimmedVal === '') {
|
||||||
|
validate(t('nameRequiredMsg'));
|
||||||
|
} else {
|
||||||
|
validate('');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[otherNames, validate, t],
|
[otherNames, validate, t],
|
||||||
);
|
);
|
||||||
@ -97,128 +104,161 @@ const NameInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormSet = ({ nodeId, field }: IProps & { field: FormListFieldData }) => {
|
const FormSet = ({ nodeId, index }: IProps & { index: number }) => {
|
||||||
const form = Form.useFormInstance();
|
const form = useFormContext();
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
||||||
Operator.Categorize,
|
Operator.Categorize,
|
||||||
nodeId,
|
nodeId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const buildFieldName = useCallback(
|
||||||
|
(name: string) => {
|
||||||
|
return `items.${index}.${name}`;
|
||||||
|
},
|
||||||
|
[index],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section className="space-y-4">
|
||||||
<Form.Item
|
<FormField
|
||||||
label={t('categoryName')}
|
control={form.control}
|
||||||
name={[field.name, 'name']}
|
name={buildFieldName('name')}
|
||||||
validateTrigger={['onChange', 'onBlur']}
|
render={({ field }) => (
|
||||||
rules={[
|
<FormItem>
|
||||||
{
|
<FormLabel>{t('categoryName')}</FormLabel>
|
||||||
required: true,
|
<FormControl>
|
||||||
whitespace: true,
|
|
||||||
message: t('nameMessage'),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<NameInput
|
<NameInput
|
||||||
otherNames={getOtherFieldValues(form, 'items', field, 'name')}
|
{...field}
|
||||||
validate={(errors: string[]) =>
|
otherNames={getOtherFieldValues(form, 'items', index, 'name')}
|
||||||
form.setFields([
|
validate={(error?: string) => {
|
||||||
{
|
const fieldName = buildFieldName('name');
|
||||||
name: ['items', field.name, 'name'],
|
if (error) {
|
||||||
errors,
|
form.setError(fieldName, { message: error });
|
||||||
},
|
} else {
|
||||||
])
|
form.clearErrors(fieldName);
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
></NameInput>
|
></NameInput>
|
||||||
</Form.Item>
|
</FormControl>
|
||||||
<Form.Item label={t('description')} name={[field.name, 'description']}>
|
<FormMessage />
|
||||||
<Input.TextArea rows={3} />
|
</FormItem>
|
||||||
</Form.Item>
|
)}
|
||||||
<Form.Item label={t('examples')} name={[field.name, 'examples']}>
|
/>
|
||||||
<Input.TextArea rows={3} />
|
<FormField
|
||||||
</Form.Item>
|
control={form.control}
|
||||||
<Form.Item label={t('nextStep')} name={[field.name, 'to']}>
|
name={buildFieldName('description')}
|
||||||
<Select
|
render={({ field }) => (
|
||||||
allowClear
|
<FormItem>
|
||||||
options={buildCategorizeToOptions(
|
<FormLabel>{t('description')}</FormLabel>
|
||||||
getOtherFieldValues(form, 'items', field, 'to'),
|
<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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
|
||||||
<Form.Item hidden name={[field.name, 'index']}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DynamicCategorize = ({ nodeId }: IProps) => {
|
const DynamicCategorize = ({ nodeId }: IProps) => {
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
const form = Form.useFormInstance();
|
const form = useFormContext();
|
||||||
|
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
|
const { fields, remove, append } = useFieldArray({
|
||||||
|
name: 'items',
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.List name="items">
|
|
||||||
{(fields, { add, remove }) => {
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
const idx = form.getFieldValue([
|
append({
|
||||||
'items',
|
|
||||||
fields.at(-1)?.name,
|
|
||||||
'index',
|
|
||||||
]);
|
|
||||||
add({
|
|
||||||
name: humanId(),
|
name: humanId(),
|
||||||
index: fields.length === 0 ? 0 : idx + 1,
|
|
||||||
});
|
});
|
||||||
if (nodeId) updateNodeInternals(nodeId);
|
if (nodeId) updateNodeInternals(nodeId);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={18} vertical>
|
<div className="flex flex-col gap-4 ">
|
||||||
{fields.map((field) => (
|
{fields.map((field, index) => (
|
||||||
<Collapse
|
<Collapsible key={field.id}>
|
||||||
size="small"
|
<div className="flex items-center justify-between space-x-4">
|
||||||
key={field.key}
|
<h4 className="font-bold">
|
||||||
className={styles.caseCard}
|
{form.getValues(`items.${index}.name`)}
|
||||||
items={[
|
</h4>
|
||||||
{
|
<CollapsibleTrigger asChild>
|
||||||
key: field.key,
|
<div className="flex gap-4">
|
||||||
label: (
|
<Button
|
||||||
<div className="flex justify-between">
|
variant="ghost"
|
||||||
<span>
|
size="sm"
|
||||||
{form.getFieldValue(['items', field.name, 'name'])}
|
className="w-9 p-0"
|
||||||
</span>
|
onClick={() => remove(index)}
|
||||||
<CloseOutlined
|
>
|
||||||
onClick={() => {
|
<X className="h-4 w-4" />
|
||||||
remove(field.name);
|
</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>
|
</div>
|
||||||
),
|
</CollapsibleTrigger>
|
||||||
children: (
|
</div>
|
||||||
<FormSet nodeId={nodeId} field={field}></FormSet>
|
<CollapsibleContent>
|
||||||
),
|
<FormSet nodeId={nodeId} index={index}></FormSet>
|
||||||
},
|
</CollapsibleContent>
|
||||||
]}
|
</Collapsible>
|
||||||
></Collapse>
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Button
|
<Button type={'button'} onClick={handleAdd}>
|
||||||
type="dashed"
|
<PlusOutlined />
|
||||||
onClick={handleAdd}
|
|
||||||
block
|
|
||||||
className={styles.addButton}
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
>
|
|
||||||
{t('addCategory')}
|
{t('addCategory')}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</div>
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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 { LargeModelFormField } from '@/components/large-model-form-field';
|
||||||
import MessageHistoryWindowSizeItem from '@/components/message-history-window-size-item';
|
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { Form } from '@/components/ui/form';
|
||||||
import { Form } from 'antd';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { IOperatorForm } from '../../interface';
|
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
||||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
|
||||||
import DynamicCategorize from './dynamic-categorize';
|
import DynamicCategorize from './dynamic-categorize';
|
||||||
import { useHandleFormValuesChange } from './hooks';
|
|
||||||
|
|
||||||
const CategorizeForm = ({ form, onValuesChange, node }: IOperatorForm) => {
|
const CategorizeForm = ({ form, node }: INextOperatorForm) => {
|
||||||
const { t } = useTranslate('flow');
|
// const { handleValuesChange } = useHandleFormValuesChange({
|
||||||
const { handleValuesChange } = useHandleFormValuesChange({
|
// form,
|
||||||
form,
|
// nodeId: node?.id,
|
||||||
nodeId: node?.id,
|
// onValuesChange,
|
||||||
onValuesChange,
|
// });
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
name="basic"
|
// onValuesChange={handleValuesChange}
|
||||||
autoComplete="off"
|
{...form}
|
||||||
form={form}
|
>
|
||||||
onValuesChange={handleValuesChange}
|
<form
|
||||||
initialValues={{ items: [{}] }}
|
className="space-y-6 p-5 overflow-auto max-h-[76vh]"
|
||||||
layout={'vertical'}
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
<DynamicInputVariable node={node}></DynamicInputVariable>
|
||||||
<Form.Item
|
<LargeModelFormField></LargeModelFormField>
|
||||||
name={'llm_id'}
|
<MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField>
|
||||||
label={t('model', { keyPrefix: 'chat' })}
|
|
||||||
tooltip={t('modelTip', { keyPrefix: 'chat' })}
|
|
||||||
>
|
|
||||||
<LLMSelect></LLMSelect>
|
|
||||||
</Form.Item>
|
|
||||||
<MessageHistoryWindowSizeItem
|
|
||||||
initialValue={1}
|
|
||||||
></MessageHistoryWindowSizeItem>
|
|
||||||
<DynamicCategorize nodeId={node?.id}></DynamicCategorize>
|
<DynamicCategorize nodeId={node?.id}></DynamicCategorize>
|
||||||
|
</form>
|
||||||
</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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user