Browse Source

Feat: Use shadcn-ui to build GenerateForm. #3221 (#5449)

### What problem does this PR solve?

Feat: Use shadcn-ui to build GenerateForm. #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.17.0
balibabu 8 months ago
parent
commit
244cf49ba4
No account linked to committer's email address

+ 30
- 0
web/src/components/message-history-window-size-item.tsx View File

import { Form, InputNumber } from 'antd'; import { Form, InputNumber } from 'antd';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from './ui/form';
import { Input } from './ui/input';


const MessageHistoryWindowSizeItem = ({ const MessageHistoryWindowSizeItem = ({
initialValue, initialValue,
}; };


export default MessageHistoryWindowSizeItem; export default MessageHistoryWindowSizeItem;

export function MessageHistoryWindowSizeFormField() {
const form = useFormContext();
const { t } = useTranslation();

return (
<FormField
control={form.control}
name={'message_history_window_size'}
render={({ field }) => (
<FormItem>
<FormLabel>{t('flow.messageHistoryWindowSize')}</FormLabel>
<FormControl>
<Input {...field} type={'number'}></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}

+ 10
- 2
web/src/components/similarity-slider/index.tsx View File



interface SimilaritySliderFormFieldProps { interface SimilaritySliderFormFieldProps {
vectorSimilarityWeightName?: string; vectorSimilarityWeightName?: string;
isTooltipShown?: boolean;
} }


export function SimilaritySliderFormField({ export function SimilaritySliderFormField({
vectorSimilarityWeightName = 'vector_similarity_weight', vectorSimilarityWeightName = 'vector_similarity_weight',
isTooltipShown,
}: SimilaritySliderFormFieldProps) { }: SimilaritySliderFormFieldProps) {
const form = useFormContext(); const form = useFormContext();
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
name={'similarity_threshold'} name={'similarity_threshold'}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('similarityThreshold')}</FormLabel>
<FormLabel tooltip={isTooltipShown && t('similarityThresholdTip')}>
{t('similarityThreshold')}
</FormLabel>
<FormControl> <FormControl>
<SingleFormSlider <SingleFormSlider
{...field} {...field}
name={vectorSimilarityWeightName} name={vectorSimilarityWeightName}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('vectorSimilarityWeight')}</FormLabel>
<FormLabel
tooltip={isTooltipShown && t('vectorSimilarityWeightTip')}
>
{t('vectorSimilarityWeight')}
</FormLabel>
<FormControl> <FormControl>
<SingleFormSlider <SingleFormSlider
{...field} {...field}

+ 20
- 4
web/src/components/ui/form.tsx View File



import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Info } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip';


const Form = FormProvider; const Form = FormProvider;




const FormLabel = React.forwardRef< const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>, React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & {
tooltip?: React.ReactNode;
}
>(({ className, tooltip, ...props }, ref) => {
const { error, formItemId } = useFormField(); const { error, formItemId } = useFormField();


return ( return (
<Label <Label
ref={ref} ref={ref}
className={cn(error && 'text-destructive', className)}
className={cn(error && 'text-destructive', className, 'flex')}
htmlFor={formItemId} htmlFor={formItemId}
{...props} {...props}
/>
>
{props.children}
{tooltip && (
<Tooltip>
<TooltipTrigger>
<Info className="size-3 ml-2" />
</TooltipTrigger>
<TooltipContent>
<p>{tooltip}</p>
</TooltipContent>
</Tooltip>
)}
</Label>
); );
}); });
FormLabel.displayName = 'FormLabel'; FormLabel.displayName = 'FormLabel';

+ 1
- 1
web/src/pages/agent/form-sheet/next.tsx View File

<span>{t(`${lowerFirst(operatorName)}Description`)}</span> <span>{t(`${lowerFirst(operatorName)}Description`)}</span>
</section> </section>
</SheetHeader> </SheetHeader>
<section>
<section className="pt-4">
{visible && ( {visible && (
<FlowFormContext.Provider value={node}> <FlowFormContext.Provider value={node}>
<OperatorForm <OperatorForm

+ 9
- 2
web/src/pages/agent/form-sheet/use-form-config-map.tsx View File

}, },
[Operator.Generate]: { [Operator.Generate]: {
component: GenerateForm, component: GenerateForm,
defaultValues: {},
schema: z.object({}),
defaultValues: {
cite: true,
prompt: t('flow.promptText'),
},
schema: z.object({
prompt: z.string().min(1, {
message: t('flow.promptMessage'),
}),
}),
}, },
[Operator.Answer]: { [Operator.Answer]: {
component: AnswerForm, component: AnswerForm,

+ 1
- 1
web/src/pages/agent/form/components/next-dynamic-input-variable.tsx View File

const { t } = useTranslation(); const { t } = useTranslation();


return ( return (
<Collapsible defaultOpen className="group/collapsible pt-4">
<Collapsible defaultOpen className="group/collapsible">
<CollapsibleTrigger className="flex justify-between w-full pb-2"> <CollapsibleTrigger className="flex justify-between w-full pb-2">
<span className="font-bold text-2xl text-colors-text-neutral-strong"> <span className="font-bold text-2xl text-colors-text-neutral-strong">
{t('flow.input')} {t('flow.input')}

+ 0
- 101
web/src/pages/agent/form/generate-form/dynamic-parameters.tsx View File

import { EditableCell, EditableRow } from '@/components/editable-cell';
import { useTranslate } from '@/hooks/common-hooks';
import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { DeleteOutlined } from '@ant-design/icons';
import { Button, Flex, Select, Table, TableProps } from 'antd';
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
import { IGenerateParameter } from '../../interface';
import { useHandleOperateParameters } from './hooks';

import styles from './index.less';
interface IProps {
node?: RAGFlowNodeType;
}

const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};

const DynamicParameters = ({ node }: IProps) => {
const nodeId = node?.id;
const { t } = useTranslate('flow');

const options = useBuildComponentIdSelectOptions(nodeId, node?.parentId);
const {
dataSource,
handleAdd,
handleRemove,
handleSave,
handleComponentIdChange,
} = useHandleOperateParameters(nodeId!);

const columns: TableProps<IGenerateParameter>['columns'] = [
{
title: t('key'),
dataIndex: 'key',
key: 'key',
width: '40%',
onCell: (record: IGenerateParameter) => ({
record,
editable: true,
dataIndex: 'key',
title: 'key',
handleSave,
}),
},
{
title: t('value'),
dataIndex: 'component_id',
key: 'component_id',
align: 'center',
width: '40%',
render(text, record) {
return (
<Select
style={{ width: '100%' }}
allowClear
options={options}
value={text}
onChange={handleComponentIdChange(record)}
/>
);
},
},
{
title: t('operation'),
dataIndex: 'operation',
width: 20,
key: 'operation',
align: 'center',
fixed: 'right',
render(_, record) {
return <DeleteOutlined onClick={handleRemove(record.id)} />;
},
},
];

return (
<section>
<Flex justify="end">
<Button size="small" onClick={handleAdd}>
{t('add')}
</Button>
</Flex>
<Table
dataSource={dataSource}
columns={columns}
rowKey={'id'}
className={styles.variableTable}
components={components}
rowClassName={() => styles.editableRow}
scroll={{ x: true }}
bordered
/>
</section>
);
};

export default DynamicParameters;

+ 0
- 70
web/src/pages/agent/form/generate-form/hooks.ts View File

import get from 'lodash/get';
import { useCallback, useMemo } from 'react';
import { v4 as uuid } from 'uuid';
import { IGenerateParameter } from '../../interface';
import useGraphStore from '../../store';

export const useHandleOperateParameters = (nodeId: string) => {
const { getNode, updateNodeForm } = useGraphStore((state) => state);
const node = getNode(nodeId);
const dataSource: IGenerateParameter[] = useMemo(
() => get(node, 'data.form.parameters', []) as IGenerateParameter[],
[node],
);

const handleComponentIdChange = useCallback(
(row: IGenerateParameter) => (value: string) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.id === item.id);
const item = newData[index];
newData.splice(index, 1, {
...item,
component_id: value,
});

updateNodeForm(nodeId, { parameters: newData });
},
[updateNodeForm, nodeId, dataSource],
);

const handleRemove = useCallback(
(id?: string) => () => {
const newData = dataSource.filter((item) => item.id !== id);
updateNodeForm(nodeId, { parameters: newData });
},
[updateNodeForm, nodeId, dataSource],
);

const handleAdd = useCallback(() => {
updateNodeForm(nodeId, {
parameters: [
...dataSource,
{
id: uuid(),
key: '',
component_id: undefined,
},
],
});
}, [dataSource, nodeId, updateNodeForm]);

const handleSave = (row: IGenerateParameter) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.id === item.id);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});

updateNodeForm(nodeId, { parameters: newData });
};

return {
handleAdd,
handleRemove,
handleComponentIdChange,
handleSave,
dataSource,
};
};

+ 0
- 21
web/src/pages/agent/form/generate-form/index.less View File

.variableTable {
margin-top: 14px;
}
.editableRow {
:global(.editable-cell) {
position: relative;
}

:global(.editable-cell-value-wrap) {
padding: 5px 12px;
cursor: pointer;
height: 30px !important;
}
&:hover {
:global(.editable-cell-value-wrap) {
padding: 4px 11px;
border: 1px solid #d9d9d9;
border-radius: 2px;
}
}
}

+ 67
- 46
web/src/pages/agent/form/generate-form/index.tsx View File

import LLMSelect from '@/components/llm-select'; import LLMSelect from '@/components/llm-select';
import MessageHistoryWindowSizeItem from '@/components/message-history-window-size-item';
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
import { PromptEditor } from '@/components/prompt-editor'; import { PromptEditor } from '@/components/prompt-editor';
import { useTranslate } from '@/hooks/common-hooks';
import { Form, Switch } from 'antd';
import { IOperatorForm } from '../../interface';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Switch } from '@/components/ui/switch';
import { useTranslation } from 'react-i18next';
import { INextOperatorForm } from '../../interface';


const GenerateForm = ({ onValuesChange, form }: IOperatorForm) => {
const { t } = useTranslate('flow');
const GenerateForm = ({ form }: INextOperatorForm) => {
const { t } = useTranslation();


return ( return (
<Form
name="basic"
autoComplete="off"
form={form}
onValuesChange={onValuesChange}
layout={'vertical'}
>
<Form.Item
name={'llm_id'}
label={t('model', { keyPrefix: 'chat' })}
tooltip={t('modelTip', { keyPrefix: 'chat' })}
<Form {...form}>
<form
className="space-y-6"
onSubmit={(e) => {
e.preventDefault();
}}
> >
<LLMSelect></LLMSelect>
</Form.Item>
<Form.Item
name={['prompt']}
label={t('systemPrompt')}
initialValue={t('promptText')}
tooltip={t('promptTip', { keyPrefix: 'knowledgeConfiguration' })}
rules={[
{
required: true,
message: t('promptMessage'),
},
]}
>
{/* <Input.TextArea rows={8}></Input.TextArea> */}
<PromptEditor></PromptEditor>
</Form.Item>
<Form.Item
name={['cite']}
label={t('cite')}
initialValue={true}
valuePropName="checked"
tooltip={t('citeTip')}
>
<Switch />
</Form.Item>
<MessageHistoryWindowSizeItem
initialValue={12}
></MessageHistoryWindowSizeItem>
<FormField
control={form.control}
name="llm_id"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('chat.modelTip')}>
{t('chat.model')}
</FormLabel>
<FormControl>
<LLMSelect {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="prompt"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('knowledgeConfiguration.promptTip')}>
{t('flow.systemPrompt')}
</FormLabel>
<FormControl>
<PromptEditor {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="cite"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('flow.citeTip')}>
{t('flow.cite')}
</FormLabel>
<FormControl>
<Switch {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<MessageHistoryWindowSizeFormField></MessageHistoryWindowSizeFormField>
</form>
</Form> </Form>
); );
}; };

+ 4
- 1
web/src/pages/agent/form/retrieval-form/next.tsx View File

}} }}
> >
<DynamicInputVariable node={node}></DynamicInputVariable> <DynamicInputVariable node={node}></DynamicInputVariable>
<SimilaritySliderFormField vectorSimilarityWeightName="keywords_similarity_weight"></SimilaritySliderFormField>
<SimilaritySliderFormField
vectorSimilarityWeightName="keywords_similarity_weight"
isTooltipShown
></SimilaritySliderFormField>
<TopNFormField></TopNFormField> <TopNFormField></TopNFormField>
<RerankFormFields></RerankFormFields> <RerankFormFields></RerankFormFields>
<KnowledgeBaseFormField></KnowledgeBaseFormField> <KnowledgeBaseFormField></KnowledgeBaseFormField>

Loading…
Cancel
Save