diff --git a/.env b/.env index ff8e5ad..dc28f6c 100644 --- a/.env +++ b/.env @@ -1,4 +1,5 @@ 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----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index b555a63..5b89112 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -55,7 +55,7 @@ const Header = () => { color: '#333', }} > - RAG Dashboard + RAG Empowerment System { letterSpacing: '0.5px', }} > - RAGflow Prototype - + T-Systems Enterprise + {navItems.map((item) => { @@ -93,7 +93,7 @@ const Sidebar = () => { opacity: 0.7, }} > - © 2025 RAG Demo + © 2025 T-Systems ); diff --git a/src/constants/llm.ts b/src/constants/llm.ts new file mode 100644 index 0000000..c3f5f6c --- /dev/null +++ b/src/constants/llm.ts @@ -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', +}; diff --git a/src/hooks/document-hooks.ts b/src/hooks/document-hooks.ts index d44520f..0b183a5 100644 --- a/src/hooks/document-hooks.ts +++ b/src/hooks/document-hooks.ts @@ -17,6 +17,7 @@ export interface UseDocumentListState { // 文档列表Hook返回值接口 export interface UseDocumentListReturn extends UseDocumentListState { fetchDocuments: (params?: IFetchKnowledgeListRequestParams) => Promise; + fetchDocumentsFilter: () => Promise; setKeywords: (keywords: string) => void; setCurrentPage: (page: number) => void; setPageSize: (size: number) => void; @@ -95,6 +96,27 @@ export const useDocumentList = ( } }, [kbId, keywords, currentPage, pageSize]); + /** + * 获取文档过滤器 + */ + const fetchDocumentsFilter = useCallback(async () => { + if (!kbId) return; + + try { + const response = await knowledgeService.getDocumentFilter({ + kb_id: kbId, + }); + if (response.data.code === 0) { + const data = response.data.data; + } else { + throw new Error(response.data.message || '获取文档过滤器失败'); + } + } catch (error: any) { + const errorMessage = error.response?.data?.message || error.message || '获取文档过滤器失败'; + console.error('Failed to fetch document filter:', error); + } + }, [kbId, keywords]); + /** * 刷新当前页面数据 */ @@ -139,6 +161,7 @@ export const useDocumentList = ( pageSize, keywords, fetchDocuments, + fetchDocumentsFilter, setKeywords: handleSetKeywords, setCurrentPage: handleSetCurrentPage, setPageSize: handleSetPageSize, diff --git a/src/hooks/knowledge-hooks.ts b/src/hooks/knowledge-hooks.ts index 2cd9820..fd077b7 100644 --- a/src/hooks/knowledge-hooks.ts +++ b/src/hooks/knowledge-hooks.ts @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import knowledgeService from '@/services/knowledge_service'; -import type { IKnowledge, IKnowledgeResult } from '@/interfaces/database/knowledge'; +import type { IKnowledge, IKnowledgeResult, IParserConfig } from '@/interfaces/database/knowledge'; import type { IFetchKnowledgeListRequestParams } from '@/interfaces/request/knowledge'; /** @@ -299,11 +299,9 @@ export const useKnowledgeOperations = () => { * 包括嵌入模型、解析器配置、相似度阈值等 */ const updateKnowledgeModelConfig = useCallback(async (data: { - id: string; + kb_id: string; embd_id?: string; - // parser_config?: Partial; - similarity_threshold?: number; - vector_similarity_weight?: number; + parser_config?: Partial; parser_id?: string; }) => { try { @@ -311,7 +309,6 @@ export const useKnowledgeOperations = () => { setError(null); const updateData = { - kb_id: data.id, ...data, }; diff --git a/src/hooks/llm-hooks.ts b/src/hooks/llm-hooks.ts new file mode 100644 index 0000000..75ca579 --- /dev/null +++ b/src/hooks/llm-hooks.ts @@ -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({}); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(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, + }; +} diff --git a/src/pages/knowledge/components/ChunkMethodForm.tsx b/src/pages/knowledge/components/ChunkMethodForm.tsx index 486fd3e..134742d 100644 --- a/src/pages/knowledge/components/ChunkMethodForm.tsx +++ b/src/pages/knowledge/components/ChunkMethodForm.tsx @@ -1,539 +1,84 @@ -import React from 'react'; -import { type UseFormReturn } from 'react-hook-form'; +import React, { useMemo } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; import { Box, Typography, - FormControl, - InputLabel, - Select, - MenuItem, - Grid, - TextField, - FormHelperText, - Button, - CircularProgress, - Switch, - FormControlLabel, - Chip, - Slider, - Accordion, - AccordionSummary, - AccordionDetails, } from '@mui/material'; -import { - Save as SaveIcon, - ExpandMore as ExpandMoreIcon, - Add as AddIcon, -} from '@mui/icons-material'; import { DOCUMENT_PARSER_TYPES, type DocumentParserType } from '@/constants/knowledge'; import { type IParserConfig } from '@/interfaces/database/knowledge'; +import { + NaiveConfiguration, + QAConfiguration, + PaperConfiguration, + ResumeConfiguration, + ManualConfiguration, + TableConfiguration, + BookConfiguration, + LawsConfiguration, + PresentationConfiguration, + OneConfiguration, + TagConfiguration, + ChunkMethodItem, +} from '../configuration'; -/** -{ - "kb_id": "dcc2871aa4cd11f08d4116ac85b1de0a", - "name": "k1123", - "description": " 213213", - "permission": "team", - "parser_id": "naive", - "embd_id": "", - "parser_config": { - "layout_recognize": "Plain Text", - "chunk_token_num": 512, - "delimiter": "\n", - "auto_keywords": 0, - "auto_questions": 0, - "html4excel": false, - "topn_tags": 3, - "raptor": { - "use_raptor": true, - "prompt": "请总结以下段落。 小心数字,不要编造。 段落如下:\n {cluster_content}\n以上就是你需要总结的内容。", - "max_token": 256, - "threshold": 0.1, - "max_cluster": 64, - "random_seed": 0 - }, - "graphrag": { - "use_graphrag": true, - "entity_types": [ - "organization", - "person", - "geo", - "event", - "category" - ], - "method": "light" - } - }, - "pagerank": 0 +// 配置组件映射表 +const ConfigurationComponentMap = { + [DOCUMENT_PARSER_TYPES.Naive]: NaiveConfiguration, + [DOCUMENT_PARSER_TYPES.Qa]: QAConfiguration, + [DOCUMENT_PARSER_TYPES.Resume]: ResumeConfiguration, + [DOCUMENT_PARSER_TYPES.Manual]: ManualConfiguration, + [DOCUMENT_PARSER_TYPES.Table]: TableConfiguration, + [DOCUMENT_PARSER_TYPES.Paper]: PaperConfiguration, + [DOCUMENT_PARSER_TYPES.Book]: BookConfiguration, + [DOCUMENT_PARSER_TYPES.Laws]: LawsConfiguration, + [DOCUMENT_PARSER_TYPES.Presentation]: PresentationConfiguration, + [DOCUMENT_PARSER_TYPES.One]: OneConfiguration, + [DOCUMENT_PARSER_TYPES.Tag]: TagConfiguration, + [DOCUMENT_PARSER_TYPES.KnowledgeGraph]: TagConfiguration, + [DOCUMENT_PARSER_TYPES.Picture]: TagConfiguration, + [DOCUMENT_PARSER_TYPES.Audio]: TagConfiguration, + [DOCUMENT_PARSER_TYPES.Email]: TagConfiguration, + // [DOCUMENT_PARSER_TYPES.KnowledgeGraph]: KnowledgeGraphConfiguration, + // [DOCUMENT_PARSER_TYPES.Picture]: PictureConfiguration, + // [DOCUMENT_PARSER_TYPES.Audio]: AudioConfiguration, + // [DOCUMENT_PARSER_TYPES.Email]: GraphragConfiguration, +}; + +// 空组件 +function EmptyComponent() { + return ( + + + 请选择一个解析方法来配置相关参数 + + + ); } - */ - - -// 解析器选项配置 -const parserOptions = [ - { value: DOCUMENT_PARSER_TYPES.Naive, label: '通用解析器', description: '适用于大多数文档类型' }, - { value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A解析器', description: '适用于问答格式文档' }, - { value: DOCUMENT_PARSER_TYPES.Resume, label: '简历解析器', description: '专门用于解析简历文档' }, - { value: DOCUMENT_PARSER_TYPES.Manual, label: '手册解析器', description: '适用于技术手册和说明书' }, - { value: DOCUMENT_PARSER_TYPES.Table, label: '表格解析器', description: '专门处理表格数据' }, - { value: DOCUMENT_PARSER_TYPES.Paper, label: '论文解析器', description: '适用于学术论文' }, - { value: DOCUMENT_PARSER_TYPES.Book, label: '书籍解析器', description: '适用于书籍和长文档' }, - { value: DOCUMENT_PARSER_TYPES.Laws, label: '法律解析器', description: '专门处理法律文档' }, - { value: DOCUMENT_PARSER_TYPES.Presentation, label: '演示文稿解析器', description: '适用于PPT等演示文档' }, - { value: DOCUMENT_PARSER_TYPES.Picture, label: '图片解析器', description: '处理图片中的文字内容' }, - { value: DOCUMENT_PARSER_TYPES.One, label: '整体解析器', description: '将整个文档作为一个块处理' }, - { value: DOCUMENT_PARSER_TYPES.Audio, label: '音频解析器', description: '处理音频文件转录' }, - { value: DOCUMENT_PARSER_TYPES.Email, label: '邮件解析器', description: '专门处理邮件格式' }, - { value: DOCUMENT_PARSER_TYPES.Tag, label: '标签解析器', description: '基于标签的文档解析' }, - { value: DOCUMENT_PARSER_TYPES.KnowledgeGraph, label: '知识图谱解析器', description: '构建知识图谱结构' }, -]; export interface ConfigFormData extends IParserConfig { parser_id: DocumentParserType; } -interface ChunkMethodFormProps { - form: UseFormReturn; - onSubmit: (data: ConfigFormData) => void; - isSubmitting?: boolean; - onCancel?: () => void; - disabled?: boolean; - submitButtonText?: string; - cancelButtonText?: string; -} +function ChunkMethodForm() { + const { control } = useFormContext(); -function ChunkMethodForm({ - form, - onSubmit, - isSubmitting = false, - onCancel, - disabled = false, - submitButtonText = '保存', - cancelButtonText = '取消' -}: ChunkMethodFormProps) { - const selectedParser: DocumentParserType = form.watch('parser_id'); - const [entityTypes, setEntityTypes] = React.useState(['organization', 'person', 'geo', 'event', 'category']); + // 监听parser_id变化 + const parser_id = useWatch({ + control, + name: 'parser_id', + }); - // 通用配置部分 - const renderGeneralConfig = () => ( - - }> - 通用 - - - - {/* 切片方法 */} - - - 切片方法 - - 选择适合您文档类型的解析器 - - - - {/* PDF解析器 */} - - - PDF解析器 - - - - - {/* 嵌入模型 */} - - - 嵌入模型 - - - - - {/* 建议文本块大小 */} - - 建议文本块大小 - - - - - 512 - - - - {/* 文本分段标识符 */} - - - - - - - ); - - // 页面排名配置部分 - const renderPageRankConfig = () => ( - - }> - 页面排名 - - - - {/* 页面排名 */} - - - 页面排名 - - - - - - {/* 自动关键词提取 */} - - - 自动关键词提取 - - - - - - {/* 自动问题提取 */} - - - 自动问题提取 - - - - - - {/* 表格转HTML */} - - - } - label="表格转HTML" - /> - - - {/* 标签集 */} - - - 标签集 - - - - - - - ); - - // RAPTOR集成配置部分 - const renderRaptorConfig = () => ( - - }> - 使用召回增强RAPTOR集成 - - - - {/* 启用RAPTOR */} - - - } - label="使用召回增强RAPTOR集成" - /> - - - {/* 提示词 */} - - - - - {/* 最大token数 */} - - 最大token数 - - - - - 256 - - - - {/* 阈值 */} - - 阈值 - - - - - 0.1 - - - - {/* 最大聚类数 */} - - 最大聚类数 - - - - - 64 - - - - {/* 随机种子 */} - - - + - - ) - }} - /> - - - - - ); - - // 提取知识图谱配置部分 - const renderGraphRagConfig = () => ( - - }> - 提取知识图谱 - - - - {/* 启用知识图谱 */} - - - } - label="提取知识图谱" - /> - - - {/* 添加实体类型 */} - - - - - {/* 实体类型标签 */} - - 实体类型 - - {entityTypes.map((type, index) => ( - { - setEntityTypes(prev => prev.filter((_, i) => i !== index)); - }} - disabled={disabled} - /> - ))} - - - - {/* 方法 */} - - - 方法 - - - - - {/* 实体归一化 */} - - } - label="实体归一化" - /> - - - {/* 社区报告生成 */} - - } - label="社区报告生成" - /> - - - - - ); + // 根据parser_id动态选择配置组件 + const ConfigurationComponent = useMemo(() => { + const parser = parser_id as DocumentParserType; + const component = ConfigurationComponentMap[parser] || EmptyComponent; + return component || EmptyComponent; + }, [parser_id]); return ( - - - 配置 - - - 在这里更新您的知识库详细信息,并更改切片方法。 - - - - {renderGeneralConfig()} - {renderPageRankConfig()} - {renderRaptorConfig()} - {renderGraphRagConfig()} - - - {/* 操作按钮 */} - - {onCancel && ( - - )} - - + + {/* 动态配置内容 */} + ); } diff --git a/src/pages/knowledge/components/GeneralForm.tsx b/src/pages/knowledge/components/GeneralForm.tsx index 8773b90..f69c94b 100644 --- a/src/pages/knowledge/components/GeneralForm.tsx +++ b/src/pages/knowledge/components/GeneralForm.tsx @@ -1,5 +1,5 @@ import React, { useRef } from 'react'; -import { type UseFormReturn } from 'react-hook-form'; +import { useFormContext, Controller } from 'react-hook-form'; import { Box, Typography, @@ -12,211 +12,138 @@ import { Avatar, Button, IconButton, - CircularProgress, } from '@mui/material'; import { PhotoCamera as PhotoCameraIcon, Delete as DeleteIcon, - Save as SaveIcon, } from '@mui/icons-material'; -export interface BasicFormData { - name: string; - description: string; - permission: string; - avatar?: string; -} - -interface GeneralFormProps { - form: UseFormReturn; - onSubmit: (data: BasicFormData) => void; - isSubmitting?: boolean; - onCancel?: () => void; - disabled?: boolean; - submitButtonText?: string; - cancelButtonText?: string; -} - -function GeneralForm({ - form, - onSubmit, - isSubmitting = false, - onCancel, - disabled = false, - submitButtonText = '提交', - cancelButtonText = '取消' -}: GeneralFormProps) { +function GeneralForm() { + const { control, watch, setValue } = useFormContext(); const fileInputRef = useRef(null); - // 处理头像上传 const handleAvatarUpload = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { - // 检查文件类型 - if (!file.type.startsWith('image/')) { - alert('请选择图片文件'); - return; - } - - // 检查文件大小 (限制为 2MB) - if (file.size > 2 * 1024 * 1024) { - alert('图片大小不能超过 2MB'); - return; - } - const reader = new FileReader(); reader.onload = (e) => { - const base64String = e.target?.result as string; - form.setValue('avatar', base64String); + setValue('avatar', e.target?.result as string); }; reader.readAsDataURL(file); } }; - // 删除头像 const handleAvatarDelete = () => { - form.setValue('avatar', undefined); - if (fileInputRef.current) { - fileInputRef.current.value = ''; - } + setValue('avatar', undefined); }; - const avatarValue = form.watch('avatar'); + const handleAvatarClick = () => { + fileInputRef.current?.click(); + }; + + const avatar = watch('avatar'); return ( - + 基础信息 - {/* 头像上传 */} - - - 头像 - - + + - {!avatarValue && form.watch('name')?.charAt(0)?.toUpperCase()} + {!avatar && } - - + + - {avatarValue && ( + {avatar && ( )} + + - - 支持 PNG、JPG 格式,文件大小不超过 2MB - - {/* 知识库名称 */} - - - + {/* 表单字段 */} + + + + ( + + )} + /> + - {/* 描述 */} - - - + + ( + + )} + /> + - {/* 权限设置 */} - - - 权限设置 - - - - - {/* 操作按钮 */} - - - {onCancel && ( - - )} - - + + ( + + 权限设置 + + + )} + /> + + diff --git a/src/pages/knowledge/configuration/audio.tsx b/src/pages/knowledge/configuration/audio.tsx new file mode 100644 index 0000000..b0d1b8f --- /dev/null +++ b/src/pages/knowledge/configuration/audio.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + + + 自动关键词配置 - 待实现 + + + + + + 自动问题配置 - 待实现 + + + + + + Raptor配置 - 待实现 + + + + + + GraphRAG配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/book.tsx b/src/pages/knowledge/configuration/book.tsx new file mode 100644 index 0000000..7e4dd28 --- /dev/null +++ b/src/pages/knowledge/configuration/book.tsx @@ -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 ( + + + + + + 布局识别配置 - 待实现 + + + + + + + PageRank配置 - 待实现 + + + + + + + + 自动关键词配置 - 待实现 + + + + + 自动问题配置 - 待实现 + + + + + + + + Raptor配置 - 待实现 + + + + + + + GraphRAG配置 - 待实现 + + + + + + + 标签配置 - 待实现 + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/common-item.tsx b/src/pages/knowledge/configuration/common-item.tsx new file mode 100644 index 0000000..b25a6f4 --- /dev/null +++ b/src/pages/knowledge/configuration/common-item.tsx @@ -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 ( + + + 切片方法 + + ( + + 选择切片方法 + + {errors.parser_id && ( + {errors.parser_id.message as string} + )} + + )} + /> + + ); +} + +export function EmbeddingModelItem() { + const { control, formState: { errors } } = useFormContext(); + + const { options: embdOptions } = useEmbeddingModelOptions(); + + return ( + + + 嵌入模型 + + ( + + 选择嵌入模型 + + {errors.embd_id && ( + {errors.embd_id.message as string} + )} + + )} + /> + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/configuration-form-container.tsx b/src/pages/knowledge/configuration/configuration-form-container.tsx new file mode 100644 index 0000000..137c4a7 --- /dev/null +++ b/src/pages/knowledge/configuration/configuration-form-container.tsx @@ -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 ( + + {children} + + ); +} + +export function MainContainer({ children }: PropsWithChildren) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/email.tsx b/src/pages/knowledge/configuration/email.tsx new file mode 100644 index 0000000..d9c8aa7 --- /dev/null +++ b/src/pages/knowledge/configuration/email.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + + + 自动关键词配置 - 待实现 + + + + + + 自动问题配置 - 待实现 + + + + + + Raptor配置 - 待实现 + + + + + + GraphRAG配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/index.tsx b/src/pages/knowledge/configuration/index.tsx new file mode 100644 index 0000000..778094f --- /dev/null +++ b/src/pages/knowledge/configuration/index.tsx @@ -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'; \ No newline at end of file diff --git a/src/pages/knowledge/configuration/knowledge-graph.tsx b/src/pages/knowledge/configuration/knowledge-graph.tsx new file mode 100644 index 0000000..9b95c07 --- /dev/null +++ b/src/pages/knowledge/configuration/knowledge-graph.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; +import { Box, Typography } from '@mui/material'; + +export function KnowledgeGraphConfiguration() { + return ( + <> + + + + + + PageRank配置 - 待实现 + + + + + + 实体类型配置 - 待实现 + + + + + + 最大Token数量配置 (最大: 16384) - 待实现 + + + + + + 分隔符配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/laws.tsx b/src/pages/knowledge/configuration/laws.tsx new file mode 100644 index 0000000..3a26c70 --- /dev/null +++ b/src/pages/knowledge/configuration/laws.tsx @@ -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 ( + + + + + + 布局识别配置 - 待实现 + + + + + + + PageRank配置 - 待实现 + + + + + + + + 自动关键词配置 - 待实现 + + + + + 自动问题配置 - 待实现 + + + + + + + + Raptor配置 - 待实现 + + + + + + + GraphRAG配置 - 待实现 + + + + + + + 标签配置 - 待实现 + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/manual.tsx b/src/pages/knowledge/configuration/manual.tsx new file mode 100644 index 0000000..6a4449a --- /dev/null +++ b/src/pages/knowledge/configuration/manual.tsx @@ -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 ( + + + + + + 布局识别配置 - 待实现 + + + + + + + PageRank配置 - 待实现 + + + + + + + + 自动关键词配置 - 待实现 + + + + + 自动问题配置 - 待实现 + + + + + + + + Raptor配置 - 待实现 + + + + + + + GraphRAG配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/naive.tsx b/src/pages/knowledge/configuration/naive.tsx new file mode 100644 index 0000000..99d2c27 --- /dev/null +++ b/src/pages/knowledge/configuration/naive.tsx @@ -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 ( + + + {/* 第一部分:基础配置 */} + + }> + 基础配置 + + + + {/* 切片方法 */} + + + {/* PDF解析器 */} + + + PDF解析器 + + ( + + + + )} + /> + + + {/* 嵌入模型 */} + + + {/* 建议文本块大小 */} + + + 建议文本块大小 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 文本分段标识符 */} + + + *文本分段标识符 + + ( + + )} + /> + + + + + + {/* 第二部分:页面排名和自动提取 */} + + }> + 页面排名和自动提取 + + + + {/* 页面排名 */} + + + 页面排名 + + ( + + )} + /> + + + {/* 自动关键词提取 */} + + + 自动关键词提取 + + ( + + )} + /> + + + {/* 自动问题提取 */} + + + 自动问题提取 + + ( + + )} + /> + + + {/* 表格转HTML */} + + ( + + } + label="表格转HTML" + /> + )} + /> + + + {/* 标签集 */} + + + 标签集 + + ( + + + + )} + /> + + + + + + {/* 第三部分:RAPTOR策略 */} + + }> + RAPTOR策略 + + + + {/* 使用召回增强RAPTOR策略 */} + + ( + + } + label="使用召回增强RAPTOR策略" + /> + )} + /> + + + {/* 提示词 */} + + + 提示词 + + ( + + )} + /> + + + {/* 最大token数 */} + + + 最大token数 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 阈值 */} + + + 阈值 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 最大聚类数 */} + + + 最大聚类数 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 随机种子 */} + + + 随机种子 + + ( + + + + + + + )} + /> + + + + + + {/* 第四部分:知识图谱 */} + + }> + 知识图谱 + + + + {/* 提取知识图谱 */} + + ( + + } + label="提取知识图谱" + /> + )} + /> + + + {/* 实体类型 */} + + + *实体类型 + + ( + + {['organization', 'person', 'geo', 'event', 'category'].map((type) => ( + {}} + variant="outlined" + /> + ))} + + + + + )} + /> + + + {/* 方法 */} + + + 方法 + + ( + + + + )} + /> + + + {/* 实体归一化 */} + + ( + + } + label="实体归一化" + /> + )} + /> + + + {/* 社区报告生成 */} + + ( + + } + label="社区报告生成" + /> + )} + /> + + + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/one.tsx b/src/pages/knowledge/configuration/one.tsx new file mode 100644 index 0000000..68bb262 --- /dev/null +++ b/src/pages/knowledge/configuration/one.tsx @@ -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 ( + + + + + 布局识别配置 - 待实现 + + + + + + + PageRank配置 - 待实现 + + + + + + 自动关键词配置 - 待实现 + + + + + + 自动问题配置 - 待实现 + + + + + + GraphRAG配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/paper.tsx b/src/pages/knowledge/configuration/paper.tsx new file mode 100644 index 0000000..dd18dd3 --- /dev/null +++ b/src/pages/knowledge/configuration/paper.tsx @@ -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 ( + + + {/* 布局识别 */} + + + 布局识别 + + ( + + 布局识别方式 + + + )} + /> + + + {/* 自动关键词 */} + + + 自动关键词数量 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 自动问题 */} + + + 自动问题数量 + + ( + + field.onChange(value)} + /> + + )} + /> + + + {/* 标签数量 */} + + + Top N 标签数量 + + ( + field.onChange(parseInt(e.target.value))} + /> + )} + /> + + + + + 论文解析器专门优化用于学术论文的解析,能够更好地识别论文的结构和内容。 + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/picture.tsx b/src/pages/knowledge/configuration/picture.tsx new file mode 100644 index 0000000..a77d399 --- /dev/null +++ b/src/pages/knowledge/configuration/picture.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + + + 自动关键词配置 - 待实现 + + + + + + 自动问题配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/presentation.tsx b/src/pages/knowledge/configuration/presentation.tsx new file mode 100644 index 0000000..f36bc6c --- /dev/null +++ b/src/pages/knowledge/configuration/presentation.tsx @@ -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 ( + + + + + + 布局识别配置 - 待实现 + + + + + + + PageRank配置 - 待实现 + + + + + + + + 自动关键词配置 - 待实现 + + + + + 自动问题配置 - 待实现 + + + + + + + + Raptor配置 - 待实现 + + + + + + + GraphRAG配置 - 待实现 + + + + + + + 标签配置 - 待实现 + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/qa.tsx b/src/pages/knowledge/configuration/qa.tsx new file mode 100644 index 0000000..e0e06a5 --- /dev/null +++ b/src/pages/knowledge/configuration/qa.tsx @@ -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 ( + + + {/* 标签数量 */} + + + Top N 标签数量 + + ( + field.onChange(parseInt(e.target.value))} + /> + )} + /> + + + + + Q&A解析器专门用于处理问答格式的文档,会自动识别问题和答案的结构。 + + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/resume.tsx b/src/pages/knowledge/configuration/resume.tsx new file mode 100644 index 0000000..a3bad6b --- /dev/null +++ b/src/pages/knowledge/configuration/resume.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + + + 标签配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/table.tsx b/src/pages/knowledge/configuration/table.tsx new file mode 100644 index 0000000..a91097b --- /dev/null +++ b/src/pages/knowledge/configuration/table.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/configuration/tag.tsx b/src/pages/knowledge/configuration/tag.tsx new file mode 100644 index 0000000..ced2767 --- /dev/null +++ b/src/pages/knowledge/configuration/tag.tsx @@ -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 ( + + + + + + + PageRank配置 - 待实现 + + + + ); +} \ No newline at end of file diff --git a/src/pages/knowledge/hooks.ts b/src/pages/knowledge/hooks.ts new file mode 100644 index 0000000..6060445 --- /dev/null +++ b/src/pages/knowledge/hooks.ts @@ -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]; +// } diff --git a/src/pages/knowledge/setting.tsx b/src/pages/knowledge/setting.tsx index e5cae02..c1963e3 100644 --- a/src/pages/knowledge/setting.tsx +++ b/src/pages/knowledge/setting.tsx @@ -1,172 +1,163 @@ import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { useForm, type UseFormReturn } from 'react-hook-form'; +import { useForm, FormProvider, useWatch, Form } from 'react-hook-form'; import { Box, - Container, Typography, Paper, Tabs, Tab, Fab, - Snackbar, - Alert, + Button, } from '@mui/material'; import { ArrowBack as ArrowBackIcon, } from '@mui/icons-material'; import { useKnowledgeDetail, useKnowledgeOperations } from '@/hooks/knowledge-hooks'; -import GeneralForm, { type BasicFormData } from './components/GeneralForm'; +import GeneralForm from './components/GeneralForm'; import ChunkMethodForm, { type ConfigFormData } from './components/ChunkMethodForm'; import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs'; import { useSnackbar } from '@/components/Provider/SnackbarProvider'; +import { DOCUMENT_PARSER_TYPES } from '@/constants/knowledge'; +import { MainContainer } from './configuration'; -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} - -function a11yProps(index: number) { - return { - id: `setting-tab-${index}`, - 'aria-controls': `setting-tabpanel-${index}`, - }; +// 统一表单数据类型 +interface BaseFormData { + name: string; + description: string; + permission: string; + avatar?: string; } function KnowledgeBaseSetting() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); - const [tabValue, setTabValue] = useState(0); + const [tabValue, setTabValue] = useState<'generalForm' | 'chunkMethodForm'>('generalForm'); // 获取知识库详情 const { knowledge, loading: detailLoading, refresh } = useKnowledgeDetail(id || ''); const { showMessage } = useSnackbar(); - + // 知识库操作hooks - const { - updateKnowledgeBasicInfo, - updateKnowledgeModelConfig, - loading: operationLoading + const { + updateKnowledgeBasicInfo, + updateKnowledgeModelConfig, + loading: operationLoading } = useKnowledgeOperations(); - // 基础信息表单 - const basicForm = useForm({ + // 统一表单管理 + const form = useForm({ defaultValues: { name: '', description: '', permission: 'me', avatar: undefined, - }, + } }); - // 解析配置表单 - const configForm = useForm({ - defaultValues: { - parser_id: 'naive', - chunk_token_count: 512, - layout_recognize: false, - task_page_size: 0, - }, + // 监听parser_id变化 + const parserId = useWatch({ + control: form.control, + name: 'parser_id', }); // 当知识库数据加载完成时,更新表单默认值 useEffect(() => { if (knowledge) { - basicForm.reset({ + form.reset({ name: knowledge.name || '', description: knowledge.description || '', permission: knowledge.permission || 'me', avatar: knowledge.avatar, - }); - - configForm.reset({ - // parser_id: knowledge.parser_id || 'naive', - // chunk_token_count: knowledge.chunk_token_count || 512, - // layout_recognize: knowledge.layout_recognize || false, - // task_page_size: knowledge.task_page_size || 0, + parser_id: knowledge.parser_id || DOCUMENT_PARSER_TYPES.Naive, + parser_config: { + chunk_token_num: knowledge.parser_config?.chunk_token_num || 512, + delimiter: knowledge.parser_config?.delimiter || '\n', + auto_keywords: knowledge.parser_config?.auto_keywords || 0, + auto_questions: knowledge.parser_config?.auto_questions || 0, + html4excel: knowledge.parser_config?.html4excel || false, + topn_tags: knowledge.parser_config?.topn_tags || 3, + raptor: { + use_raptor: knowledge.parser_config?.raptor?.use_raptor || false, + prompt: knowledge.parser_config?.raptor?.prompt || '请总结以下段落。小心数字,不要编造。段落如下:\n {cluster_content}\n以上就是你需要总结的内容。', + max_token: knowledge.parser_config?.raptor?.max_token || 256, + threshold: knowledge.parser_config?.raptor?.threshold || 0.1, + max_cluster: knowledge.parser_config?.raptor?.max_cluster || 64, + random_seed: knowledge.parser_config?.raptor?.random_seed || 0, + }, + graphrag: { + use_graphrag: knowledge.parser_config?.graphrag?.use_graphrag || false, + entity_types: knowledge.parser_config?.graphrag?.entity_types || ['organization', 'person', 'geo', 'event', 'category'], + method: knowledge.parser_config?.graphrag?.method || 'light', + }, + }, }); } - }, [knowledge, basicForm, configForm]); + }, [knowledge, form]); - const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { + const handleTabChange = (event: React.SyntheticEvent, newValue: 'generalForm' | 'chunkMethodForm') => { setTabValue(newValue); }; - const handleBasicInfoSubmit = async (data: BasicFormData) => { + const handleSubmit = async ({data}: {data: any}) => { if (!knowledge) return; - + console.log('提交数据:', data); + try { - const kb = { - ...data, - // parser_id: knowledge.parser_id, - kb_id: knowledge.id, - } as any; + // 分别处理基础信息和配置信息 + if (tabValue === 'generalForm') { + const basicData = { + name: data.name, + description: data.description, + permission: data.permission, + avatar: data.avatar, + kb_id: knowledge.id, + parser_id: knowledge.parser_id, + } as any; - await updateKnowledgeBasicInfo(kb); - showMessage.success('基础信息更新成功'); + await updateKnowledgeBasicInfo(basicData); + showMessage.success('基础信息更新成功'); + } else { + const configData = { + kb_id: knowledge.id, + name: knowledge.name, + description: knowledge.description, + permission: knowledge.permission, + avatar: knowledge.avatar, + parser_id: data.parser_id, + embd_id: data.embd_id, + parser_config: data.parser_config, + }; + + await updateKnowledgeModelConfig(configData); + showMessage.success('解析配置更新成功'); + } + // 刷新知识库详情 refresh(); } catch (error) { - // showMessage.error('基础信息更新失败'); + showMessage.error(`${tabValue === 'generalForm' ? '基础信息' : '解析配置'}更新失败`); } }; - const handleConfigSubmit = async (data: ConfigFormData) => { - if (!id) return; - - try { - await updateKnowledgeModelConfig({ - id, - parser_id: data.parser_id, - // 可以根据需要添加更多配置字段 - }); - showMessage.success('解析配置更新成功'); - // 刷新知识库详情 - refresh(); - } catch (error) { - // showMessage.error('解析配置更新失败'); - } - }; - - const handleBackToDetail = () => { + const handleNavigateBack = () => { navigate(`/knowledge/${id}`); }; if (detailLoading) { return ( - + 加载中... - + ); } return ( - + {/* 面包屑导航 */} - + 知识库设置 @@ -176,40 +167,37 @@ function KnowledgeBaseSetting() { - - - - - - - + +
+ + + + + + - - - + + {tabValue === 'generalForm' && ( + + )} + {tabValue === 'chunkMethodForm' && ( + + )} + - - - - + + + +
+
{/* 返回按钮 */} -
+ ); } diff --git a/src/pages/knowledge/参考data-setting页面架构分析.md b/src/pages/knowledge/参考data-setting页面架构分析.md new file mode 100644 index 0000000..d57677d --- /dev/null +++ b/src/pages/knowledge/参考data-setting页面架构分析.md @@ -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 ( + + + {/* 第一组配置项 */} + + + {/* 第二组配置项 */} + + + ); +} +``` + +### 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 +
+ {/* 页面标题 */} + + +
+
+ + {/* 滚动内容区域 */} +
+ + {/* 通用表单 */} + + + + {/* GraphRAG配置 */} + + + + {/* RAPTOR配置 */} + + + + {/* 解析类型选择 */} + + + {/* 条件渲染: 内置解析方法 */} + {parseType === 1 && ( + <> + + + + )} + + {/* 条件渲染: 数据流水线 */} + {parseType === 2 && ( + + )} + +
+ + {/* 操作按钮区域 */} +
+ + +
+
+ +
+
+``` + +### 2. 响应式设计 +- **固定宽度**: 表单区域固定 768px 宽度 +- **垂直滚动**: 内容区域支持垂直滚动 +- **弹性布局**: 使用 Flexbox 实现自适应高度 + +## 组件设计模式 + +### 1. 容器组件模式 +```typescript +// 主容器 - 控制整体间距 +export function MainContainer({ children, className }) { + return
{children}
; +} + +// 配置容器 - 控制配置项间距 +export function ConfigurationFormContainer({ children, className }) { + return
{children}
; +} +``` + +### 2. 表单字段组件模式 +```typescript +export function XxxFormField() { + const form = useFormContext(); + + return ( + ( + +
+ 标签 + + + +
+
+
+ +
+
+ )} + /> + ); +} +``` + +### 3. 条件渲染模式 +```typescript +// 基于状态的条件渲染 +{parseType === 1 && } +{parseType === 2 && } + +// 基于选择的动态组件 + +``` + +## 技术特点 + +### 1. 类型安全 +- **TypeScript**: 全面的类型定义 +- **Zod验证**: 运行时类型验证 +- **表单类型推导**: 从schema自动推导表单类型 + +### 2. 性能优化 +- **useMemo**: 缓存动态组件选择 +- **useCallback**: 缓存事件处理函数 +- **条件渲染**: 避免不必要的组件渲染 + +### 3. 可维护性 +- **模块化设计**: 每个解析方法独立组件 +- **统一接口**: 所有配置组件遵循相同模式 +- **代码复用**: 通用组件和钩子函数 + +### 4. 扩展性 +- **插件化架构**: 新增解析方法只需添加对应组件 +- **配置驱动**: 通过配置映射控制组件渲染 +- **钩子系统**: 自定义钩子支持功能扩展 + +## 适配建议 + +### 1. 保持现有布局样式 +- 使用你现有的 Accordion 布局结构 +- 保持四个配置分组的设计 +- 适配参考项目的组件组织方式 + +### 2. 采用核心架构模式 +- **动态组件映射**: 实现解析方法到配置组件的映射 +- **统一表单管理**: 使用 `useFormContext` 共享表单状态 +- **模块化组件**: 将不同配置拆分为独立组件 + +### 3. 数据结构对齐 +- 参考 `form-schema.ts` 的数据结构设计 +- 实现条件验证和跨字段验证 +- 支持嵌套配置对象 + +### 4. 状态管理优化 +- 使用 `useWatch` 监听关键字段变化 +- 实现响应式的条件渲染 +- 优化表单初始化和数据合并逻辑 + +这个架构设计具有很强的可扩展性和维护性,非常适合复杂的配置表单场景。你可以基于这个分析来改进你的配置页面实现。 \ No newline at end of file diff --git a/src/pages/knowledge/参考setting项目架构分析.md b/src/pages/knowledge/参考setting项目架构分析.md new file mode 100644 index 0000000..569e3ee --- /dev/null +++ b/src/pages/knowledge/参考setting项目架构分析.md @@ -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>({ + 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 +
+ +
+
+ + + + 通用 + 解析方法 + + + + + + + + +
+ + +
+
+``` + +#### 表单容器组件 +```typescript +// configuration-form-container.tsx +export function ConfigurationFormContainer({ children, className }) { + return ( + + {children} + + ); +} + +export function MainContainer({ children }) { + return
{children}
; +} +``` + +## 组件设计模式 + +### 1. 通用表单组件 (general-form.tsx) +- 使用 `FormField` 组件统一表单字段样式 +- 采用 1/4 和 3/4 的标签与输入框布局比例 +- 集成头像上传、权限选择等通用功能 + +### 2. 配置组件模式 +每个配置组件遵循统一的结构: +```typescript +export function NaiveConfiguration() { + return ( + + + + + + + + + + + ); +} +``` + +### 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` 监听字段变化 +- 实现数据获取钩子 +- 优化表单重置和提交逻辑 + +这种架构设计为大型表单应用提供了良好的可维护性和扩展性基础,值得在用户项目中借鉴和应用。 \ No newline at end of file diff --git a/src/services/knowledge_service.ts b/src/services/knowledge_service.ts index 0cb4c8d..4f15bca 100644 --- a/src/services/knowledge_service.ts +++ b/src/services/knowledge_service.ts @@ -11,7 +11,7 @@ import type { IKnowledgeFile, IChunk, IRenameTag, - ParserConfig, + IParserConfig, } from '@/interfaces/database/knowledge'; import type { GridRowSelectionModel } from '@mui/x-data-grid'; @@ -120,8 +120,8 @@ const knowledgeService = { return post(api.document_run, data); }, - // 更改文档解析器 - changeDocumentParser: (data: { doc_id: string; parser_config: ParserConfig }) => { + // 更改文档解析器配置 + changeDocumentParser: (data: { doc_id: string; parser_config: IParserConfig }) => { return post(api.document_change_parser, data); }, diff --git a/src/services/user_service.ts b/src/services/user_service.ts index e27df1b..f93499d 100644 --- a/src/services/user_service.ts +++ b/src/services/user_service.ts @@ -2,6 +2,7 @@ import api from './api'; import request, { post } from '@/utils/request'; import type { ITenantInfo } from '@/interfaces/database/knowledge'; import type { IUserInfo, ITenant } from '@/interfaces/database/user-setting'; +import type { LlmModelType } from '@/constants/knowledge'; // 用户相关API服务 const userService = { @@ -71,6 +72,11 @@ const userService = { agreeTenant: (tenantId: string) => { return request.put(api.agreeTenant(tenantId)); }, + + // 获取LLM模型列表 + llm_list: (data: { model_type?: LlmModelType }) => { + return request.get(api.llm_list, { params: data }); + }, }; export default userService; \ No newline at end of file