mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-07-03 18:45:12 +08:00
### What problem does this PR solve? Feat: Add data set configuration form #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
5d5dbb3bcb
commit
5218ff775c
19
web/src/components/page-rank-form-field.tsx
Normal file
19
web/src/components/page-rank-form-field.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { SliderInputFormField } from './slider-input-form-field';
|
||||
|
||||
export function PageRankFormField() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name={'pagerank'}
|
||||
label={t('pageRank')}
|
||||
tooltip={t('pageRankTip')}
|
||||
defaultValue={0}
|
||||
max={100}
|
||||
min={1}
|
||||
></SliderInputFormField>
|
||||
);
|
||||
}
|
||||
|
||||
export default PageRankFormField;
|
@ -1,12 +1,11 @@
|
||||
import { DocumentParserType } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Switch as AntSwitch, Form, Select } from 'antd';
|
||||
import { upperFirst } from 'lodash';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { DatasetConfigurationContainer } from '../dataset-configuration-container';
|
||||
import EntityTypesItem from '../entity-types-item';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { EntityTypesFormField } from '../entity-types-form-field';
|
||||
import { FormContainer } from '../form-container';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '../ui/form';
|
||||
import { RAGFlowSelect } from '../ui/select';
|
||||
import { Switch } from '../ui/switch';
|
||||
|
||||
const excludedTagParseMethods = [
|
||||
@ -48,22 +48,6 @@ type GraphRagItemsProps = {
|
||||
marginBottom?: boolean;
|
||||
};
|
||||
|
||||
export function UseGraphRagItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
name={['parser_config', 'graphrag', 'use_graphrag']}
|
||||
label={t('useGraphRag')}
|
||||
initialValue={false}
|
||||
valuePropName="checked"
|
||||
tooltip={t('useGraphRagTip')}
|
||||
>
|
||||
<AntSwitch />
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
||||
export function UseGraphRagFormField() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
@ -93,6 +77,12 @@ export function UseGraphRagFormField() {
|
||||
// The three types "table", "resume" and "one" do not display this configuration.
|
||||
const GraphRagItems = ({ marginBottom = false }: GraphRagItemsProps) => {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
|
||||
const useRaptor = useWatch({
|
||||
control: form.control,
|
||||
name: 'parser_config.graphrag.use_graphrag',
|
||||
});
|
||||
|
||||
const methodOptions = useMemo(() => {
|
||||
return [MethodValue.Light, MethodValue.General].map((x) => ({
|
||||
@ -103,39 +93,23 @@ const GraphRagItems = ({ marginBottom = false }: GraphRagItemsProps) => {
|
||||
|
||||
const renderWideTooltip = useCallback(
|
||||
(title: React.ReactNode | string) => {
|
||||
return {
|
||||
title: typeof title === 'string' ? t(title) : title,
|
||||
overlayInnerStyle: { width: '32vw' },
|
||||
};
|
||||
return typeof title === 'string' ? t(title) : title;
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
return (
|
||||
<DatasetConfigurationContainer className={cn({ 'mb-4': marginBottom })}>
|
||||
<UseGraphRagItem></UseGraphRagItem>
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.parser_config.graphrag.use_graphrag !==
|
||||
curValues.parser_config.graphrag.use_graphrag
|
||||
}
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const useRaptor = getFieldValue([
|
||||
'parser_config',
|
||||
'graphrag',
|
||||
'use_graphrag',
|
||||
]);
|
||||
|
||||
return (
|
||||
useRaptor && (
|
||||
<>
|
||||
<EntityTypesItem
|
||||
field={['parser_config', 'graphrag', 'entity_types']}
|
||||
></EntityTypesItem>
|
||||
<Form.Item
|
||||
name={['parser_config', 'graphrag', 'method']}
|
||||
label={t('graphRagMethod')}
|
||||
<FormContainer className={cn({ 'mb-4': marginBottom })}>
|
||||
<UseGraphRagFormField></UseGraphRagFormField>
|
||||
{useRaptor && (
|
||||
<>
|
||||
<EntityTypesFormField name="parser_config.graphrag.entity_types"></EntityTypesFormField>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parser_config.graphrag.method"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel
|
||||
tooltip={renderWideTooltip(
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
@ -143,30 +117,60 @@ const GraphRagItems = ({ marginBottom = false }: GraphRagItemsProps) => {
|
||||
}}
|
||||
></div>,
|
||||
)}
|
||||
initialValue={MethodValue.Light}
|
||||
>
|
||||
<Select options={methodOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['parser_config', 'graphrag', 'resolution']}
|
||||
label={t('resolution')}
|
||||
tooltip={renderWideTooltip('resolutionTip')}
|
||||
>
|
||||
<AntSwitch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['parser_config', 'graphrag', 'community']}
|
||||
label={t('community')}
|
||||
tooltip={renderWideTooltip('communityTip')}
|
||||
>
|
||||
<AntSwitch />
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</DatasetConfigurationContainer>
|
||||
{t('graphRagMethod')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
options={methodOptions}
|
||||
></RAGFlowSelect>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parser_config.graphrag.resolution"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={renderWideTooltip('resolutionTip')}>
|
||||
{t('resolution')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
></Switch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parser_config.graphrag.community"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={renderWideTooltip('communityTip')}>
|
||||
{t('community')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
></Switch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
77
web/src/pages/dataset/setting/category-panel.tsx
Normal file
77
web/src/pages/dataset/setting/category-panel.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useSelectParserList } from '@/hooks/user-setting-hooks';
|
||||
import { Col, Divider, Empty, Row, Typography } from 'antd';
|
||||
import DOMPurify from 'dompurify';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import { useMemo } from 'react';
|
||||
import styles from './index.less';
|
||||
import { TagTabs } from './tag-tabs';
|
||||
import { ImageMap } from './utils';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
const parserList = useSelectParserList();
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
|
||||
const item = useMemo(() => {
|
||||
const item = parserList.find((x) => x.value === chunkMethod);
|
||||
if (item) {
|
||||
return {
|
||||
title: item.label,
|
||||
description: t(camelCase(item.value)),
|
||||
};
|
||||
}
|
||||
return { title: '', description: '' };
|
||||
}, [parserList, chunkMethod, t]);
|
||||
|
||||
const imageList = useMemo(() => {
|
||||
if (chunkMethod in ImageMap) {
|
||||
return ImageMap[chunkMethod as keyof typeof ImageMap];
|
||||
}
|
||||
return [];
|
||||
}, [chunkMethod]);
|
||||
|
||||
return (
|
||||
<section className={styles.categoryPanelWrapper}>
|
||||
{imageList.length > 0 ? (
|
||||
<>
|
||||
<h5 className="font-semibold text-base mt-0 mb-1">
|
||||
{`"${item.title}" ${t('methodTitle')}`}
|
||||
</h5>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(item.description),
|
||||
}}
|
||||
></p>
|
||||
<h5 className="font-semibold text-base mt-4 mb-1">{`"${item.title}" ${t('methodExamples')}`}</h5>
|
||||
<Text>{t('methodExamplesDescription')}</Text>
|
||||
<Row gutter={[10, 10]} className={styles.imageRow}>
|
||||
{imageList.map((x) => (
|
||||
<Col span={12} key={x}>
|
||||
<SvgIcon
|
||||
name={x}
|
||||
width={'100%'}
|
||||
className={styles.image}
|
||||
></SvgIcon>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<h5 className="font-semibold text-base mt-4 mb-1">
|
||||
{item.title} {t('dialogueExamplesTitle')}
|
||||
</h5>
|
||||
<Divider></Divider>
|
||||
</>
|
||||
) : (
|
||||
<Empty description={''} image={null}>
|
||||
<p>{t('methodEmpty')}</p>
|
||||
<SvgIcon name={'chunk-method/chunk-empty'} width={'100%'}></SvgIcon>
|
||||
</Empty>
|
||||
)}
|
||||
{chunkMethod === 'tag' && <TagTabs></TagTabs>}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategoryPanel;
|
78
web/src/pages/dataset/setting/chunk-method-form.tsx
Normal file
78
web/src/pages/dataset/setting/chunk-method-form.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { DocumentParserType } from '@/constants/knowledge';
|
||||
import { useMemo, useState } 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';
|
||||
import { useFetchKnowledgeConfigurationOnMount } from './hooks';
|
||||
|
||||
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 { t } = useTranslation();
|
||||
const [finalParserId, setFinalParserId] = useState<DocumentParserType>(
|
||||
DocumentParserType.Naive,
|
||||
);
|
||||
|
||||
const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form);
|
||||
|
||||
const parserId: DocumentParserType = useWatch({
|
||||
control: form.control,
|
||||
name: 'parser_id',
|
||||
});
|
||||
|
||||
const ConfigurationComponent = useMemo(() => {
|
||||
return finalParserId
|
||||
? ConfigurationComponentMap[finalParserId]
|
||||
: EmptyComponent;
|
||||
}, [finalParserId]);
|
||||
|
||||
// useEffect(() => {
|
||||
// setFinalParserId(parserId);
|
||||
// }, [parserId]);
|
||||
|
||||
// useEffect(() => {
|
||||
// setFinalParserId(knowledgeDetails.parser_id as DocumentParserType);
|
||||
// }, [knowledgeDetails.parser_id]);
|
||||
|
||||
return (
|
||||
<section>
|
||||
<ConfigurationComponent></ConfigurationComponent>
|
||||
</section>
|
||||
);
|
||||
}
|
31
web/src/pages/dataset/setting/configuration/audio.tsx
Normal file
31
web/src/pages/dataset/setting/configuration/audio.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function AudioConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
33
web/src/pages/dataset/setting/configuration/book.tsx
Normal file
33
web/src/pages/dataset/setting/configuration/book.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function BookConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
75
web/src/pages/dataset/setting/configuration/common-item.tsx
Normal file
75
web/src/pages/dataset/setting/configuration/common-item.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { RAGFlowSelect } from '@/components/ui/select';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import {
|
||||
useHasParsedDocument,
|
||||
useSelectChunkMethodList,
|
||||
useSelectEmbeddingModelOptions,
|
||||
} from '../hooks';
|
||||
|
||||
export function ChunkMethodItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
// const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form);
|
||||
const parserList = useSelectChunkMethodList();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'parser_id'}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('chunkMethodTip')}>
|
||||
{t('chunkMethod')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
options={parserList}
|
||||
placeholder={t('chunkMethodPlaceholder')}
|
||||
// onChange={handleChunkMethodSelectChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmbeddingModelItem() {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
const form = useFormContext();
|
||||
const embeddingModelOptions = useSelectEmbeddingModelOptions();
|
||||
const disabled = useHasParsedDocument();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'embd_id'}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('embeddingModelTip')}>
|
||||
{t('embeddingModel')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<RAGFlowSelect
|
||||
{...field}
|
||||
options={embeddingModelOptions}
|
||||
disabled={disabled}
|
||||
placeholder={t('embeddingModelPlaceholder')}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
31
web/src/pages/dataset/setting/configuration/email.tsx
Normal file
31
web/src/pages/dataset/setting/configuration/email.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function EmailConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { DelimiterFormField } from '@/components/delimiter-form-field';
|
||||
import { EntityTypesFormField } from '@/components/entity-types-form-field';
|
||||
import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function KnowledgeGraphConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<EntityTypesFormField></EntityTypesFormField>
|
||||
<MaxTokenNumberFormField max={8192 * 2}></MaxTokenNumberFormField>
|
||||
<DelimiterFormField></DelimiterFormField>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
33
web/src/pages/dataset/setting/configuration/laws.tsx
Normal file
33
web/src/pages/dataset/setting/configuration/laws.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function LawsConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
33
web/src/pages/dataset/setting/configuration/manual.tsx
Normal file
33
web/src/pages/dataset/setting/configuration/manual.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function ManualConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
39
web/src/pages/dataset/setting/configuration/naive.tsx
Normal file
39
web/src/pages/dataset/setting/configuration/naive.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
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 { FormContainer } from '@/components/form-container';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function NaiveConfiguration() {
|
||||
return (
|
||||
<section className="space-y-5 mb-4 overflow-auto">
|
||||
<FormContainer>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
<MaxTokenNumberFormField></MaxTokenNumberFormField>
|
||||
<DelimiterFormField></DelimiterFormField>
|
||||
</FormContainer>
|
||||
<FormContainer>
|
||||
<PageRankFormField></PageRankFormField>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
<ExcelToHtmlFormField></ExcelToHtmlFormField>
|
||||
<TagItems></TagItems>
|
||||
</FormContainer>
|
||||
<FormContainer>
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
</FormContainer>
|
||||
<GraphRagItems></GraphRagItems>
|
||||
</section>
|
||||
);
|
||||
}
|
30
web/src/pages/dataset/setting/configuration/one.tsx
Normal file
30
web/src/pages/dataset/setting/configuration/one.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function OneConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
33
web/src/pages/dataset/setting/configuration/paper.tsx
Normal file
33
web/src/pages/dataset/setting/configuration/paper.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function PaperConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
24
web/src/pages/dataset/setting/configuration/picture.tsx
Normal file
24
web/src/pages/dataset/setting/configuration/picture.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function PictureConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
33
web/src/pages/dataset/setting/configuration/presentation.tsx
Normal file
33
web/src/pages/dataset/setting/configuration/presentation.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
AutoKeywordsFormField,
|
||||
AutoQuestionsFormField,
|
||||
} from '@/components/auto-keywords-form-field';
|
||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function PresentationConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<>
|
||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||
</>
|
||||
|
||||
<RaptorFormFields></RaptorFormFields>
|
||||
|
||||
<GraphRagItems marginBottom></GraphRagItems>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
16
web/src/pages/dataset/setting/configuration/qa.tsx
Normal file
16
web/src/pages/dataset/setting/configuration/qa.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function QAConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
16
web/src/pages/dataset/setting/configuration/resume.tsx
Normal file
16
web/src/pages/dataset/setting/configuration/resume.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { TagItems } from '../tag-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function ResumeConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
|
||||
<TagItems></TagItems>
|
||||
</>
|
||||
);
|
||||
}
|
13
web/src/pages/dataset/setting/configuration/table.tsx
Normal file
13
web/src/pages/dataset/setting/configuration/table.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function TableConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
</>
|
||||
);
|
||||
}
|
13
web/src/pages/dataset/setting/configuration/tag.tsx
Normal file
13
web/src/pages/dataset/setting/configuration/tag.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import PageRankFormField from '@/components/page-rank-form-field';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function TagConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<EmbeddingModelItem></EmbeddingModelItem>
|
||||
<ChunkMethodItem></ChunkMethodItem>
|
||||
|
||||
<PageRankFormField></PageRankFormField>
|
||||
</>
|
||||
);
|
||||
}
|
108
web/src/pages/dataset/setting/general-form.tsx
Normal file
108
web/src/pages/dataset/setting/general-form.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import { FileUploader } from '@/components/file-uploader';
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function GeneralForm() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<FormContainer className="space-y-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('knowledgeConfiguration.name')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field}></Input>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('knowledgeConfiguration.description')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field}></Input>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="avatar"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('knowledgeConfiguration.photo')}</FormLabel>
|
||||
<FormControl>
|
||||
<FileUploader
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
maxFileCount={1}
|
||||
maxSize={4 * 1024 * 1024}
|
||||
// progresses={progresses}
|
||||
// pass the onUpload function here for direct upload
|
||||
// onUpload={uploadFiles}
|
||||
// disabled={isUploading}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="type"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel tooltip={t('knowledgeConfiguration.permissionsTip')}>
|
||||
{t('knowledgeConfiguration.permissions')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex flex-col space-y-1"
|
||||
>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="me" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{t('knowledgeConfiguration.me')}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="team" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
{t('knowledgeConfiguration.team')}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</FormContainer>
|
||||
);
|
||||
}
|
123
web/src/pages/dataset/setting/hooks.ts
Normal file
123
web/src/pages/dataset/setting/hooks.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
|
||||
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
||||
import { useNavigateToDataset } from '@/hooks/route-hook';
|
||||
import {
|
||||
useFetchKnowledgeBaseConfiguration,
|
||||
useUpdateKnowledge,
|
||||
} from '@/hooks/use-knowledge-request';
|
||||
import { useSelectParserList } from '@/hooks/user-setting-hooks';
|
||||
import {
|
||||
getBase64FromUploadFileList,
|
||||
getUploadFileListFromBase64,
|
||||
} from '@/utils/file-util';
|
||||
import { useIsFetching } from '@tanstack/react-query';
|
||||
import { Form, UploadFile } from 'antd';
|
||||
import { FormInstance } from 'antd/lib';
|
||||
import pick from 'lodash/pick';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
|
||||
export const useSubmitKnowledgeConfiguration = (form: FormInstance) => {
|
||||
const { saveKnowledgeConfiguration, loading } = useUpdateKnowledge();
|
||||
const navigateToDataset = useNavigateToDataset();
|
||||
|
||||
const submitKnowledgeConfiguration = useCallback(async () => {
|
||||
const values = await form.validateFields();
|
||||
const avatar = await getBase64FromUploadFileList(values.avatar);
|
||||
saveKnowledgeConfiguration({
|
||||
...values,
|
||||
avatar,
|
||||
});
|
||||
navigateToDataset();
|
||||
}, [saveKnowledgeConfiguration, form, navigateToDataset]);
|
||||
|
||||
return {
|
||||
submitKnowledgeConfiguration,
|
||||
submitLoading: loading,
|
||||
navigateToDataset,
|
||||
};
|
||||
};
|
||||
|
||||
// The value that does not need to be displayed in the analysis method Select
|
||||
const HiddenFields = ['email', 'picture', 'audio'];
|
||||
|
||||
export function useSelectChunkMethodList() {
|
||||
const parserList = useSelectParserList();
|
||||
|
||||
return parserList.filter((x) => !HiddenFields.some((y) => y === x.value));
|
||||
}
|
||||
|
||||
export function useSelectEmbeddingModelOptions() {
|
||||
const allOptions = useSelectLlmOptionsByModelType();
|
||||
return allOptions[LlmModelType.Embedding];
|
||||
}
|
||||
|
||||
export function useHasParsedDocument() {
|
||||
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration();
|
||||
return knowledgeDetails.chunk_num > 0;
|
||||
}
|
||||
|
||||
export const useFetchKnowledgeConfigurationOnMount = (form: UseFormReturn) => {
|
||||
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
useEffect(() => {
|
||||
const fileList: UploadFile[] = getUploadFileListFromBase64(
|
||||
knowledgeDetails.avatar,
|
||||
);
|
||||
form.reset({
|
||||
...pick(knowledgeDetails, [
|
||||
'description',
|
||||
'name',
|
||||
'permission',
|
||||
'embd_id',
|
||||
'parser_id',
|
||||
'language',
|
||||
'parser_config',
|
||||
'pagerank',
|
||||
]),
|
||||
avatar: fileList,
|
||||
});
|
||||
}, [form, knowledgeDetails]);
|
||||
|
||||
return knowledgeDetails;
|
||||
};
|
||||
|
||||
export const useSelectKnowledgeDetailsLoading = () =>
|
||||
useIsFetching({ queryKey: ['fetchKnowledgeDetail'] }) > 0;
|
||||
|
||||
export const useHandleChunkMethodChange = () => {
|
||||
const [form] = Form.useForm();
|
||||
const chunkMethod = Form.useWatch('parser_id', form);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ useHandleChunkMethodChange ~ chunkMethod:', chunkMethod);
|
||||
}, [chunkMethod]);
|
||||
|
||||
return { form, chunkMethod };
|
||||
};
|
||||
|
||||
export const useRenameKnowledgeTag = () => {
|
||||
const [tag, setTag] = useState<string>('');
|
||||
const {
|
||||
visible: tagRenameVisible,
|
||||
hideModal: hideTagRenameModal,
|
||||
showModal: showFileRenameModal,
|
||||
} = useSetModalState();
|
||||
|
||||
const handleShowTagRenameModal = useCallback(
|
||||
(record: string) => {
|
||||
setTag(record);
|
||||
showFileRenameModal();
|
||||
},
|
||||
[showFileRenameModal],
|
||||
);
|
||||
|
||||
return {
|
||||
initialName: tag,
|
||||
tagRenameVisible,
|
||||
hideTagRenameModal,
|
||||
showTagRenameModal: handleShowTagRenameModal,
|
||||
};
|
||||
};
|
@ -1,25 +1,137 @@
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import AdvancedSettingForm from './advanced-setting-form';
|
||||
import BasicSettingForm from './basic-setting-form';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { DocumentParserType } from '@/constants/knowledge';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import CategoryPanel from './category-panel';
|
||||
import { ChunkMethodForm } from './chunk-method-form';
|
||||
import { GeneralForm } from './general-form';
|
||||
|
||||
const enum DocumentType {
|
||||
DeepDOC = 'DeepDOC',
|
||||
PlainText = 'Plain Text',
|
||||
}
|
||||
|
||||
const initialEntityTypes = [
|
||||
'organization',
|
||||
'person',
|
||||
'geo',
|
||||
'event',
|
||||
'category',
|
||||
];
|
||||
|
||||
const enum MethodValue {
|
||||
General = 'general',
|
||||
Light = 'light',
|
||||
}
|
||||
|
||||
export default function DatasetSettings() {
|
||||
return (
|
||||
<section className="p-8 overflow-y-scroll max-h-[90vh]">
|
||||
<div className="text-3xl font-bold pb-6">Basic settings</div>
|
||||
<Card className="border-0 p-6 bg-colors-background-inverse-weak">
|
||||
<CardContent>
|
||||
<div className="w-2/5">
|
||||
<BasicSettingForm></BasicSettingForm>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(1, {
|
||||
message: 'Username must be at least 2 characters.',
|
||||
}),
|
||||
description: z.string().min(2, {
|
||||
message: 'Username must be at least 2 characters.',
|
||||
}),
|
||||
avatar: z.instanceof(File),
|
||||
permission: z.string(),
|
||||
parser_id: z.string(),
|
||||
parser_config: z.object({
|
||||
layout_recognize: z.string(),
|
||||
chunk_token_num: z.number(),
|
||||
delimiter: z.string(),
|
||||
auto_keywords: z.number(),
|
||||
auto_questions: z.number(),
|
||||
html4excel: z.boolean(),
|
||||
tag_kb_ids: z.array(z.string()),
|
||||
topn_tags: z.number(),
|
||||
raptor: z.object({
|
||||
use_raptor: z.boolean(),
|
||||
prompt: z.string(),
|
||||
max_token: z.number(),
|
||||
threshold: z.number(),
|
||||
max_cluster: z.number(),
|
||||
random_seed: z.number(),
|
||||
}),
|
||||
graphrag: z.object({
|
||||
use_graphrag: z.boolean(),
|
||||
entity_types: z.array(z.string()),
|
||||
method: z.string(),
|
||||
resolution: z.boolean(),
|
||||
community: z.boolean(),
|
||||
}),
|
||||
}),
|
||||
pagerank: z.number(),
|
||||
// icon: z.array(z.instanceof(File)),
|
||||
});
|
||||
|
||||
<div className="text-3xl font-bold pb-6 pt-8">Advanced settings</div>
|
||||
<Card className="border-0 p-6 bg-colors-background-inverse-weak">
|
||||
<CardContent>
|
||||
<AdvancedSettingForm></AdvancedSettingForm>
|
||||
</CardContent>
|
||||
</Card>
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
parser_id: DocumentParserType.Naive,
|
||||
permission: 'me',
|
||||
parser_config: {
|
||||
layout_recognize: DocumentType.DeepDOC,
|
||||
chunk_token_num: 512,
|
||||
delimiter: `\n`,
|
||||
auto_keywords: 0,
|
||||
auto_questions: 0,
|
||||
html4excel: false,
|
||||
topn_tags: 3,
|
||||
raptor: {
|
||||
use_raptor: false,
|
||||
max_token: 256,
|
||||
threshold: 0.1,
|
||||
max_cluster: 64,
|
||||
random_seed: 0,
|
||||
},
|
||||
graphrag: {
|
||||
use_graphrag: false,
|
||||
entity_types: initialEntityTypes,
|
||||
method: MethodValue.Light,
|
||||
},
|
||||
},
|
||||
pagerank: 0,
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(data: z.infer<typeof formSchema>) {
|
||||
console.log('🚀 ~ DatasetSettings ~ data:', data);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="p-5 ">
|
||||
<div className="pb-5">
|
||||
<div className="text-2xl font-semibold">Configuration</div>
|
||||
<p className="text-text-sub-title pt-2">
|
||||
Update your knowledge base configuration here, particularly the chunk
|
||||
method.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-14">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6 basis-full"
|
||||
>
|
||||
<Tabs defaultValue="account">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="account">Account</TabsTrigger>
|
||||
<TabsTrigger value="password">Password</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="account">
|
||||
<GeneralForm></GeneralForm>
|
||||
</TabsContent>
|
||||
<TabsContent value="password">
|
||||
<ChunkMethodForm></ChunkMethodForm>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</form>
|
||||
</Form>
|
||||
<CategoryPanel chunkMethod={DocumentParserType.Naive}></CategoryPanel>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
144
web/src/pages/dataset/setting/tag-item.tsx
Normal file
144
web/src/pages/dataset/setting/tag-item.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { MultiSelect } from '@/components/ui/multi-select';
|
||||
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import { Avatar, Flex, Form, InputNumber, Select, Slider, Space } from 'antd';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const TagSetItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
|
||||
const { list: knowledgeList } = useFetchKnowledgeList(true);
|
||||
|
||||
const knowledgeOptions = knowledgeList
|
||||
.filter((x) => x.parser_id === 'tag')
|
||||
.map((x) => ({
|
||||
label: x.name,
|
||||
value: x.id,
|
||||
icon: () => (
|
||||
<Space>
|
||||
<Avatar size={20} icon={<UserOutlined />} src={x.avatar} />
|
||||
{x.name}
|
||||
</Space>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="parser_config.tag_kb_ids"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel
|
||||
tooltip={
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
t('knowledgeConfiguration.tagSetTip'),
|
||||
),
|
||||
}}
|
||||
></div>
|
||||
}
|
||||
>
|
||||
{t('knowledgeConfiguration.tagSet')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<MultiSelect
|
||||
options={knowledgeOptions}
|
||||
onValueChange={field.onChange}
|
||||
placeholder={t('chat.knowledgeBasesMessage')}
|
||||
variant="inverted"
|
||||
maxCount={0}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
label={t('knowledgeConfiguration.tagSet')}
|
||||
name={['parser_config', 'tag_kb_ids']}
|
||||
tooltip={
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(t('knowledgeConfiguration.tagSetTip')),
|
||||
}}
|
||||
></div>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
message: t('chat.knowledgeBasesMessage'),
|
||||
type: 'array',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
options={knowledgeOptions}
|
||||
placeholder={t('chat.knowledgeBasesMessage')}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export const TopNTagsItem = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name={'parser_config.topn_tags'}
|
||||
label={t('knowledgeConfiguration.topnTags')}
|
||||
max={10}
|
||||
min={1}
|
||||
defaultValue={3}
|
||||
></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() {
|
||||
const form = useFormContext();
|
||||
const ids: string[] = useWatch({
|
||||
control: form.control,
|
||||
name: 'parser_config.tag_kb_ids',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<TagSetItem></TagSetItem>
|
||||
{Array.isArray(ids) && ids.length > 0 && <TopNTagsItem></TopNTagsItem>}
|
||||
</>
|
||||
);
|
||||
}
|
307
web/src/pages/dataset/setting/tag-table/index.tsx
Normal file
307
web/src/pages/dataset/setting/tag-table/index.tsx
Normal file
@ -0,0 +1,307 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { useDeleteTag, useFetchTagList } from '@/hooks/knowledge-hooks';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRenameKnowledgeTag } from '../hooks';
|
||||
import { RenameDialog } from './rename-dialog';
|
||||
|
||||
export type ITag = {
|
||||
tag: string;
|
||||
frequency: number;
|
||||
};
|
||||
|
||||
export function TagTable() {
|
||||
const { t } = useTranslation();
|
||||
const { list } = useFetchTagList();
|
||||
const [tagList, setTagList] = useState<ITag[]>([]);
|
||||
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[],
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = useState({});
|
||||
|
||||
const { deleteTag } = useDeleteTag();
|
||||
|
||||
useEffect(() => {
|
||||
setTagList(list.map((x) => ({ tag: x[0], frequency: x[1] })));
|
||||
}, [list]);
|
||||
|
||||
const handleDeleteTag = useCallback(
|
||||
(tags: string[]) => () => {
|
||||
deleteTag(tags);
|
||||
},
|
||||
[deleteTag],
|
||||
);
|
||||
|
||||
const {
|
||||
showTagRenameModal,
|
||||
hideTagRenameModal,
|
||||
tagRenameVisible,
|
||||
initialName,
|
||||
} = useRenameKnowledgeTag();
|
||||
|
||||
const columns: ColumnDef<ITag>[] = [
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: 'tag',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
{t('knowledgeConfiguration.tagName')}
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const value: string = row.getValue('tag');
|
||||
return <div>{value}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'frequency',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
{t('knowledgeConfiguration.frequency')}
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<div className="capitalize ">{row.getValue('frequency')}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableHiding: false,
|
||||
header: t('common.action'),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
<Tooltip>
|
||||
<ConfirmDeleteDialog onOk={handleDeleteTag([row.original.tag])}>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
</ConfirmDeleteDialog>
|
||||
<TooltipContent>
|
||||
<p>{t('common.delete')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => showTagRenameModal(row.original.tag)}
|
||||
>
|
||||
<Pencil />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{t('common.rename')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
data: tagList,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
});
|
||||
|
||||
const selectedRowLength = table.getFilteredSelectedRowModel().rows.length;
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className="w-full">
|
||||
<div className="flex items-center justify-between py-4 ">
|
||||
<Input
|
||||
placeholder={t('knowledgeConfiguration.searchTags')}
|
||||
value={(table.getColumn('tag')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) =>
|
||||
table.getColumn('tag')?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="w-1/2"
|
||||
/>
|
||||
{selectedRowLength > 0 && (
|
||||
<ConfirmDeleteDialog
|
||||
onOk={handleDeleteTag(
|
||||
table
|
||||
.getFilteredSelectedRowModel()
|
||||
.rows.map((x) => x.original.tag),
|
||||
)}
|
||||
>
|
||||
<Button variant="outline" size="icon">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</ConfirmDeleteDialog>
|
||||
)}
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '}
|
||||
row(s) selected.
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
{t('common.previousPage')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
{t('common.nextPage')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{tagRenameVisible && (
|
||||
<RenameDialog
|
||||
hideModal={hideTagRenameModal}
|
||||
initialName={initialName}
|
||||
></RenameDialog>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { LoadingButton } from '@/components/ui/loading-button';
|
||||
import { useTagIsRenaming } from '@/hooks/knowledge-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RenameForm } from './rename-form';
|
||||
|
||||
export function RenameDialog({
|
||||
hideModal,
|
||||
initialName,
|
||||
}: IModalProps<any> & { initialName: string }) {
|
||||
const { t } = useTranslation();
|
||||
const loading = useTagIsRenaming();
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('common.rename')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<RenameForm
|
||||
initialName={initialName}
|
||||
hideModal={hideModal}
|
||||
></RenameForm>
|
||||
<DialogFooter>
|
||||
<LoadingButton type="submit" form={TagRenameId} loading={loading}>
|
||||
{t('common.save')}
|
||||
</LoadingButton>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useRenameTag } from '@/hooks/knowledge-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function RenameForm({
|
||||
initialName,
|
||||
hideModal,
|
||||
}: IModalProps<any> & { initialName: string }) {
|
||||
const { t } = useTranslation();
|
||||
const FormSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
})
|
||||
.trim(),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
});
|
||||
|
||||
const { renameTag } = useRenameTag();
|
||||
|
||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
const ret = await renameTag({ fromTag: initialName, toTag: data.name });
|
||||
if (ret) {
|
||||
hideModal?.();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue('name', initialName);
|
||||
}, [form, initialName]);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6"
|
||||
id={TagRenameId}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('common.name')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
40
web/src/pages/dataset/setting/tag-tabs.tsx
Normal file
40
web/src/pages/dataset/setting/tag-tabs.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Segmented } from 'antd';
|
||||
import { SegmentedLabeledOption } from 'antd/es/segmented';
|
||||
import { upperFirst } from 'lodash';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TagTable } from './tag-table';
|
||||
import { TagWordCloud } from './tag-word-cloud';
|
||||
|
||||
enum TagType {
|
||||
Cloud = 'cloud',
|
||||
Table = 'table',
|
||||
}
|
||||
|
||||
const TagContentMap = {
|
||||
[TagType.Cloud]: <TagWordCloud></TagWordCloud>,
|
||||
[TagType.Table]: <TagTable></TagTable>,
|
||||
};
|
||||
|
||||
export function TagTabs() {
|
||||
const [value, setValue] = useState<TagType>(TagType.Cloud);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const options: SegmentedLabeledOption[] = [TagType.Cloud, TagType.Table].map(
|
||||
(x) => ({
|
||||
label: t(`knowledgeConfiguration.tag${upperFirst(x)}`),
|
||||
value: x,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="mt-4">
|
||||
<Segmented
|
||||
value={value}
|
||||
options={options}
|
||||
onChange={(val) => setValue(val as TagType)}
|
||||
/>
|
||||
{TagContentMap[value]}
|
||||
</section>
|
||||
);
|
||||
}
|
62
web/src/pages/dataset/setting/tag-word-cloud.tsx
Normal file
62
web/src/pages/dataset/setting/tag-word-cloud.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { useFetchTagList } from '@/hooks/knowledge-hooks';
|
||||
import { Chart } from '@antv/g2';
|
||||
import { sumBy } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
export function TagWordCloud() {
|
||||
const domRef = useRef<HTMLDivElement>(null);
|
||||
let chartRef = useRef<Chart>();
|
||||
const { list } = useFetchTagList();
|
||||
|
||||
const { list: tagList } = useMemo(() => {
|
||||
const nextList = list.sort((a, b) => b[1] - a[1]).slice(0, 256);
|
||||
|
||||
return {
|
||||
list: nextList.map((x) => ({ text: x[0], value: x[1], name: x[0] })),
|
||||
sumValue: sumBy(nextList, (x: [string, number]) => x[1]),
|
||||
length: nextList.length,
|
||||
};
|
||||
}, [list]);
|
||||
|
||||
const renderWordCloud = useCallback(() => {
|
||||
if (domRef.current) {
|
||||
chartRef.current = new Chart({ container: domRef.current });
|
||||
|
||||
chartRef.current.options({
|
||||
type: 'wordCloud',
|
||||
autoFit: true,
|
||||
layout: {
|
||||
fontSize: [10, 50],
|
||||
// fontSize: (d: any) => {
|
||||
// if (d.value) {
|
||||
// return (d.value / sumValue) * 100 * (length / 10);
|
||||
// }
|
||||
// return 0;
|
||||
// },
|
||||
},
|
||||
data: {
|
||||
type: 'inline',
|
||||
value: tagList,
|
||||
},
|
||||
encode: { color: 'text' },
|
||||
legend: false,
|
||||
tooltip: {
|
||||
title: 'name', // title
|
||||
items: ['value'], // data item
|
||||
},
|
||||
});
|
||||
|
||||
chartRef.current.render();
|
||||
}
|
||||
}, [tagList]);
|
||||
|
||||
useEffect(() => {
|
||||
renderWordCloud();
|
||||
|
||||
return () => {
|
||||
chartRef.current?.destroy();
|
||||
};
|
||||
}, [renderWordCloud]);
|
||||
|
||||
return <div ref={domRef} className="w-full h-[38vh]"></div>;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user