Feat: Bind options to the parser operator form. #9869 (#10069)

### What problem does this PR solve?

Feat: Bind options to the parser operator form. #9869

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-12 16:01:24 +08:00
committed by GitHub
parent 3404469e2a
commit 592f3b1555
11 changed files with 215 additions and 70 deletions

View File

@ -1,32 +1,53 @@
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { LLMFormField } from '@/components/llm-setting-items/llm-form-field';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import {
SelectWithSearch,
SelectWithSearchFlagOptionType,
} from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { FileType } from '../../constant';
import { OutputFormatMap } from './constant';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
export function LanguageFormField({ prefix }: CommonProps) {
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`lang`, prefix)}
label="lang"
>
<SelectWithSearch options={[]}></SelectWithSearch>
</RAGFlowFormItem>
);
function buildOutputOptionsFormatMap() {
return Object.entries(OutputFormatMap).reduce<
Record<string, SelectWithSearchFlagOptionType[]>
>((pre, [key, value]) => {
pre[key] = buildOptions(value);
return pre;
}, {});
}
export function OutputFormatFormField({ prefix }: CommonProps) {
export type OutputFormatFormFieldProps = CommonProps & {
fileType: FileType;
};
export function OutputFormatFormField({
prefix,
fileType,
}: OutputFormatFormFieldProps) {
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`output_format`, prefix)}
label="output_format"
>
<SelectWithSearch options={[]}></SelectWithSearch>
<SelectWithSearch
options={buildOutputOptionsFormatMap()[fileType]}
></SelectWithSearch>
</RAGFlowFormItem>
);
}
export function ParserMethodFormField({ prefix }: CommonProps) {
return (
<LayoutRecognizeFormField
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
horizontal={false}
></LayoutRecognizeFormField>
);
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`parse_method`, prefix)}

View File

@ -0,0 +1,52 @@
import { FileType } from '../../constant';
export enum PdfOutputFormat {
Json = 'json',
Markdown = 'markdown',
}
export enum SpreadsheetOutputFormat {
Json = 'json',
Html = 'html',
}
export enum ImageOutputFormat {
Text = 'text',
}
export enum EmailOutputFormat {
Json = 'json',
Text = 'text',
}
export enum TextMarkdownOutputFormat {
Text = 'text',
}
export enum DocxOutputFormat {
Markdown = 'markdown',
}
export enum PptOutputFormat {
Json = 'json',
}
export enum VideoOutputFormat {
Json = 'json',
}
export enum AudioOutputFormat {
Text = 'text',
}
export const OutputFormatMap = {
[FileType.PDF]: PdfOutputFormat,
[FileType.Spreadsheet]: SpreadsheetOutputFormat,
[FileType.Image]: ImageOutputFormat,
[FileType.Email]: EmailOutputFormat,
[FileType.TextMarkdown]: TextMarkdownOutputFormat,
[FileType.Docx]: DocxOutputFormat,
[FileType.PowerPoint]: PptOutputFormat,
[FileType.Video]: VideoOutputFormat,
[FileType.Audio]: AudioOutputFormat,
};

View File

@ -1,6 +1,7 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { FileType } from '../../constant';
import { OutputFormatFormField } from './common-form-fields';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
@ -25,7 +26,10 @@ export function EmailFormFields({ prefix }: CommonProps) {
>
<SelectWithSearch options={options}></SelectWithSearch>
</RAGFlowFormItem>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Email}
></OutputFormatFormField>
</>
);
}

View File

@ -1,3 +1,4 @@
import { FileType } from '../../constant';
import {
LargeModelFormField,
OutputFormatFormField,
@ -11,7 +12,10 @@ export function ImageFormFields({ prefix }: CommonProps) {
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Image}
></OutputFormatFormField>
</>
);
}

View File

@ -1,13 +1,20 @@
import { FormContainer } from '@/components/form-container';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { BlockButton, Button } from '@/components/ui/button';
import { Form } from '@/components/ui/form';
import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils';
import { buildOptions } from '@/utils/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useHover } from 'ahooks';
import { Trash2 } from 'lucide-react';
import { memo, useCallback } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { memo, useCallback, useRef } from 'react';
import {
UseFieldArrayRemove,
useFieldArray,
useForm,
useFormContext,
} from 'react-hook-form';
import { z } from 'zod';
import { FileType, initialParserValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
@ -34,6 +41,51 @@ const FileFormatWidgetMap = {
[FileType.Image]: ImageFormFields,
};
type ParserItemProps = {
name: string;
index: number;
fieldLength: number;
remove: UseFieldArrayRemove;
};
function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
const form = useFormContext();
const ref = useRef(null);
const isHovering = useHover(ref);
const prefix = `${name}.${index}`;
const fileFormat = form.getValues(`${name}.${index}.fileFormat`);
const Widget =
fileFormat && fileFormat in FileFormatWidgetMap
? FileFormatWidgetMap[fileFormat as keyof typeof FileFormatWidgetMap]
: OutputFormatFormField;
return (
<section
className={cn('space-y-5 p-5 rounded-md', {
'bg-state-error-5': isHovering,
})}
>
<div className="flex justify-between items-center">
<span className="text-text-primary text-sm">Parser {index}</span>
{index > 0 && (
<Button variant={'ghost'} onClick={() => remove(index)} ref={ref}>
<Trash2 />
</Button>
)}
</div>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
label="File Formats"
>
<SelectWithSearch options={FileFormatOptions}></SelectWithSearch>
</RAGFlowFormItem>
<Widget prefix={prefix} fileType={fileFormat as FileType}></Widget>
{index < fieldLength - 1 && <Separator />}
</section>
);
}
export const FormSchema = z.object({
parser: z.array(
z.object({
@ -67,35 +119,22 @@ const ParserForm = ({ node }: INextOperatorForm) => {
return (
<Form {...form}>
{fields.map((field, index) => {
const prefix = `${name}.${index}`;
const fileFormat = form.getValues(`${name}.${index}.fileFormat`);
const Widget =
fileFormat && fileFormat in FileFormatWidgetMap
? FileFormatWidgetMap[
fileFormat as keyof typeof FileFormatWidgetMap
]
: OutputFormatFormField;
return (
<FormContainer key={field.id}>
<div className="flex justify-between items-center">
<span className="text-text-primary text-sm">Parser {index}</span>
<Button variant={'ghost'} onClick={() => remove(index)}>
<Trash2 />
</Button>
</div>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
label="File Formats"
>
<SelectWithSearch options={FileFormatOptions}></SelectWithSearch>
</RAGFlowFormItem>
<Widget prefix={prefix}></Widget>
</FormContainer>
);
})}
<BlockButton onClick={add}>Add Parser</BlockButton>
<form className="px-5">
{fields.map((field, index) => {
return (
<ParserItem
key={field.id}
name={name}
index={index}
fieldLength={fields.length}
remove={remove}
></ParserItem>
);
})}
<BlockButton onClick={add} type="button">
Add Parser
</BlockButton>
</form>
<div className="p-5">
<Output list={outputList}></Output>
</div>

View File

@ -1,10 +1,12 @@
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
import { FileType } from '../../constant';
import {
LanguageFormField,
LargeModelFormField,
OutputFormatFormField,
ParserMethodFormField,
} from './common-form-fields';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
export function PdfFormFields({ prefix }: CommonProps) {
return (
@ -12,8 +14,14 @@ export function PdfFormFields({ prefix }: CommonProps) {
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<LanguageFormField prefix={prefix}></LanguageFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<CrossLanguageFormField
name={buildFieldNameWithPrefix(`lang`, prefix)}
label="lang"
></CrossLanguageFormField>
<OutputFormatFormField
prefix={prefix}
fileType={FileType.Image}
></OutputFormatFormField>
</>
);
}

View File

@ -1,15 +1,21 @@
import {
LargeModelFormField,
OutputFormatFormField,
OutputFormatFormFieldProps,
} from './common-form-fields';
import { CommonProps } from './interface';
export function VideoFormFields({ prefix }: CommonProps) {
export function VideoFormFields({
prefix,
fileType,
}: OutputFormatFormFieldProps) {
return (
<>
{/* Multimodal Model */}
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<OutputFormatFormField prefix={prefix}></OutputFormatFormField>
<OutputFormatFormField
prefix={prefix}
fileType={fileType}
></OutputFormatFormField>
</>
);
}