mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-14 17:55:54 +08:00
### What problem does this PR solve? Feat: Save the configuration information of the knowledge base document #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
b271cc34b3
commit
fef44a71c5
@ -0,0 +1,88 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Plus, Trash2 } from 'lucide-react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function DynamicPageRange() {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name: 'parser_config.pages',
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormLabel tooltip={t('knowledgeDetails.pageRangesTip')}>
|
||||
{t('knowledgeDetails.pageRanges')}
|
||||
</FormLabel>
|
||||
{fields.map((field, index) => {
|
||||
const typeField = `parser_config.pages.${index}.from`;
|
||||
return (
|
||||
<div key={field.id} className="flex items-center gap-1">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={typeField}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-2/5">
|
||||
<FormDescription />
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t('common.pleaseInput')}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`parser_config.pages.${index}.to`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormDescription />
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t('common.pleaseInput')}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Trash2
|
||||
className="cursor-pointer mx-3 size-4 text-colors-text-functional-danger"
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
onClick={() => append({ from: 1, to: 100 })}
|
||||
className="mt-4"
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
type="button"
|
||||
>
|
||||
<Plus />
|
||||
{t('knowledgeDetails.addPage')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -15,15 +15,17 @@ import {
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { DocumentParserType } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IParserConfig } from '@/interfaces/database/document';
|
||||
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import get from 'lodash/get';
|
||||
import omit from 'lodash/omit';
|
||||
import {} from 'module';
|
||||
import { useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
@ -33,10 +35,7 @@ import { DatasetConfigurationContainer } from '../dataset-configuration-containe
|
||||
import { DelimiterFormField } from '../delimiter-form-field';
|
||||
import { EntityTypesFormField } from '../entity-types-form-field';
|
||||
import { ExcelToHtmlFormField } from '../excel-to-html-form-field';
|
||||
import {
|
||||
DocumentType,
|
||||
LayoutRecognizeFormField,
|
||||
} from '../layout-recognize-form-field';
|
||||
import { LayoutRecognizeFormField } from '../layout-recognize-form-field';
|
||||
import { MaxTokenNumberFormField } from '../max-token-number-from-field';
|
||||
import {
|
||||
UseGraphRagFormField,
|
||||
@ -47,7 +46,12 @@ import RaptorFormFields, {
|
||||
} from '../parse-configuration/raptor-form-fields';
|
||||
import { Input } from '../ui/input';
|
||||
import { RAGFlowSelect } from '../ui/select';
|
||||
import { DynamicPageRange } from './dynamic-page-range';
|
||||
import { useFetchParserListOnMount, useShowAutoKeywords } from './hooks';
|
||||
import {
|
||||
useDefaultParserValues,
|
||||
useFillDefaultValueOnMount,
|
||||
} from './use-default-parser-values';
|
||||
|
||||
const FormId = 'ChunkMethodDialogForm';
|
||||
|
||||
@ -78,8 +82,10 @@ export function ChunkMethodDialog({
|
||||
parserId,
|
||||
documentId,
|
||||
documentExtension,
|
||||
visible,
|
||||
parserConfig,
|
||||
}: IProps) {
|
||||
const { t } = useTranslate('knowledgeDetails');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { parserList } = useFetchParserListOnMount(
|
||||
documentId,
|
||||
@ -94,6 +100,10 @@ export function ChunkMethodDialog({
|
||||
return knowledgeDetails.parser_config?.graphrag?.use_graphrag;
|
||||
}, [knowledgeDetails.parser_config?.graphrag?.use_graphrag]);
|
||||
|
||||
const defaultParserValues = useDefaultParserValues();
|
||||
|
||||
const fillDefaultParserValue = useFillDefaultValueOnMount();
|
||||
|
||||
const FormSchema = z.object({
|
||||
parser_id: z
|
||||
.string()
|
||||
@ -104,16 +114,34 @@ export function ChunkMethodDialog({
|
||||
parser_config: z.object({
|
||||
task_page_size: z.coerce.number(),
|
||||
layout_recognize: z.string(),
|
||||
chunk_token_num: z.coerce.number(),
|
||||
delimiter: z.string(),
|
||||
auto_keywords: z.coerce.number(),
|
||||
auto_questions: z.coerce.number(),
|
||||
html4excel: z.boolean(),
|
||||
raptor: z.object({
|
||||
use_raptor: z.boolean().optional(),
|
||||
prompt: z.string(),
|
||||
max_token: z.coerce.number(),
|
||||
threshold: z.coerce.number(),
|
||||
max_cluster: z.coerce.number(),
|
||||
random_seed: z.coerce.number(),
|
||||
}),
|
||||
graphrag: z.object({
|
||||
use_graphrag: z.boolean(),
|
||||
}),
|
||||
entity_types: z.array(z.string()),
|
||||
pages: z.array(
|
||||
z.object({ from: z.coerce.number(), to: z.coerce.number() }),
|
||||
),
|
||||
}),
|
||||
});
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
parser_id: parserId,
|
||||
parser_config: {
|
||||
task_page_size: 12,
|
||||
layout_recognize: DocumentType.DeepDOC,
|
||||
},
|
||||
|
||||
parser_config: defaultParserValues,
|
||||
},
|
||||
});
|
||||
|
||||
@ -155,22 +183,59 @@ export function ChunkMethodDialog({
|
||||
|
||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
console.log('🚀 ~ onSubmit ~ data:', data);
|
||||
// const ret = await onOk?.();
|
||||
// if (ret) {
|
||||
// hideModal?.();
|
||||
// }
|
||||
const nextData = {
|
||||
...data,
|
||||
parser_config: {
|
||||
...data.parser_config,
|
||||
pages: data.parser_config?.pages?.map((x: any) => [x.from, x.to]) ?? [],
|
||||
},
|
||||
};
|
||||
console.log('🚀 ~ onSubmit ~ nextData:', nextData);
|
||||
const ret = await onOk?.(nextData);
|
||||
if (ret) {
|
||||
hideModal?.();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
const pages =
|
||||
parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? [];
|
||||
form.reset({
|
||||
parser_id: parserId,
|
||||
parser_config: fillDefaultParserValue({
|
||||
pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }],
|
||||
...omit(parserConfig, 'pages'),
|
||||
graphrag: {
|
||||
use_graphrag: get(
|
||||
parserConfig,
|
||||
'graphrag.use_graphrag',
|
||||
useGraphRag,
|
||||
),
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [
|
||||
fillDefaultParserValue,
|
||||
form,
|
||||
knowledgeDetails.parser_config,
|
||||
parserConfig,
|
||||
parserId,
|
||||
useGraphRag,
|
||||
visible,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent className="max-w-[50vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('chunkMethod')}</DialogTitle>
|
||||
<DialogTitle>{t('knowledgeDetails.chunkMethod')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6"
|
||||
className="space-y-6 max-h-[70vh] overflow-auto"
|
||||
id={FormId}
|
||||
>
|
||||
<FormField
|
||||
@ -178,7 +243,7 @@ export function ChunkMethodDialog({
|
||||
name="parser_id"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t('knowledgeDetails.chunkMethod')}</FormLabel>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
@ -189,14 +254,15 @@ export function ChunkMethodDialog({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{showPages && <DynamicPageRange></DynamicPageRange>}
|
||||
{showPages && layoutRecognize && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parser_config.task_page_size"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('taskPageSizeTip')}>
|
||||
{t('taskPageSize')}
|
||||
<FormLabel tooltip={t('knowledgeDetails.taskPageSizeTip')}>
|
||||
{t('knowledgeDetails.taskPageSize')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@ -251,7 +317,7 @@ export function ChunkMethodDialog({
|
||||
</Form>
|
||||
<DialogFooter>
|
||||
<Button type="submit" form={FormId}>
|
||||
Save changes
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
@ -0,0 +1,60 @@
|
||||
import { IParserConfig } from '@/interfaces/database/document';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DocumentType } from '../layout-recognize-form-field';
|
||||
|
||||
export function useDefaultParserValues() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const defaultParserValues = useMemo(() => {
|
||||
const defaultParserValues = {
|
||||
task_page_size: 12,
|
||||
layout_recognize: DocumentType.DeepDOC,
|
||||
chunk_token_num: 512,
|
||||
delimiter: '\n',
|
||||
auto_keywords: 0,
|
||||
auto_questions: 0,
|
||||
html4excel: false,
|
||||
raptor: {
|
||||
use_raptor: false,
|
||||
prompt: t('knowledgeConfiguration.promptText'),
|
||||
max_token: 256,
|
||||
threshold: 0.1,
|
||||
max_cluster: 64,
|
||||
random_seed: 0,
|
||||
},
|
||||
graphrag: {
|
||||
use_graphrag: false,
|
||||
},
|
||||
entity_types: [],
|
||||
pages: [],
|
||||
};
|
||||
|
||||
return defaultParserValues;
|
||||
}, [t]);
|
||||
|
||||
return defaultParserValues;
|
||||
}
|
||||
|
||||
export function useFillDefaultValueOnMount() {
|
||||
const defaultParserValues = useDefaultParserValues();
|
||||
|
||||
const fillDefaultValue = useCallback(
|
||||
(parserConfig: IParserConfig) => {
|
||||
return Object.entries(defaultParserValues).reduce<Record<string, any>>(
|
||||
(pre, [key, value]) => {
|
||||
if (key in parserConfig) {
|
||||
pre[key] = parserConfig[key as keyof IParserConfig];
|
||||
} else {
|
||||
pre[key] = value;
|
||||
}
|
||||
return pre;
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
[defaultParserValues],
|
||||
);
|
||||
|
||||
return fillDefaultValue;
|
||||
}
|
@ -48,7 +48,7 @@ export function DelimiterFormField() {
|
||||
<FormLabel tooltip={t('knowledgeDetails.delimiterTip')}>
|
||||
{t('knowledgeDetails.delimiter')}
|
||||
</FormLabel>
|
||||
<FormControl defaultValue={`\n`}>
|
||||
<FormControl>
|
||||
<DelimiterInput {...field}></DelimiterInput>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
@ -30,6 +30,7 @@ export function UploaderTabs({ setFiles }: UploaderTabsProps) {
|
||||
maxFileCount={8}
|
||||
maxSize={8 * 1024 * 1024}
|
||||
onValueChange={setFiles}
|
||||
accept={{ '*': [] }}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="password">{t('common.comingSoon')}</TabsContent>
|
||||
|
@ -83,7 +83,7 @@ const RaptorFormFields = () => {
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('promptTip')}>{t('prompt')}</FormLabel>
|
||||
<FormControl defaultValue={t('promptText')}>
|
||||
<FormControl>
|
||||
<Textarea {...field} rows={8} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@ -124,7 +124,11 @@ const RaptorFormFields = () => {
|
||||
<FormControl defaultValue={0}>
|
||||
<div className="flex gap-4">
|
||||
<Input {...field} />
|
||||
<Button size={'sm'} onClick={handleGenerate}>
|
||||
<Button
|
||||
size={'sm'}
|
||||
onClick={handleGenerate}
|
||||
type={'button'}
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from '@/components/ui/hover-card';
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { CircleX, Play, RefreshCw } from 'lucide-react';
|
||||
import { PropsWithChildren, useCallback } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RunningStatus } from './constant';
|
||||
import { ParsingCard } from './parsing-card';
|
||||
@ -26,22 +26,6 @@ const IconMap = {
|
||||
[RunningStatus.FAIL]: <RefreshCw />,
|
||||
};
|
||||
|
||||
function MenuItem({
|
||||
children,
|
||||
onClick,
|
||||
}: PropsWithChildren & { onClick?(): void }) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ParsingStatusCell({
|
||||
record,
|
||||
showChangeParserModal,
|
||||
@ -67,19 +51,21 @@ export function ParsingStatusCell({
|
||||
return (
|
||||
<section className="flex gap-2 items-center ">
|
||||
<div>
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant={'ghost'} size={'sm'}>
|
||||
{parser_id}
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent>
|
||||
<MenuItem onClick={handleShowChangeParserModal}>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={handleShowChangeParserModal}>
|
||||
{t('knowledgeDetails.chunkMethod')}
|
||||
</MenuItem>
|
||||
<MenuItem>{t('knowledgeDetails.setMetaData')}</MenuItem>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
{t('knowledgeDetails.setMetaData')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<Separator orientation="vertical" />
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useSetDocumentParser } from '@/hooks/use-document-request';
|
||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||
import { IChangeParserRequestBody } from '@/interfaces/request/document';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export const useChangeDocumentParser = () => {
|
||||
@ -15,12 +15,12 @@ export const useChangeDocumentParser = () => {
|
||||
} = useSetModalState();
|
||||
|
||||
const onChangeParserOk = useCallback(
|
||||
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
|
||||
async (parserConfigInfo: IChangeParserRequestBody) => {
|
||||
if (record?.id) {
|
||||
const ret = await setDocumentParser({
|
||||
parserId,
|
||||
parserId: parserConfigInfo.parser_id,
|
||||
documentId: record?.id,
|
||||
parserConfig,
|
||||
parserConfig: parserConfigInfo.parser_config,
|
||||
});
|
||||
if (ret === 0) {
|
||||
hideChangeParserModal();
|
||||
|
@ -83,7 +83,7 @@ export function useDatasetTableColumns({
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
// meta: { cellClassName: 'max-w-[20vw]' },
|
||||
meta: { cellClassName: 'max-w-[20vw]' },
|
||||
cell: ({ row }) => {
|
||||
const name: string = row.getValue('name');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user