mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-26 17:16:52 +08:00
Feat: Use data pipeline to visualize the parsing configuration of the knowledge base (#10423)
### What problem does this PR solve? #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: jinhai <haijin.chn@gmail.com> Signed-off-by: Jin Hai <haijin.chn@gmail.com> Co-authored-by: chanx <1243304602@qq.com> Co-authored-by: balibabu <cike8899@users.noreply.github.com> Co-authored-by: Lynn <lynn_inf@hotmail.com> Co-authored-by: 纷繁下的无奈 <zhileihuang@126.com> Co-authored-by: huangzl <huangzl@shinemo.com> Co-authored-by: writinwaters <93570324+writinwaters@users.noreply.github.com> Co-authored-by: Wilmer <33392318@qq.com> Co-authored-by: Adrian Weidig <adrianweidig@gmx.net> Co-authored-by: Zhichang Yu <yuzhichang@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Yongteng Lei <yongtengrey@outlook.com> Co-authored-by: Liu An <asiro@qq.com> Co-authored-by: buua436 <66937541+buua436@users.noreply.github.com> Co-authored-by: BadwomanCraZY <511528396@qq.com> Co-authored-by: cucusenok <31804608+cucusenok@users.noreply.github.com> Co-authored-by: Russell Valentine <russ@coldstonelabs.org> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Billy Bao <newyorkupperbay@gmail.com> Co-authored-by: Zhedong Cen <cenzhedong2@126.com> Co-authored-by: TensorNull <129579691+TensorNull@users.noreply.github.com> Co-authored-by: TensorNull <tensor.null@gmail.com> Co-authored-by: TeslaZY <TeslaZY@outlook.com> Co-authored-by: Ajay <160579663+aybanda@users.noreply.github.com> Co-authored-by: AB <aj@Ajays-MacBook-Air.local> Co-authored-by: 天海蒼灆 <huangaoqin@tecpie.com> Co-authored-by: He Wang <wanghechn@qq.com> Co-authored-by: Atsushi Hatakeyama <atu729@icloud.com> Co-authored-by: Jin Hai <haijin.chn@gmail.com> Co-authored-by: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Co-authored-by: Mohamed Mathari <nocodeventure@Mac-mini-van-Mohamed.fritz.box> Co-authored-by: Stephen Hu <stephenhu@seismic.com> Co-authored-by: Shaun Zhang <zhangwfjh@users.noreply.github.com> Co-authored-by: zhimeng123 <60221886+zhimeng123@users.noreply.github.com> Co-authored-by: mxc <mxc@example.com> Co-authored-by: Dominik Novotný <50611433+SgtMarmite@users.noreply.github.com> Co-authored-by: EVGENY M <168018528+rjohny55@users.noreply.github.com> Co-authored-by: mcoder6425 <mcoder64@gmail.com> Co-authored-by: lemsn <lemsn@msn.com> Co-authored-by: lemsn <lemsn@126.com> Co-authored-by: Adrian Gora <47756404+adagora@users.noreply.github.com> Co-authored-by: Womsxd <45663319+Womsxd@users.noreply.github.com> Co-authored-by: FatMii <39074672+FatMii@users.noreply.github.com>
This commit is contained in:
@ -1,31 +0,0 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { NaiveConfiguration } from './naive';
|
||||
import { SavingButton } from './saving-button';
|
||||
|
||||
export function ChunkMethodForm() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section className="h-full flex flex-col">
|
||||
<div className="overflow-auto flex-1 min-h-0">
|
||||
<NaiveConfiguration></NaiveConfiguration>
|
||||
</div>
|
||||
<div className="text-right pt-4 flex justify-end gap-3">
|
||||
<Button
|
||||
type="reset"
|
||||
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
}}
|
||||
>
|
||||
{t('knowledgeConfiguration.cancel')}
|
||||
</Button>
|
||||
<SavingButton></SavingButton>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,191 @@
|
||||
import { IDataPipelineSelectNode } from '@/components/data-pipeline-select';
|
||||
import { IconFont } from '@/components/icon-font';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { Link } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import LinkDataPipelineModal from './link-data-pipline-modal';
|
||||
export interface IDataPipelineNodeProps extends IDataPipelineSelectNode {
|
||||
isDefault?: boolean;
|
||||
linked?: boolean;
|
||||
}
|
||||
|
||||
export interface ILinkDataPipelineProps {
|
||||
data?: IDataPipelineNodeProps;
|
||||
handleLinkOrEditSubmit?: (data: IDataPipelineNodeProps | undefined) => void;
|
||||
}
|
||||
|
||||
interface DataPipelineItemProps extends IDataPipelineNodeProps {
|
||||
openLinkModalFunc?: (open: boolean, data?: IDataPipelineNodeProps) => void;
|
||||
}
|
||||
|
||||
const DataPipelineItem = (props: DataPipelineItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { name, avatar, isDefault, linked, openLinkModalFunc } = props;
|
||||
const openUnlinkModal = () => {
|
||||
Modal.show({
|
||||
visible: true,
|
||||
className: '!w-[560px]',
|
||||
title: t('dataflowParser.unlinkPipelineModalTitle'),
|
||||
children: (
|
||||
<div
|
||||
className="text-sm text-text-secondary"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: t('dataflowParser.unlinkPipelineModalContent'),
|
||||
}}
|
||||
></div>
|
||||
),
|
||||
onVisibleChange: () => {
|
||||
Modal.hide();
|
||||
},
|
||||
footer: (
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant={'outline'} onClick={() => Modal.hide()}>
|
||||
{t('dataflowParser.changeStepModalCancelText')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
className="!bg-state-error text-bg-base"
|
||||
onClick={() => {
|
||||
Modal.hide();
|
||||
}}
|
||||
>
|
||||
{t('dataflowParser.unlinkPipelineModalConfirmText')}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-1 px-2 rounded-md border">
|
||||
<div className="flex items-center gap-1">
|
||||
<RAGFlowAvatar avatar={avatar} name={name} className="size-4" />
|
||||
<div>{name}</div>
|
||||
{/* {isDefault && (
|
||||
<div className="text-xs bg-text-secondary text-bg-base px-2 py-1 rounded-md">
|
||||
{t('knowledgeConfiguration.default')}
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
{/* <div className="flex gap-1 items-center">
|
||||
<Button
|
||||
variant={'transparent'}
|
||||
className="border-none"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openLinkModalFunc?.(true, { ...omit(props, ['openLinkModalFunc']) })
|
||||
}
|
||||
>
|
||||
<Settings2 />
|
||||
</Button>
|
||||
{!isDefault && (
|
||||
<>
|
||||
{linked && (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'transparent'}
|
||||
className="border-none"
|
||||
onClick={() => {
|
||||
openUnlinkModal();
|
||||
}}
|
||||
>
|
||||
<Unlink />
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const LinkDataPipeline = (props: ILinkDataPipelineProps) => {
|
||||
const { data, handleLinkOrEditSubmit: submit } = props;
|
||||
const { t } = useTranslation();
|
||||
const [openLinkModal, setOpenLinkModal] = useState(false);
|
||||
const [currentDataPipeline, setCurrentDataPipeline] =
|
||||
useState<IDataPipelineNodeProps>();
|
||||
const pipelineNode: IDataPipelineNodeProps[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: data?.id,
|
||||
name: data?.name,
|
||||
avatar: data?.avatar,
|
||||
isDefault: data?.isDefault,
|
||||
linked: true,
|
||||
},
|
||||
],
|
||||
[data],
|
||||
);
|
||||
const openLinkModalFunc = (open: boolean, data?: IDataPipelineNodeProps) => {
|
||||
console.log('open', open, data);
|
||||
setOpenLinkModal(open);
|
||||
if (data) {
|
||||
setCurrentDataPipeline(data);
|
||||
} else {
|
||||
setCurrentDataPipeline(undefined);
|
||||
}
|
||||
};
|
||||
const handleLinkOrEditSubmit = (
|
||||
data: IDataPipelineSelectNode | undefined,
|
||||
) => {
|
||||
console.log('handleLinkOrEditSubmit', data);
|
||||
submit?.(data);
|
||||
setOpenLinkModal(false);
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<section className="flex flex-col">
|
||||
<div className="flex items-center gap-1 text-text-primary text-sm">
|
||||
<IconFont name="Pipeline" />
|
||||
{t('knowledgeConfiguration.dataPipeline')}
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-center text-xs text-text-secondary">
|
||||
{t('knowledgeConfiguration.linkPipelineSetTip')}
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant={'transparent'}
|
||||
onClick={() => {
|
||||
openLinkModalFunc?.(true);
|
||||
}}
|
||||
>
|
||||
<Link />
|
||||
<span className="text-xs text-text-primary">
|
||||
{t('knowledgeConfiguration.linkDataPipeline')}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex flex-col gap-2">
|
||||
{pipelineNode.map(
|
||||
(item) =>
|
||||
item.id && (
|
||||
<DataPipelineItem
|
||||
key={item.id}
|
||||
openLinkModalFunc={openLinkModalFunc}
|
||||
id={item.id}
|
||||
name={item.name}
|
||||
avatar={item.avatar}
|
||||
isDefault={item.isDefault}
|
||||
linked={item.linked}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</section>
|
||||
<LinkDataPipelineModal
|
||||
data={currentDataPipeline}
|
||||
open={openLinkModal}
|
||||
setOpen={(open: boolean) => {
|
||||
openLinkModalFunc(open);
|
||||
}}
|
||||
onSubmit={handleLinkOrEditSubmit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default LinkDataPipeline;
|
||||
@ -0,0 +1,163 @@
|
||||
import {
|
||||
DataFlowSelect,
|
||||
IDataPipelineSelectNode,
|
||||
} from '@/components/data-pipeline-select';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from 'i18next';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { pipelineFormSchema } from '../form-schema';
|
||||
import { IDataPipelineNodeProps } from './link-data-pipeline';
|
||||
|
||||
const LinkDataPipelineModal = ({
|
||||
data,
|
||||
open,
|
||||
setOpen,
|
||||
onSubmit,
|
||||
}: {
|
||||
data: IDataPipelineNodeProps | undefined;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
onSubmit?: (pipeline: IDataPipelineSelectNode | undefined) => void;
|
||||
}) => {
|
||||
const isEdit = !!data;
|
||||
const [list, setList] = useState<IDataPipelineSelectNode[]>();
|
||||
const form = useForm<z.infer<typeof pipelineFormSchema>>({
|
||||
resolver: zodResolver(pipelineFormSchema),
|
||||
defaultValues: {
|
||||
pipeline_id: '',
|
||||
set_default: false,
|
||||
file_filter: '',
|
||||
},
|
||||
});
|
||||
// const [open, setOpen] = useState(false);
|
||||
const { navigateToAgents } = useNavigatePage();
|
||||
const handleFormSubmit = (values: any) => {
|
||||
console.log(values, data);
|
||||
// const param = {
|
||||
// ...data,
|
||||
// ...values,
|
||||
// };
|
||||
const pipeline = list?.find((item) => item.id === values.pipeline_id);
|
||||
onSubmit?.(pipeline);
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
className="!w-[560px]"
|
||||
title={
|
||||
!isEdit
|
||||
? t('knowledgeConfiguration.linkDataPipeline')
|
||||
: t('knowledgeConfiguration.eidtLinkDataPipeline')
|
||||
}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
showfooter={false}
|
||||
>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleFormSubmit)}>
|
||||
<div className="flex flex-col gap-4 ">
|
||||
{!isEdit && (
|
||||
<DataFlowSelect
|
||||
toDataPipeline={navigateToAgents}
|
||||
formFieldName="pipeline_id"
|
||||
setDataList={setList}
|
||||
/>
|
||||
)}
|
||||
{/* <FormField
|
||||
control={form.control}
|
||||
name={'file_filter'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-2 justify-between ">
|
||||
<FormLabel
|
||||
tooltip={t('knowledgeConfiguration.fileFilterTip')}
|
||||
className="text-sm text-text-primary whitespace-wrap "
|
||||
>
|
||||
{t('knowledgeConfiguration.fileFilter')}
|
||||
</FormLabel>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t(
|
||||
'knowledgeConfiguration.filterPlaceholder',
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-full"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{isEdit && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'set_default'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-2 justify-between ">
|
||||
<FormLabel
|
||||
tooltip={t('knowledgeConfiguration.setDefaultTip')}
|
||||
className="text-sm text-text-primary whitespace-wrap "
|
||||
>
|
||||
{t('knowledgeConfiguration.setDefault')}
|
||||
</FormLabel>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<Switch
|
||||
value={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-full"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)} */}
|
||||
<div className="flex justify-end gap-1">
|
||||
<Button
|
||||
type="button"
|
||||
variant={'outline'}
|
||||
className="btn-primary"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{t('modal.cancelText')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={'default'}
|
||||
className="btn-primary"
|
||||
onClick={form.handleSubmit(handleFormSubmit)}
|
||||
>
|
||||
{t('modal.okText')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default LinkDataPipelineModal;
|
||||
@ -9,6 +9,9 @@ export function ConfigurationFormContainer({
|
||||
return <section className={cn('space-y-4', className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function MainContainer({ children }: PropsWithChildren) {
|
||||
return <section className="space-y-5">{children}</section>;
|
||||
export function MainContainer({
|
||||
children,
|
||||
className,
|
||||
}: PropsWithChildren & { className?: string }) {
|
||||
return <section className={cn('space-y-5', className)}>{children}</section>;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
@ -10,15 +11,18 @@ import { RAGFlowSelect } from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ArrowUpRight } from 'lucide-react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import {
|
||||
useHasParsedDocument,
|
||||
useSelectChunkMethodList,
|
||||
useSelectEmbeddingModelOptions,
|
||||
} from '../hooks';
|
||||
|
||||
export function ChunkMethodItem() {
|
||||
interface IProps {
|
||||
line?: 1 | 2;
|
||||
isEdit?: boolean;
|
||||
}
|
||||
export function ChunkMethodItem(props: IProps) {
|
||||
const { line } = props;
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
// const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form);
|
||||
@ -29,28 +33,29 @@ export function ChunkMethodItem() {
|
||||
control={form.control}
|
||||
name={'parser_id'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<FormItem className=" items-center space-y-1">
|
||||
<div className={line === 1 ? 'flex items-center' : ''}>
|
||||
<FormLabel
|
||||
required
|
||||
tooltip={t('chunkMethodTip')}
|
||||
className="text-sm text-muted-foreground whitespace-wrap w-1/4"
|
||||
className={cn('text-sm', {
|
||||
'w-1/4 whitespace-pre-wrap': line === 1,
|
||||
})}
|
||||
>
|
||||
{t('chunkMethod')}
|
||||
{t('dataPipeline')}
|
||||
</FormLabel>
|
||||
<div className="w-3/4 ">
|
||||
<div className={line === 1 ? 'w-3/4 ' : 'w-full'}>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
options={parserList}
|
||||
placeholder={t('chunkMethodPlaceholder')}
|
||||
// onChange={handleChunkMethodSelectChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<div className={line === 1 ? 'w-1/4' : ''}></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
@ -58,49 +63,55 @@ export function ChunkMethodItem() {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmbeddingModelItem({ line = 1 }: { line?: 1 | 2 }) {
|
||||
export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
const embeddingModelOptions = useSelectEmbeddingModelOptions();
|
||||
const disabled = useHasParsedDocument();
|
||||
|
||||
const disabled = useHasParsedDocument(isEdit);
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'embd_id'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className={cn({ 'flex items-center': line === 1 })}>
|
||||
<FormLabel
|
||||
required
|
||||
tooltip={t('embeddingModelTip')}
|
||||
className={cn('text-sm whitespace-wrap ', {
|
||||
'w-1/4': line === 1,
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'embd_id'}
|
||||
render={({ field }) => (
|
||||
<FormItem className={cn(' items-center space-y-0 ')}>
|
||||
<div
|
||||
className={cn('flex', {
|
||||
' items-center': line === 1,
|
||||
'flex-col gap-1': line === 2,
|
||||
})}
|
||||
>
|
||||
{t('embeddingModel')}
|
||||
</FormLabel>
|
||||
<div
|
||||
className={cn('text-muted-foreground', { 'w-3/4': line === 1 })}
|
||||
>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
options={embeddingModelOptions}
|
||||
disabled={disabled}
|
||||
placeholder={t('embeddingModelPlaceholder')}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel
|
||||
required
|
||||
tooltip={t('embeddingModelTip')}
|
||||
className={cn('text-sm whitespace-wrap ', {
|
||||
'w-1/4': line === 1,
|
||||
})}
|
||||
>
|
||||
{t('embeddingModel')}
|
||||
</FormLabel>
|
||||
<div
|
||||
className={cn('text-muted-foreground', { 'w-3/4': line === 1 })}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectWithSearch
|
||||
onChange={field.onChange}
|
||||
value={field.value}
|
||||
options={embeddingModelOptions}
|
||||
disabled={isEdit ? disabled : false}
|
||||
placeholder={t('embeddingModelPlaceholder')}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex pt-1">
|
||||
<div className={line === 1 ? 'w-1/4' : ''}></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,155 +153,6 @@ export function ParseTypeItem() {
|
||||
);
|
||||
}
|
||||
|
||||
export function DataFlowItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'data_flow'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="">
|
||||
<div className="flex gap-2 justify-between ">
|
||||
<FormLabel
|
||||
tooltip={t('dataFlowTip')}
|
||||
className="text-sm text-text-primary whitespace-wrap "
|
||||
>
|
||||
{t('dataFlow')}
|
||||
</FormLabel>
|
||||
<div className="text-sm flex text-text-primary">
|
||||
{t('buildItFromScratch')}
|
||||
<ArrowUpRight size={14} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
placeholder={t('dataFlowPlaceholder')}
|
||||
options={[{ value: '0', label: t('dataFlowDefault') }]}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function DataExtractKnowledgeItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
{' '}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'extractKnowledgeGraph'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="">
|
||||
<FormLabel
|
||||
tooltip={t('extractKnowledgeGraphTip')}
|
||||
className="text-sm whitespace-wrap "
|
||||
>
|
||||
{t('extractKnowledgeGraph')}
|
||||
</FormLabel>
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>{' '}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'useRAPTORToEnhanceRetrieval'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="">
|
||||
<FormLabel
|
||||
tooltip={t('useRAPTORToEnhanceRetrievalTip')}
|
||||
className="text-sm whitespace-wrap "
|
||||
>
|
||||
{t('useRAPTORToEnhanceRetrieval')}
|
||||
</FormLabel>
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function TeamItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'team'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="">
|
||||
<FormLabel
|
||||
tooltip={t('teamTip')}
|
||||
className="text-sm whitespace-wrap "
|
||||
>
|
||||
<span className="text-destructive mr-1"> *</span>
|
||||
{t('team')}
|
||||
</FormLabel>
|
||||
<div className="text-muted-foreground">
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
placeholder={t('teamPlaceholder')}
|
||||
options={[{ value: '0', label: t('teamDefault') }]}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function EnableAutoGenerateItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
|
||||
@ -11,6 +11,9 @@ export const formSchema = z.object({
|
||||
avatar: z.any().nullish(),
|
||||
permission: z.string().optional(),
|
||||
parser_id: z.string(),
|
||||
pipeline_id: z.string().optional(),
|
||||
pipeline_name: z.string().optional(),
|
||||
pipeline_avatar: z.string().optional(),
|
||||
embd_id: z.string(),
|
||||
parser_config: z
|
||||
.object({
|
||||
@ -71,3 +74,18 @@ export const formSchema = z.object({
|
||||
pagerank: z.number(),
|
||||
// icon: z.array(z.instanceof(File)),
|
||||
});
|
||||
|
||||
export const pipelineFormSchema = z.object({
|
||||
pipeline_id: z.string().optional(),
|
||||
set_default: z.boolean().optional(),
|
||||
file_filter: z.string().optional(),
|
||||
});
|
||||
|
||||
// export const linkPiplineFormSchema = pipelineFormSchema.pick({
|
||||
// pipeline_id: true,
|
||||
// file_filter: true,
|
||||
// });
|
||||
// export const editPiplineFormSchema = pipelineFormSchema.pick({
|
||||
// set_default: true,
|
||||
// file_filter: true,
|
||||
// });
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { AvatarUpload } from '@/components/avatar-upload';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
@ -9,6 +10,7 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TagItems } from './components/tag-item';
|
||||
import { EmbeddingModelItem } from './configuration/common-item';
|
||||
import { PermissionFormField } from './permission-form-field';
|
||||
|
||||
@ -17,14 +19,14 @@ export function GeneralForm() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem className="items-center space-y-0">
|
||||
<div className="flex">
|
||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||
<FormLabel className="text-sm whitespace-nowrap w-1/4">
|
||||
<span className="text-red-600">*</span>
|
||||
{t('common.name')}
|
||||
</FormLabel>
|
||||
@ -45,7 +47,7 @@ export function GeneralForm() {
|
||||
render={({ field }) => (
|
||||
<FormItem className="items-center space-y-0">
|
||||
<div className="flex">
|
||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||
<FormLabel className="text-sm whitespace-nowrap w-1/4">
|
||||
{t('setting.avatar')}
|
||||
</FormLabel>
|
||||
<FormControl className="w-3/4">
|
||||
@ -70,7 +72,7 @@ export function GeneralForm() {
|
||||
return (
|
||||
<FormItem className="items-center space-y-0">
|
||||
<div className="flex">
|
||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||
<FormLabel className="text-sm whitespace-nowrap w-1/4">
|
||||
{t('flow.description')}
|
||||
</FormLabel>
|
||||
<FormControl className="w-3/4">
|
||||
@ -87,6 +89,9 @@ export function GeneralForm() {
|
||||
/>
|
||||
<PermissionFormField></PermissionFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
</section>
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,8 +25,10 @@ export function useSelectEmbeddingModelOptions() {
|
||||
return allOptions[LlmModelType.Embedding];
|
||||
}
|
||||
|
||||
export function useHasParsedDocument() {
|
||||
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration();
|
||||
export function useHasParsedDocument(isEdit?: boolean) {
|
||||
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration({
|
||||
isEdit,
|
||||
});
|
||||
return knowledgeDetails.chunk_num > 0;
|
||||
}
|
||||
|
||||
@ -39,6 +41,16 @@ export const useFetchKnowledgeConfigurationOnMount = (
|
||||
const parser_config = {
|
||||
...form.formState?.defaultValues?.parser_config,
|
||||
...knowledgeDetails.parser_config,
|
||||
raptor: {
|
||||
...form.formState?.defaultValues?.parser_config?.raptor,
|
||||
...knowledgeDetails.parser_config?.raptor,
|
||||
use_raptor: true,
|
||||
},
|
||||
graphrag: {
|
||||
...form.formState?.defaultValues?.parser_config?.graphrag,
|
||||
...knowledgeDetails.parser_config?.graphrag,
|
||||
use_graphrag: true,
|
||||
},
|
||||
};
|
||||
const formValues = {
|
||||
...pick({ ...knowledgeDetails, parser_config: parser_config }, [
|
||||
@ -52,7 +64,7 @@ export const useFetchKnowledgeConfigurationOnMount = (
|
||||
'pagerank',
|
||||
'avatar',
|
||||
]),
|
||||
};
|
||||
} as z.infer<typeof formSchema>;
|
||||
form.reset(formValues);
|
||||
}, [form, knowledgeDetails]);
|
||||
|
||||
|
||||
@ -1,16 +1,29 @@
|
||||
import { IDataPipelineSelectNode } from '@/components/data-pipeline-select';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import Divider from '@/components/ui/divider';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { DocumentParserType } from '@/constants/knowledge';
|
||||
import { PermissionRole } from '@/constants/permission';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import { TopTitle } from '../dataset-title';
|
||||
import { ChunkMethodForm } from './chunk-method-form';
|
||||
import {
|
||||
GenerateType,
|
||||
IGenerateLogButtonProps,
|
||||
} from '../dataset/generate-button/generate';
|
||||
import LinkDataPipeline, {
|
||||
IDataPipelineNodeProps,
|
||||
} from './components/link-data-pipeline';
|
||||
import { MainContainer } from './configuration-form-container';
|
||||
import { formSchema } from './form-schema';
|
||||
import { GeneralForm } from './general-form';
|
||||
import { useFetchKnowledgeConfigurationOnMount } from './hooks';
|
||||
|
||||
import { SavingButton } from './saving-button';
|
||||
const enum DocumentType {
|
||||
DeepDOC = 'DeepDOC',
|
||||
PlainText = 'Plain Text',
|
||||
@ -46,24 +59,84 @@ export default function DatasetSettings() {
|
||||
html4excel: false,
|
||||
topn_tags: 3,
|
||||
raptor: {
|
||||
use_raptor: false,
|
||||
use_raptor: true,
|
||||
max_token: 256,
|
||||
threshold: 0.1,
|
||||
max_cluster: 64,
|
||||
random_seed: 0,
|
||||
prompt: t('knowledgeConfiguration.promptText'),
|
||||
},
|
||||
graphrag: {
|
||||
use_graphrag: false,
|
||||
use_graphrag: true,
|
||||
entity_types: initialEntityTypes,
|
||||
method: MethodValue.Light,
|
||||
},
|
||||
},
|
||||
pipeline_id: '',
|
||||
pagerank: 0,
|
||||
},
|
||||
});
|
||||
|
||||
useFetchKnowledgeConfigurationOnMount(form);
|
||||
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
|
||||
|
||||
const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>();
|
||||
const [graphRagGenerateData, setGraphRagGenerateData] =
|
||||
useState<IGenerateLogButtonProps>();
|
||||
const [raptorGenerateData, setRaptorGenerateData] =
|
||||
useState<IGenerateLogButtonProps>();
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails);
|
||||
if (knowledgeDetails) {
|
||||
const data: IDataPipelineNodeProps = {
|
||||
id: knowledgeDetails.pipeline_id,
|
||||
name: knowledgeDetails.pipeline_name,
|
||||
avatar: knowledgeDetails.pipeline_avatar,
|
||||
linked: true,
|
||||
};
|
||||
setPipelineData(data);
|
||||
setGraphRagGenerateData({
|
||||
finish_at: knowledgeDetails.mindmap_task_finish_at,
|
||||
task_id: knowledgeDetails.mindmap_task_id,
|
||||
} as IGenerateLogButtonProps);
|
||||
setRaptorGenerateData({
|
||||
finish_at: knowledgeDetails.raptor_task_finish_at,
|
||||
task_id: knowledgeDetails.raptor_task_id,
|
||||
} as IGenerateLogButtonProps);
|
||||
}
|
||||
}, [knowledgeDetails]);
|
||||
|
||||
async function onSubmit(data: z.infer<typeof formSchema>) {
|
||||
console.log('🚀 ~ DatasetSettings ~ data:', data);
|
||||
try {
|
||||
console.log('Form validation passed, submit data', data);
|
||||
} catch (error) {
|
||||
console.error('An error occurred during submission:', error);
|
||||
}
|
||||
}
|
||||
const handleLinkOrEditSubmit = (
|
||||
data: IDataPipelineSelectNode | undefined,
|
||||
) => {
|
||||
console.log('🚀 ~ DatasetSettings ~ data:', data);
|
||||
if (data) {
|
||||
setPipelineData(data);
|
||||
form.setValue('pipeline_id', data.id || '');
|
||||
// form.setValue('pipeline_name', data.name || '');
|
||||
// form.setValue('pipeline_avatar', data.avatar || '');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeletePipelineTask = (type: GenerateType) => {
|
||||
if (type === GenerateType.KnowledgeGraph) {
|
||||
setGraphRagGenerateData({
|
||||
finish_at: '',
|
||||
task_id: '',
|
||||
} as IGenerateLogButtonProps);
|
||||
} else if (type === GenerateType.Raptor) {
|
||||
setRaptorGenerateData({
|
||||
finish_at: '',
|
||||
task_id: '',
|
||||
} as IGenerateLogButtonProps);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<section className="p-5 h-full flex flex-col">
|
||||
<TopTitle
|
||||
@ -76,9 +149,41 @@ export default function DatasetSettings() {
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6 flex-1"
|
||||
>
|
||||
<div className="w-[768px]">
|
||||
<GeneralForm></GeneralForm>
|
||||
<ChunkMethodForm></ChunkMethodForm>
|
||||
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
|
||||
<MainContainer className="text-text-secondary">
|
||||
<GeneralForm></GeneralForm>
|
||||
<Divider />
|
||||
|
||||
<GraphRagItems
|
||||
className="border-none p-0"
|
||||
data={graphRagGenerateData as IGenerateLogButtonProps}
|
||||
onDelete={() =>
|
||||
handleDeletePipelineTask(GenerateType.KnowledgeGraph)
|
||||
}
|
||||
></GraphRagItems>
|
||||
<Divider />
|
||||
<RaptorFormFields
|
||||
data={raptorGenerateData as IGenerateLogButtonProps}
|
||||
onDelete={() => handleDeletePipelineTask(GenerateType.Raptor)}
|
||||
></RaptorFormFields>
|
||||
<Divider />
|
||||
<LinkDataPipeline
|
||||
data={pipelineData}
|
||||
handleLinkOrEditSubmit={handleLinkOrEditSubmit}
|
||||
/>
|
||||
</MainContainer>
|
||||
</div>
|
||||
<div className="text-right items-center flex justify-end gap-3 w-[768px]">
|
||||
<Button
|
||||
type="reset"
|
||||
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
}}
|
||||
>
|
||||
{t('knowledgeConfiguration.cancel')}
|
||||
</Button>
|
||||
<SavingButton></SavingButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import {
|
||||
ConfigurationFormContainer,
|
||||
MainContainer,
|
||||
} from './configuration-form-container';
|
||||
import { EnableAutoGenerateItem } from './configuration/common-item';
|
||||
|
||||
export function NaiveConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<GraphRagItems className="border-none p-0"></GraphRagItems>
|
||||
<ConfigurationFormContainer>
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
</ConfigurationFormContainer>
|
||||
<EnableAutoGenerateItem />
|
||||
{/* <ConfigurationFormContainer>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
|
||||
<MaxTokenNumberFormField initialValue={512}></MaxTokenNumberFormField>
|
||||
<DelimiterFormField></DelimiterFormField>
|
||||
</ConfigurationFormContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<PageRankFormField></PageRankFormField>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
<ExcelToHtmlFormField></ExcelToHtmlFormField>
|
||||
<TagItems></TagItems>
|
||||
</ConfigurationFormContainer> */}
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
@ -62,7 +62,7 @@ export function SavingButton() {
|
||||
if (beValid) {
|
||||
form.handleSubmit(async (values) => {
|
||||
console.log('saveKnowledgeConfiguration: ', values);
|
||||
delete values['avatar'];
|
||||
// delete values['avatar'];
|
||||
await saveKnowledgeConfiguration({
|
||||
kb_id,
|
||||
...values,
|
||||
|
||||
Reference in New Issue
Block a user