feat(knowledge): add configuration components and refactor general form

feat(hooks): add llm-related hooks and constants
docs: add architecture analysis for reference projects
This commit is contained in:
2025-10-15 16:24:53 +08:00
parent 486815d83e
commit fe8747983e
33 changed files with 2627 additions and 812 deletions

View File

@@ -1,172 +1,163 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useForm, type UseFormReturn } from 'react-hook-form';
import { useForm, FormProvider, useWatch, Form } from 'react-hook-form';
import {
Box,
Container,
Typography,
Paper,
Tabs,
Tab,
Fab,
Snackbar,
Alert,
Button,
} from '@mui/material';
import {
ArrowBack as ArrowBackIcon,
} from '@mui/icons-material';
import { useKnowledgeDetail, useKnowledgeOperations } from '@/hooks/knowledge-hooks';
import GeneralForm, { type BasicFormData } from './components/GeneralForm';
import GeneralForm from './components/GeneralForm';
import ChunkMethodForm, { type ConfigFormData } from './components/ChunkMethodForm';
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
import { useSnackbar } from '@/components/Provider/SnackbarProvider';
import { DOCUMENT_PARSER_TYPES } from '@/constants/knowledge';
import { MainContainer } from './configuration';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`setting-tabpanel-${index}`}
aria-labelledby={`setting-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
{children}
</Box>
)}
</div>
);
}
function a11yProps(index: number) {
return {
id: `setting-tab-${index}`,
'aria-controls': `setting-tabpanel-${index}`,
};
// 统一表单数据类型
interface BaseFormData {
name: string;
description: string;
permission: string;
avatar?: string;
}
function KnowledgeBaseSetting() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [tabValue, setTabValue] = useState(0);
const [tabValue, setTabValue] = useState<'generalForm' | 'chunkMethodForm'>('generalForm');
// 获取知识库详情
const { knowledge, loading: detailLoading, refresh } = useKnowledgeDetail(id || '');
const { showMessage } = useSnackbar();
// 知识库操作hooks
const {
updateKnowledgeBasicInfo,
updateKnowledgeModelConfig,
loading: operationLoading
const {
updateKnowledgeBasicInfo,
updateKnowledgeModelConfig,
loading: operationLoading
} = useKnowledgeOperations();
// 基础信息表单
const basicForm = useForm<BasicFormData>({
// 统一表单管理
const form = useForm<any>({
defaultValues: {
name: '',
description: '',
permission: 'me',
avatar: undefined,
},
}
});
// 解析配置表单
const configForm = useForm<ConfigFormData>({
defaultValues: {
parser_id: 'naive',
chunk_token_count: 512,
layout_recognize: false,
task_page_size: 0,
},
// 监听parser_id变化
const parserId = useWatch({
control: form.control,
name: 'parser_id',
});
// 当知识库数据加载完成时,更新表单默认值
useEffect(() => {
if (knowledge) {
basicForm.reset({
form.reset({
name: knowledge.name || '',
description: knowledge.description || '',
permission: knowledge.permission || 'me',
avatar: knowledge.avatar,
});
configForm.reset({
// parser_id: knowledge.parser_id || 'naive',
// chunk_token_count: knowledge.chunk_token_count || 512,
// layout_recognize: knowledge.layout_recognize || false,
// task_page_size: knowledge.task_page_size || 0,
parser_id: knowledge.parser_id || DOCUMENT_PARSER_TYPES.Naive,
parser_config: {
chunk_token_num: knowledge.parser_config?.chunk_token_num || 512,
delimiter: knowledge.parser_config?.delimiter || '\n',
auto_keywords: knowledge.parser_config?.auto_keywords || 0,
auto_questions: knowledge.parser_config?.auto_questions || 0,
html4excel: knowledge.parser_config?.html4excel || false,
topn_tags: knowledge.parser_config?.topn_tags || 3,
raptor: {
use_raptor: knowledge.parser_config?.raptor?.use_raptor || false,
prompt: knowledge.parser_config?.raptor?.prompt || '请总结以下段落。小心数字,不要编造。段落如下:\n {cluster_content}\n以上就是你需要总结的内容。',
max_token: knowledge.parser_config?.raptor?.max_token || 256,
threshold: knowledge.parser_config?.raptor?.threshold || 0.1,
max_cluster: knowledge.parser_config?.raptor?.max_cluster || 64,
random_seed: knowledge.parser_config?.raptor?.random_seed || 0,
},
graphrag: {
use_graphrag: knowledge.parser_config?.graphrag?.use_graphrag || false,
entity_types: knowledge.parser_config?.graphrag?.entity_types || ['organization', 'person', 'geo', 'event', 'category'],
method: knowledge.parser_config?.graphrag?.method || 'light',
},
},
});
}
}, [knowledge, basicForm, configForm]);
}, [knowledge, form]);
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
const handleTabChange = (event: React.SyntheticEvent, newValue: 'generalForm' | 'chunkMethodForm') => {
setTabValue(newValue);
};
const handleBasicInfoSubmit = async (data: BasicFormData) => {
const handleSubmit = async ({data}: {data: any}) => {
if (!knowledge) return;
console.log('提交数据:', data);
try {
const kb = {
...data,
// parser_id: knowledge.parser_id,
kb_id: knowledge.id,
} as any;
// 分别处理基础信息和配置信息
if (tabValue === 'generalForm') {
const basicData = {
name: data.name,
description: data.description,
permission: data.permission,
avatar: data.avatar,
kb_id: knowledge.id,
parser_id: knowledge.parser_id,
} as any;
await updateKnowledgeBasicInfo(kb);
showMessage.success('基础信息更新成功');
await updateKnowledgeBasicInfo(basicData);
showMessage.success('基础信息更新成功');
} else {
const configData = {
kb_id: knowledge.id,
name: knowledge.name,
description: knowledge.description,
permission: knowledge.permission,
avatar: knowledge.avatar,
parser_id: data.parser_id,
embd_id: data.embd_id,
parser_config: data.parser_config,
};
await updateKnowledgeModelConfig(configData);
showMessage.success('解析配置更新成功');
}
// 刷新知识库详情
refresh();
} catch (error) {
// showMessage.error('基础信息更新失败');
showMessage.error(`${tabValue === 'generalForm' ? '基础信息' : '解析配置'}更新失败`);
}
};
const handleConfigSubmit = async (data: ConfigFormData) => {
if (!id) return;
try {
await updateKnowledgeModelConfig({
id,
parser_id: data.parser_id,
// 可以根据需要添加更多配置字段
});
showMessage.success('解析配置更新成功');
// 刷新知识库详情
refresh();
} catch (error) {
// showMessage.error('解析配置更新失败');
}
};
const handleBackToDetail = () => {
const handleNavigateBack = () => {
navigate(`/knowledge/${id}`);
};
if (detailLoading) {
return (
<Container maxWidth="lg" sx={{ py: 4 }}>
<MainContainer>
<Typography>...</Typography>
</Container>
</MainContainer>
);
}
return (
<Container maxWidth="lg" sx={{ py: 4 }}>
<MainContainer>
{/* 面包屑导航 */}
<KnowledgeBreadcrumbs knowledge={knowledge} />
<Box sx={{ mb: 3 }}>
<Typography variant="h4" gutterBottom>
@@ -176,40 +167,37 @@ function KnowledgeBaseSetting() {
</Typography>
</Box>
<Paper sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={tabValue} onChange={handleTabChange} aria-label="设置选项卡">
<Tab label="基础信息" {...a11yProps(0)} />
<Tab label="解析配置" {...a11yProps(1)} />
</Tabs>
</Box>
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={tabValue} onChange={handleTabChange} aria-label="设置选项卡">
<Tab label="基础信息" value="generalForm" />
<Tab label="解析配置" value="chunkMethodForm" />
</Tabs>
</Box>
<TabPanel value={tabValue} index={0}>
<GeneralForm
form={basicForm}
onSubmit={handleBasicInfoSubmit}
isSubmitting={operationLoading}
submitButtonText="保存基础信息"
disabled={detailLoading}
/>
</TabPanel>
<Box sx={{ mt: 3 }}>
{tabValue === 'generalForm' && (
<GeneralForm />
)}
{tabValue === 'chunkMethodForm' && (
<ChunkMethodForm />
)}
</Box>
<TabPanel value={tabValue} index={1}>
<ChunkMethodForm
form={configForm as any}
onSubmit={handleConfigSubmit}
isSubmitting={operationLoading}
submitButtonText="保存解析配置"
disabled={detailLoading}
/>
</TabPanel>
</Paper>
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end' }}>
<Button type="submit" variant="contained">
</Button>
</Box>
</Form>
</FormProvider>
{/* 返回按钮 */}
<Fab
color="primary"
aria-label="返回知识库详情"
onClick={handleBackToDetail}
onClick={handleNavigateBack}
sx={{
position: 'fixed',
bottom: 128,
@@ -218,7 +206,7 @@ function KnowledgeBaseSetting() {
>
<ArrowBackIcon />
</Fab>
</Container>
</MainContainer>
);
}