Fix: Added table of contents extraction functionality and optimized form item layout #9869 (#10492)

### What problem does this PR solve?

Fix: Added table of contents extraction functionality and optimized form
item layout #9869

- Added `EnableTocToggle` component to toggle table of contents
extraction on and off
- Added multiple parser configuration components (such as naive, book,
laws, etc.), displaying different parser components based on built-in
slicing methods

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-10-11 18:45:55 +08:00
committed by GitHub
parent 6a0f448419
commit c21cea2038
35 changed files with 694 additions and 173 deletions

View File

@ -20,6 +20,7 @@ import { IParserConfig } from '@/interfaces/database/document';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { import {
ChunkMethodItem, ChunkMethodItem,
EnableTocToggle,
ParseTypeItem, ParseTypeItem,
} from '@/pages/dataset/dataset-setting/configuration/common-item'; } from '@/pages/dataset/dataset-setting/configuration/common-item';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -113,6 +114,7 @@ export function ChunkMethodDialog({
auto_keywords: z.coerce.number().optional(), auto_keywords: z.coerce.number().optional(),
auto_questions: z.coerce.number().optional(), auto_questions: z.coerce.number().optional(),
html4excel: z.boolean().optional(), html4excel: z.boolean().optional(),
toc_extraction: z.boolean().optional(),
// raptor: z // raptor: z
// .object({ // .object({
// use_raptor: z.boolean().optional(), // use_raptor: z.boolean().optional(),
@ -247,7 +249,7 @@ export function ChunkMethodDialog({
}, [parseType, form]); }, [parseType, form]);
return ( return (
<Dialog open onOpenChange={hideModal}> <Dialog open onOpenChange={hideModal}>
<DialogContent className="max-w-[50vw]"> <DialogContent className="max-w-[50vw] text-text-primary">
<DialogHeader> <DialogHeader>
<DialogTitle>{t('knowledgeDetails.chunkMethod')}</DialogTitle> <DialogTitle>{t('knowledgeDetails.chunkMethod')}</DialogTitle>
</DialogHeader> </DialogHeader>
@ -338,6 +340,7 @@ export function ChunkMethodDialog({
show={showAutoKeywords(selectedTag) || showExcelToHtml} show={showAutoKeywords(selectedTag) || showExcelToHtml}
className="space-y-3" className="space-y-3"
> >
<EnableTocToggle />
{showAutoKeywords(selectedTag) && ( {showAutoKeywords(selectedTag) && (
<> <>
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>

View File

@ -15,6 +15,7 @@ export function useDefaultParserValues() {
auto_keywords: 0, auto_keywords: 0,
auto_questions: 0, auto_questions: 0,
html4excel: false, html4excel: false,
toc_extraction: false,
// raptor: { // raptor: {
// use_raptor: false, // use_raptor: false,
// prompt: t('knowledgeConfiguration.promptText'), // prompt: t('knowledgeConfiguration.promptText'),

View File

@ -1,5 +1,7 @@
import { AgentCategory } from '@/constants/agent'; import { AgentCategory } from '@/constants/agent';
import { FormLayout } from '@/constants/form';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchAgentList } from '@/hooks/use-agent-request'; import { useFetchAgentList } from '@/hooks/use-agent-request';
import { buildSelectOptions } from '@/utils/component-util'; import { buildSelectOptions } from '@/utils/component-util';
import { ArrowUpRight } from 'lucide-react'; import { ArrowUpRight } from 'lucide-react';
@ -21,18 +23,27 @@ export interface IDataPipelineSelectNode {
} }
interface IProps { interface IProps {
toDataPipeline?: () => void; showToDataPipeline?: boolean;
formFieldName: string; formFieldName: string;
isMult?: boolean; isMult?: boolean;
setDataList?: (data: IDataPipelineSelectNode[]) => void; setDataList?: (data: IDataPipelineSelectNode[]) => void;
layout?: FormLayout;
} }
export function DataFlowSelect(props: IProps) { export function DataFlowSelect(props: IProps) {
const { toDataPipeline, formFieldName, isMult = false, setDataList } = props; const {
showToDataPipeline,
formFieldName,
isMult = false,
setDataList,
layout = FormLayout.Vertical,
} = props;
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
const { navigateToAgents } = useNavigatePage();
const toDataPipLine = () => { const toDataPipLine = () => {
toDataPipeline?.(); navigateToAgents();
}; };
const { data: dataPipelineOptions } = useFetchAgentList({ const { data: dataPipelineOptions } = useFetchAgentList({
canvas_category: AgentCategory.DataflowCanvas, canvas_category: AgentCategory.DataflowCanvas,
@ -69,47 +80,92 @@ export function DataFlowSelect(props: IProps) {
name={formFieldName} name={formFieldName}
render={({ field }) => ( render={({ field }) => (
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex flex-col gap-1"> {layout === FormLayout.Vertical && (
<div className="flex gap-2 justify-between "> <div className="flex flex-col gap-1">
<FormLabel <div className="flex gap-2 justify-between ">
tooltip={t('dataFlowTip')} <FormLabel
className="text-sm text-text-primary whitespace-wrap " // tooltip={t('dataFlowTip')}
> className="text-sm text-text-primary whitespace-wrap "
{t('dataPipeline')}
</FormLabel>
{toDataPipeline && (
<div
className="text-sm flex text-text-primary cursor-pointer"
onClick={toDataPipLine}
> >
{t('buildItFromScratch')} {t('manualSetup')}
<ArrowUpRight size={14} /> </FormLabel>
</div> {showToDataPipeline && (
)} <div
</div> className="text-sm flex text-text-primary cursor-pointer"
onClick={toDataPipLine}
>
{t('buildItFromScratch')}
<ArrowUpRight size={14} />
</div>
)}
</div>
<div className="text-muted-foreground"> <div className="text-muted-foreground">
<FormControl> <FormControl>
<> <>
{!isMult && ( {!isMult && (
<SelectWithSearch <SelectWithSearch
{...field} {...field}
placeholder={t('dataFlowPlaceholder')} placeholder={t('dataFlowPlaceholder')}
options={options} options={options}
/> />
)} )}
{isMult && ( {isMult && (
<MultiSelect <MultiSelect
{...field} {...field}
onValueChange={field.onChange} onValueChange={field.onChange}
placeholder={t('dataFlowPlaceholder')} placeholder={t('dataFlowPlaceholder')}
options={options} options={options}
/> />
)} )}
</> </>
</FormControl> </FormControl>
</div>
</div> </div>
</div> )}
{layout === FormLayout.Horizontal && (
<div className="flex gap-1 items-center">
<div className="flex gap-2 justify-between w-1/4">
<FormLabel
// tooltip={t('dataFlowTip')}
className="text-sm text-text-secondary whitespace-wrap "
>
{t('manualSetup')}
</FormLabel>
</div>
<div className="text-muted-foreground w-3/4 flex flex-col items-end">
{showToDataPipeline && (
<div
className="text-sm flex text-text-primary cursor-pointer"
onClick={toDataPipLine}
>
{t('buildItFromScratch')}
<ArrowUpRight size={14} />
</div>
)}
<FormControl>
<>
{!isMult && (
<SelectWithSearch
{...field}
placeholder={t('dataFlowPlaceholder')}
options={options}
/>
)}
{isMult && (
<MultiSelect
{...field}
onValueChange={field.onChange}
placeholder={t('dataFlowPlaceholder')}
options={options}
/>
)}
</>
</FormControl>
</div>
</div>
)}
<div className="flex pt-1"> <div className="flex pt-1">
<FormMessage /> <FormMessage />
</div> </div>

View File

@ -61,7 +61,7 @@ export function DelimiterFormField() {
<FormLabel <FormLabel
required required
tooltip={t('knowledgeDetails.delimiterTip')} tooltip={t('knowledgeDetails.delimiterTip')}
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4" className="text-sm text-text-secondary whitespace-break-spaces w-1/4"
> >
{t('knowledgeDetails.delimiter')} {t('knowledgeDetails.delimiter')}
</FormLabel> </FormLabel>

View File

@ -28,7 +28,7 @@ export function ExcelToHtmlFormField() {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FormLabel <FormLabel
tooltip={t('html4excelTip')} tooltip={t('html4excelTip')}
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4" className="text-sm text-text-secondary whitespace-break-spaces w-1/4"
> >
{t('html4excel')} {t('html4excel')}
</FormLabel> </FormLabel>

View File

@ -79,7 +79,7 @@ export function LayoutRecognizeFormField({
> >
<FormLabel <FormLabel
tooltip={t('layoutRecognizeTip')} tooltip={t('layoutRecognizeTip')}
className={cn('text-sm text-muted-foreground whitespace-wrap', { className={cn('text-sm text-text-secondary whitespace-wrap', {
['w-1/4']: horizontal, ['w-1/4']: horizontal,
})} })}
> >

View File

@ -17,7 +17,7 @@ export function MaxTokenNumberFormField({ max = 2048, initialValue }: IProps) {
tooltip={t('chunkTokenNumberTip')} tooltip={t('chunkTokenNumberTip')}
max={max} max={max}
defaultValue={initialValue ?? 0} defaultValue={initialValue ?? 0}
layout={FormLayout.Vertical} layout={FormLayout.Horizontal}
></SliderInputFormField> ></SliderInputFormField>
); );
} }

View File

@ -36,7 +36,7 @@ export function SliderInputFormField({
tooltip, tooltip,
defaultValue, defaultValue,
className, className,
layout = FormLayout.Vertical, layout = FormLayout.Horizontal,
}: SliderInputFormFieldProps) { }: SliderInputFormFieldProps) {
const form = useFormContext(); const form = useFormContext();

View File

@ -1,5 +1,4 @@
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Radio as LucideRadio } from 'lucide-react';
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
const RadioGroupContext = React.createContext<{ const RadioGroupContext = React.createContext<{
@ -57,7 +56,7 @@ function Radio({ value, checked, disabled, onChange, children }: RadioProps) {
onClick={handleClick} onClick={handleClick}
> >
{isChecked && ( {isChecked && (
<LucideRadio className="h-3 w-3 fill-primary text-primary" /> <div className="h-3 w-3 fill-primary text-primary bg-text-primary rounded-full" />
)} )}
</span> </span>
{children && <span className="text-foreground">{children}</span>} {children && <span className="text-foreground">{children}</span>}

View File

@ -19,7 +19,8 @@ export const useNavigatePage = () => {
const navigateToDataset = useCallback( const navigateToDataset = useCallback(
(id: string) => () => { (id: string) => () => {
navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`); // navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`);
navigate(`${Routes.Dataset}/${id}`);
}, },
[navigate], [navigate],
); );

View File

@ -126,8 +126,8 @@ export default {
startDate: 'Start Date', startDate: 'Start Date',
source: 'Source', source: 'Source',
fileName: 'File Name', fileName: 'File Name',
datasetLogs: 'Dataset Logs', datasetLogs: 'Dataset',
fileLogs: 'File Logs', fileLogs: 'File',
overview: 'Overview', overview: 'Overview',
success: 'Success', success: 'Success',
failed: 'Failed', failed: 'Failed',
@ -270,6 +270,9 @@ export default {
reRankModelWaring: 'Re-rank model is very time consuming.', reRankModelWaring: 'Re-rank model is very time consuming.',
}, },
knowledgeConfiguration: { knowledgeConfiguration: {
tocExtraction: 'toc toggle',
tocExtractionTip:
" For existing chunks, generate a hierarchical table of contents (one directory per file). During queries, when Directory Enhancement is activated, the system will use a large model to determine which directory items are relevant to the user's question, thereby identifying the relevant chunks.",
deleteGenerateModalContent: ` deleteGenerateModalContent: `
<p>Deleting the generated <strong class='text-text-primary'>{{type}}</strong> results <p>Deleting the generated <strong class='text-text-primary'>{{type}}</strong> results
will remove all derived entities and relationships from this dataset. will remove all derived entities and relationships from this dataset.

View File

@ -114,8 +114,8 @@ export default {
startDate: '开始时间', startDate: '开始时间',
source: '来源', source: '来源',
fileName: '文件名', fileName: '文件名',
datasetLogs: '数据集日志', datasetLogs: '数据集',
fileLogs: '文件日志', fileLogs: '文件',
overview: '概览', overview: '概览',
success: '成功', success: '成功',
failed: '失败', failed: '失败',
@ -255,6 +255,9 @@ export default {
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
}, },
knowledgeConfiguration: { knowledgeConfiguration: {
tocExtraction: '目录提取',
tocExtractionTip:
'对于已有的chunk生成层级结构的目录信息每个文件一个目录。在查询时激活`目录增强`后系统会用大模型去判断用户问题和哪些目录项相关从而找到相关的chunk。',
deleteGenerateModalContent: ` deleteGenerateModalContent: `
<p>删除生成的 <strong class='text-text-primary'>{{type}}</strong> 结果 <p>删除生成的 <strong class='text-text-primary'>{{type}}</strong> 结果
将从此数据集中移除所有派生实体和关系。 将从此数据集中移除所有派生实体和关系。

View File

@ -0,0 +1,64 @@
import { useFormContext, useWatch } from 'react-hook-form';
import { DocumentParserType } from '@/constants/knowledge';
import { useMemo } from 'react';
import { AudioConfiguration } from './configuration/audio';
import { BookConfiguration } from './configuration/book';
import { EmailConfiguration } from './configuration/email';
import { KnowledgeGraphConfiguration } from './configuration/knowledge-graph';
import { LawsConfiguration } from './configuration/laws';
import { ManualConfiguration } from './configuration/manual';
import { NaiveConfiguration } from './configuration/naive';
import { OneConfiguration } from './configuration/one';
import { PaperConfiguration } from './configuration/paper';
import { PictureConfiguration } from './configuration/picture';
import { PresentationConfiguration } from './configuration/presentation';
import { QAConfiguration } from './configuration/qa';
import { ResumeConfiguration } from './configuration/resume';
import { TableConfiguration } from './configuration/table';
import { TagConfiguration } from './configuration/tag';
const ConfigurationComponentMap = {
[DocumentParserType.Naive]: NaiveConfiguration,
[DocumentParserType.Qa]: QAConfiguration,
[DocumentParserType.Resume]: ResumeConfiguration,
[DocumentParserType.Manual]: ManualConfiguration,
[DocumentParserType.Table]: TableConfiguration,
[DocumentParserType.Paper]: PaperConfiguration,
[DocumentParserType.Book]: BookConfiguration,
[DocumentParserType.Laws]: LawsConfiguration,
[DocumentParserType.Presentation]: PresentationConfiguration,
[DocumentParserType.Picture]: PictureConfiguration,
[DocumentParserType.One]: OneConfiguration,
[DocumentParserType.Audio]: AudioConfiguration,
[DocumentParserType.Email]: EmailConfiguration,
[DocumentParserType.Tag]: TagConfiguration,
[DocumentParserType.KnowledgeGraph]: KnowledgeGraphConfiguration,
};
function EmptyComponent() {
return <div></div>;
}
export function ChunkMethodForm() {
const form = useFormContext();
const finalParserId: DocumentParserType = useWatch({
control: form.control,
name: 'parser_id',
});
const ConfigurationComponent = useMemo(() => {
return finalParserId
? ConfigurationComponentMap[finalParserId]
: EmptyComponent;
}, [finalParserId]);
return (
<section className="h-full flex flex-col">
<div className="overflow-auto flex-1 min-h-0">
<ConfigurationComponent></ConfigurationComponent>
</div>
</section>
);
}

View File

@ -8,8 +8,9 @@ import {
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { MultiSelect } from '@/components/ui/multi-select'; import { MultiSelect } from '@/components/ui/multi-select';
import { FormLayout } from '@/constants/form';
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { Flex, Form, InputNumber, Select, Slider, Space } from 'antd'; import { Form, Select, Space } from 'antd';
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import { useFormContext, useWatch } from 'react-hook-form'; import { useFormContext, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -44,7 +45,7 @@ export const TagSetItem = () => {
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
className="text-sm text-muted-foreground whitespace-nowrap w-1/4" className="text-sm text-text-secondary whitespace-nowrap w-1/4"
tooltip={ tooltip={
<div <div
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
@ -116,27 +117,9 @@ export const TopNTagsItem = () => {
max={10} max={10}
min={1} min={1}
defaultValue={3} defaultValue={3}
layout={FormLayout.Horizontal}
></SliderInputFormField> ></SliderInputFormField>
); );
return (
<Form.Item label={t('knowledgeConfiguration.topnTags')}>
<Flex gap={20} align="center">
<Flex flex={1}>
<Form.Item
name={['parser_config', 'topn_tags']}
noStyle
initialValue={3}
>
<Slider max={10} min={1} style={{ width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item name={['parser_config', 'topn_tags']} noStyle>
<InputNumber max={10} min={1} />
</Form.Item>
</Flex>
</Form.Item>
);
}; };
export function TagItems() { export function TagItems() {

View File

@ -0,0 +1,20 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { ConfigurationFormContainer } from '../configuration-form-container';
import { TagItems } from '../components/tag-item';
export function AudioConfiguration() {
return (
<ConfigurationFormContainer>
<>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,28 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
export function BookConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
</MainContainer>
);
}

View File

@ -42,7 +42,7 @@ export function ChunkMethodItem(props: IProps) {
'w-1/4 whitespace-pre-wrap': line === 1, 'w-1/4 whitespace-pre-wrap': line === 1,
})} })}
> >
{t('dataPipeline')} {t('builtIn')}
</FormLabel> </FormLabel>
<div className={line === 1 ? 'w-3/4 ' : 'w-full'}> <div className={line === 1 ? 'w-3/4 ' : 'w-full'}>
<FormControl> <FormControl>
@ -115,7 +115,7 @@ export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) {
); );
} }
export function ParseTypeItem() { export function ParseTypeItem({ line = 2 }: { line?: number }) {
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
@ -125,17 +125,26 @@ export function ParseTypeItem() {
name={'parseType'} name={'parseType'}
render={({ field }) => ( render={({ field }) => (
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className=""> <div
className={cn('flex', {
' items-center': line === 1,
'flex-col gap-1': line === 2,
})}
>
<FormLabel <FormLabel
tooltip={t('parseTypeTip')} // tooltip={t('parseTypeTip')}
className="text-sm whitespace-wrap " className={cn('text-sm whitespace-wrap ', {
'w-1/4': line === 1,
})}
> >
{t('parseType')} {t('parseType')}
</FormLabel> </FormLabel>
<div className="text-muted-foreground"> <div
className={cn('text-muted-foreground', { 'w-3/4': line === 1 })}
>
<FormControl> <FormControl>
<Radio.Group {...field}> <Radio.Group {...field}>
<div className="w-3/4 flex gap-2 justify-between text-muted-foreground"> <div className="w-1/2 flex gap-2 justify-between text-muted-foreground">
<Radio value={1}>{t('builtIn')}</Radio> <Radio value={1}>{t('builtIn')}</Radio>
<Radio value={2}>{t('manualSetup')}</Radio> <Radio value={2}>{t('manualSetup')}</Radio>
</div> </div>
@ -144,7 +153,7 @@ export function ParseTypeItem() {
</div> </div>
</div> </div>
<div className="flex pt-1"> <div className="flex pt-1">
<div className="w-1/4"></div> <div className={line === 1 ? 'w-1/4' : ''}></div>
<FormMessage /> <FormMessage />
</div> </div>
</FormItem> </FormItem>
@ -188,3 +197,39 @@ export function EnableAutoGenerateItem() {
/> />
); );
} }
export function EnableTocToggle() {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
return (
<FormField
control={form.control}
name={'parser_config.toc_extraction'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={t('tocExtractionTip')}
className="text-sm whitespace-wrap w-1/4"
>
{t('tocExtraction')}
</FormLabel>
<div className="text-muted-foreground w-3/4">
<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>
)}
/>
);
}

View File

@ -0,0 +1,18 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { TagItems } from '../components/tag-item';
import { ConfigurationFormContainer } from '../configuration-form-container';
export function EmailConfiguration() {
return (
<ConfigurationFormContainer>
<>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,15 @@
import { DelimiterFormField } from '@/components/delimiter-form-field';
import { EntityTypesFormField } from '@/components/entity-types-form-field';
import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field';
export function KnowledgeGraphConfiguration() {
return (
<>
<>
<EntityTypesFormField></EntityTypesFormField>
<MaxTokenNumberFormField max={8192 * 2}></MaxTokenNumberFormField>
<DelimiterFormField></DelimiterFormField>
</>
</>
);
}

View File

@ -0,0 +1,29 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
export function LawsConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
</MainContainer>
);
}

View File

@ -0,0 +1,27 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
export function ManualConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer>
<TagItems></TagItems>
</MainContainer>
);
}

View File

@ -0,0 +1,33 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { DelimiterFormField } from '@/components/delimiter-form-field';
import { ExcelToHtmlFormField } from '@/components/excel-to-html-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
import { EnableTocToggle } from './common-item';
export function NaiveConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
<MaxTokenNumberFormField initialValue={512}></MaxTokenNumberFormField>
<DelimiterFormField></DelimiterFormField>
<EnableTocToggle />
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
<ExcelToHtmlFormField></ExcelToHtmlFormField>
<TagItems></TagItems>
</ConfigurationFormContainer>
</MainContainer>
);
}

View File

@ -0,0 +1,21 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import { ConfigurationFormContainer } from '../configuration-form-container';
export function OneConfiguration() {
return (
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
<>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,28 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
export function PaperConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
</MainContainer>
);
}

View File

@ -0,0 +1,18 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { TagItems } from '../components/tag-item';
import { ConfigurationFormContainer } from '../configuration-form-container';
export function PictureConfiguration() {
return (
<ConfigurationFormContainer>
<>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,29 @@
import {
AutoKeywordsFormField,
AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { TagItems } from '../components/tag-item';
import {
ConfigurationFormContainer,
MainContainer,
} from '../configuration-form-container';
export function PresentationConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
</MainContainer>
);
}

View File

@ -0,0 +1,10 @@
import { TagItems } from '../components/tag-item';
import { ConfigurationFormContainer } from '../configuration-form-container';
export function QAConfiguration() {
return (
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,10 @@
import { TagItems } from '../components/tag-item';
import { ConfigurationFormContainer } from '../configuration-form-container';
export function ResumeConfiguration() {
return (
<ConfigurationFormContainer>
<TagItems></TagItems>
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,12 @@
import { ConfigurationFormContainer } from '../configuration-form-container';
export function TableConfiguration() {
return (
<ConfigurationFormContainer>
{/* <ChunkMethodItem></ChunkMethodItem>
<EmbeddingModelItem></EmbeddingModelItem>
<PageRankFormField></PageRankFormField> */}
</ConfigurationFormContainer>
);
}

View File

@ -0,0 +1,5 @@
import { ConfigurationFormContainer } from '../configuration-form-container';
export function TagConfiguration() {
return <ConfigurationFormContainer></ConfigurationFormContainer>;
}

View File

@ -1,79 +1,92 @@
import { t } from 'i18next';
import { z } from 'zod'; import { z } from 'zod';
export const formSchema = z.object({ export const formSchema = z
name: z.string().min(1, { .object({
message: 'Username must be at least 2 characters.', parseType: z.number(),
}), name: z.string().min(1, {
description: z.string().min(2, { message: 'Username must be at least 2 characters.',
message: 'Username must be at least 2 characters.', }),
}), description: z.string().min(2, {
// avatar: z.instanceof(File), message: 'Username must be at least 2 characters.',
avatar: z.any().nullish(), }),
permission: z.string().optional(), // avatar: z.instanceof(File),
parser_id: z.string(), avatar: z.any().nullish(),
pipeline_id: z.string().optional(), permission: z.string().optional(),
pipeline_name: z.string().optional(), parser_id: z.string(),
pipeline_avatar: z.string().optional(), pipeline_id: z.string().optional(),
embd_id: z.string(), pipeline_name: z.string().optional(),
parser_config: z pipeline_avatar: z.string().optional(),
.object({ embd_id: z.string(),
layout_recognize: z.string(), parser_config: z
chunk_token_num: z.number(), .object({
delimiter: z.string(), layout_recognize: z.string(),
auto_keywords: z.number().optional(), chunk_token_num: z.number(),
auto_questions: z.number().optional(), delimiter: z.string(),
html4excel: z.boolean(), auto_keywords: z.number().optional(),
tag_kb_ids: z.array(z.string()).nullish(), auto_questions: z.number().optional(),
topn_tags: z.number().optional(), html4excel: z.boolean(),
raptor: z tag_kb_ids: z.array(z.string()).nullish(),
.object({ topn_tags: z.number().optional(),
use_raptor: z.boolean().optional(), toc_extraction: z.boolean().optional(),
prompt: z.string().optional(), raptor: z
max_token: z.number().optional(), .object({
threshold: z.number().optional(), use_raptor: z.boolean().optional(),
max_cluster: z.number().optional(), prompt: z.string().optional(),
random_seed: z.number().optional(), max_token: z.number().optional(),
}) threshold: z.number().optional(),
.refine( max_cluster: z.number().optional(),
(data) => { random_seed: z.number().optional(),
if (data.use_raptor && !data.prompt) { })
return false; .refine(
} (data) => {
return true; if (data.use_raptor && !data.prompt) {
}, return false;
{ }
message: 'Prompt is required', return true;
path: ['prompt'], },
}, {
), message: 'Prompt is required',
graphrag: z path: ['prompt'],
.object({ },
use_graphrag: z.boolean().optional(), ),
entity_types: z.array(z.string()).optional(), graphrag: z
method: z.string().optional(), .object({
resolution: z.boolean().optional(), use_graphrag: z.boolean().optional(),
community: z.boolean().optional(), entity_types: z.array(z.string()).optional(),
}) method: z.string().optional(),
.refine( resolution: z.boolean().optional(),
(data) => { community: z.boolean().optional(),
if ( })
data.use_graphrag && .refine(
(!data.entity_types || data.entity_types.length === 0) (data) => {
) { if (
return false; data.use_graphrag &&
} (!data.entity_types || data.entity_types.length === 0)
return true; ) {
}, return false;
{ }
message: 'Please enter Entity types', return true;
path: ['entity_types'], },
}, {
), message: 'Please enter Entity types',
}) path: ['entity_types'],
.optional(), },
pagerank: z.number(), ),
// icon: z.array(z.instanceof(File)), })
}); .optional(),
pagerank: z.number(),
// icon: z.array(z.instanceof(File)),
})
.superRefine((data, ctx) => {
if (data.parseType === 2 && !data.pipeline_id) {
ctx.addIssue({
path: ['pipeline_id'],
message: t('common.pleaseSelect'),
code: 'custom',
});
}
});
export const pipelineFormSchema = z.object({ export const pipelineFormSchema = z.object({
pipeline_id: z.string().optional(), pipeline_id: z.string().optional(),

View File

@ -1,14 +1,18 @@
import { IDataPipelineSelectNode } from '@/components/data-pipeline-select'; import {
DataFlowSelect,
IDataPipelineSelectNode,
} from '@/components/data-pipeline-select';
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import Divider from '@/components/ui/divider'; import Divider from '@/components/ui/divider';
import { Form } from '@/components/ui/form'; import { Form } from '@/components/ui/form';
import { FormLayout } from '@/constants/form';
import { DocumentParserType } from '@/constants/knowledge'; import { DocumentParserType } from '@/constants/knowledge';
import { PermissionRole } from '@/constants/permission'; import { PermissionRole } from '@/constants/permission';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
import { TopTitle } from '../dataset-title'; import { TopTitle } from '../dataset-title';
@ -16,10 +20,10 @@ import {
GenerateType, GenerateType,
IGenerateLogButtonProps, IGenerateLogButtonProps,
} from '../dataset/generate-button/generate'; } from '../dataset/generate-button/generate';
import LinkDataPipeline, { import { ChunkMethodForm } from './chunk-method-form';
IDataPipelineNodeProps, import { IDataPipelineNodeProps } from './components/link-data-pipeline';
} from './components/link-data-pipeline';
import { MainContainer } from './configuration-form-container'; import { MainContainer } from './configuration-form-container';
import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item';
import { formSchema } from './form-schema'; import { formSchema } from './form-schema';
import { GeneralForm } from './general-form'; import { GeneralForm } from './general-form';
import { useFetchKnowledgeConfigurationOnMount } from './hooks'; import { useFetchKnowledgeConfigurationOnMount } from './hooks';
@ -44,6 +48,7 @@ const enum MethodValue {
export default function DatasetSettings() { export default function DatasetSettings() {
const { t } = useTranslation(); const { t } = useTranslation();
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
@ -58,6 +63,7 @@ export default function DatasetSettings() {
auto_questions: 0, auto_questions: 0,
html4excel: false, html4excel: false,
topn_tags: 3, topn_tags: 3,
toc_extraction: false,
raptor: { raptor: {
use_raptor: true, use_raptor: true,
max_token: 256, max_token: 256,
@ -73,17 +79,17 @@ export default function DatasetSettings() {
}, },
}, },
pipeline_id: '', pipeline_id: '',
parseType: 1,
pagerank: 0, pagerank: 0,
}, },
}); });
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form); const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>(); const [pipelineData, setPipelineData] = useState<IDataPipelineNodeProps>();
const [graphRagGenerateData, setGraphRagGenerateData] = const [graphRagGenerateData, setGraphRagGenerateData] =
useState<IGenerateLogButtonProps>(); useState<IGenerateLogButtonProps>();
const [raptorGenerateData, setRaptorGenerateData] = const [raptorGenerateData, setRaptorGenerateData] =
useState<IGenerateLogButtonProps>(); useState<IGenerateLogButtonProps>();
useEffect(() => { useEffect(() => {
console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails); console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails);
if (knowledgeDetails) { if (knowledgeDetails) {
@ -102,8 +108,10 @@ export default function DatasetSettings() {
finish_at: knowledgeDetails.raptor_task_finish_at, finish_at: knowledgeDetails.raptor_task_finish_at,
task_id: knowledgeDetails.raptor_task_id, task_id: knowledgeDetails.raptor_task_id,
} as IGenerateLogButtonProps); } as IGenerateLogButtonProps);
form.setValue('parseType', knowledgeDetails.pipeline_id ? 2 : 1);
form.setValue('pipeline_id', knowledgeDetails.pipeline_id || '');
} }
}, [knowledgeDetails]); }, [knowledgeDetails, form]);
async function onSubmit(data: z.infer<typeof formSchema>) { async function onSubmit(data: z.infer<typeof formSchema>) {
try { try {
@ -137,6 +145,22 @@ export default function DatasetSettings() {
} as IGenerateLogButtonProps); } as IGenerateLogButtonProps);
} }
}; };
const parseType = useWatch({
control: form.control,
name: 'parseType',
defaultValue: knowledgeDetails.pipeline_id ? 2 : 1,
});
const selectedTag = useWatch({
name: 'parser_id',
control: form.control,
});
useEffect(() => {
if (parseType === 1) {
form.setValue('pipeline_id', '');
}
console.log('parseType', parseType);
}, [parseType, form]);
return ( return (
<section className="p-5 h-full flex flex-col"> <section className="p-5 h-full flex flex-col">
<TopTitle <TopTitle
@ -167,10 +191,30 @@ export default function DatasetSettings() {
onDelete={() => handleDeletePipelineTask(GenerateType.Raptor)} onDelete={() => handleDeletePipelineTask(GenerateType.Raptor)}
></RaptorFormFields> ></RaptorFormFields>
<Divider /> <Divider />
<LinkDataPipeline <ParseTypeItem line={1} />
{parseType === 1 && (
<ChunkMethodItem line={1}></ChunkMethodItem>
)}
{parseType === 2 && (
<DataFlowSelect
isMult={false}
showToDataPipeline={true}
formFieldName="pipeline_id"
layout={FormLayout.Horizontal}
/>
)}
<Divider />
{parseType === 1 && (
<ChunkMethodForm
selectedTag={selectedTag as DocumentParserType}
/>
)}
{/* <LinkDataPipeline
data={pipelineData} data={pipelineData}
handleLinkOrEditSubmit={handleLinkOrEditSubmit} handleLinkOrEditSubmit={handleLinkOrEditSubmit}
/> /> */}
</MainContainer> </MainContainer>
</div> </div>
<div className="text-right items-center flex justify-end gap-3 w-[768px]"> <div className="text-right items-center flex justify-end gap-3 w-[768px]">

View File

@ -62,6 +62,7 @@ export function SavingButton() {
if (beValid) { if (beValid) {
form.handleSubmit(async (values) => { form.handleSubmit(async (values) => {
console.log('saveKnowledgeConfiguration: ', values); console.log('saveKnowledgeConfiguration: ', values);
delete values['parseType'];
// delete values['avatar']; // delete values['avatar'];
await saveKnowledgeConfiguration({ await saveKnowledgeConfiguration({
kb_id, kb_id,

View File

@ -29,11 +29,6 @@ export function SideBar({ refreshCount }: PropType) {
const items = useMemo(() => { const items = useMemo(() => {
const list = [ const list = [
{
icon: <DatabaseZap className="size-4" />,
label: t(`knowledgeDetails.overview`),
key: Routes.DataSetOverview,
},
{ {
icon: <FolderOpen className="size-4" />, icon: <FolderOpen className="size-4" />,
label: t(`knowledgeDetails.subbarFiles`), label: t(`knowledgeDetails.subbarFiles`),
@ -44,6 +39,11 @@ export function SideBar({ refreshCount }: PropType) {
label: t(`knowledgeDetails.testing`), label: t(`knowledgeDetails.testing`),
key: Routes.DatasetTesting, key: Routes.DatasetTesting,
}, },
{
icon: <DatabaseZap className="size-4" />,
label: t(`knowledgeDetails.overview`),
key: Routes.DataSetOverview,
},
{ {
icon: <Banknote className="size-4" />, icon: <Banknote className="size-4" />,
label: t(`knowledgeDetails.configuration`), label: t(`knowledgeDetails.configuration`),

View File

@ -16,6 +16,7 @@ import {
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { FormLayout } from '@/constants/form';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -137,8 +138,9 @@ export function InputForm({ onOk }: IModalProps<any>) {
{parseType === 2 && ( {parseType === 2 && (
<DataFlowSelect <DataFlowSelect
isMult={false} isMult={false}
toDataPipeline={navigateToAgents} showToDataPipeline={true}
formFieldName="pipeline_id" formFieldName="pipeline_id"
layout={FormLayout.Vertical}
/> />
)} )}
</form> </form>