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:
@@ -55,7 +55,7 @@ const Header = () => {
|
||||
color: '#333',
|
||||
}}
|
||||
>
|
||||
RAG Dashboard
|
||||
RAG Empowerment System
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
|
||||
@@ -41,8 +41,8 @@ const Sidebar = () => {
|
||||
letterSpacing: '0.5px',
|
||||
}}
|
||||
>
|
||||
RAGflow Prototype
|
||||
</Typography>
|
||||
T-Systems Enterprise
|
||||
</Typography>
|
||||
|
||||
<List>
|
||||
{navItems.map((item) => {
|
||||
@@ -93,7 +93,7 @@ const Sidebar = () => {
|
||||
opacity: 0.7,
|
||||
}}
|
||||
>
|
||||
© 2025 RAG Demo
|
||||
© 2025 T-Systems
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
126
src/constants/llm.ts
Normal file
126
src/constants/llm.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
export const LLM_FACTORY_LIST = Object.freeze({
|
||||
TongYiQianWen: 'Tongyi-Qianwen',
|
||||
Moonshot: 'Moonshot',
|
||||
OpenAI: 'OpenAI',
|
||||
ZhipuAI: 'ZHIPU-AI',
|
||||
WenXinYiYan: '文心一言',
|
||||
Ollama: 'Ollama',
|
||||
Xinference: 'Xinference',
|
||||
ModelScope: 'ModelScope',
|
||||
DeepSeek: 'DeepSeek',
|
||||
VolcEngine: 'VolcEngine',
|
||||
BaiChuan: 'BaiChuan',
|
||||
Jina: 'Jina',
|
||||
MiniMax: 'MiniMax',
|
||||
Mistral: 'Mistral',
|
||||
AzureOpenAI: 'Azure-OpenAI',
|
||||
Bedrock: 'Bedrock',
|
||||
Gemini: 'Gemini',
|
||||
Groq: 'Groq',
|
||||
OpenRouter: 'OpenRouter',
|
||||
LocalAI: 'LocalAI',
|
||||
StepFun: 'StepFun',
|
||||
NVIDIA: 'NVIDIA',
|
||||
LMStudio: 'LM-Studio',
|
||||
OpenAiAPICompatible: 'OpenAI-API-Compatible',
|
||||
Cohere: 'Cohere',
|
||||
LeptonAI: 'LeptonAI',
|
||||
TogetherAI: 'TogetherAI',
|
||||
PerfXCloud: 'PerfXCloud',
|
||||
Upstage: 'Upstage',
|
||||
NovitaAI: 'NovitaAI',
|
||||
SILICONFLOW: 'SILICONFLOW',
|
||||
PPIO: 'PPIO',
|
||||
ZeroOneAI: '01.AI',
|
||||
Replicate: 'Replicate',
|
||||
TencentHunYuan: 'Tencent Hunyuan',
|
||||
XunFeiSpark: 'XunFei Spark',
|
||||
BaiduYiYan: 'BaiduYiyan',
|
||||
FishAudio: 'Fish Audio',
|
||||
TencentCloud: 'Tencent Cloud',
|
||||
Anthropic: 'Anthropic',
|
||||
VoyageAI: 'Voyage AI',
|
||||
GoogleCloud: 'Google Cloud',
|
||||
HuggingFace: 'HuggingFace',
|
||||
YouDao: 'Youdao',
|
||||
BAAI: 'BAAI',
|
||||
NomicAI: 'nomic-ai',
|
||||
JinaAI: 'jinaai',
|
||||
SentenceTransformers: 'sentence-transformers',
|
||||
GPUStack: 'GPUStack',
|
||||
VLLM: 'VLLM',
|
||||
GiteeAI: 'GiteeAI',
|
||||
Ai302: '302.AI',
|
||||
DeepInfra: 'DeepInfra',
|
||||
Grok: 'Grok',
|
||||
XAI: 'xAI',
|
||||
TokenPony: 'TokenPony',
|
||||
Meituan: 'Meituan',
|
||||
CometAPI: 'CometAPI',
|
||||
DeerAPI: 'DeerAPI',
|
||||
} as const);
|
||||
|
||||
export type LLMFactory = (typeof LLM_FACTORY_LIST)[keyof typeof LLM_FACTORY_LIST];
|
||||
|
||||
// Please lowercase the file name
|
||||
export const IconMap = {
|
||||
[LLM_FACTORY_LIST.TongYiQianWen]: 'tongyi',
|
||||
[LLM_FACTORY_LIST.Moonshot]: 'moonshot',
|
||||
[LLM_FACTORY_LIST.OpenAI]: 'openai',
|
||||
[LLM_FACTORY_LIST.ZhipuAI]: 'zhipu',
|
||||
[LLM_FACTORY_LIST.WenXinYiYan]: 'wenxin',
|
||||
[LLM_FACTORY_LIST.Ollama]: 'ollama',
|
||||
[LLM_FACTORY_LIST.Xinference]: 'xinference',
|
||||
[LLM_FACTORY_LIST.ModelScope]: 'modelscope',
|
||||
[LLM_FACTORY_LIST.DeepSeek]: 'deepseek',
|
||||
[LLM_FACTORY_LIST.VolcEngine]: 'volc_engine',
|
||||
[LLM_FACTORY_LIST.BaiChuan]: 'baichuan',
|
||||
[LLM_FACTORY_LIST.Jina]: 'jina',
|
||||
[LLM_FACTORY_LIST.MiniMax]: 'chat-minimax',
|
||||
[LLM_FACTORY_LIST.Mistral]: 'mistral',
|
||||
[LLM_FACTORY_LIST.AzureOpenAI]: 'azure',
|
||||
[LLM_FACTORY_LIST.Bedrock]: 'bedrock',
|
||||
[LLM_FACTORY_LIST.Gemini]: 'gemini',
|
||||
[LLM_FACTORY_LIST.Groq]: 'groq-next',
|
||||
[LLM_FACTORY_LIST.OpenRouter]: 'open-router',
|
||||
[LLM_FACTORY_LIST.LocalAI]: 'local-ai',
|
||||
[LLM_FACTORY_LIST.StepFun]: 'stepfun',
|
||||
[LLM_FACTORY_LIST.NVIDIA]: 'nvidia',
|
||||
[LLM_FACTORY_LIST.LMStudio]: 'lm-studio',
|
||||
[LLM_FACTORY_LIST.OpenAiAPICompatible]: 'openai-api',
|
||||
[LLM_FACTORY_LIST.Cohere]: 'cohere',
|
||||
[LLM_FACTORY_LIST.LeptonAI]: 'lepton-ai',
|
||||
[LLM_FACTORY_LIST.TogetherAI]: 'together-ai',
|
||||
[LLM_FACTORY_LIST.PerfXCloud]: 'perfx-cloud',
|
||||
[LLM_FACTORY_LIST.Upstage]: 'upstage',
|
||||
[LLM_FACTORY_LIST.NovitaAI]: 'novita-ai',
|
||||
[LLM_FACTORY_LIST.SILICONFLOW]: 'siliconflow',
|
||||
[LLM_FACTORY_LIST.PPIO]: 'ppio',
|
||||
[LLM_FACTORY_LIST.ZeroOneAI]: 'yi',
|
||||
[LLM_FACTORY_LIST.Replicate]: 'replicate',
|
||||
[LLM_FACTORY_LIST.TencentHunYuan]: 'hunyuan',
|
||||
[LLM_FACTORY_LIST.XunFeiSpark]: 'spark',
|
||||
[LLM_FACTORY_LIST.BaiduYiYan]: 'yiyan',
|
||||
[LLM_FACTORY_LIST.FishAudio]: 'fish-audio',
|
||||
[LLM_FACTORY_LIST.TencentCloud]: 'tencent-cloud',
|
||||
[LLM_FACTORY_LIST.Anthropic]: 'anthropic',
|
||||
[LLM_FACTORY_LIST.VoyageAI]: 'voyage',
|
||||
[LLM_FACTORY_LIST.GoogleCloud]: 'google-cloud',
|
||||
[LLM_FACTORY_LIST.HuggingFace]: 'huggingface',
|
||||
[LLM_FACTORY_LIST.YouDao]: 'youdao',
|
||||
[LLM_FACTORY_LIST.BAAI]: 'baai',
|
||||
[LLM_FACTORY_LIST.NomicAI]: 'nomic-ai',
|
||||
[LLM_FACTORY_LIST.JinaAI]: 'jina',
|
||||
[LLM_FACTORY_LIST.SentenceTransformers]: 'sentence-transformers',
|
||||
[LLM_FACTORY_LIST.GPUStack]: 'gpustack',
|
||||
[LLM_FACTORY_LIST.VLLM]: 'vllm',
|
||||
[LLM_FACTORY_LIST.GiteeAI]: 'gitee-ai',
|
||||
[LLM_FACTORY_LIST.Ai302]: 'ai302',
|
||||
[LLM_FACTORY_LIST.DeepInfra]: 'deepinfra',
|
||||
[LLM_FACTORY_LIST.Grok]: 'grok',
|
||||
[LLM_FACTORY_LIST.XAI]: 'xai',
|
||||
[LLM_FACTORY_LIST.TokenPony]: 'token-pony',
|
||||
[LLM_FACTORY_LIST.Meituan]: 'longcat',
|
||||
[LLM_FACTORY_LIST.CometAPI]: 'cometapi',
|
||||
[LLM_FACTORY_LIST.DeerAPI]: 'deerapi',
|
||||
};
|
||||
@@ -17,6 +17,7 @@ export interface UseDocumentListState {
|
||||
// 文档列表Hook返回值接口
|
||||
export interface UseDocumentListReturn extends UseDocumentListState {
|
||||
fetchDocuments: (params?: IFetchKnowledgeListRequestParams) => Promise<void>;
|
||||
fetchDocumentsFilter: () => Promise<void>;
|
||||
setKeywords: (keywords: string) => void;
|
||||
setCurrentPage: (page: number) => void;
|
||||
setPageSize: (size: number) => void;
|
||||
@@ -95,6 +96,27 @@ export const useDocumentList = (
|
||||
}
|
||||
}, [kbId, keywords, currentPage, pageSize]);
|
||||
|
||||
/**
|
||||
* 获取文档过滤器
|
||||
*/
|
||||
const fetchDocumentsFilter = useCallback(async () => {
|
||||
if (!kbId) return;
|
||||
|
||||
try {
|
||||
const response = await knowledgeService.getDocumentFilter({
|
||||
kb_id: kbId,
|
||||
});
|
||||
if (response.data.code === 0) {
|
||||
const data = response.data.data;
|
||||
} else {
|
||||
throw new Error(response.data.message || '获取文档过滤器失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.message || error.message || '获取文档过滤器失败';
|
||||
console.error('Failed to fetch document filter:', error);
|
||||
}
|
||||
}, [kbId, keywords]);
|
||||
|
||||
/**
|
||||
* 刷新当前页面数据
|
||||
*/
|
||||
@@ -139,6 +161,7 @@ export const useDocumentList = (
|
||||
pageSize,
|
||||
keywords,
|
||||
fetchDocuments,
|
||||
fetchDocumentsFilter,
|
||||
setKeywords: handleSetKeywords,
|
||||
setCurrentPage: handleSetCurrentPage,
|
||||
setPageSize: handleSetPageSize,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
import type { IKnowledge, IKnowledgeResult } from '@/interfaces/database/knowledge';
|
||||
import type { IKnowledge, IKnowledgeResult, IParserConfig } from '@/interfaces/database/knowledge';
|
||||
import type { IFetchKnowledgeListRequestParams } from '@/interfaces/request/knowledge';
|
||||
|
||||
/**
|
||||
@@ -299,11 +299,9 @@ export const useKnowledgeOperations = () => {
|
||||
* 包括嵌入模型、解析器配置、相似度阈值等
|
||||
*/
|
||||
const updateKnowledgeModelConfig = useCallback(async (data: {
|
||||
id: string;
|
||||
kb_id: string;
|
||||
embd_id?: string;
|
||||
// parser_config?: Partial<ParserConfig>;
|
||||
similarity_threshold?: number;
|
||||
vector_similarity_weight?: number;
|
||||
parser_config?: Partial<IParserConfig>;
|
||||
parser_id?: string;
|
||||
}) => {
|
||||
try {
|
||||
@@ -311,7 +309,6 @@ export const useKnowledgeOperations = () => {
|
||||
setError(null);
|
||||
|
||||
const updateData = {
|
||||
kb_id: data.id,
|
||||
...data,
|
||||
};
|
||||
|
||||
|
||||
195
src/hooks/llm-hooks.ts
Normal file
195
src/hooks/llm-hooks.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { LLM_MODEL_TYPES, type LlmModelType } from "@/constants/knowledge";
|
||||
import type { IThirdOAIModelCollection, IThirdOAIModel } from "@/interfaces/database/llm";
|
||||
import userService from "@/services/user_service";
|
||||
|
||||
/**
|
||||
* 获取LLM模型列表的Hook
|
||||
* @param modelType 可选的模型类型过滤
|
||||
* @returns LLM模型数据和相关状态
|
||||
*/
|
||||
export function useLlmList(modelType?: LlmModelType) {
|
||||
const [data, setData] = useState<IThirdOAIModelCollection>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchLlmList = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await userService.llm_list({ model_type: modelType });
|
||||
|
||||
if (response.data?.code === 0) {
|
||||
setData(response.data?.data ?? {});
|
||||
} else {
|
||||
setError(response.data?.message || '获取LLM列表失败');
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '获取LLM列表失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [modelType]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchLlmList();
|
||||
}, [fetchLlmList]);
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
refresh: fetchLlmList,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的LLM选项列表
|
||||
* @param modelType 可选的模型类型过滤
|
||||
* @returns 格式化的选项列表
|
||||
*/
|
||||
export function useLlmOptions(modelType?: LlmModelType) {
|
||||
const { data: llmInfo, loading, error } = useLlmList(modelType);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return Object.entries(llmInfo).map(([key, value]) => {
|
||||
return {
|
||||
label: key,
|
||||
options: value
|
||||
.filter((x) => x.available) // 只显示可用的模型
|
||||
.map((x) => ({
|
||||
label: x.llm_name,
|
||||
value: `${x.llm_name}@${x.fid}`,
|
||||
disabled: !x.available,
|
||||
model: x,
|
||||
})),
|
||||
};
|
||||
}).filter((group) => group.options.length > 0); // 过滤掉空组
|
||||
}, [llmInfo]);
|
||||
|
||||
return {
|
||||
options,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模型类型获取分组的LLM选项
|
||||
* @returns 按模型类型分组的选项
|
||||
*/
|
||||
export function useLlmOptionsByModelType() {
|
||||
const { data: llmInfo, loading, error } = useLlmList();
|
||||
|
||||
const getOptionsByModelType = useCallback((modelType: LlmModelType) => {
|
||||
return Object.entries(llmInfo)
|
||||
.filter(([, value]) =>
|
||||
modelType
|
||||
? value.some((x) => x.model_type.includes(modelType))
|
||||
: true,
|
||||
)
|
||||
.map(([key, value]) => {
|
||||
return {
|
||||
label: key,
|
||||
options: value
|
||||
.filter(
|
||||
(x) =>
|
||||
(modelType ? x.model_type.includes(modelType) : true) &&
|
||||
x.available,
|
||||
)
|
||||
.map((x) => ({
|
||||
label: x.llm_name,
|
||||
value: `${x.llm_name}@${x.fid}`,
|
||||
disabled: !x.available,
|
||||
model: x,
|
||||
})),
|
||||
};
|
||||
})
|
||||
.filter((x) => x.options.length > 0);
|
||||
}, [llmInfo]);
|
||||
|
||||
return {
|
||||
getOptionsByModelType,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌入模型选项
|
||||
* @returns 嵌入模型选项列表
|
||||
*/
|
||||
export function useEmbeddingModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.Embedding);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天模型选项
|
||||
* @returns 聊天模型选项列表
|
||||
*/
|
||||
export function useChatModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.Chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取重排序模型选项
|
||||
* @returns 重排序模型选项列表
|
||||
*/
|
||||
export function useRerankModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.Rerank);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像转文本模型选项
|
||||
* @returns 图像转文本模型选项列表
|
||||
*/
|
||||
export function useImage2TextModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.Image2text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语音转文本模型选项
|
||||
* @returns 语音转文本模型选项列表
|
||||
*/
|
||||
export function useSpeech2TextModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.Speech2text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文本转语音模型选项
|
||||
* @returns 文本转语音模型选项列表
|
||||
*/
|
||||
export function useTTSModelOptions() {
|
||||
return useLlmOptions(LLM_MODEL_TYPES.TTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模型ID获取模型详情
|
||||
* @param modelId 模型ID (格式: llm_name@fid)
|
||||
* @returns 模型详情
|
||||
*/
|
||||
export function useLlmModelDetail(modelId?: string) {
|
||||
const { data: llmInfo, loading, error } = useLlmList();
|
||||
|
||||
const modelDetail = useMemo(() => {
|
||||
if (!modelId || !llmInfo) return null;
|
||||
|
||||
const [llmName, fid] = modelId.split('@');
|
||||
|
||||
for (const models of Object.values(llmInfo)) {
|
||||
const model = models.find(
|
||||
(m) => m.llm_name === llmName && m.fid === fid
|
||||
);
|
||||
if (model) return model;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [modelId, llmInfo]);
|
||||
|
||||
return {
|
||||
modelDetail,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
@@ -1,539 +1,84 @@
|
||||
import React from 'react';
|
||||
import { type UseFormReturn } from 'react-hook-form';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid,
|
||||
TextField,
|
||||
FormHelperText,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Switch,
|
||||
FormControlLabel,
|
||||
Chip,
|
||||
Slider,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Save as SaveIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Add as AddIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { DOCUMENT_PARSER_TYPES, type DocumentParserType } from '@/constants/knowledge';
|
||||
import { type IParserConfig } from '@/interfaces/database/knowledge';
|
||||
import {
|
||||
NaiveConfiguration,
|
||||
QAConfiguration,
|
||||
PaperConfiguration,
|
||||
ResumeConfiguration,
|
||||
ManualConfiguration,
|
||||
TableConfiguration,
|
||||
BookConfiguration,
|
||||
LawsConfiguration,
|
||||
PresentationConfiguration,
|
||||
OneConfiguration,
|
||||
TagConfiguration,
|
||||
ChunkMethodItem,
|
||||
} from '../configuration';
|
||||
|
||||
/**
|
||||
{
|
||||
"kb_id": "dcc2871aa4cd11f08d4116ac85b1de0a",
|
||||
"name": "k1123",
|
||||
"description": " 213213",
|
||||
"permission": "team",
|
||||
"parser_id": "naive",
|
||||
"embd_id": "",
|
||||
"parser_config": {
|
||||
"layout_recognize": "Plain Text",
|
||||
"chunk_token_num": 512,
|
||||
"delimiter": "\n",
|
||||
"auto_keywords": 0,
|
||||
"auto_questions": 0,
|
||||
"html4excel": false,
|
||||
"topn_tags": 3,
|
||||
"raptor": {
|
||||
"use_raptor": true,
|
||||
"prompt": "请总结以下段落。 小心数字,不要编造。 段落如下:\n {cluster_content}\n以上就是你需要总结的内容。",
|
||||
"max_token": 256,
|
||||
"threshold": 0.1,
|
||||
"max_cluster": 64,
|
||||
"random_seed": 0
|
||||
},
|
||||
"graphrag": {
|
||||
"use_graphrag": true,
|
||||
"entity_types": [
|
||||
"organization",
|
||||
"person",
|
||||
"geo",
|
||||
"event",
|
||||
"category"
|
||||
],
|
||||
"method": "light"
|
||||
}
|
||||
},
|
||||
"pagerank": 0
|
||||
// 配置组件映射表
|
||||
const ConfigurationComponentMap = {
|
||||
[DOCUMENT_PARSER_TYPES.Naive]: NaiveConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Qa]: QAConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Resume]: ResumeConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Manual]: ManualConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Table]: TableConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Paper]: PaperConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Book]: BookConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Laws]: LawsConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Presentation]: PresentationConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.One]: OneConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Tag]: TagConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.KnowledgeGraph]: TagConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Picture]: TagConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Audio]: TagConfiguration,
|
||||
[DOCUMENT_PARSER_TYPES.Email]: TagConfiguration,
|
||||
// [DOCUMENT_PARSER_TYPES.KnowledgeGraph]: KnowledgeGraphConfiguration,
|
||||
// [DOCUMENT_PARSER_TYPES.Picture]: PictureConfiguration,
|
||||
// [DOCUMENT_PARSER_TYPES.Audio]: AudioConfiguration,
|
||||
// [DOCUMENT_PARSER_TYPES.Email]: GraphragConfiguration,
|
||||
};
|
||||
|
||||
// 空组件
|
||||
function EmptyComponent() {
|
||||
return (
|
||||
<Box sx={{ textAlign: 'center', py: 4 }}>
|
||||
<Typography color="text.secondary">
|
||||
请选择一个解析方法来配置相关参数
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// 解析器选项配置
|
||||
const parserOptions = [
|
||||
{ value: DOCUMENT_PARSER_TYPES.Naive, label: '通用解析器', description: '适用于大多数文档类型' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A解析器', description: '适用于问答格式文档' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Resume, label: '简历解析器', description: '专门用于解析简历文档' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Manual, label: '手册解析器', description: '适用于技术手册和说明书' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Table, label: '表格解析器', description: '专门处理表格数据' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Paper, label: '论文解析器', description: '适用于学术论文' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Book, label: '书籍解析器', description: '适用于书籍和长文档' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Laws, label: '法律解析器', description: '专门处理法律文档' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Presentation, label: '演示文稿解析器', description: '适用于PPT等演示文档' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Picture, label: '图片解析器', description: '处理图片中的文字内容' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.One, label: '整体解析器', description: '将整个文档作为一个块处理' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Audio, label: '音频解析器', description: '处理音频文件转录' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Email, label: '邮件解析器', description: '专门处理邮件格式' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Tag, label: '标签解析器', description: '基于标签的文档解析' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.KnowledgeGraph, label: '知识图谱解析器', description: '构建知识图谱结构' },
|
||||
];
|
||||
|
||||
export interface ConfigFormData extends IParserConfig {
|
||||
parser_id: DocumentParserType;
|
||||
}
|
||||
|
||||
interface ChunkMethodFormProps {
|
||||
form: UseFormReturn<ConfigFormData>;
|
||||
onSubmit: (data: ConfigFormData) => void;
|
||||
isSubmitting?: boolean;
|
||||
onCancel?: () => void;
|
||||
disabled?: boolean;
|
||||
submitButtonText?: string;
|
||||
cancelButtonText?: string;
|
||||
}
|
||||
function ChunkMethodForm() {
|
||||
const { control } = useFormContext();
|
||||
|
||||
function ChunkMethodForm({
|
||||
form,
|
||||
onSubmit,
|
||||
isSubmitting = false,
|
||||
onCancel,
|
||||
disabled = false,
|
||||
submitButtonText = '保存',
|
||||
cancelButtonText = '取消'
|
||||
}: ChunkMethodFormProps) {
|
||||
const selectedParser: DocumentParserType = form.watch('parser_id');
|
||||
const [entityTypes, setEntityTypes] = React.useState<string[]>(['organization', 'person', 'geo', 'event', 'category']);
|
||||
// 监听parser_id变化
|
||||
const parser_id = useWatch({
|
||||
control,
|
||||
name: 'parser_id',
|
||||
});
|
||||
|
||||
// 通用配置部分
|
||||
const renderGeneralConfig = () => (
|
||||
<Accordion defaultExpanded>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">通用</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container spacing={3}>
|
||||
{/* 切片方法 */}
|
||||
<Grid size={12}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>切片方法</InputLabel>
|
||||
<Select
|
||||
label="切片方法"
|
||||
defaultValue={DOCUMENT_PARSER_TYPES.Naive}
|
||||
{...form.register('parser_id')}
|
||||
value={form.watch('parser_id') || DOCUMENT_PARSER_TYPES.Naive}
|
||||
>
|
||||
{parserOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<Box>
|
||||
<Typography variant="body1">{option.label}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{option.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>选择适合您文档类型的解析器</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* PDF解析器 */}
|
||||
<Grid size={12}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>PDF解析器</InputLabel>
|
||||
<Select
|
||||
label="PDF解析器"
|
||||
defaultValue="Naive"
|
||||
disabled={disabled}
|
||||
>
|
||||
<MenuItem value="Naive">Naive</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* 嵌入模型 */}
|
||||
<Grid size={12}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>嵌入模型</InputLabel>
|
||||
<Select
|
||||
label="嵌入模型"
|
||||
defaultValue=""
|
||||
disabled={disabled}
|
||||
>
|
||||
<MenuItem value="">请选择嵌入模型</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* 建议文本块大小 */}
|
||||
<Grid size={12}>
|
||||
<Typography gutterBottom>建议文本块大小</Typography>
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
defaultValue={512}
|
||||
min={50}
|
||||
max={2048}
|
||||
step={1}
|
||||
valueLabelDisplay="on"
|
||||
disabled={disabled}
|
||||
{...form.register('chunk_token_num', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ ml: 2 }}>
|
||||
512
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* 文本分段标识符 */}
|
||||
<Grid size={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="文本分段标识符"
|
||||
placeholder="\\n"
|
||||
disabled={disabled}
|
||||
{...form.register('delimiter')}
|
||||
helperText="用于分割文本的分隔符"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
// 页面排名配置部分
|
||||
const renderPageRankConfig = () => (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">页面排名</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container spacing={3}>
|
||||
{/* 页面排名 */}
|
||||
<Grid size={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Typography>页面排名</Typography>
|
||||
<Switch disabled={disabled} />
|
||||
<TextField
|
||||
type="number"
|
||||
size="small"
|
||||
defaultValue={0}
|
||||
disabled={disabled}
|
||||
sx={{ width: 100 }}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* 自动关键词提取 */}
|
||||
<Grid size={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Typography>自动关键词提取</Typography>
|
||||
<Switch disabled={disabled} />
|
||||
<TextField
|
||||
type="number"
|
||||
size="small"
|
||||
defaultValue={0}
|
||||
disabled={disabled}
|
||||
sx={{ width: 100 }}
|
||||
{...form.register('auto_keywords', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* 自动问题提取 */}
|
||||
<Grid size={12}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Typography>自动问题提取</Typography>
|
||||
<Switch disabled={disabled} />
|
||||
<TextField
|
||||
type="number"
|
||||
size="small"
|
||||
defaultValue={0}
|
||||
disabled={disabled}
|
||||
sx={{ width: 100 }}
|
||||
{...form.register('auto_questions', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* 表格转HTML */}
|
||||
<Grid size={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
{...form.register('html4excel')}
|
||||
/>
|
||||
}
|
||||
label="表格转HTML"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 标签集 */}
|
||||
<Grid size={12}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>标签集</InputLabel>
|
||||
<Select
|
||||
label="标签集"
|
||||
defaultValue=""
|
||||
disabled={disabled}
|
||||
>
|
||||
<MenuItem value="">请选择</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
// RAPTOR集成配置部分
|
||||
const renderRaptorConfig = () => (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">使用召回增强RAPTOR集成</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container spacing={3}>
|
||||
{/* 启用RAPTOR */}
|
||||
<Grid size={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.use_raptor')}
|
||||
/>
|
||||
}
|
||||
label="使用召回增强RAPTOR集成"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 提示词 */}
|
||||
<Grid size={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={4}
|
||||
label="提示词"
|
||||
placeholder="请总结以下段落。小心数字,不要编造。段落如下: {cluster_content} 以上就是你需要总结的内容。"
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.prompt')}
|
||||
helperText="用于RAPTOR集成的提示词模板"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 最大token数 */}
|
||||
<Grid size={{xs:12,sm:6}}>
|
||||
<Typography gutterBottom>最大token数</Typography>
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
defaultValue={256}
|
||||
min={50}
|
||||
max={1000}
|
||||
step={1}
|
||||
valueLabelDisplay="on"
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.max_token', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ ml: 2 }}>
|
||||
256
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* 阈值 */}
|
||||
<Grid size={{xs:12,sm:6}}>
|
||||
<Typography gutterBottom>阈值</Typography>
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
defaultValue={0.1}
|
||||
min={0 as number}
|
||||
max={1}
|
||||
step={0.01}
|
||||
valueLabelDisplay="on"
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.threshold', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ ml: 2 }}>
|
||||
0.1
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* 最大聚类数 */}
|
||||
<Grid size={{xs:12,sm:6}}>
|
||||
<Typography gutterBottom>最大聚类数</Typography>
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
defaultValue={64}
|
||||
min={1}
|
||||
max={200 as number}
|
||||
step={1}
|
||||
valueLabelDisplay="on"
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.max_cluster', { valueAsNumber: true })}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ ml: 2 }}>
|
||||
64
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* 随机种子 */}
|
||||
<Grid size={{xs:12,sm:6}}>
|
||||
<TextField
|
||||
fullWidth
|
||||
type="number"
|
||||
label="随机种子"
|
||||
defaultValue={0}
|
||||
disabled={disabled}
|
||||
{...form.register('raptor.random_seed', { valueAsNumber: true })}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<Button size="small" disabled={disabled}>
|
||||
+
|
||||
</Button>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
// 提取知识图谱配置部分
|
||||
const renderGraphRagConfig = () => (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">提取知识图谱</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container spacing={3}>
|
||||
{/* 启用知识图谱 */}
|
||||
<Grid size={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
{...form.register('graphrag.use_graphrag')}
|
||||
/>
|
||||
}
|
||||
label="提取知识图谱"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 添加实体类型 */}
|
||||
<Grid size={12}>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
// 添加新实体类型的逻辑
|
||||
}}
|
||||
>
|
||||
+ 实体类型
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
{/* 实体类型标签 */}
|
||||
<Grid size={12}>
|
||||
<Typography variant="subtitle2" gutterBottom>实体类型</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
|
||||
{entityTypes.map((type, index) => (
|
||||
<Chip
|
||||
key={index}
|
||||
label={type}
|
||||
onDelete={disabled ? undefined : () => {
|
||||
setEntityTypes(prev => prev.filter((_, i) => i !== index));
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* 方法 */}
|
||||
<Grid size={12}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>方法</InputLabel>
|
||||
<Select
|
||||
label="方法"
|
||||
defaultValue="Light"
|
||||
{...form.register('graphrag.method')}
|
||||
>
|
||||
<MenuItem value="Light">Light</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* 实体归一化 */}
|
||||
<Grid size={12}>
|
||||
<FormControlLabel
|
||||
control={<Switch disabled={disabled} />}
|
||||
label="实体归一化"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 社区报告生成 */}
|
||||
<Grid size={12}>
|
||||
<FormControlLabel
|
||||
control={<Switch disabled={disabled} />}
|
||||
label="社区报告生成"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
// 根据parser_id动态选择配置组件
|
||||
const ConfigurationComponent = useMemo(() => {
|
||||
const parser = parser_id as DocumentParserType;
|
||||
const component = ConfigurationComponentMap[parser] || EmptyComponent;
|
||||
return component || EmptyComponent;
|
||||
}, [parser_id]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
配置
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom sx={{ mb: 3 }}>
|
||||
在这里更新您的知识库详细信息,并更改切片方法。
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
{renderGeneralConfig()}
|
||||
{renderPageRankConfig()}
|
||||
{renderRaptorConfig()}
|
||||
{renderGraphRagConfig()}
|
||||
</Box>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
|
||||
{onCancel && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={onCancel}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{cancelButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={form.handleSubmit(onSubmit)}
|
||||
disabled={disabled || isSubmitting}
|
||||
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SaveIcon />}
|
||||
>
|
||||
{submitButtonText}
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 动态配置内容 */}
|
||||
<ConfigurationComponent />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { type UseFormReturn } from 'react-hook-form';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -12,211 +12,138 @@ import {
|
||||
Avatar,
|
||||
Button,
|
||||
IconButton,
|
||||
CircularProgress,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
PhotoCamera as PhotoCameraIcon,
|
||||
Delete as DeleteIcon,
|
||||
Save as SaveIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
export interface BasicFormData {
|
||||
name: string;
|
||||
description: string;
|
||||
permission: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
interface GeneralFormProps {
|
||||
form: UseFormReturn<BasicFormData>;
|
||||
onSubmit: (data: BasicFormData) => void;
|
||||
isSubmitting?: boolean;
|
||||
onCancel?: () => void;
|
||||
disabled?: boolean;
|
||||
submitButtonText?: string;
|
||||
cancelButtonText?: string;
|
||||
}
|
||||
|
||||
function GeneralForm({
|
||||
form,
|
||||
onSubmit,
|
||||
isSubmitting = false,
|
||||
onCancel,
|
||||
disabled = false,
|
||||
submitButtonText = '提交',
|
||||
cancelButtonText = '取消'
|
||||
}: GeneralFormProps) {
|
||||
function GeneralForm() {
|
||||
const { control, watch, setValue } = useFormContext();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// 处理头像上传
|
||||
const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
// 检查文件类型
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('请选择图片文件');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件大小 (限制为 2MB)
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
alert('图片大小不能超过 2MB');
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const base64String = e.target?.result as string;
|
||||
form.setValue('avatar', base64String);
|
||||
setValue('avatar', e.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除头像
|
||||
const handleAvatarDelete = () => {
|
||||
form.setValue('avatar', undefined);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
setValue('avatar', undefined);
|
||||
};
|
||||
|
||||
const avatarValue = form.watch('avatar');
|
||||
const handleAvatarClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const avatar = watch('avatar');
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
基础信息
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{/* 头像上传 */}
|
||||
<Grid size={12}>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
头像
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Grid size={{xs:12, md:6}}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
|
||||
<Avatar
|
||||
src={avatarValue}
|
||||
sx={{ width: 80, height: 80 }}
|
||||
src={avatar}
|
||||
sx={{ width: 120, height: 120, cursor: 'pointer' }}
|
||||
onClick={handleAvatarClick}
|
||||
>
|
||||
{!avatarValue && form.watch('name')?.charAt(0)?.toUpperCase()}
|
||||
{!avatar && <PhotoCameraIcon sx={{ fontSize: 40 }} />}
|
||||
</Avatar>
|
||||
<Box>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleAvatarUpload}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<PhotoCameraIcon />}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={disabled}
|
||||
size="small"
|
||||
sx={{ mr: 1 }}
|
||||
startIcon={<PhotoCameraIcon />}
|
||||
onClick={handleAvatarClick}
|
||||
>
|
||||
选择图片
|
||||
上传头像
|
||||
</Button>
|
||||
{avatarValue && (
|
||||
{avatar && (
|
||||
<IconButton
|
||||
onClick={handleAvatarDelete}
|
||||
disabled={disabled}
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={handleAvatarDelete}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleAvatarUpload}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
支持 PNG、JPG 格式,文件大小不超过 2MB
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* 知识库名称 */}
|
||||
<Grid size={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="知识库名称"
|
||||
placeholder="请输入知识库名称"
|
||||
disabled={disabled}
|
||||
{...form.register('name', {
|
||||
required: '请输入知识库名称',
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: '知识库名称至少需要2个字符',
|
||||
},
|
||||
maxLength: {
|
||||
value: 50,
|
||||
message: '知识库名称不能超过50个字符',
|
||||
},
|
||||
})}
|
||||
error={!!form.formState.errors.name}
|
||||
helperText={form.formState.errors.name?.message?.toString() || '请输入知识库名称'}
|
||||
/>
|
||||
</Grid>
|
||||
{/* 表单字段 */}
|
||||
<Grid size={{xs:12,md:8}}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={{xs:12}}>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: '知识库名称不能为空' }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
label="知识库名称"
|
||||
fullWidth
|
||||
required
|
||||
error={!!error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 描述 */}
|
||||
<Grid size={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={4}
|
||||
label="描述"
|
||||
placeholder="请输入知识库描述"
|
||||
disabled={disabled}
|
||||
{...form.register('description', {
|
||||
maxLength: {
|
||||
value: 500,
|
||||
message: '描述不能超过500个字符',
|
||||
},
|
||||
})}
|
||||
error={!!form.formState.errors.description}
|
||||
helperText={form.formState.errors.description?.message?.toString() || '请输入知识库描述'}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{xs:12}}>
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
label="描述"
|
||||
fullWidth
|
||||
multiline
|
||||
rows={3}
|
||||
placeholder="请输入知识库描述..."
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* 权限设置 */}
|
||||
<Grid size={{xs:12,sm:6}}>
|
||||
<FormControl fullWidth disabled={disabled}>
|
||||
<InputLabel>权限设置</InputLabel>
|
||||
<Select
|
||||
label="权限设置"
|
||||
{...form.register('permission')}
|
||||
value={form.watch('permission') || 'me'}
|
||||
>
|
||||
<MenuItem value="me">仅自己</MenuItem>
|
||||
<MenuItem value="team">团队可见</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<Grid size={12}>
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 2 }}>
|
||||
{onCancel && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={onCancel}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{cancelButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={form.handleSubmit(onSubmit)}
|
||||
disabled={disabled || isSubmitting}
|
||||
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SaveIcon />}
|
||||
>
|
||||
{submitButtonText}
|
||||
</Button>
|
||||
</Box>
|
||||
<Grid size={{xs:12}}>
|
||||
<Controller
|
||||
name="permission"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>权限设置</InputLabel>
|
||||
<Select {...field} label="权限设置">
|
||||
<MenuItem value="me">仅自己</MenuItem>
|
||||
<MenuItem value="team">团队成员</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
49
src/pages/knowledge/configuration/audio.tsx
Normal file
49
src/pages/knowledge/configuration/audio.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function AudioConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
61
src/pages/knowledge/configuration/book.tsx
Normal file
61
src/pages/knowledge/configuration/book.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function BookConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2, p: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
118
src/pages/knowledge/configuration/common-item.tsx
Normal file
118
src/pages/knowledge/configuration/common-item.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormHelperText,
|
||||
Box,
|
||||
Typography,
|
||||
ListSubheader,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { DOCUMENT_PARSER_TYPES } from '@/constants/knowledge';
|
||||
import { useSelectChunkMethodList } from '../hooks';
|
||||
import { useEmbeddingModelOptions } from '@/hooks/llm-hooks';
|
||||
|
||||
// 解析器选项配置
|
||||
const PARSER_OPTIONS = [
|
||||
{ value: DOCUMENT_PARSER_TYPES.Naive, label: 'General', description: '通用解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A', description: 'Q&A解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Resume, label: 'Resume', description: 'Resume解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Manual, label: 'Manual', description: 'Manual解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Table, label: 'Table', description: 'Table解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Paper, label: 'Paper', description: 'Paper解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Book, label: 'Book', description: 'Book解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Laws, label: 'Laws', description: 'Laws解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Presentation, label: 'Presentation', description: 'Presentation解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.One, label: 'One', description: 'One解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Tag, label: 'Tag', description: 'Tag解析器' },
|
||||
];
|
||||
|
||||
export function ChunkMethodItem() {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const parserIds = useSelectChunkMethodList();
|
||||
|
||||
const parserOptions = parserIds.map((x) => ({
|
||||
value: x,
|
||||
label: PARSER_OPTIONS.find((y) => y.value === x)?.label || x,
|
||||
description: PARSER_OPTIONS.find((y) => y.value === x)?.description || x,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
切片方法
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth error={!!errors.parser_id}>
|
||||
<InputLabel>选择切片方法</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="选择切片方法"
|
||||
>
|
||||
{parserOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<Box>
|
||||
<Typography variant="body1">{option.label}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{option.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{errors.parser_id && (
|
||||
<FormHelperText>{errors.parser_id.message as string}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmbeddingModelItem() {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
|
||||
const { options: embdOptions } = useEmbeddingModelOptions();
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
嵌入模型
|
||||
</Typography>
|
||||
<Controller
|
||||
name="embd_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth error={!!errors.embd_id}>
|
||||
<InputLabel>选择嵌入模型</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="选择嵌入模型"
|
||||
>
|
||||
{
|
||||
embdOptions.map((group) => [
|
||||
<ListSubheader key={`header-${group.label}`} disableSticky>
|
||||
{group.label}
|
||||
</ListSubheader>,
|
||||
...group.options.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value} disabled={option.disabled}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))
|
||||
])
|
||||
}
|
||||
</Select>
|
||||
{errors.embd_id && (
|
||||
<FormHelperText>{errors.embd_id.message as string}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import React, { type PropsWithChildren } from 'react';
|
||||
import { Box, Paper } from '@mui/material';
|
||||
|
||||
interface ConfigurationFormContainerProps extends PropsWithChildren {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ConfigurationFormContainer({
|
||||
children,
|
||||
className,
|
||||
}: ConfigurationFormContainerProps) {
|
||||
return (
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: 3,
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
boxShadow: 'none',
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export function MainContainer({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
49
src/pages/knowledge/configuration/email.tsx
Normal file
49
src/pages/knowledge/configuration/email.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function EmailConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
20
src/pages/knowledge/configuration/index.tsx
Normal file
20
src/pages/knowledge/configuration/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// 配置组件统一导出
|
||||
export { NaiveConfiguration } from './naive';
|
||||
export { QAConfiguration } from './qa';
|
||||
export { PaperConfiguration } from './paper';
|
||||
export { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
export { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
|
||||
// 所有解析器配置组件
|
||||
export { AudioConfiguration } from './audio';
|
||||
export { BookConfiguration } from './book';
|
||||
export { EmailConfiguration } from './email';
|
||||
export { KnowledgeGraphConfiguration } from './knowledge-graph';
|
||||
export { LawsConfiguration } from './laws';
|
||||
export { ManualConfiguration } from './manual';
|
||||
export { OneConfiguration } from './one';
|
||||
export { PictureConfiguration } from './picture';
|
||||
export { PresentationConfiguration } from './presentation';
|
||||
export { ResumeConfiguration } from './resume';
|
||||
export { TableConfiguration } from './table';
|
||||
export { TagConfiguration } from './tag';
|
||||
36
src/pages/knowledge/configuration/knowledge-graph.tsx
Normal file
36
src/pages/knowledge/configuration/knowledge-graph.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function KnowledgeGraphConfiguration() {
|
||||
return (
|
||||
<>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
实体类型配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
最大Token数量配置 (最大: 16384) - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
分隔符配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
61
src/pages/knowledge/configuration/laws.tsx
Normal file
61
src/pages/knowledge/configuration/laws.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function LawsConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
59
src/pages/knowledge/configuration/manual.tsx
Normal file
59
src/pages/knowledge/configuration/manual.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function ManualConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
484
src/pages/knowledge/configuration/naive.tsx
Normal file
484
src/pages/knowledge/configuration/naive.tsx
Normal file
@@ -0,0 +1,484 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField,
|
||||
FormControlLabel,
|
||||
Switch,
|
||||
Slider,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Chip,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { ExpandMore as ExpandMoreIcon, Add as AddIcon } from '@mui/icons-material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
|
||||
export function NaiveConfiguration() {
|
||||
const { control, watch, formState: { errors } } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
{/* 第一部分:基础配置 */}
|
||||
<Accordion defaultExpanded>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">基础配置</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 切片方法 */}
|
||||
<ChunkMethodItem />
|
||||
|
||||
{/* PDF解析器 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
PDF解析器
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.pdf_parser"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
displayEmpty
|
||||
defaultValue="Naive"
|
||||
>
|
||||
<MenuItem value="Naive">Naive</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 嵌入模型 */}
|
||||
<EmbeddingModelItem />
|
||||
|
||||
{/* 建议文本块大小 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
建议文本块大小
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.chunk_token_num"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={128}
|
||||
max={2048}
|
||||
step={64}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={512}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 文本分段标识符 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
*文本分段标识符
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.delimiter"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
placeholder="\\n"
|
||||
defaultValue="\\n"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* 第二部分:页面排名和自动提取 */}
|
||||
<Accordion defaultExpanded>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">页面排名和自动提取</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 页面排名 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
页面排名
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.page_rank"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 自动关键词提取 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
自动关键词提取
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_keywords"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 自动问题提取 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
自动问题提取
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_questions"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 表格转HTML */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.html4excel"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="表格转HTML"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 标签集 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
标签集
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
displayEmpty
|
||||
defaultValue=""
|
||||
>
|
||||
<MenuItem value="">请选择</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* 第三部分:RAPTOR策略 */}
|
||||
<Accordion defaultExpanded>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">RAPTOR策略</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 使用召回增强RAPTOR策略 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.raptor.use_raptor"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="使用召回增强RAPTOR策略"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 提示词 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
提示词
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.prompt"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
multiline
|
||||
rows={4}
|
||||
fullWidth
|
||||
defaultValue="请总结以下段落。小心数字,不要编造。段落如下:\n{cluster_content}\n以上就是你需要总结的内容。"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 最大token数 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
最大token数
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.max_token"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={64}
|
||||
max={512}
|
||||
step={32}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={256}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 阈值 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
阈值
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.threshold"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.1}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={0.1}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 最大聚类数 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
最大聚类数
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.max_cluster"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={16}
|
||||
max={128}
|
||||
step={16}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={64}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 随机种子 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
随机种子
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.random_seed"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
defaultValue={0}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<IconButton>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* 第四部分:知识图谱 */}
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">知识图谱</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 提取知识图谱 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.use_graphrag"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="提取知识图谱"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 实体类型 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
*实体类型
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.graphrag.entity_types"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2 }}>
|
||||
{['organization', 'person', 'geo', 'event', 'category'].map((type) => (
|
||||
<Chip
|
||||
key={type}
|
||||
label={type}
|
||||
onDelete={() => {}}
|
||||
variant="outlined"
|
||||
/>
|
||||
))}
|
||||
<IconButton size="small">
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 方法 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
方法
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.graphrag.method"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
defaultValue="Light"
|
||||
>
|
||||
<MenuItem value="Light">Light</MenuItem>
|
||||
<MenuItem value="General">General</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 实体归一化 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.entity_normalize"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="实体归一化"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 社区报告生成 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.community_report"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="社区报告生成"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</MainContainer>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
48
src/pages/knowledge/configuration/one.tsx
Normal file
48
src/pages/knowledge/configuration/one.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function OneConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
121
src/pages/knowledge/configuration/paper.tsx
Normal file
121
src/pages/knowledge/configuration/paper.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField,
|
||||
Slider,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
|
||||
export function PaperConfiguration() {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
{/* 布局识别 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
布局识别
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.layout_recognize"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>布局识别方式</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="布局识别方式"
|
||||
>
|
||||
<MenuItem value="DeepDOC">DeepDOC</MenuItem>
|
||||
<MenuItem value="Plain Text">Plain Text</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 自动关键词 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自动关键词数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_keywords"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={10}
|
||||
step={1}
|
||||
marks
|
||||
valueLabelDisplay="auto"
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 自动问题 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自动问题数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_questions"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={10}
|
||||
step={1}
|
||||
marks
|
||||
valueLabelDisplay="auto"
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 标签数量 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Top N 标签数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.topn_tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
label="标签数量"
|
||||
inputProps={{ min: 1, max: 10 }}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
论文解析器专门优化用于学术论文的解析,能够更好地识别论文的结构和内容。
|
||||
</Typography>
|
||||
</Box>
|
||||
</MainContainer>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
37
src/pages/knowledge/configuration/picture.tsx
Normal file
37
src/pages/knowledge/configuration/picture.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function PictureConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
61
src/pages/knowledge/configuration/presentation.tsx
Normal file
61
src/pages/knowledge/configuration/presentation.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function PresentationConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
45
src/pages/knowledge/configuration/qa.tsx
Normal file
45
src/pages/knowledge/configuration/qa.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
|
||||
export function QAConfiguration() {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
{/* 标签数量 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Top N 标签数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.topn_tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
label="标签数量"
|
||||
inputProps={{ min: 1, max: 10 }}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Q&A解析器专门用于处理问答格式的文档,会自动识别问题和答案的结构。
|
||||
</Typography>
|
||||
</Box>
|
||||
</MainContainer>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
25
src/pages/knowledge/configuration/resume.tsx
Normal file
25
src/pages/knowledge/configuration/resume.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function ResumeConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
19
src/pages/knowledge/configuration/table.tsx
Normal file
19
src/pages/knowledge/configuration/table.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function TableConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
19
src/pages/knowledge/configuration/tag.tsx
Normal file
19
src/pages/knowledge/configuration/tag.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function TagConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
30
src/pages/knowledge/hooks.ts
Normal file
30
src/pages/knowledge/hooks.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useUserData } from "@/hooks/useUserData";
|
||||
|
||||
const HiddenFields = ['email', 'picture', 'audio'];
|
||||
|
||||
/**
|
||||
* 解析器ID格式为 "naive:General",我们只需要 "naive" 部分
|
||||
* "parser_ids": "naive:General,qa:Q&A,resume:Resume,manual:Manual,
|
||||
* table:Table,paper:Paper,book:Book,laws:Laws,presentation:Presentation,
|
||||
* picture:Picture,one:One,audio:Audio,email:Email,tag:Tag",
|
||||
* @returns 解析器ID列表
|
||||
*/
|
||||
export function useSelectChunkMethodList() {
|
||||
const {tenantInfo} = useUserData();
|
||||
const parserList = tenantInfo?.parser_ids?.split(',') || [];
|
||||
// 解析器ID格式为 "naive:General",我们只需要 "naive" 部分
|
||||
const parserIds = parserList.map((x) => x.split(':')[0]);
|
||||
// const parserLabels = parserList.map((x) => x.split(':')[1]);
|
||||
|
||||
const filteredParserIds = parserIds.filter((x) => !HiddenFields.some((y) => y === x));
|
||||
// return filteredParserIds.map((x) => ({
|
||||
// value: x,
|
||||
// label: parserLabels[parserIds.indexOf(x)],
|
||||
// }));
|
||||
return filteredParserIds
|
||||
}
|
||||
|
||||
// export function useSelectEmbeddingModelOptions() {
|
||||
// const allOptions = useSelectLlmOptionsByModelType();
|
||||
// return allOptions[LlmModelType.Embedding];
|
||||
// }
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
366
src/pages/knowledge/参考data-setting页面架构分析.md
Normal file
366
src/pages/knowledge/参考data-setting页面架构分析.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# 参考配置页面架构分析
|
||||
|
||||
## 项目概述
|
||||
|
||||
参考项目是一个基于 React + TypeScript 的知识库配置系统,使用 `react-hook-form` + `zod` 进行表单管理和验证,采用模块化的组件架构设计。
|
||||
|
||||
## 整体架构
|
||||
|
||||
### 1. 文件结构
|
||||
|
||||
```
|
||||
dataset-setting/
|
||||
├── index.tsx # 主配置页面入口
|
||||
├── form-schema.ts # 表单数据结构和验证规则
|
||||
├── general-form.tsx # 通用表单组件
|
||||
├── chunk-method-form.tsx # 动态解析方法表单
|
||||
├── hooks.ts # 自定义钩子函数
|
||||
├── configuration-form-container.tsx # 表单容器组件
|
||||
├── components/ # 通用组件
|
||||
│ └── tag-item.tsx
|
||||
└── configuration/ # 解析方法配置组件
|
||||
├── common-item.tsx # 通用配置项
|
||||
├── naive.tsx # 通用解析配置
|
||||
├── paper.tsx # 论文解析配置
|
||||
├── qa.tsx # 问答解析配置
|
||||
├── book.tsx # 书籍解析配置
|
||||
├── laws.tsx # 法律文档解析配置
|
||||
├── manual.tsx # 手动解析配置
|
||||
├── table.tsx # 表格解析配置
|
||||
├── presentation.tsx # 演示文稿解析配置
|
||||
├── picture.tsx # 图片解析配置
|
||||
├── one.tsx # One解析配置
|
||||
├── audio.tsx # 音频解析配置
|
||||
├── email.tsx # 邮件解析配置
|
||||
├── tag.tsx # 标签解析配置
|
||||
└── knowledge-graph.tsx # 知识图谱解析配置
|
||||
```
|
||||
|
||||
### 2. 核心架构模式
|
||||
|
||||
#### 2.1 主从架构模式
|
||||
- **主页面** (`index.tsx`): 统一的表单管理和状态控制
|
||||
- **子组件**: 各种专门化的表单组件,通过 `useFormContext` 共享表单状态
|
||||
|
||||
#### 2.2 策略模式
|
||||
- **动态组件映射**: 根据选择的解析方法动态渲染对应的配置组件
|
||||
- **配置组件映射表**: `ConfigurationComponentMap` 实现解析方法到组件的映射
|
||||
|
||||
## 核心功能模块
|
||||
|
||||
### 1. 表单数据结构 (`form-schema.ts`)
|
||||
|
||||
#### 主要数据结构
|
||||
```typescript
|
||||
const formSchema = z.object({
|
||||
// 基础信息
|
||||
parseType: z.number(), // 解析类型 (1: 内置解析, 2: 数据流水线)
|
||||
name: z.string(), // 知识库名称
|
||||
description: z.string(), // 描述
|
||||
avatar: z.any().nullish(), // 头像
|
||||
permission: z.string(), // 权限设置
|
||||
|
||||
// 解析器配置
|
||||
parser_id: z.string(), // 解析器ID
|
||||
embd_id: z.string(), // 嵌入模型ID
|
||||
|
||||
// 数据流水线配置
|
||||
pipeline_id: z.string(), // 流水线ID
|
||||
pipeline_name: z.string(), // 流水线名称
|
||||
pipeline_avatar: z.string(), // 流水线头像
|
||||
|
||||
// 解析器详细配置
|
||||
parser_config: z.object({
|
||||
layout_recognize: z.string(), // 布局识别
|
||||
chunk_token_num: z.number(), // 分块token数量
|
||||
delimiter: z.string(), // 分隔符
|
||||
auto_keywords: z.number(), // 自动关键词数量
|
||||
auto_questions: z.number(), // 自动问题数量
|
||||
html4excel: z.boolean(), // Excel转HTML
|
||||
tag_kb_ids: z.array(z.string()), // 标签知识库ID
|
||||
topn_tags: z.number(), // 顶部标签数量
|
||||
toc_extraction: z.boolean(), // 目录提取
|
||||
|
||||
// RAPTOR配置
|
||||
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配置
|
||||
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(), // PageRank权重
|
||||
});
|
||||
```
|
||||
|
||||
#### 验证规则特点
|
||||
1. **条件验证**: 使用 `refine` 方法实现复杂的条件验证逻辑
|
||||
2. **跨字段验证**: 使用 `superRefine` 实现字段间的依赖验证
|
||||
3. **国际化支持**: 错误消息支持多语言
|
||||
|
||||
### 2. 动态表单系统
|
||||
|
||||
#### 2.1 解析方法映射机制
|
||||
```typescript
|
||||
const ConfigurationComponentMap = {
|
||||
[DocumentParserType.Naive]: NaiveConfiguration,
|
||||
[DocumentParserType.Qa]: QAConfiguration,
|
||||
[DocumentParserType.Resume]: ResumeConfiguration,
|
||||
// ... 其他解析方法映射
|
||||
};
|
||||
```
|
||||
|
||||
#### 2.2 动态组件渲染
|
||||
```typescript
|
||||
const ConfigurationComponent = useMemo(() => {
|
||||
return finalParserId
|
||||
? ConfigurationComponentMap[finalParserId]
|
||||
: EmptyComponent;
|
||||
}, [finalParserId]);
|
||||
```
|
||||
|
||||
#### 2.3 配置组件结构模式
|
||||
每个配置组件都遵循统一的结构模式:
|
||||
```typescript
|
||||
export function XxxConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
{/* 第一组配置项 */}
|
||||
</ConfigurationFormContainer>
|
||||
<ConfigurationFormContainer>
|
||||
{/* 第二组配置项 */}
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 状态管理系统
|
||||
|
||||
#### 3.1 表单状态管理
|
||||
- **统一表单实例**: 主页面创建 `useForm` 实例
|
||||
- **上下文共享**: 子组件通过 `useFormContext` 访问表单状态
|
||||
- **响应式更新**: 使用 `useWatch` 监听字段变化
|
||||
|
||||
#### 3.2 数据流管理
|
||||
```typescript
|
||||
// 监听解析类型变化
|
||||
const parseType = useWatch({
|
||||
control: form.control,
|
||||
name: 'parseType',
|
||||
});
|
||||
|
||||
// 监听解析器选择变化
|
||||
const selectedTag = useWatch({
|
||||
name: 'parser_id',
|
||||
control: form.control,
|
||||
});
|
||||
```
|
||||
|
||||
#### 3.3 副作用处理
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
if (parseType === 1) {
|
||||
form.setValue('pipeline_id', '');
|
||||
}
|
||||
}, [parseType, form]);
|
||||
```
|
||||
|
||||
### 4. 数据获取和初始化
|
||||
|
||||
#### 4.1 自定义钩子 (`hooks.ts`)
|
||||
```typescript
|
||||
// 获取知识库配置并初始化表单
|
||||
export const useFetchKnowledgeConfigurationOnMount = (form) => {
|
||||
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
useEffect(() => {
|
||||
// 合并默认值和服务器数据
|
||||
const parser_config = {
|
||||
...form.formState?.defaultValues?.parser_config,
|
||||
...knowledgeDetails.parser_config,
|
||||
// 特殊处理嵌套对象
|
||||
};
|
||||
|
||||
// 重置表单数据
|
||||
form.reset(formValues);
|
||||
}, [form, knowledgeDetails]);
|
||||
};
|
||||
```
|
||||
|
||||
#### 4.2 数据处理策略
|
||||
1. **默认值合并**: 将服务器数据与表单默认值合并
|
||||
2. **嵌套对象处理**: 特殊处理 `raptor` 和 `graphrag` 等嵌套配置
|
||||
3. **条件初始化**: 根据数据状态设置表单字段值
|
||||
|
||||
## 页面布局结构
|
||||
|
||||
### 1. 主页面布局
|
||||
```typescript
|
||||
<section className="p-5 h-full flex flex-col">
|
||||
{/* 页面标题 */}
|
||||
<TopTitle />
|
||||
|
||||
<div className="flex gap-14 flex-1 min-h-0">
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
{/* 滚动内容区域 */}
|
||||
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto">
|
||||
<MainContainer>
|
||||
{/* 通用表单 */}
|
||||
<GeneralForm />
|
||||
<Divider />
|
||||
|
||||
{/* GraphRAG配置 */}
|
||||
<GraphRagItems />
|
||||
<Divider />
|
||||
|
||||
{/* RAPTOR配置 */}
|
||||
<RaptorFormFields />
|
||||
<Divider />
|
||||
|
||||
{/* 解析类型选择 */}
|
||||
<ParseTypeItem />
|
||||
|
||||
{/* 条件渲染: 内置解析方法 */}
|
||||
{parseType === 1 && (
|
||||
<>
|
||||
<ChunkMethodItem />
|
||||
<ChunkMethodForm selectedTag={selectedTag} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 条件渲染: 数据流水线 */}
|
||||
{parseType === 2 && (
|
||||
<DataFlowSelect />
|
||||
)}
|
||||
</MainContainer>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮区域 */}
|
||||
<div className="text-right items-center flex justify-end gap-3">
|
||||
<Button type="reset">取消</Button>
|
||||
<SavingButton />
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
### 2. 响应式设计
|
||||
- **固定宽度**: 表单区域固定 768px 宽度
|
||||
- **垂直滚动**: 内容区域支持垂直滚动
|
||||
- **弹性布局**: 使用 Flexbox 实现自适应高度
|
||||
|
||||
## 组件设计模式
|
||||
|
||||
### 1. 容器组件模式
|
||||
```typescript
|
||||
// 主容器 - 控制整体间距
|
||||
export function MainContainer({ children, className }) {
|
||||
return <section className={cn('space-y-5', className)}>{children}</section>;
|
||||
}
|
||||
|
||||
// 配置容器 - 控制配置项间距
|
||||
export function ConfigurationFormContainer({ children, className }) {
|
||||
return <section className={cn('space-y-4', className)}>{children}</section>;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 表单字段组件模式
|
||||
```typescript
|
||||
export function XxxFormField() {
|
||||
const form = useFormContext();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fieldName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="items-center space-y-0">
|
||||
<div className="flex">
|
||||
<FormLabel className="w-1/4">标签</FormLabel>
|
||||
<FormControl className="w-3/4">
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 条件渲染模式
|
||||
```typescript
|
||||
// 基于状态的条件渲染
|
||||
{parseType === 1 && <InternalParsingComponents />}
|
||||
{parseType === 2 && <PipelineComponents />}
|
||||
|
||||
// 基于选择的动态组件
|
||||
<ConfigurationComponent />
|
||||
```
|
||||
|
||||
## 技术特点
|
||||
|
||||
### 1. 类型安全
|
||||
- **TypeScript**: 全面的类型定义
|
||||
- **Zod验证**: 运行时类型验证
|
||||
- **表单类型推导**: 从schema自动推导表单类型
|
||||
|
||||
### 2. 性能优化
|
||||
- **useMemo**: 缓存动态组件选择
|
||||
- **useCallback**: 缓存事件处理函数
|
||||
- **条件渲染**: 避免不必要的组件渲染
|
||||
|
||||
### 3. 可维护性
|
||||
- **模块化设计**: 每个解析方法独立组件
|
||||
- **统一接口**: 所有配置组件遵循相同模式
|
||||
- **代码复用**: 通用组件和钩子函数
|
||||
|
||||
### 4. 扩展性
|
||||
- **插件化架构**: 新增解析方法只需添加对应组件
|
||||
- **配置驱动**: 通过配置映射控制组件渲染
|
||||
- **钩子系统**: 自定义钩子支持功能扩展
|
||||
|
||||
## 适配建议
|
||||
|
||||
### 1. 保持现有布局样式
|
||||
- 使用你现有的 Accordion 布局结构
|
||||
- 保持四个配置分组的设计
|
||||
- 适配参考项目的组件组织方式
|
||||
|
||||
### 2. 采用核心架构模式
|
||||
- **动态组件映射**: 实现解析方法到配置组件的映射
|
||||
- **统一表单管理**: 使用 `useFormContext` 共享表单状态
|
||||
- **模块化组件**: 将不同配置拆分为独立组件
|
||||
|
||||
### 3. 数据结构对齐
|
||||
- 参考 `form-schema.ts` 的数据结构设计
|
||||
- 实现条件验证和跨字段验证
|
||||
- 支持嵌套配置对象
|
||||
|
||||
### 4. 状态管理优化
|
||||
- 使用 `useWatch` 监听关键字段变化
|
||||
- 实现响应式的条件渲染
|
||||
- 优化表单初始化和数据合并逻辑
|
||||
|
||||
这个架构设计具有很强的可扩展性和维护性,非常适合复杂的配置表单场景。你可以基于这个分析来改进你的配置页面实现。
|
||||
265
src/pages/knowledge/参考setting项目架构分析.md
Normal file
265
src/pages/knowledge/参考setting项目架构分析.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# 参考Setting项目架构分析
|
||||
|
||||
## 项目概述
|
||||
|
||||
参考项目位于 `rag_web_core/src/pages/dataset/setting`,采用了基于 `react-hook-form` 和 `zod` 的现代表单管理架构,实现了高度模块化和可扩展的配置页面。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
setting/
|
||||
├── index.tsx # 主页面入口
|
||||
├── form-schema.ts # 表单数据结构定义
|
||||
├── general-form.tsx # 通用表单组件
|
||||
├── chunk-method-form.tsx # 动态解析方法表单
|
||||
├── configuration-form-container.tsx # 表单容器组件
|
||||
├── hooks.ts # 数据获取和状态管理
|
||||
├── saving-button.tsx # 保存按钮组件
|
||||
├── permission-form-field.tsx # 权限表单字段
|
||||
├── tag-item.tsx # 标签项组件
|
||||
├── configuration/ # 配置组件目录
|
||||
│ ├── common-item.tsx # 通用配置项
|
||||
│ ├── naive.tsx # 通用解析配置
|
||||
│ ├── qa.tsx # Q&A解析配置
|
||||
│ ├── paper.tsx # 论文解析配置
|
||||
│ ├── book.tsx # 书籍解析配置
|
||||
│ ├── table.tsx # 表格解析配置
|
||||
│ ├── audio.tsx # 音频解析配置
|
||||
│ ├── email.tsx # 邮件解析配置
|
||||
│ ├── laws.tsx # 法律解析配置
|
||||
│ ├── manual.tsx # 手册解析配置
|
||||
│ ├── one.tsx # One解析配置
|
||||
│ ├── picture.tsx # 图片解析配置
|
||||
│ ├── presentation.tsx # 演示文稿解析配置
|
||||
│ ├── resume.tsx # 简历解析配置
|
||||
│ ├── knowledge-graph.tsx # 知识图谱配置
|
||||
│ └── tag.tsx # 标签配置
|
||||
└── tag-table/ # 标签表格相关组件
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 核心架构模式
|
||||
|
||||
### 1. 主从架构模式 (Master-Slave)
|
||||
- **主控制器**: `index.tsx` 作为主页面,管理整体状态和表单实例
|
||||
- **从组件**: `general-form.tsx` 和 `chunk-method-form.tsx` 作为子表单组件
|
||||
|
||||
### 2. 策略模式 (Strategy Pattern)
|
||||
- **配置映射**: `ConfigurationComponentMap` 根据 `parser_id` 动态选择配置组件
|
||||
- **组件策略**: 每个解析类型对应一个独立的配置组件
|
||||
|
||||
### 3. 模块化设计
|
||||
- **功能分离**: 通用配置与特定解析配置分离
|
||||
- **组件复用**: 通过 `configuration-form-container.tsx` 提供统一的布局容器
|
||||
|
||||
## 关键技术实现
|
||||
|
||||
### 1. 表单管理系统
|
||||
|
||||
#### 数据结构 (form-schema.ts)
|
||||
```typescript
|
||||
export const formSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
description: z.string().min(2),
|
||||
avatar: z.any().nullish(),
|
||||
permission: z.string().optional(),
|
||||
parser_id: z.string(),
|
||||
embd_id: z.string(),
|
||||
parser_config: z.object({
|
||||
layout_recognize: z.string(),
|
||||
chunk_token_num: z.number(),
|
||||
delimiter: z.string(),
|
||||
auto_keywords: z.number().optional(),
|
||||
auto_questions: z.number().optional(),
|
||||
html4excel: z.boolean(),
|
||||
raptor: z.object({...}),
|
||||
graphrag: z.object({...}),
|
||||
}).optional(),
|
||||
pagerank: z.number(),
|
||||
});
|
||||
```
|
||||
|
||||
#### 表单初始化
|
||||
```typescript
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
parser_id: DocumentParserType.Naive,
|
||||
permission: PermissionRole.Me,
|
||||
parser_config: {
|
||||
layout_recognize: DocumentType.DeepDOC,
|
||||
chunk_token_num: 512,
|
||||
delimiter: `\n`,
|
||||
// ... 其他默认值
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 动态表单系统
|
||||
|
||||
#### 配置组件映射
|
||||
```typescript
|
||||
const ConfigurationComponentMap = {
|
||||
[DocumentParserType.Naive]: NaiveConfiguration,
|
||||
[DocumentParserType.Qa]: QAConfiguration,
|
||||
[DocumentParserType.Resume]: ResumeConfiguration,
|
||||
// ... 其他映射
|
||||
};
|
||||
```
|
||||
|
||||
#### 动态组件渲染
|
||||
```typescript
|
||||
const ConfigurationComponent = useMemo(() => {
|
||||
return finalParserId
|
||||
? ConfigurationComponentMap[finalParserId]
|
||||
: EmptyComponent;
|
||||
}, [finalParserId]);
|
||||
```
|
||||
|
||||
### 3. 状态管理
|
||||
|
||||
#### 表单上下文共享
|
||||
- 使用 `useFormContext()` 在子组件中访问表单实例
|
||||
- 通过 `useWatch()` 监听特定字段变化
|
||||
|
||||
#### 数据获取钩子
|
||||
```typescript
|
||||
// hooks.ts
|
||||
export function useFetchKnowledgeConfigurationOnMount(form) {
|
||||
// 获取知识库配置并初始化表单
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 页面布局结构
|
||||
|
||||
#### 主页面布局 (index.tsx)
|
||||
```typescript
|
||||
<section className="p-5 h-full flex flex-col">
|
||||
<TopTitle />
|
||||
<div className="flex gap-14 flex-1 min-h-0">
|
||||
<Form {...form}>
|
||||
<form className="space-y-6 flex-1">
|
||||
<Tabs>
|
||||
<TabsList>
|
||||
<TabsTrigger value="generalForm">通用</TabsTrigger>
|
||||
<TabsTrigger value="chunkMethodForm">解析方法</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="generalForm">
|
||||
<GeneralForm />
|
||||
</TabsContent>
|
||||
<TabsContent value="chunkMethodForm">
|
||||
<ChunkMethodForm />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</form>
|
||||
</Form>
|
||||
<ChunkMethodLearnMore />
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
#### 表单容器组件
|
||||
```typescript
|
||||
// configuration-form-container.tsx
|
||||
export function ConfigurationFormContainer({ children, className }) {
|
||||
return (
|
||||
<FormContainer className={cn('p-10', className)}>
|
||||
{children}
|
||||
</FormContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export function MainContainer({ children }) {
|
||||
return <section className="space-y-5">{children}</section>;
|
||||
}
|
||||
```
|
||||
|
||||
## 组件设计模式
|
||||
|
||||
### 1. 通用表单组件 (general-form.tsx)
|
||||
- 使用 `FormField` 组件统一表单字段样式
|
||||
- 采用 1/4 和 3/4 的标签与输入框布局比例
|
||||
- 集成头像上传、权限选择等通用功能
|
||||
|
||||
### 2. 配置组件模式
|
||||
每个配置组件遵循统一的结构:
|
||||
```typescript
|
||||
export function NaiveConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
<LayoutRecognizeFormField />
|
||||
<MaxTokenNumberFormField />
|
||||
<DelimiterFormField />
|
||||
<AutoKeywordsFormField />
|
||||
<AutoQuestionsFormField />
|
||||
<TagItems />
|
||||
</MainContainer>
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 表单字段组件
|
||||
- 统一的 `FormField` 包装器
|
||||
- 一致的错误处理和验证显示
|
||||
- 响应式布局设计
|
||||
|
||||
## 技术特色
|
||||
|
||||
### 1. 类型安全
|
||||
- 使用 TypeScript 和 Zod 确保类型安全
|
||||
- 表单数据结构与后端API对齐
|
||||
|
||||
### 2. 性能优化
|
||||
- 使用 `useMemo` 优化动态组件渲染
|
||||
- `useWatch` 精确监听字段变化,避免不必要的重渲染
|
||||
|
||||
### 3. 可扩展性
|
||||
- 新增解析类型只需添加配置组件和映射关系
|
||||
- 模块化设计便于功能扩展
|
||||
|
||||
### 4. 用户体验
|
||||
- Tab切换提供清晰的功能分区
|
||||
- 右侧学习面板提供上下文帮助
|
||||
- 统一的保存和重置操作
|
||||
|
||||
## 与用户项目的对比
|
||||
|
||||
### 用户当前架构
|
||||
- 使用 Material-UI 组件库
|
||||
- 基于 `react-hook-form` 但结构相对简单
|
||||
- 单一的 Accordion 布局
|
||||
- 配置逻辑集中在一个组件中
|
||||
|
||||
### 参考项目优势
|
||||
- 更清晰的模块化分离
|
||||
- 动态配置组件系统
|
||||
- 更好的代码组织和可维护性
|
||||
- 统一的设计语言和布局模式
|
||||
|
||||
## 适配建议
|
||||
|
||||
### 1. 保持现有样式
|
||||
- 继续使用 Material-UI 组件
|
||||
- 保持 Accordion 布局风格
|
||||
- 适配现有的主题和设计规范
|
||||
|
||||
### 2. 采用核心架构
|
||||
- 引入动态配置组件映射机制
|
||||
- 分离通用配置和特定解析配置
|
||||
- 使用 `useFormContext` 实现组件间状态共享
|
||||
|
||||
### 3. 数据结构对齐
|
||||
- 参考 `form-schema.ts` 调整数据结构
|
||||
- 使用 Zod 进行数据验证
|
||||
- 统一默认值设置
|
||||
|
||||
### 4. 状态管理优化
|
||||
- 使用 `useWatch` 监听字段变化
|
||||
- 实现数据获取钩子
|
||||
- 优化表单重置和提交逻辑
|
||||
|
||||
这种架构设计为大型表单应用提供了良好的可维护性和扩展性基础,值得在用户项目中借鉴和应用。
|
||||
@@ -11,7 +11,7 @@ import type {
|
||||
IKnowledgeFile,
|
||||
IChunk,
|
||||
IRenameTag,
|
||||
ParserConfig,
|
||||
IParserConfig,
|
||||
} from '@/interfaces/database/knowledge';
|
||||
import type { GridRowSelectionModel } from '@mui/x-data-grid';
|
||||
|
||||
@@ -120,8 +120,8 @@ const knowledgeService = {
|
||||
return post(api.document_run, data);
|
||||
},
|
||||
|
||||
// 更改文档解析器
|
||||
changeDocumentParser: (data: { doc_id: string; parser_config: ParserConfig }) => {
|
||||
// 更改文档解析器配置
|
||||
changeDocumentParser: (data: { doc_id: string; parser_config: IParserConfig }) => {
|
||||
return post(api.document_change_parser, data);
|
||||
},
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import api from './api';
|
||||
import request, { post } from '@/utils/request';
|
||||
import type { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import type { IUserInfo, ITenant } from '@/interfaces/database/user-setting';
|
||||
import type { LlmModelType } from '@/constants/knowledge';
|
||||
|
||||
// 用户相关API服务
|
||||
const userService = {
|
||||
@@ -71,6 +72,11 @@ const userService = {
|
||||
agreeTenant: (tenantId: string) => {
|
||||
return request.put(api.agreeTenant(tenantId));
|
||||
},
|
||||
|
||||
// 获取LLM模型列表
|
||||
llm_list: (data: { model_type?: LlmModelType }) => {
|
||||
return request.get(api.llm_list, { params: data });
|
||||
},
|
||||
};
|
||||
|
||||
export default userService;
|
||||
Reference in New Issue
Block a user