Fix: Fixed an issue where parser configurations could be added infinitely #9869 (#10464)

### What problem does this PR solve?

Fix: Fixed an issue where parser configurations could be added
infinitely #9869

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
balibabu
2025-10-10 16:30:13 +08:00
committed by GitHub
parent fc46d6bb87
commit f35c5ed119
12 changed files with 83 additions and 56 deletions

View File

@ -39,9 +39,9 @@ export function HomeCard({
/>
</div>
<div className="flex flex-col justify-between gap-1 flex-1 h-full w-[calc(100%-50px)]">
<section className="flex justify-between w-full">
<section className="flex gap-1 items-center w-full">
<div className="text-base font-bold w-80% text-ellipsis overflow-hidden leading-snug">
<section className="flex justify-between">
<section className="flex flex-1 min-w-0 gap-1 items-center">
<div className="text-base font-bold leading-snug truncate">
{data.name}
</div>
{icon}

View File

@ -106,7 +106,7 @@ export function Header() {
}, [navigate]);
return (
<section className="p-5 pr-14 flex justify-between items-center ">
<section className="py-5 px-10 flex justify-between items-center ">
<div className="flex items-center gap-4">
<img
src={'/logo.svg'}

View File

@ -1729,13 +1729,15 @@ This delimiter is used to split the input text into several text pieces echo of
regularExpressions: 'Regular Expressions',
overlappedPercent: 'Overlapped percent',
searchMethod: 'Search method',
searchMethodTip: `Defines how the content can be searched — by full-text, embedding, or both.
The Tokenizer will store the content in the corresponding data structures for the selected methods.`,
begin: 'File',
parserMethod: 'Parsing method',
systemPrompt: 'System Prompt',
systemPromptPlaceholder:
'Enter system prompt for image analysis, if empty the system default value will be used',
exportJson: 'Export JSON',
viewResult: 'View Result',
viewResult: 'View result',
running: 'Running',
summary: 'Augmented Context',
keywords: 'Keywords',

View File

@ -1634,6 +1634,8 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
regularExpressions: '正则表达式',
overlappedPercent: '重叠百分比',
searchMethod: '搜索方法',
searchMethodTip: `决定该数据集启用的搜索方式,可选择全文、向量,或两者兼有。
Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
filenameEmbdWeight: '文件名嵌入权重',
begin: '文件',
parserMethod: '解析方法',

View File

@ -9,18 +9,29 @@ import {
SelectWithSearchFlagOptionType,
} from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { upperFirst } from 'lodash';
import { upperCase, upperFirst } from 'lodash';
import { useTranslation } from 'react-i18next';
import { FileType, OutputFormatMap, PdfOutputFormat } from '../../constant';
import {
FileType,
OutputFormatMap,
SpreadsheetOutputFormat,
} from '../../constant';
import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
const UppercaseFields = [
SpreadsheetOutputFormat.Html,
SpreadsheetOutputFormat.Json,
];
function buildOutputOptionsFormatMap() {
return Object.entries(OutputFormatMap).reduce<
Record<string, SelectWithSearchFlagOptionType[]>
>((pre, [key, value]) => {
pre[key] = Object.values(value).map((v) => ({
label: v === PdfOutputFormat.Json ? 'JSON' : upperFirst(v),
label: UppercaseFields.some((x) => x === v)
? upperCase(v)
: upperFirst(v),
value: v,
}));
return pre;

View File

@ -1,4 +1,7 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import {
SelectWithSearch,
SelectWithSearchFlagOptionType,
} 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';
@ -49,6 +52,7 @@ type ParserItemProps = {
index: number;
fieldLength: number;
remove: UseFieldArrayRemove;
fileFormatOptions: SelectWithSearchFlagOptionType[];
};
export const FormSchema = z.object({
@ -67,7 +71,13 @@ export const FormSchema = z.object({
export type ParserFormSchemaType = z.infer<typeof FormSchema>;
function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
function ParserItem({
name,
index,
fieldLength,
remove,
fileFormatOptions,
}: ParserItemProps) {
const { t } = useTranslation();
const form = useFormContext<ParserFormSchemaType>();
const ref = useRef(null);
@ -79,23 +89,15 @@ function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
const values = form.getValues();
const parserList = values.setups.slice(); // Adding, deleting, or modifying the parser array will not change the reference.
const FileFormatOptions = buildOptions(
FileType,
t,
'dataflow.fileFormatOptions',
).filter(
(x) => x.value !== FileType.Video, // Temporarily hide the video option
);
const filteredFileFormatOptions = useMemo(() => {
const otherFileFormatList = parserList
.filter((_, idx) => idx !== index)
.map((x) => x.fileFormat);
return FileFormatOptions.filter((x) => {
return fileFormatOptions.filter((x) => {
return !otherFileFormatList.includes(x.value);
});
}, [FileFormatOptions, index, parserList]);
}, [fileFormatOptions, index, parserList]);
const Widget =
typeof fileFormat === 'string' && fileFormat in FileFormatWidgetMap
@ -158,6 +160,14 @@ const ParserForm = ({ node }: INextOperatorForm) => {
const { t } = useTranslation();
const defaultValues = useFormValues(initialParserValues, node);
const FileFormatOptions = buildOptions(
FileType,
t,
'dataflow.fileFormatOptions',
).filter(
(x) => x.value !== FileType.Video, // Temporarily hide the video option
);
const form = useForm<z.infer<typeof FormSchema>>({
defaultValues,
resolver: zodResolver(FormSchema),
@ -194,12 +204,15 @@ const ParserForm = ({ node }: INextOperatorForm) => {
index={index}
fieldLength={fields.length}
remove={remove}
fileFormatOptions={FileFormatOptions}
></ParserItem>
);
})}
<BlockButton onClick={add} type="button" className="mt-2.5">
{t('dataflow.addParser')}
</BlockButton>
{fields.length < FileFormatOptions.length && (
<BlockButton onClick={add} type="button" className="mt-2.5">
{t('dataflow.addParser')}
</BlockButton>
)}
</form>
<div className="p-5">
<Output list={outputList}></Output>

View File

@ -58,6 +58,7 @@ const TokenizerForm = ({ node }: INextOperatorForm) => {
<RAGFlowFormItem
name="search_method"
label={t('dataflow.searchMethod')}
tooltip={t('dataflow.searchMethodTip')}
>
{(field) => (
<MultiSelect

View File

@ -162,9 +162,7 @@ export default function DataFlow() {
onClick={handleRunAgent}
loading={running}
>
<CirclePlay
className={isParsing || isLogEmpty ? 'animate-spin' : ''}
/>
<CirclePlay className={isParsing ? 'animate-spin' : ''} />
{isParsing || running ? t('dataflow.running') : t('flow.run')}
</ButtonLoading>

View File

@ -78,7 +78,7 @@ export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
<div className="flex-1 flex items-center gap-5">
<Progress value={progress} className="h-1 flex-1" />
<span className="text-accent-primary text-xs">
{progress}%
{progress.toFixed(2)}%
</span>
</div>
</section>

View File

@ -55,10 +55,10 @@ export function LogSheet({
return (
<Sheet open onOpenChange={hideModal} modal={false}>
<SheetContent
className={cn('top-20')}
className={cn('top-20 h-auto flex flex-col p-0 gap-0')}
onInteractOutside={(e) => e.preventDefault()}
>
<SheetHeader>
<SheetHeader className="p-5">
<SheetTitle className="flex items-center gap-2.5">
<Logs className="size-4" /> {t('flow.log')}
{isCompleted && (
@ -82,30 +82,32 @@ export function LogSheet({
)}
</SheetTitle>
</SheetHeader>
<section className="max-h-[82vh] overflow-auto mt-6">
<section className="flex-1 overflow-auto px-5 pt-5">
{isLogEmpty ? (
<SkeletonCard className="mt-2" />
) : (
<DataflowTimeline traceList={logs}></DataflowTimeline>
)}
</section>
{isParsing ? (
<Button
className="w-full mt-8 bg-state-error/10 text-state-error hover:bg-state-error hover:text-bg-base"
onClick={handleCancel}
>
<CirclePause /> {t('dataflow.cancel')}
</Button>
) : (
<Button
onClick={handleDownloadJson}
disabled={isEndOutputEmpty(logs)}
className="w-full mt-8 bg-accent-primary-5 text-text-secondary hover:bg-accent-primary-5 hover:text-accent-primary hover:border-accent-primary hover:border"
>
<SquareArrowOutUpRight />
{t('dataflow.exportJson')}
</Button>
)}
<div className="px-5 pb-5">
{isParsing ? (
<Button
className="w-full mt-8 bg-state-error/10 text-state-error hover:bg-state-error hover:text-bg-base"
onClick={handleCancel}
>
<CirclePause /> {t('dataflow.cancel')}
</Button>
) : (
<Button
onClick={handleDownloadJson}
disabled={isEndOutputEmpty(logs)}
className="w-full mt-8 bg-accent-primary-5 text-text-secondary hover:bg-accent-primary-5 hover:text-accent-primary hover:border-accent-primary hover:border"
>
<SquareArrowOutUpRight />
{t('dataflow.exportJson')}
</Button>
)}
</div>
</SheetContent>
</Sheet>
);

View File

@ -42,7 +42,7 @@ export function Banner() {
export function NextBanner() {
const { t } = useTranslation();
return (
<section className="text-5xl pt-10 pb-14 font-bold">
<section className="text-5xl pt-10 pb-14 font-bold px-10">
<span className="text-text-primary">{t('header.welcome')}</span>
<span className="pl-3 text-transparent bg-clip-text bg-gradient-to-l from-[#40EBE3] to-[#4A51FF]">
RAGFlow

View File

@ -4,15 +4,13 @@ import { Datasets } from './datasets';
const Home = () => {
return (
<div className="mx-8">
<section>
<NextBanner></NextBanner>
<section className="h-[calc(100dvh-260px)] overflow-auto scrollbar-thin">
<Datasets></Datasets>
<Applications></Applications>
</section>
<section>
<NextBanner></NextBanner>
<section className="h-[calc(100dvh-260px)] overflow-auto px-10">
<Datasets></Datasets>
<Applications></Applications>
</section>
</div>
</section>
);
};