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:
1
.env
1
.env
@@ -1,4 +1,5 @@
|
|||||||
VITE_API_BASE_URL = http://150.158.121.95
|
VITE_API_BASE_URL = http://150.158.121.95
|
||||||
|
# VITE_API_BASE_URL = http://154.9.253.114
|
||||||
|
|
||||||
VITE_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
VITE_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const Header = () => {
|
|||||||
color: '#333',
|
color: '#333',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
RAG Dashboard
|
RAG Empowerment System
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ const Sidebar = () => {
|
|||||||
letterSpacing: '0.5px',
|
letterSpacing: '0.5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
RAGflow Prototype
|
T-Systems Enterprise
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
{navItems.map((item) => {
|
{navItems.map((item) => {
|
||||||
@@ -93,7 +93,7 @@ const Sidebar = () => {
|
|||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
© 2025 RAG Demo
|
© 2025 T-Systems
|
||||||
</Box>
|
</Box>
|
||||||
</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返回值接口
|
// 文档列表Hook返回值接口
|
||||||
export interface UseDocumentListReturn extends UseDocumentListState {
|
export interface UseDocumentListReturn extends UseDocumentListState {
|
||||||
fetchDocuments: (params?: IFetchKnowledgeListRequestParams) => Promise<void>;
|
fetchDocuments: (params?: IFetchKnowledgeListRequestParams) => Promise<void>;
|
||||||
|
fetchDocumentsFilter: () => Promise<void>;
|
||||||
setKeywords: (keywords: string) => void;
|
setKeywords: (keywords: string) => void;
|
||||||
setCurrentPage: (page: number) => void;
|
setCurrentPage: (page: number) => void;
|
||||||
setPageSize: (size: number) => void;
|
setPageSize: (size: number) => void;
|
||||||
@@ -95,6 +96,27 @@ export const useDocumentList = (
|
|||||||
}
|
}
|
||||||
}, [kbId, keywords, currentPage, pageSize]);
|
}, [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,
|
pageSize,
|
||||||
keywords,
|
keywords,
|
||||||
fetchDocuments,
|
fetchDocuments,
|
||||||
|
fetchDocumentsFilter,
|
||||||
setKeywords: handleSetKeywords,
|
setKeywords: handleSetKeywords,
|
||||||
setCurrentPage: handleSetCurrentPage,
|
setCurrentPage: handleSetCurrentPage,
|
||||||
setPageSize: handleSetPageSize,
|
setPageSize: handleSetPageSize,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import knowledgeService from '@/services/knowledge_service';
|
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';
|
import type { IFetchKnowledgeListRequestParams } from '@/interfaces/request/knowledge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,11 +299,9 @@ export const useKnowledgeOperations = () => {
|
|||||||
* 包括嵌入模型、解析器配置、相似度阈值等
|
* 包括嵌入模型、解析器配置、相似度阈值等
|
||||||
*/
|
*/
|
||||||
const updateKnowledgeModelConfig = useCallback(async (data: {
|
const updateKnowledgeModelConfig = useCallback(async (data: {
|
||||||
id: string;
|
kb_id: string;
|
||||||
embd_id?: string;
|
embd_id?: string;
|
||||||
// parser_config?: Partial<ParserConfig>;
|
parser_config?: Partial<IParserConfig>;
|
||||||
similarity_threshold?: number;
|
|
||||||
vector_similarity_weight?: number;
|
|
||||||
parser_id?: string;
|
parser_id?: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
@@ -311,7 +309,6 @@ export const useKnowledgeOperations = () => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
const updateData = {
|
const updateData = {
|
||||||
kb_id: data.id,
|
|
||||||
...data,
|
...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 React, { useMemo } from 'react';
|
||||||
import { type UseFormReturn } from 'react-hook-form';
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
Grid,
|
|
||||||
TextField,
|
|
||||||
FormHelperText,
|
|
||||||
Button,
|
|
||||||
CircularProgress,
|
|
||||||
Switch,
|
|
||||||
FormControlLabel,
|
|
||||||
Chip,
|
|
||||||
Slider,
|
|
||||||
Accordion,
|
|
||||||
AccordionSummary,
|
|
||||||
AccordionDetails,
|
|
||||||
} from '@mui/material';
|
} 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 { DOCUMENT_PARSER_TYPES, type DocumentParserType } from '@/constants/knowledge';
|
||||||
import { type IParserConfig } from '@/interfaces/database/knowledge';
|
import { type IParserConfig } from '@/interfaces/database/knowledge';
|
||||||
|
import {
|
||||||
|
NaiveConfiguration,
|
||||||
|
QAConfiguration,
|
||||||
|
PaperConfiguration,
|
||||||
|
ResumeConfiguration,
|
||||||
|
ManualConfiguration,
|
||||||
|
TableConfiguration,
|
||||||
|
BookConfiguration,
|
||||||
|
LawsConfiguration,
|
||||||
|
PresentationConfiguration,
|
||||||
|
OneConfiguration,
|
||||||
|
TagConfiguration,
|
||||||
|
ChunkMethodItem,
|
||||||
|
} from '../configuration';
|
||||||
|
|
||||||
/**
|
// 配置组件映射表
|
||||||
{
|
const ConfigurationComponentMap = {
|
||||||
"kb_id": "dcc2871aa4cd11f08d4116ac85b1de0a",
|
[DOCUMENT_PARSER_TYPES.Naive]: NaiveConfiguration,
|
||||||
"name": "k1123",
|
[DOCUMENT_PARSER_TYPES.Qa]: QAConfiguration,
|
||||||
"description": " 213213",
|
[DOCUMENT_PARSER_TYPES.Resume]: ResumeConfiguration,
|
||||||
"permission": "team",
|
[DOCUMENT_PARSER_TYPES.Manual]: ManualConfiguration,
|
||||||
"parser_id": "naive",
|
[DOCUMENT_PARSER_TYPES.Table]: TableConfiguration,
|
||||||
"embd_id": "",
|
[DOCUMENT_PARSER_TYPES.Paper]: PaperConfiguration,
|
||||||
"parser_config": {
|
[DOCUMENT_PARSER_TYPES.Book]: BookConfiguration,
|
||||||
"layout_recognize": "Plain Text",
|
[DOCUMENT_PARSER_TYPES.Laws]: LawsConfiguration,
|
||||||
"chunk_token_num": 512,
|
[DOCUMENT_PARSER_TYPES.Presentation]: PresentationConfiguration,
|
||||||
"delimiter": "\n",
|
[DOCUMENT_PARSER_TYPES.One]: OneConfiguration,
|
||||||
"auto_keywords": 0,
|
[DOCUMENT_PARSER_TYPES.Tag]: TagConfiguration,
|
||||||
"auto_questions": 0,
|
[DOCUMENT_PARSER_TYPES.KnowledgeGraph]: TagConfiguration,
|
||||||
"html4excel": false,
|
[DOCUMENT_PARSER_TYPES.Picture]: TagConfiguration,
|
||||||
"topn_tags": 3,
|
[DOCUMENT_PARSER_TYPES.Audio]: TagConfiguration,
|
||||||
"raptor": {
|
[DOCUMENT_PARSER_TYPES.Email]: TagConfiguration,
|
||||||
"use_raptor": true,
|
// [DOCUMENT_PARSER_TYPES.KnowledgeGraph]: KnowledgeGraphConfiguration,
|
||||||
"prompt": "请总结以下段落。 小心数字,不要编造。 段落如下:\n {cluster_content}\n以上就是你需要总结的内容。",
|
// [DOCUMENT_PARSER_TYPES.Picture]: PictureConfiguration,
|
||||||
"max_token": 256,
|
// [DOCUMENT_PARSER_TYPES.Audio]: AudioConfiguration,
|
||||||
"threshold": 0.1,
|
// [DOCUMENT_PARSER_TYPES.Email]: GraphragConfiguration,
|
||||||
"max_cluster": 64,
|
};
|
||||||
"random_seed": 0
|
|
||||||
},
|
// 空组件
|
||||||
"graphrag": {
|
function EmptyComponent() {
|
||||||
"use_graphrag": true,
|
return (
|
||||||
"entity_types": [
|
<Box sx={{ textAlign: 'center', py: 4 }}>
|
||||||
"organization",
|
<Typography color="text.secondary">
|
||||||
"person",
|
请选择一个解析方法来配置相关参数
|
||||||
"geo",
|
</Typography>
|
||||||
"event",
|
</Box>
|
||||||
"category"
|
);
|
||||||
],
|
|
||||||
"method": "light"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pagerank": 0
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// 解析器选项配置
|
|
||||||
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 {
|
export interface ConfigFormData extends IParserConfig {
|
||||||
parser_id: DocumentParserType;
|
parser_id: DocumentParserType;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChunkMethodFormProps {
|
function ChunkMethodForm() {
|
||||||
form: UseFormReturn<ConfigFormData>;
|
const { control } = useFormContext();
|
||||||
onSubmit: (data: ConfigFormData) => void;
|
|
||||||
isSubmitting?: boolean;
|
|
||||||
onCancel?: () => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
submitButtonText?: string;
|
|
||||||
cancelButtonText?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ChunkMethodForm({
|
// 监听parser_id变化
|
||||||
form,
|
const parser_id = useWatch({
|
||||||
onSubmit,
|
control,
|
||||||
isSubmitting = false,
|
name: 'parser_id',
|
||||||
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 renderGeneralConfig = () => (
|
const ConfigurationComponent = useMemo(() => {
|
||||||
<Accordion defaultExpanded>
|
const parser = parser_id as DocumentParserType;
|
||||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
const component = ConfigurationComponentMap[parser] || EmptyComponent;
|
||||||
<Typography variant="h6">通用</Typography>
|
return component || EmptyComponent;
|
||||||
</AccordionSummary>
|
}, [parser_id]);
|
||||||
<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>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||||
<Typography variant="h5" gutterBottom>
|
{/* 动态配置内容 */}
|
||||||
配置
|
<ConfigurationComponent />
|
||||||
</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>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { type UseFormReturn } from 'react-hook-form';
|
import { useFormContext, Controller } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
@@ -12,211 +12,138 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
CircularProgress,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
PhotoCamera as PhotoCameraIcon,
|
PhotoCamera as PhotoCameraIcon,
|
||||||
Delete as DeleteIcon,
|
Delete as DeleteIcon,
|
||||||
Save as SaveIcon,
|
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
export interface BasicFormData {
|
function GeneralForm() {
|
||||||
name: string;
|
const { control, watch, setValue } = useFormContext();
|
||||||
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) {
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// 处理头像上传
|
|
||||||
const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
// 检查文件类型
|
|
||||||
if (!file.type.startsWith('image/')) {
|
|
||||||
alert('请选择图片文件');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查文件大小 (限制为 2MB)
|
|
||||||
if (file.size > 2 * 1024 * 1024) {
|
|
||||||
alert('图片大小不能超过 2MB');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
const base64String = e.target?.result as string;
|
setValue('avatar', e.target?.result as string);
|
||||||
form.setValue('avatar', base64String);
|
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除头像
|
|
||||||
const handleAvatarDelete = () => {
|
const handleAvatarDelete = () => {
|
||||||
form.setValue('avatar', undefined);
|
setValue('avatar', undefined);
|
||||||
if (fileInputRef.current) {
|
|
||||||
fileInputRef.current.value = '';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const avatarValue = form.watch('avatar');
|
const handleAvatarClick = () => {
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const avatar = watch('avatar');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ p: 3 }}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
基础信息
|
基础信息
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
{/* 头像上传 */}
|
<Grid size={{xs:12, md:6}}>
|
||||||
<Grid size={12}>
|
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
|
||||||
<Typography variant="subtitle2" gutterBottom>
|
|
||||||
头像
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
||||||
<Avatar
|
<Avatar
|
||||||
src={avatarValue}
|
src={avatar}
|
||||||
sx={{ width: 80, height: 80 }}
|
sx={{ width: 120, height: 120, cursor: 'pointer' }}
|
||||||
|
onClick={handleAvatarClick}
|
||||||
>
|
>
|
||||||
{!avatarValue && form.watch('name')?.charAt(0)?.toUpperCase()}
|
{!avatar && <PhotoCameraIcon sx={{ fontSize: 40 }} />}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box>
|
|
||||||
<input
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||||
ref={fileInputRef}
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
style={{ display: 'none' }}
|
|
||||||
onChange={handleAvatarUpload}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
startIcon={<PhotoCameraIcon />}
|
|
||||||
onClick={() => fileInputRef.current?.click()}
|
|
||||||
disabled={disabled}
|
|
||||||
size="small"
|
size="small"
|
||||||
sx={{ mr: 1 }}
|
startIcon={<PhotoCameraIcon />}
|
||||||
|
onClick={handleAvatarClick}
|
||||||
>
|
>
|
||||||
选择图片
|
上传头像
|
||||||
</Button>
|
</Button>
|
||||||
{avatarValue && (
|
{avatar && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleAvatarDelete}
|
|
||||||
disabled={disabled}
|
|
||||||
size="small"
|
size="small"
|
||||||
color="error"
|
color="error"
|
||||||
|
onClick={handleAvatarDelete}
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleAvatarUpload}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
支持 PNG、JPG 格式,文件大小不超过 2MB
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* 知识库名称 */}
|
{/* 表单字段 */}
|
||||||
<Grid size={12}>
|
<Grid size={{xs:12,md:8}}>
|
||||||
<TextField
|
<Grid container spacing={2}>
|
||||||
fullWidth
|
<Grid size={{xs:12}}>
|
||||||
label="知识库名称"
|
<Controller
|
||||||
placeholder="请输入知识库名称"
|
name="name"
|
||||||
disabled={disabled}
|
control={control}
|
||||||
{...form.register('name', {
|
rules={{ required: '知识库名称不能为空' }}
|
||||||
required: '请输入知识库名称',
|
render={({ field, fieldState: { error } }) => (
|
||||||
minLength: {
|
<TextField
|
||||||
value: 2,
|
{...field}
|
||||||
message: '知识库名称至少需要2个字符',
|
label="知识库名称"
|
||||||
},
|
fullWidth
|
||||||
maxLength: {
|
required
|
||||||
value: 50,
|
error={!!error}
|
||||||
message: '知识库名称不能超过50个字符',
|
helperText={error?.message}
|
||||||
},
|
/>
|
||||||
})}
|
)}
|
||||||
error={!!form.formState.errors.name}
|
/>
|
||||||
helperText={form.formState.errors.name?.message?.toString() || '请输入知识库名称'}
|
</Grid>
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* 描述 */}
|
<Grid size={{xs:12}}>
|
||||||
<Grid size={12}>
|
<Controller
|
||||||
<TextField
|
name="description"
|
||||||
fullWidth
|
control={control}
|
||||||
multiline
|
render={({ field }) => (
|
||||||
rows={4}
|
<TextField
|
||||||
label="描述"
|
{...field}
|
||||||
placeholder="请输入知识库描述"
|
label="描述"
|
||||||
disabled={disabled}
|
fullWidth
|
||||||
{...form.register('description', {
|
multiline
|
||||||
maxLength: {
|
rows={3}
|
||||||
value: 500,
|
placeholder="请输入知识库描述..."
|
||||||
message: '描述不能超过500个字符',
|
/>
|
||||||
},
|
)}
|
||||||
})}
|
/>
|
||||||
error={!!form.formState.errors.description}
|
</Grid>
|
||||||
helperText={form.formState.errors.description?.message?.toString() || '请输入知识库描述'}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* 权限设置 */}
|
<Grid size={{xs:12}}>
|
||||||
<Grid size={{xs:12,sm:6}}>
|
<Controller
|
||||||
<FormControl fullWidth disabled={disabled}>
|
name="permission"
|
||||||
<InputLabel>权限设置</InputLabel>
|
control={control}
|
||||||
<Select
|
render={({ field }) => (
|
||||||
label="权限设置"
|
<FormControl fullWidth>
|
||||||
{...form.register('permission')}
|
<InputLabel>权限设置</InputLabel>
|
||||||
value={form.watch('permission') || 'me'}
|
<Select {...field} label="权限设置">
|
||||||
>
|
<MenuItem value="me">仅自己</MenuItem>
|
||||||
<MenuItem value="me">仅自己</MenuItem>
|
<MenuItem value="team">团队成员</MenuItem>
|
||||||
<MenuItem value="team">团队可见</MenuItem>
|
</Select>
|
||||||
</Select>
|
</FormControl>
|
||||||
</FormControl>
|
)}
|
||||||
</Grid>
|
/>
|
||||||
|
</Grid>
|
||||||
{/* 操作按钮 */}
|
</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>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</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 React, { useState, useEffect } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
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 {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
|
||||||
Typography,
|
Typography,
|
||||||
Paper,
|
Paper,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
Fab,
|
Fab,
|
||||||
Snackbar,
|
Button,
|
||||||
Alert,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
ArrowBack as ArrowBackIcon,
|
ArrowBack as ArrowBackIcon,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useKnowledgeDetail, useKnowledgeOperations } from '@/hooks/knowledge-hooks';
|
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 ChunkMethodForm, { type ConfigFormData } from './components/ChunkMethodForm';
|
||||||
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
|
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
|
||||||
import { useSnackbar } from '@/components/Provider/SnackbarProvider';
|
import { useSnackbar } from '@/components/Provider/SnackbarProvider';
|
||||||
|
import { DOCUMENT_PARSER_TYPES } from '@/constants/knowledge';
|
||||||
|
import { MainContainer } from './configuration';
|
||||||
|
|
||||||
interface TabPanelProps {
|
// 统一表单数据类型
|
||||||
children?: React.ReactNode;
|
interface BaseFormData {
|
||||||
index: number;
|
name: string;
|
||||||
value: number;
|
description: string;
|
||||||
}
|
permission: string;
|
||||||
|
avatar?: string;
|
||||||
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}`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function KnowledgeBaseSetting() {
|
function KnowledgeBaseSetting() {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [tabValue, setTabValue] = useState(0);
|
const [tabValue, setTabValue] = useState<'generalForm' | 'chunkMethodForm'>('generalForm');
|
||||||
|
|
||||||
// 获取知识库详情
|
// 获取知识库详情
|
||||||
const { knowledge, loading: detailLoading, refresh } = useKnowledgeDetail(id || '');
|
const { knowledge, loading: detailLoading, refresh } = useKnowledgeDetail(id || '');
|
||||||
const { showMessage } = useSnackbar();
|
const { showMessage } = useSnackbar();
|
||||||
|
|
||||||
// 知识库操作hooks
|
// 知识库操作hooks
|
||||||
const {
|
const {
|
||||||
updateKnowledgeBasicInfo,
|
updateKnowledgeBasicInfo,
|
||||||
updateKnowledgeModelConfig,
|
updateKnowledgeModelConfig,
|
||||||
loading: operationLoading
|
loading: operationLoading
|
||||||
} = useKnowledgeOperations();
|
} = useKnowledgeOperations();
|
||||||
|
|
||||||
// 基础信息表单
|
// 统一表单管理
|
||||||
const basicForm = useForm<BasicFormData>({
|
const form = useForm<any>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
permission: 'me',
|
permission: 'me',
|
||||||
avatar: undefined,
|
avatar: undefined,
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 解析配置表单
|
// 监听parser_id变化
|
||||||
const configForm = useForm<ConfigFormData>({
|
const parserId = useWatch({
|
||||||
defaultValues: {
|
control: form.control,
|
||||||
parser_id: 'naive',
|
name: 'parser_id',
|
||||||
chunk_token_count: 512,
|
|
||||||
layout_recognize: false,
|
|
||||||
task_page_size: 0,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当知识库数据加载完成时,更新表单默认值
|
// 当知识库数据加载完成时,更新表单默认值
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (knowledge) {
|
if (knowledge) {
|
||||||
basicForm.reset({
|
form.reset({
|
||||||
name: knowledge.name || '',
|
name: knowledge.name || '',
|
||||||
description: knowledge.description || '',
|
description: knowledge.description || '',
|
||||||
permission: knowledge.permission || 'me',
|
permission: knowledge.permission || 'me',
|
||||||
avatar: knowledge.avatar,
|
avatar: knowledge.avatar,
|
||||||
});
|
parser_id: knowledge.parser_id || DOCUMENT_PARSER_TYPES.Naive,
|
||||||
|
parser_config: {
|
||||||
configForm.reset({
|
chunk_token_num: knowledge.parser_config?.chunk_token_num || 512,
|
||||||
// parser_id: knowledge.parser_id || 'naive',
|
delimiter: knowledge.parser_config?.delimiter || '\n',
|
||||||
// chunk_token_count: knowledge.chunk_token_count || 512,
|
auto_keywords: knowledge.parser_config?.auto_keywords || 0,
|
||||||
// layout_recognize: knowledge.layout_recognize || false,
|
auto_questions: knowledge.parser_config?.auto_questions || 0,
|
||||||
// task_page_size: knowledge.task_page_size || 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);
|
setTabValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBasicInfoSubmit = async (data: BasicFormData) => {
|
const handleSubmit = async ({data}: {data: any}) => {
|
||||||
if (!knowledge) return;
|
if (!knowledge) return;
|
||||||
|
console.log('提交数据:', data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const kb = {
|
// 分别处理基础信息和配置信息
|
||||||
...data,
|
if (tabValue === 'generalForm') {
|
||||||
// parser_id: knowledge.parser_id,
|
const basicData = {
|
||||||
kb_id: knowledge.id,
|
name: data.name,
|
||||||
} as any;
|
description: data.description,
|
||||||
|
permission: data.permission,
|
||||||
|
avatar: data.avatar,
|
||||||
|
kb_id: knowledge.id,
|
||||||
|
parser_id: knowledge.parser_id,
|
||||||
|
} as any;
|
||||||
|
|
||||||
await updateKnowledgeBasicInfo(kb);
|
await updateKnowledgeBasicInfo(basicData);
|
||||||
showMessage.success('基础信息更新成功');
|
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();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// showMessage.error('基础信息更新失败');
|
showMessage.error(`${tabValue === 'generalForm' ? '基础信息' : '解析配置'}更新失败`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfigSubmit = async (data: ConfigFormData) => {
|
const handleNavigateBack = () => {
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await updateKnowledgeModelConfig({
|
|
||||||
id,
|
|
||||||
parser_id: data.parser_id,
|
|
||||||
// 可以根据需要添加更多配置字段
|
|
||||||
});
|
|
||||||
showMessage.success('解析配置更新成功');
|
|
||||||
// 刷新知识库详情
|
|
||||||
refresh();
|
|
||||||
} catch (error) {
|
|
||||||
// showMessage.error('解析配置更新失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBackToDetail = () => {
|
|
||||||
navigate(`/knowledge/${id}`);
|
navigate(`/knowledge/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (detailLoading) {
|
if (detailLoading) {
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
<MainContainer>
|
||||||
<Typography>加载中...</Typography>
|
<Typography>加载中...</Typography>
|
||||||
</Container>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
<MainContainer>
|
||||||
{/* 面包屑导航 */}
|
{/* 面包屑导航 */}
|
||||||
<KnowledgeBreadcrumbs knowledge={knowledge} />
|
<KnowledgeBreadcrumbs knowledge={knowledge} />
|
||||||
|
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Typography variant="h4" gutterBottom>
|
||||||
知识库设置
|
知识库设置
|
||||||
@@ -176,40 +167,37 @@ function KnowledgeBaseSetting() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Paper sx={{ width: '100%' }}>
|
<FormProvider {...form}>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<Tabs value={tabValue} onChange={handleTabChange} aria-label="设置选项卡">
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
<Tab label="基础信息" {...a11yProps(0)} />
|
<Tabs value={tabValue} onChange={handleTabChange} aria-label="设置选项卡">
|
||||||
<Tab label="解析配置" {...a11yProps(1)} />
|
<Tab label="基础信息" value="generalForm" />
|
||||||
</Tabs>
|
<Tab label="解析配置" value="chunkMethodForm" />
|
||||||
</Box>
|
</Tabs>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<TabPanel value={tabValue} index={0}>
|
<Box sx={{ mt: 3 }}>
|
||||||
<GeneralForm
|
{tabValue === 'generalForm' && (
|
||||||
form={basicForm}
|
<GeneralForm />
|
||||||
onSubmit={handleBasicInfoSubmit}
|
)}
|
||||||
isSubmitting={operationLoading}
|
{tabValue === 'chunkMethodForm' && (
|
||||||
submitButtonText="保存基础信息"
|
<ChunkMethodForm />
|
||||||
disabled={detailLoading}
|
)}
|
||||||
/>
|
</Box>
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel value={tabValue} index={1}>
|
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
<ChunkMethodForm
|
<Button type="submit" variant="contained">
|
||||||
form={configForm as any}
|
保存
|
||||||
onSubmit={handleConfigSubmit}
|
</Button>
|
||||||
isSubmitting={operationLoading}
|
</Box>
|
||||||
submitButtonText="保存解析配置"
|
</Form>
|
||||||
disabled={detailLoading}
|
</FormProvider>
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
</Paper>
|
|
||||||
|
|
||||||
{/* 返回按钮 */}
|
{/* 返回按钮 */}
|
||||||
<Fab
|
<Fab
|
||||||
color="primary"
|
color="primary"
|
||||||
aria-label="返回知识库详情"
|
aria-label="返回知识库详情"
|
||||||
onClick={handleBackToDetail}
|
onClick={handleNavigateBack}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
bottom: 128,
|
bottom: 128,
|
||||||
@@ -218,7 +206,7 @@ function KnowledgeBaseSetting() {
|
|||||||
>
|
>
|
||||||
<ArrowBackIcon />
|
<ArrowBackIcon />
|
||||||
</Fab>
|
</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,
|
IKnowledgeFile,
|
||||||
IChunk,
|
IChunk,
|
||||||
IRenameTag,
|
IRenameTag,
|
||||||
ParserConfig,
|
IParserConfig,
|
||||||
} from '@/interfaces/database/knowledge';
|
} from '@/interfaces/database/knowledge';
|
||||||
import type { GridRowSelectionModel } from '@mui/x-data-grid';
|
import type { GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
|
||||||
@@ -120,8 +120,8 @@ const knowledgeService = {
|
|||||||
return post(api.document_run, data);
|
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);
|
return post(api.document_change_parser, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import api from './api';
|
|||||||
import request, { post } from '@/utils/request';
|
import request, { post } from '@/utils/request';
|
||||||
import type { ITenantInfo } from '@/interfaces/database/knowledge';
|
import type { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||||
import type { IUserInfo, ITenant } from '@/interfaces/database/user-setting';
|
import type { IUserInfo, ITenant } from '@/interfaces/database/user-setting';
|
||||||
|
import type { LlmModelType } from '@/constants/knowledge';
|
||||||
|
|
||||||
// 用户相关API服务
|
// 用户相关API服务
|
||||||
const userService = {
|
const userService = {
|
||||||
@@ -71,6 +72,11 @@ const userService = {
|
|||||||
agreeTenant: (tenantId: string) => {
|
agreeTenant: (tenantId: string) => {
|
||||||
return request.put(api.agreeTenant(tenantId));
|
return request.put(api.agreeTenant(tenantId));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 获取LLM模型列表
|
||||||
|
llm_list: (data: { model_type?: LlmModelType }) => {
|
||||||
|
return request.get(api.llm_list, { params: data });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default userService;
|
export default userService;
|
||||||
Reference in New Issue
Block a user