diff --git a/frontend/app/[locale]/chat/components/chatLeftSidebar.tsx b/frontend/app/[locale]/chat/components/chatLeftSidebar.tsx index be0941956..1f6370bdd 100644 --- a/frontend/app/[locale]/chat/components/chatLeftSidebar.tsx +++ b/frontend/app/[locale]/chat/components/chatLeftSidebar.tsx @@ -133,7 +133,7 @@ export function ChatSidebar({ const [editingTitle, setEditingTitle] = useState(""); const inputRef = useRef(null); - // 获取用户认证状态 + // Get user authentication status const { isLoading: userAuthLoading, isSpeedMode } = useAuth(); // Add delete dialog status diff --git a/frontend/app/[locale]/chat/internal/chatInterface.tsx b/frontend/app/[locale]/chat/internal/chatInterface.tsx index 7bfd41fa2..9605b1718 100644 --- a/frontend/app/[locale]/chat/internal/chatInterface.tsx +++ b/frontend/app/[locale]/chat/internal/chatInterface.tsx @@ -1097,7 +1097,7 @@ export function ChatInterface() { } else { // Cache has content, display normally setIsLoadingHistoricalConversation(false); - setIsLoading(false); // 确保 isLoading 状态也被重置 + setIsLoading(false); // Ensure isLoading state is also reset // For cases where there are cached messages, also trigger scrolling to the bottom. setShouldScrollToBottom(true); diff --git a/frontend/app/[locale]/chat/streaming/chatStreamMain.tsx b/frontend/app/[locale]/chat/streaming/chatStreamMain.tsx index 744bfe8b1..6f4d77e90 100644 --- a/frontend/app/[locale]/chat/streaming/chatStreamMain.tsx +++ b/frontend/app/[locale]/chat/streaming/chatStreamMain.tsx @@ -70,7 +70,7 @@ export function ChatStreamMain({ const lastUserMessageIdRef = useRef(null); const messagesEndRef = useRef(null); - // 处理消息分类 + // Handle message classification useEffect(() => { const finalMsgs: ChatMessageType[] = []; const taskMsgs: any[] = []; @@ -310,7 +310,7 @@ export function ChatStreamMain({ setShowScrollButton(false); } - // 显示顶部渐变效果 + // Show top gradient effect if (scrollTop > 10) { setShowTopFade(true); } else { diff --git a/frontend/app/[locale]/chat/streaming/chatStreamMessage.tsx b/frontend/app/[locale]/chat/streaming/chatStreamMessage.tsx index 335c816d7..13cd1f432 100644 --- a/frontend/app/[locale]/chat/streaming/chatStreamMessage.tsx +++ b/frontend/app/[locale]/chat/streaming/chatStreamMessage.tsx @@ -161,7 +161,7 @@ export function ChatStreamMessage({ diff --git a/frontend/app/[locale]/chat/streaming/taskWindow.tsx b/frontend/app/[locale]/chat/streaming/taskWindow.tsx index c4e6636da..bee865ea9 100644 --- a/frontend/app/[locale]/chat/streaming/taskWindow.tsx +++ b/frontend/app/[locale]/chat/streaming/taskWindow.tsx @@ -938,7 +938,7 @@ export function TaskWindow({ messages, isStreaming = false }: TaskWindowProps) { if (lastMessage.finalAnswer) { const timer = setTimeout(() => { setIsExpanded(false); - }, 1000); // 1秒后折叠 + }, 1000); // Collapse after 1 second return () => clearTimeout(timer); } } @@ -1004,9 +1004,9 @@ export function TaskWindow({ messages, isStreaming = false }: TaskWindowProps) { return (
- {/* 使用flex布局确保圆点与文本内容对齐 */} + {/* Use flex layout to ensure dots align with text content */}
- {/* 圆点容器 */} + {/* Dot container */}
- {/* 消息内容 */} + {/* Message content */}
{renderMessageContent(message)} @@ -1056,7 +1056,7 @@ export function TaskWindow({ messages, isStreaming = false }: TaskWindowProps) { ); }; - // 计算容器高度:内容高度 + header高度,但不超过最大高度 + // Calculate container height: content height + header height, but not exceeding maximum height const maxHeight = 300; const headerHeight = 55; const availableHeight = maxHeight - headerHeight; diff --git a/frontend/app/[locale]/page.tsx b/frontend/app/[locale]/page.tsx index 9f746dbb9..d20ce661b 100644 --- a/frontend/app/[locale]/page.tsx +++ b/frontend/app/[locale]/page.tsx @@ -57,7 +57,7 @@ export default function Home() { const [adminRequiredPromptOpen, setAdminRequiredPromptOpen] = useState(false); - // 处理需要登录的操作 + // Handle operations that require login const handleAuthRequired = (e: React.MouseEvent) => { if (!user) { e.preventDefault(); @@ -65,24 +65,24 @@ export default function Home() { } }; - // 确认登录对话框 + // Confirm login dialog const handleCloseLoginPrompt = () => { setLoginPromptOpen(false); }; - // 处理登录按钮点击 + // Handle login button click const handleLoginClick = () => { setLoginPromptOpen(false); openLoginModal(); }; - // 处理注册按钮点击 + // Handle register button click const handleRegisterClick = () => { setLoginPromptOpen(false); openRegisterModal(); }; - // 处理需要管理员权限的操作 + // Handle operations that require admin privileges const handleAdminRequired = (e: React.MouseEvent) => { if (user?.role !== "admin") { e.preventDefault(); @@ -90,7 +90,7 @@ export default function Home() { } }; - // 关闭管理员提示框 + // Close admin prompt dialog const handleCloseAdminPrompt = () => { setAdminRequiredPromptOpen(false); }; @@ -115,7 +115,7 @@ export default function Home() {
- {/* Github 按钮 */} + {/* Github button */} Github - {/* ModelEngine 链接始终显示 */} + {/* ModelEngine link always visible */} - {/* 登录状态切换显示 - 只在完整版显示 */} + {/* Login status toggle display - only shown in full version */} {!isSpeedMode && ( <> {userLoading ? ( @@ -172,7 +172,7 @@ export default function Home() { )}
- {/* 右侧汉堡按钮 为移动版预留 */} + {/* Right hamburger button reserved for mobile version */}
- {/* 文档数量标签 */} + {/* Document count tag */} {t('knowledgeBase.tag.documents', { count: kb.documentCount || 0 })} - {/* 分块数量标签 */} + {/* Chunk count tag */} {t('knowledgeBase.tag.chunks', { count: kb.chunkCount || 0 })} - {/* 知识库来源标签 */} + {/* Knowledge base source tag */} {t('knowledgeBase.tag.source', { source: kb.source })} - {/* 创建日期标签 - 只显示日期 */} + {/* Creation date tag - only show date */} {t('knowledgeBase.tag.createdAt', { date: formatDate(kb.createdAt) })} - {/* 强制换行 */} + {/* Force line break */}
- {/* 模型标签 - 显示正常或不匹配 */} + {/* Model tag - show normal or mismatch */} {t('knowledgeBase.tag.model', { model: getModelDisplayName(kb.embeddingModel) })} diff --git a/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadArea.tsx b/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadArea.tsx index f5ace4790..49881ec86 100644 --- a/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadArea.tsx +++ b/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadArea.tsx @@ -60,7 +60,7 @@ const UploadArea = forwardRef(({ prevFileListRef.current = fileList; }, [fileList]); - // 重置所有状态的函数 + // Function to reset all states const resetAllStates = useCallback(() => { setFileList([]); setNameStatus('available'); @@ -68,23 +68,23 @@ const UploadArea = forwardRef(({ setIsKnowledgeBaseReady(false); }, []); - // 监听知识库变化,重置文件列表并获取知识库信息 + // Listen for knowledge base changes, reset file list and get knowledge base info useEffect(() => { - // 如果知识库名称没有变化,不进行重置 + // If knowledge base name hasn't changed, don't reset if (indexName === currentKnowledgeBaseRef.current) { return; } - // 取消之前的请求 + // Cancel previous request if (pendingRequestRef.current) { pendingRequestRef.current.abort(); pendingRequestRef.current = null; } - // 立即重置状态和清空文件列表 + // Immediately reset state and clear file list resetAllStates(); - // 更新当前知识库引用 + // Update current knowledge base reference currentKnowledgeBaseRef.current = indexName; if (!indexName || isCreatingMode) { @@ -93,11 +93,11 @@ const UploadArea = forwardRef(({ return; } - // 创建新的 AbortController + // Create new AbortController const abortController = new AbortController(); pendingRequestRef.current = abortController; - // 使用服务函数获取知识库信息 + // Use service function to get knowledge base info fetchKnowledgeBaseInfo( indexName, abortController, @@ -114,7 +114,7 @@ const UploadArea = forwardRef(({ message ); - // 清理函数 + // Cleanup function return () => { if (pendingRequestRef.current) { pendingRequestRef.current.abort(); @@ -123,12 +123,12 @@ const UploadArea = forwardRef(({ }; }, [indexName, isCreatingMode, resetAllStates, t, message]); - // 暴露文件列表给父组件 + // Expose file list to parent component useImperativeHandle(ref, () => ({ fileList }), [fileList]); - // 检查知识库名称是否已存在 + // Check if knowledge base name already exists useEffect(() => { if (!isCreatingMode || !newKnowledgeBaseName) { setNameStatus('available'); @@ -154,30 +154,30 @@ const UploadArea = forwardRef(({ }; }, [isCreatingMode, newKnowledgeBaseName, t]); - // 处理文件变更 + // Handle file changes const handleChange = useCallback(({ fileList: newFileList }: { fileList: UploadFile[] }) => { - // 确保只更新当前知识库的文件列表 + // Ensure only updating current knowledge base's file list if (isCreatingMode || indexName === currentKnowledgeBaseRef.current) { setFileList(newFileList); } else { return; } - // 检查上传是否刚刚完成 + // Check if upload just completed const prevFileList = prevFileListRef.current; const uploadWasInProgress = prevFileList.some(f => f.status === 'uploading'); const uploadIsNowFinished = newFileList.length > 0 && !newFileList.some(f => f.status === 'uploading'); if (uploadWasInProgress && uploadIsNowFinished) { - // 上传完成后仅调用外部的上传完成回调,由 KnowledgeBaseManager 统一管理轮询 + // After upload completion only call external upload completion callback, let KnowledgeBaseManager manage polling uniformly if (onUpload) { onUpload(); } } - // 触发文件选择回调, 传递所有文件 + // Trigger file selection callback, pass all files const files = newFileList .map(file => file.originFileObj) .filter((file): file is RcFile => !!file); @@ -187,17 +187,17 @@ const UploadArea = forwardRef(({ } }, [indexName, onFileSelect, isCreatingMode, newKnowledgeBaseName, onUpload]); - // 处理自定义上传请求 + // Handle custom upload request const handleCustomRequest = useCallback((options: any) => { - // 实际上传由父组件的 handleFileUpload 处理 + // Actual upload is handled by parent component's handleFileUpload const { onSuccess, file } = options; setTimeout(() => { onSuccess({}, file); }, 100); }, []); - // 上传组件属性 + // Upload component properties const uploadProps: UploadProps = { name: 'file', multiple: true, diff --git a/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadAreaUI.tsx b/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadAreaUI.tsx index 30d128b83..eb4e1b40c 100644 --- a/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadAreaUI.tsx +++ b/frontend/app/[locale]/setup/knowledgeSetup/components/upload/UploadAreaUI.tsx @@ -38,7 +38,7 @@ const UploadAreaUI: React.FC = ({ }) => { const { t } = useTranslation('common'); - // 加载中状态UI + // Loading state UI if (isLoading) { return (
@@ -56,7 +56,7 @@ const UploadAreaUI: React.FC = ({ ); } - // 知识库未就绪UI + // Knowledge base not ready UI if (!isKnowledgeBaseReady && !isCreatingMode) { return (
@@ -69,7 +69,7 @@ const UploadAreaUI: React.FC = ({ ); } - // 禁用状态UI + // Disabled state UI if (disabled) { return (
@@ -83,7 +83,7 @@ const UploadAreaUI: React.FC = ({ ); } - // 名称已存在UI - 根据status渲染不同消息 + // Name already exists UI - render different messages based on status if (isCreatingMode && (nameStatus === NAME_CHECK_STATUS.EXISTS_IN_TENANT || nameStatus === NAME_CHECK_STATUS.EXISTS_IN_OTHER_TENANT)) { const messageKey = nameStatus === NAME_CHECK_STATUS.EXISTS_IN_TENANT ? 'knowledgeBase.message.nameExists' @@ -119,16 +119,16 @@ const UploadAreaUI: React.FC = ({ ); } - // 默认UI状态 + // Default UI state return (
- {/* 上传区域容器 */} + {/* Upload area container */}
0 ? 'w-[40%] pr-2' : 'w-full' }`}>
- {/* 上传区域层 */} + {/* Upload area layer */}
{ e.preventDefault(); e.stopPropagation(); }} @@ -153,7 +153,7 @@ const UploadAreaUI: React.FC = ({
- {/* 文件列表区域 */} + {/* File list area */}
0 ? 'w-[60%] opacity-100 pl-2' : 'w-0 opacity-0' diff --git a/frontend/app/[locale]/setup/knowledgeSetup/config.tsx b/frontend/app/[locale]/setup/knowledgeSetup/config.tsx index 8513f3906..c09a42ec4 100644 --- a/frontend/app/[locale]/setup/knowledgeSetup/config.tsx +++ b/frontend/app/[locale]/setup/knowledgeSetup/config.tsx @@ -75,9 +75,9 @@ interface AppProviderProps { } /** - * AppProvider - 为应用提供全局状态管理 - * - * 将知识库、文档和UI状态管理组合在一起,方便一次引入所有上下文 + * AppProvider - Provides global state management for the application + * + * Combines knowledge base, document and UI state management together for easy one-time import of all contexts */ const AppProvider: React.FC = ({ children }) => { return ( @@ -113,7 +113,7 @@ function DataConfig({ isActive }: DataConfigProps) { const { message } = App.useApp(); const { confirm } = useConfirmModal(); - // 组件初始化时清除缓存 + // Clear cache when component initializes useEffect(() => { localStorage.removeItem("preloaded_kb_data"); localStorage.removeItem("kb_cache"); @@ -149,7 +149,7 @@ function DataConfig({ isActive }: DataConfigProps) { const [uploadFiles, setUploadFiles] = useState([]); const [hasClickedUpload, setHasClickedUpload] = useState(false); - // 添加监听选中新知识库的事件 + // Add event listener for selecting new knowledge base useEffect(() => { const handleSelectNewKnowledgeBase = (e: CustomEvent) => { const { knowledgeBase } = e.detail; @@ -180,30 +180,30 @@ function DataConfig({ isActive }: DataConfigProps) { setHasClickedUpload, ]); - // 基于 isActive 状态的用户配置加载和保存逻辑 - const prevIsActiveRef = useRef(null); // 初始化为 null 来区分首次渲染 - const hasLoadedRef = useRef(false); // 跟踪是否已经加载过配置 - const savedSelectedIdsRef = useRef([]); // 保存当前选中的知识库ID - const savedKnowledgeBasesRef = useRef([]); // 保存当前知识库列表 - const hasUserInteractedRef = useRef(false); // 跟踪用户是否有过交互(防止初始加载时误保存空状态) + // User configuration loading and saving logic based on isActive state + const prevIsActiveRef = useRef(null); // Initialize as null to distinguish first render + const hasLoadedRef = useRef(false); // Track whether configuration has been loaded + const savedSelectedIdsRef = useRef([]); // Save currently selected knowledge base IDs + const savedKnowledgeBasesRef = useRef([]); // Save current knowledge base list + const hasUserInteractedRef = useRef(false); // Track whether user has interacted (prevent saving empty state during initial load) - // 监听 isActive 状态变化 + // Listen for isActive state changes useLayoutEffect(() => { - // 清除可能影响状态的缓存 + // Clear cache that might affect state localStorage.removeItem("preloaded_kb_data"); localStorage.removeItem("kb_cache"); const prevIsActive = prevIsActiveRef.current; - // 进入第二页时标记准备加载 + // Mark ready to load when entering second page if ((prevIsActive === null || !prevIsActive) && isActive) { - hasLoadedRef.current = false; // 重置加载状态 - hasUserInteractedRef.current = false; // 重置交互状态,防止误保存 + hasLoadedRef.current = false; // Reset loading state + hasUserInteractedRef.current = false; // Reset interaction state to prevent incorrect saving } - // 离开第二页时保存用户配置 + // Save user configuration when leaving second page if (prevIsActive === true && !isActive) { - // 只有在用户有过交互后才保存,防止初始加载时误保存空状态 + // Only save after user has interacted to prevent saving empty state during initial load if (hasUserInteractedRef.current) { const saveConfig = async () => { localStorage.removeItem("preloaded_kb_data"); @@ -219,20 +219,20 @@ function DataConfig({ isActive }: DataConfigProps) { saveConfig(); } - hasLoadedRef.current = false; // 重置加载状态 + hasLoadedRef.current = false; // Reset loading state } - // 更新 ref + // Update ref prevIsActiveRef.current = isActive; }, [isActive]); - // 实时保存当前状态到 ref,确保卸载时能访问到 + // Save current state to ref in real-time to ensure access during unmount useEffect(() => { savedSelectedIdsRef.current = kbState.selectedIds; savedKnowledgeBasesRef.current = kbState.knowledgeBases; }, [kbState.selectedIds, kbState.knowledgeBases]); - // 获取授权头的辅助函数 + // Helper function to get authorization headers const getAuthHeaders = () => { const session = typeof window !== "undefined" ? localStorage.getItem("session") : null; @@ -246,18 +246,18 @@ function DataConfig({ isActive }: DataConfigProps) { }; }; - // 组件卸载时的保存逻辑 + // Save logic when component unmounts useEffect(() => { return () => { - // 组件卸载时,如果之前是活跃状态且用户有过交互,则执行保存 + // When component unmounts, if previously active and user has interacted, execute save if (prevIsActiveRef.current === true && hasUserInteractedRef.current) { - // 使用保存的状态而不是当前可能已清空的状态 + // Use saved state instead of current potentially cleared state const selectedKbNames = savedKnowledgeBasesRef.current .filter((kb) => savedSelectedIdsRef.current.includes(kb.id)) .map((kb) => kb.name); try { - // 使用fetch with keepalive确保请求能在页面卸载时发送 + // Use fetch with keepalive to ensure request can be sent during page unload fetch(API_ENDPOINTS.tenantConfig.updateKnowledgeList, { method: "POST", headers: { @@ -276,9 +276,9 @@ function DataConfig({ isActive }: DataConfigProps) { }; }, []); - // 单独监听知识库加载状态,当知识库加载完成且处于活跃状态时加载用户配置 + // Separately listen for knowledge base loading state, load user configuration when knowledge base loading is complete and in active state useEffect(() => { - // 只有在第二页活跃、知识库已加载、且尚未加载用户配置时才执行 + // Only execute when second page is active, knowledge base is loaded, and user configuration hasn't been loaded yet if ( isActive && kbState.knowledgeBases.length > 0 && @@ -303,12 +303,12 @@ function DataConfig({ isActive }: DataConfigProps) { const baseNamePrefix = t("knowledgeBase.name.new"); const existingNames = new Set(existingKbs.map((kb) => kb.name)); - // 如果基础名称未被使用,直接返回 + // If base name is not used, return directly if (!existingNames.has(baseNamePrefix)) { return baseNamePrefix; } - // 否则尝试添加数字后缀,直到找到未被使用的名称 + // Otherwise try adding numeric suffix until finding unused name let counter = 1; while (existingNames.has(`${baseNamePrefix}${counter}`)) { counter++; @@ -322,26 +322,26 @@ function DataConfig({ isActive }: DataConfigProps) { kb: KnowledgeBase, fromUserClick: boolean = true ) => { - // 只有当是用户点击时才重置创建模式 + // Only reset creation mode when user clicks if (fromUserClick) { - hasUserInteractedRef.current = true; // 标记用户有交互 + hasUserInteractedRef.current = true; // Mark user interaction setIsCreatingMode(false); // Reset creating mode - setHasClickedUpload(false); // 重置上传按钮点击状态 + setHasClickedUpload(false); // Reset upload button click state } - // 无论是否切换知识库,都需要获取最新文档信息 + // Whether switching knowledge base or not, need to get latest document information const isChangingKB = !kbState.activeKnowledgeBase || kb.id !== kbState.activeKnowledgeBase.id; - // 如果是切换知识库,更新激活状态 + // If switching knowledge base, update active state if (isChangingKB) { setActiveKnowledgeBase(kb); } - // 设置活动知识库ID到轮询服务 + // Set active knowledge base ID to polling service knowledgeBasePollingService.setActiveKnowledgeBase(kb.id); - // 调用知识库切换处理函数 + // Call knowledge base switch handling function handleKnowledgeBaseChange(kb); }; @@ -351,16 +351,16 @@ function DataConfig({ isActive }: DataConfigProps) { // Set loading state before fetching documents docDispatch({ type: DOCUMENT_ACTION_TYPES.SET_LOADING_DOCUMENTS, payload: true }); - // 获取最新文档数据 + // Get latest document data const documents = await knowledgeBaseService.getAllFiles(kb.id); - // 触发文档更新事件 + // Trigger document update event knowledgeBasePollingService.triggerDocumentsUpdate(kb.id, documents); - // 后台更新知识库统计信息,但不重复获取文档 + // Background update knowledge base statistics, but don't duplicate document fetching setTimeout(async () => { try { - // 直接调用 fetchKnowledgeBases 更新知识库列表数据 + // Directly call fetchKnowledgeBases to update knowledge base list data await fetchKnowledgeBases(false, true); } catch (error) { console.error("获取知识库最新数据失败:", error); @@ -390,7 +390,7 @@ function DataConfig({ isActive }: DataConfigProps) { e.preventDefault(); setDragging(false); - // 如果是创建模式或有活动知识库,则处理文件 + // If in creation mode or has active knowledge base, process files if (isCreatingMode || kbState.activeKnowledgeBase) { const files = Array.from(e.dataTransfer.files); if (files.length > 0) { @@ -404,7 +404,7 @@ function DataConfig({ isActive }: DataConfigProps) { // Handle knowledge base deletion const handleDelete = (id: string) => { - hasUserInteractedRef.current = true; // 标记用户有交互 + hasUserInteractedRef.current = true; // Mark user interaction confirm({ title: t("knowledgeBase.modal.deleteConfirm.title"), content: t("knowledgeBase.modal.deleteConfirm.content"), @@ -448,13 +448,13 @@ function DataConfig({ isActive }: DataConfigProps) { // Handle new knowledge base creation const handleCreateNew = () => { - hasUserInteractedRef.current = true; // 标记用户有交互 + hasUserInteractedRef.current = true; // Mark user interaction // Generate default knowledge base name const defaultName = generateUniqueKbName(kbState.knowledgeBases); setNewKbName(defaultName); setIsCreatingMode(true); - setHasClickedUpload(false); // 重置上传按钮点击状态 - setUploadFiles([]); // 重置上传文件数组,清空所有待上传文件 + setHasClickedUpload(false); // Reset upload button click state + setUploadFiles([]); // Reset upload files array, clear all pending upload files }; // Handle document deletion @@ -479,7 +479,7 @@ function DataConfig({ isActive }: DataConfigProps) { }); }; - // 处理文件上传 - 在创建模式下先创建知识库再上传,在普通模式下直接上传 + // Handle file upload - in creation mode create knowledge base first then upload, in normal mode upload directly const handleFileUpload = async () => { if (!uploadFiles.length) { message.warning(t("document.message.noFiles")); @@ -591,12 +591,12 @@ function DataConfig({ isActive }: DataConfigProps) { // Get current viewing knowledge base documents const viewingDocuments = (() => { - // 在创建模式下返回空数组,因为新知识库还没有文档 + // In creation mode return empty array because new knowledge base has no documents yet if (isCreatingMode) { return []; } - // 正常模式下,使用activeKnowledgeBase + // In normal mode, use activeKnowledgeBase return kbState.activeKnowledgeBase ? docState.documentsMap[kbState.activeKnowledgeBase.id] || [] : []; @@ -606,7 +606,7 @@ function DataConfig({ isActive }: DataConfigProps) { const viewingKbName = kbState.activeKnowledgeBase?.name || (isCreatingMode ? newKbName : ""); - // 只要有文档上传成功,立即自动切换创建模式为 false + // As long as any document upload succeeds, immediately switch creation mode to false useEffect(() => { if (isCreatingMode && viewingDocuments.length > 0) { setIsCreatingMode(false); @@ -615,13 +615,13 @@ function DataConfig({ isActive }: DataConfigProps) { // Handle knowledge base selection const handleSelectKnowledgeBase = (id: string) => { - hasUserInteractedRef.current = true; // 标记用户有交互 + hasUserInteractedRef.current = true; // Mark user interaction selectKnowledgeBase(id); // When selecting knowledge base also get latest data (low priority background operation) setTimeout(async () => { try { - // 使用较低优先级刷新数据,因为这不是关键操作 + // Use lower priority to refresh data as this is not a critical operation await refreshKnowledgeBaseData(true); } catch (error) { console.error("刷新知识库数据失败:", error); @@ -630,7 +630,7 @@ function DataConfig({ isActive }: DataConfigProps) { }, 500); // Delay execution, lower priority }; - // 在组件初始化或活动知识库变化时更新轮询服务中的活动知识库ID + // Update active knowledge base ID in polling service when component initializes or active knowledge base changes useEffect(() => { if (kbState.activeKnowledgeBase) { knowledgeBasePollingService.setActiveKnowledgeBase( @@ -643,15 +643,15 @@ function DataConfig({ isActive }: DataConfigProps) { } }, [kbState.activeKnowledgeBase, isCreatingMode, newKbName]); - // 在组件卸载时清理轮询 + // Clean up polling when component unmounts useEffect(() => { return () => { - // 停止所有轮询 + // Stop all polling knowledgeBasePollingService.stopAllPolling(); }; }, []); - // 创建模式下,知识库名称变化时,重置"名称已存在"状态 + // In creation mode, reset "name already exists" state when knowledge base name changes const handleNameChange = (name: string) => { setNewKbName(name); }; diff --git a/frontend/app/[locale]/setup/modelSetup/components/model/ModelDeleteDialog.tsx b/frontend/app/[locale]/setup/modelSetup/components/model/ModelDeleteDialog.tsx index b8f5c1bc2..9a08c4824 100644 --- a/frontend/app/[locale]/setup/modelSetup/components/model/ModelDeleteDialog.tsx +++ b/frontend/app/[locale]/setup/modelSetup/components/model/ModelDeleteDialog.tsx @@ -43,7 +43,7 @@ export const ModelDeleteDialog = ({ const [selectedModelForSettings, setSelectedModelForSettings] = useState(null) const [modelMaxTokens, setModelMaxTokens] = useState("4096") - // 获取模型的颜色方案 + // Get model color scheme const getModelColorScheme = (type: ModelType): { bg: string; text: string; border: string } => { switch (type) { case MODEL_TYPES.LLM: @@ -65,7 +65,7 @@ export const ModelDeleteDialog = ({ } } - // 获取模型的图标 + // Get model icon const getModelIcon = (type: ModelType) => { switch (type) { case MODEL_TYPES.LLM: @@ -87,7 +87,7 @@ export const ModelDeleteDialog = ({ } } - // 获取模型的显示名称 + // Get model display name const getModelTypeName = (type: ModelType | null): string => { if (!type) return t('model.type.unknown') switch (type) { @@ -219,7 +219,7 @@ export const ModelDeleteDialog = ({ await modelService.deleteCustomModel(displayName) let configUpdates: any = {} - // 检查每个模型配置,如果当前使用的是被删除的模型,则清空配置 + // Check each model configuration, if currently using a deleted model, clear the configuration if (modelConfig.llm.displayName === displayName) { configUpdates.llm = { modelName: "", displayName: "", apiConfig: { apiKey: "", modelUrl: "" } } } @@ -252,25 +252,25 @@ export const ModelDeleteDialog = ({ configUpdates.tts = { modelName: "", displayName: "" } } - // 如果有配置需要更新,则更新localStorage + // If there are configurations to update, update localStorage if (Object.keys(configUpdates).length > 0) { updateModelConfig(configUpdates) } - // 显示成功消息 + // Show success message message.success(t('model.message.deleteSuccess', { name: displayName })) - // 直接调用父组件的onSuccess回调刷新模型列表 - // 这会触发一次modelService.getCustomModels()调用,避免重复请求 + // Directly call parent component's onSuccess callback to refresh model list + // This triggers a modelService.getCustomModels() call, avoiding duplicate requests await onSuccess() - // 删除后根据剩余数量调整层级导航 + // Adjust hierarchical navigation based on remaining count after deletion if (deletingModelType) { const remainingByTypeAndSource = customModels.filter(model => model.type === deletingModelType && (!selectedSource || model.source === selectedSource) && model.displayName !== displayName ) if (selectedSource && remainingByTypeAndSource.length === 0) { - // 当前来源下已无模型,退回到来源选择 + // No models under current source, return to source selection setSelectedSource(null) } const remainingByType = customModels.filter(model => @@ -292,7 +292,7 @@ export const ModelDeleteDialog = ({ } } - // 处理关闭对话框 + // Handle closing dialog const handleClose = () => { setDeletingModelType(null) setSelectedSource(null) diff --git a/frontend/app/[locale]/setup/modelSetup/components/model/ModelEditDialog.tsx b/frontend/app/[locale]/setup/modelSetup/components/model/ModelEditDialog.tsx index f74c523b2..84fe6aaa4 100644 --- a/frontend/app/[locale]/setup/modelSetup/components/model/ModelEditDialog.tsx +++ b/frontend/app/[locale]/setup/modelSetup/components/model/ModelEditDialog.tsx @@ -117,14 +117,14 @@ export const ModelEditDialog = ({ isOpen, model, onClose, onSuccess }: ModelEdit if (!model) return setLoading(true) try { - // 使用更新接口而不是删除 + 新增 + // Use update interface instead of delete + add const modelType = form.type as ModelType // Determine max tokens let maxTokensValue = parseInt(form.maxTokens) if (isEmbeddingModel) maxTokensValue = 0 await modelService.updateSingleModel({ - model_id: model.id, // 使用模型名称作为ID + model_id: model.id, // Use model name as ID displayName: form.displayName, url: form.url, apiKey: form.apiKey.trim() === "" ? "sk-no-api-key" : form.apiKey, @@ -132,7 +132,7 @@ export const ModelEditDialog = ({ isOpen, model, onClose, onSuccess }: ModelEdit source: model.source }) - // 更新本地配置(仅当当前编辑模型在配置中被选中时) + // Update local configuration (only when currently edited model is selected in configuration) const modelConfigKeyMap: Record = { llm: MODEL_TYPES.LLM, embedding: MODEL_TYPES.EMBEDDING, diff --git a/frontend/app/[locale]/setup/modelSetup/components/model/ModelListCard.tsx b/frontend/app/[locale]/setup/modelSetup/components/model/ModelListCard.tsx index 40f107002..a44359221 100644 --- a/frontend/app/[locale]/setup/modelSetup/components/model/ModelListCard.tsx +++ b/frontend/app/[locale]/setup/modelSetup/components/model/ModelListCard.tsx @@ -8,7 +8,7 @@ import { CloseOutlined } from '@ant-design/icons' import { MODEL_TYPES, MODEL_STATUS } from '@/const/modelConfig' import { ModelConnectStatus, ModelOption, ModelSource, ModelType } from '@/types/modelConfig' -// 统一管理模型连接状态颜色 +// Unified management of model connection status colors const CONNECT_STATUS_COLORS: Record = { [MODEL_STATUS.AVAILABLE]: "#52c41a", [MODEL_STATUS.UNAVAILABLE]: "#ff4d4f", @@ -17,7 +17,7 @@ const CONNECT_STATUS_COLORS: Record = { default: "#17202a" }; -// 动画定义不再包含颜色,由样式传递 +// Animation definition no longer includes colors, passed through styles const PULSE_ANIMATION = ` @keyframes pulse { 0% { @@ -37,7 +37,7 @@ const PULSE_ANIMATION = ` } `; -// 只拼接样式,颜色和动画通过参数传递 +// Only concatenate styles, colors and animations passed through parameters const getStatusStyle = (status?: ModelConnectStatus): React.CSSProperties => { const color = (status && CONNECT_STATUS_COLORS[status]) || CONNECT_STATUS_COLORS.default; const baseStyle: React.CSSProperties = { @@ -59,14 +59,14 @@ const getStatusStyle = (status?: ModelConnectStatus): React.CSSProperties => { return { ...baseStyle, animation: 'pulse 1.5s infinite', - // 用CSS变量传递动画色 + // Pass animation color through CSS variables ['--pulse-color' as any]: color }; } return baseStyle; }; -// 获取模型来源对应的标签样式 +// Get tag styles corresponding to model source const getSourceTagStyle = (source: string): React.CSSProperties => { const baseStyle: React.CSSProperties = { marginRight: '4px', @@ -110,8 +110,8 @@ interface ModelListCardProps { onModelChange: (value: string) => void officialModels: ModelOption[] customModels: ModelOption[] - onVerifyModel?: (modelName: string, modelType: ModelType) => void // 新增验证模型的回调 - errorFields?: {[key: string]: boolean} // 新增错误字段状态 + onVerifyModel?: (modelName: string, modelType: ModelType) => void // New callback for verifying models + errorFields?: {[key: string]: boolean} // New error field state } export const ModelListCard = ({ @@ -127,36 +127,36 @@ export const ModelListCard = ({ }: ModelListCardProps) => { const { t } = useTranslation() - // 添加模型列表状态,用于更新 + // Add model list state for updates const [modelsData, setModelsData] = useState({ official: [...officialModels], custom: [...customModels] }); - // 在组件中创建一个style元素,包含动画定义 + // Create a style element in the component containing animation definitions useEffect(() => { - // 创建style元素 + // Create style element const styleElement = document.createElement('style'); styleElement.type = 'text/css'; styleElement.innerHTML = PULSE_ANIMATION; document.head.appendChild(styleElement); - // 清理函数,组件卸载时移除style元素 + // Cleanup function, remove style element when component unmounts return () => { document.head.removeChild(styleElement); }; }, []); - // 获取模型列表时需要考虑具体的选项类型 + // When getting model list, need to consider specific option type const getModelsBySource = (): { official: ModelOption[]; custom: ModelOption[] } => { - // 每种类型只显示对应类型的模型 + // Each type only shows models of corresponding type return { official: modelsData.official.filter(model => model.type === type), custom: modelsData.custom.filter(model => model.type === type) } } - // 获取模型来源 + // Get model source const getModelSource = (displayName: string): string => { if (type === MODEL_TYPES.TTS || type === MODEL_TYPES.STT || type === MODEL_TYPES.VLM) { const modelOfType = modelsData.custom.find((m) => m.type === type && m.displayName === displayName) @@ -172,10 +172,10 @@ export const ModelListCard = ({ const modelsBySource = getModelsBySource() - // 本地更新模型状态 + // Local update model status const updateLocalModelStatus = (displayName: string, status: ModelConnectStatus) => { setModelsData(prevData => { - // 查找要更新的模型 + // Find model to update const modelToUpdate = prevData.custom.find(m => m.displayName === displayName && m.type === type); if (!modelToUpdate) { @@ -200,12 +200,12 @@ export const ModelListCard = ({ }); }; - // 当父组件传入的模型列表更新时,更新本地状态 + // When parent component's model list updates, update local state useEffect(() => { - // 更新本地状态,但不触发fetchModelsStatus + // Update local state but don't trigger fetchModelsStatus setModelsData(prevData => { const updatedOfficialModels = officialModels.map(model => { - // 保留已有的connect_status,如果存在的话 + // Preserve existing connect_status if it exists const existingModel = prevData.official.find(m => m.name === model.name && m.type === model.type); return { ...model, @@ -214,7 +214,7 @@ export const ModelListCard = ({ }); const updatedCustomModels = customModels.map(model => { - // 优先使用新传入的状态,这样能反映后端的最新状态 + // Prioritize using newly passed status to reflect latest backend state return { ...model, connect_status: model.connect_status || MODEL_STATUS.UNCHECKED as ModelConnectStatus @@ -228,20 +228,20 @@ export const ModelListCard = ({ }); }, [officialModels, customModels, type, modelId]); - // 处理状态指示灯点击事件 + // Handle status indicator click event const handleStatusClick = (e: React.MouseEvent, displayName: string) => { - e.stopPropagation(); // 阻止事件冒泡 - e.preventDefault(); // 阻止默认行为 - e.nativeEvent.stopImmediatePropagation(); // 阻止所有同级事件处理程序 + e.stopPropagation(); // Prevent event bubbling + e.preventDefault(); // Prevent default behavior + e.nativeEvent.stopImmediatePropagation(); // Prevent all sibling event handlers if (onVerifyModel && displayName) { - // 先更新本地状态为"检测中" + // First update local state to "checking" updateLocalModelStatus(displayName, MODEL_STATUS.CHECKING); - // 然后调用验证函数 + // Then call verification function onVerifyModel(displayName, type); } - return false; // 确保不会继续冒泡 + return false; // Ensure no further bubbling }; return ( diff --git a/frontend/app/[locale]/setup/modelSetup/config.tsx b/frontend/app/[locale]/setup/modelSetup/config.tsx index 015d3dfc3..ff8faa285 100644 --- a/frontend/app/[locale]/setup/modelSetup/config.tsx +++ b/frontend/app/[locale]/setup/modelSetup/config.tsx @@ -16,7 +16,7 @@ import { ModelConfigSection, ModelConfigSectionRef } from './components/modelCon const { Title } = Typography -// 添加接口定义 +// Add interface definition interface AppModelConfigProps { skipModelVerification?: boolean; onSelectedModelsChange?: (selected: Record>) => void; @@ -27,7 +27,7 @@ export default function AppModelConfig({ skipModelVerification = false, onSelect const [isClientSide, setIsClientSide] = useState(false) const modelConfigRef = useRef(null) - // 添加useEffect钩子用于初始化加载配置 + // Add useEffect hook for initial configuration loading useEffect(() => { setIsClientSide(true) @@ -36,7 +36,7 @@ export default function AppModelConfig({ skipModelVerification = false, onSelect } }, [skipModelVerification]) - // 将子组件的选中模型上报给父级(若提供回调) + // Report selected models from child component to parent (if callback provided) useEffect(() => { if (!onSelectedModelsChange) return; const timer = setInterval(() => { diff --git a/frontend/app/[locale]/setup/page.tsx b/frontend/app/[locale]/setup/page.tsx index 89ef6e91f..a3a612b6a 100644 --- a/frontend/app/[locale]/setup/page.tsx +++ b/frontend/app/[locale]/setup/page.tsx @@ -74,7 +74,7 @@ function Header({ {t("setup.header.description")}
- {/* 语言切换 */} + {/* Language switch */}
- {/* ModelEngine连通性状态 */} + {/* ModelEngine connectivity status */}
= ({ icon, size = "small", }) => { - // 根据类型获取样式 + // Get styles based on type const getStyleByType = (): React.CSSProperties => { switch (type) { case "success": @@ -49,7 +49,7 @@ export const StatusBadge: React.FC = ({ } }; - // 根据尺寸获取大小样式 + // Get size styles based on size const getSizeStyle = (): React.CSSProperties => { switch (size) { case "large": diff --git a/frontend/const/layoutConstants.ts b/frontend/const/layoutConstants.ts index b014c011e..b9d170330 100644 --- a/frontend/const/layoutConstants.ts +++ b/frontend/const/layoutConstants.ts @@ -80,7 +80,7 @@ export const STANDARD_CARD = { BASE_CLASSES: "bg-white border border-gray-200 rounded-md flex flex-col overflow-hidden", // Padding - PADDING: "16px", // 对应 p-4 + PADDING: "16px", // Corresponds to p-4 // Content area scroll configuration CONTENT_SCROLL: { @@ -92,10 +92,10 @@ export const STANDARD_CARD = { // Card header configuration export const CARD_HEADER = { // Header margin - MARGIN_BOTTOM: "16px", // 对应 mb-4 + MARGIN_BOTTOM: "16px", // Corresponds to mb-4 // Header padding - PADDING: "0 8px", // 对应 px-2 + PADDING: "0 8px", // Corresponds to px-2 // Divider style DIVIDER_CLASSES: "h-[1px] bg-gray-200 mt-2", diff --git a/frontend/hooks/useMemory.ts b/frontend/hooks/useMemory.ts index 7087ad103..34e505f77 100644 --- a/frontend/hooks/useMemory.ts +++ b/frontend/hooks/useMemory.ts @@ -22,12 +22,12 @@ import { pageSize, MemoryGroup, UseMemoryOptions } from "@/types/memory" export function useMemory({ visible, currentUserId, currentTenantId, message }: UseMemoryOptions) { const { t } = useTranslation() - /* ----------------------- 基础设置状态 ----------------------- */ + /* ----------------------- Basic Settings State ----------------------- */ const [memoryEnabled, setMemoryEnabledState] = useState(true) const [shareOption, setShareOptionState] = useState<"always" | "ask" | "never">("always") - /* ------------------------- 原逻辑状态 ------------------------- */ - // 分组禁用状态(仅 Agent 共享、用户 Agent 页签生效) + /* ------------------------- Original Logic State ------------------------- */ + // Group disabled state (only effective for Agent shared, user Agent tabs) const [disabledGroups, setDisabledGroups] = useState>({}) const disableAgentIdSet = useRef>(new Set()) @@ -35,28 +35,28 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: const [openKey, setOpenKey] = useState() - // 当前激活 Tab + // Currently active Tab const [activeTabKey, setActiveTabKey] = useState("base") - // 分页状态 + // Pagination state const [pageMap, setPageMap] = useState>({ agentShared: 1, userAgent: 1 }) - /* ------------------------------ 数据分组 ------------------------------ */ + /* ------------------------------ Data Groups ------------------------------ */ const [tenantSharedGroup, setTenantSharedGroup] = useState({ title: "", key: "tenant", items: [] }) const [agentSharedGroups, setAgentSharedGroups] = useState([]) const [userPersonalGroup, setUserPersonalGroup] = useState({ title: "", key: "user-personal", items: [] }) const [userAgentGroups, setUserAgentGroups] = useState([]) - /* ------------------------------ 新增记忆状态 ------------------------------ */ + /* ------------------------------ New Memory State ------------------------------ */ const [addingMemoryKey, setAddingMemoryKey] = useState(null) const [newMemoryContent, setNewMemoryContent] = useState("") const [isAddingMemory, setIsAddingMemory] = useState(false) - /* --------------------------- 初始化加载 --------------------------- */ + /* --------------------------- Initialization Loading --------------------------- */ useEffect(() => { if (!visible) return - // 1. 加载配置 + // 1. Load configuration loadMemoryConfig().then((cfg) => { setMemoryEnabledState(cfg.memoryEnabled) setShareOptionState(cfg.shareOption) @@ -68,7 +68,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: }) }, [visible, message]) - /* --------------------------- 加载分组数据 --------------------------- */ + /* --------------------------- Load Group Data --------------------------- */ useEffect(() => { if (!visible || !memoryEnabled) return @@ -81,7 +81,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: const agentGrps = await fetchAgentSharedGroups() setAgentSharedGroups(agentGrps) - // 同步禁用状态 + // Sync disabled state const newDisabled: Record = {} agentGrps.forEach((g) => { const id = g.key.replace(/^agent-/, "") @@ -95,7 +95,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: const userAgentGrps = await fetchUserAgentGroups() setUserAgentGroups(userAgentGrps) - // 同步禁用状态 + // Sync disabled state const newDisabled: Record = {} userAgentGrps.forEach((g) => { const id = g.key.replace(/^user-agent-/, "") @@ -105,8 +105,8 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } } catch (e) { console.error("load groups error", e) - const errorMessage = e instanceof Error ? e.message : "加载记忆数据失败" - if (errorMessage.includes("Authentication") || errorMessage.includes("ElasticSearch") || errorMessage.includes("连接")) { + const errorMessage = e instanceof Error ? e.message : "Failed to load memory data" + if (errorMessage.includes("Authentication") || errorMessage.includes("ElasticSearch") || errorMessage.includes("connection")) { message.error(t('useMemory.memoryServiceConnectionError')) } else { message.error(t('useMemory.loadDataError')) @@ -117,7 +117,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: loadGroupsForActiveTab() }, [visible, memoryEnabled, activeTabKey, currentTenantId, currentUserId]) - /* --------------------------- 工具方法 --------------------------- */ + /* --------------------------- Utility Methods --------------------------- */ const toggleGroup = useCallback((key: string, enabled: boolean) => { setDisabledGroups((prev) => ({ ...prev, [key]: !enabled })) @@ -126,7 +126,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: const agentId = key.split("-").slice(-1)[0] if (!enabled) { - // 关闭 -> 添加到禁用列表 + // Disable -> Add to disabled list if (isAgentGroup) { addDisabledAgentId(agentId) disableAgentIdSet.current.add(agentId) @@ -135,7 +135,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: disableUserAgentIdSet.current.add(agentId) } } else { - // 开启 -> 从禁用列表移除 + // Enable -> Remove from disabled list if (isAgentGroup) { removeDisabledAgentId(agentId) disableAgentIdSet.current.delete(agentId) @@ -145,7 +145,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } } - // 关闭时折叠该 panel + // Collapse panel when disabled if (!enabled) { setOpenKey((prev) => (prev === key ? undefined : prev)) } @@ -185,14 +185,14 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } } - // 延迟工具:等待后端索引刷新后再重新拉取数据 + // Delay utility: Wait for backend index refresh before refetching data const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) - /* ------------------------------ 新增记忆相关方法 ------------------------------ */ + /* ------------------------------ New Memory Related Methods ------------------------------ */ const startAddingMemory = useCallback((groupKey: string) => { setAddingMemoryKey(groupKey) setNewMemoryContent("") - setOpenKey(groupKey) // 确保分组展开 + setOpenKey(groupKey) // Ensure group is expanded }, []) const cancelAddingMemory = useCallback(() => { @@ -205,7 +205,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: setIsAddingMemory(true) try { - // 根据当前页签和分组确定memory_level和agent_id + // Determine memory_level and agent_id based on current tab and group let memoryLevel = "" let agentId: string | undefined @@ -222,14 +222,14 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } const messages = [{ role: "user", content: newMemoryContent.trim() }] - // 前端手动触发infer=False避免调用LLM + // Frontend manually triggers infer=False to avoid calling LLM await addMemory(messages, memoryLevel, agentId, false) await delay(600); message.success(t('useMemory.addMemorySuccess')) cancelAddingMemory() - // 重新加载当前页签数据 + // Reload current tab data const loadGroupsForActiveTab = async () => { try { if (activeTabKey === "tenant") { @@ -252,7 +252,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: await loadGroupsForActiveTab() } catch (e) { console.error("Add memory error:", e) - const errorMessage = e instanceof Error ? e.message : "添加记忆失败" + const errorMessage = e instanceof Error ? e.message : "Failed to add memory" if (errorMessage.includes("Authentication") || errorMessage.includes("ElasticSearch")) { message.error(t('useMemory.memoryServiceConnectionError')) } else { @@ -263,7 +263,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } }, [addingMemoryKey, newMemoryContent, activeTabKey, currentTenantId, currentUserId]) - /* ------------------------------ 清空记忆相关方法 ------------------------------ */ + /* ------------------------------ Clear Memory Related Methods ------------------------------ */ const handleClearMemory = useCallback(async (groupKey: string, groupTitle: string) => { try { const { memoryLevel, agentId } = _computeMemoryParams(activeTabKey, groupKey) @@ -271,7 +271,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: await delay(300); message.success(t('useMemory.clearMemorySuccess', { groupTitle, count: result.deleted_count })) - // 重新加载当前页签数据 + // Reload current tab data const loadGroupsForActiveTab = async () => { try { if (activeTabKey === "tenant") { @@ -295,7 +295,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: await loadGroupsForActiveTab() } catch (e) { console.error("Clear memory error:", e) - const errorMessage = e instanceof Error ? e.message : "清空记忆失败" + const errorMessage = e instanceof Error ? e.message : "Failed to clear memory" if (errorMessage.includes("Authentication") || errorMessage.includes("ElasticSearch")) { message.error(t('useMemory.memoryServiceConnectionError')) } else { @@ -328,14 +328,14 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } }, [activeTabKey, currentTenantId, currentUserId]) - /* ---------------------- Tab 切换时展开第一个分组 ---------------------- */ + /* ---------------------- Expand first group when tab switches ---------------------- */ useEffect(() => { const groups = getGroupsForTab(activeTabKey).filter((g) => !disabledGroups[g.key]) setOpenKey(groups.length ? groups[0].key : undefined) // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeTabKey, disabledGroups]) - /* ----------------- 弹窗首次打开时展开当前 Tab 的首个分组 ---------------- */ + /* ----------------- Expand first group of current tab when modal first opens ---------------- */ useEffect(() => { if (visible) { const groups = getGroupsForTab(activeTabKey).filter((g) => !disabledGroups[g.key]) @@ -344,7 +344,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: // eslint-disable-next-line react-hooks/exhaustive-deps }, [visible, disabledGroups]) - /* ----------------- memoryEnabled 或 shareOption 变动时处理 ---------------- */ + /* ----------------- Handle when memoryEnabled or shareOption changes ---------------- */ useEffect(() => { if (!memoryEnabled && activeTabKey !== "base") { setActiveTabKey("base") @@ -357,7 +357,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: } }, [shareOption]) - // ----------------- 分页切换后保持 openKey 合法 ----------------- + // ----------------- Keep openKey valid after pagination switch ----------------- useEffect(() => { if (activeTabKey === "agentShared" || activeTabKey === "userAgent") { const groups = getGroupsForTab(activeTabKey).filter((g) => !disabledGroups[g.key]) @@ -371,7 +371,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeTabKey, pageMap]) - /* ------------------- 包装后的 setter ------------------- */ + /* ------------------- Wrapped setters ------------------- */ const setMemoryEnabled = useCallback((enabled: boolean) => { setMemoryEnabledState(enabled) setMemorySwitch(enabled).catch((e) => { @@ -468,7 +468,7 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: userAgentGroups, pageSize, getGroupsForTab, - // 新增记忆相关 + // New memory related addingMemoryKey, newMemoryContent, setNewMemoryContent, @@ -476,9 +476,9 @@ export function useMemory({ visible, currentUserId, currentTenantId, message }: startAddingMemory, cancelAddingMemory, confirmAddingMemory, - // 清空记忆相关 + // Clear memory related handleClearMemory, - // 删除记忆相关 + // Delete memory related handleDeleteMemory, } } diff --git a/frontend/hooks/useResponsiveTextSize.ts b/frontend/hooks/useResponsiveTextSize.ts index ea8b839ea..705d0884f 100644 --- a/frontend/hooks/useResponsiveTextSize.ts +++ b/frontend/hooks/useResponsiveTextSize.ts @@ -1,6 +1,6 @@ import { useState, useRef, useEffect } from "react"; -// 自定义Hook - 根据文本内容动态调整字体大小 +// Custom Hook - Dynamically adjust font size based on text content export const useResponsiveTextSize = (text: string, containerWidth: number, maxFontSize: number = 24) => { const [fontSize, setFontSize] = useState(maxFontSize); const textRef = useRef(null); @@ -12,11 +12,11 @@ export const useResponsiveTextSize = (text: string, containerWidth: number, maxF const element = textRef.current; if (!element) return; - // 从最大字体开始尝试 + // Start trying from maximum font size let currentSize = maxFontSize; element.style.fontSize = `${currentSize}px`; - // 如果文本溢出,减小字体大小直到适合 + // If text overflows, reduce font size until it fits while (element.scrollWidth > containerWidth && currentSize > 12) { currentSize -= 1; element.style.fontSize = `${currentSize}px`; @@ -25,10 +25,10 @@ export const useResponsiveTextSize = (text: string, containerWidth: number, maxF setFontSize(currentSize); }; - // 初始调整 + // Initial adjustment adjustFontSize(); - // 监听窗口大小变化 + // Listen for window size changes window.addEventListener('resize', adjustFontSize); return () => { diff --git a/frontend/lib/auth.ts b/frontend/lib/auth.ts index 640b29fbd..ac2e487bf 100644 --- a/frontend/lib/auth.ts +++ b/frontend/lib/auth.ts @@ -9,7 +9,7 @@ import { fetchWithErrorHandling } from "@/services/api"; import { STORAGE_KEYS } from "@/const/auth"; import { Session } from "@/types/auth"; -// 获取用户角色对应的颜色 +// Get color corresponding to user role export function getRoleColor(role: string): string { switch (role) { case "admin": @@ -20,20 +20,20 @@ export function getRoleColor(role: string): string { } } -// 根据邮箱生成头像 +// Generate avatar based on email export function generateAvatarUrl(email: string): string { - // 使用本地dicebear包生成头像 + // Use local dicebear package to generate avatar const avatar = createAvatar(initialsStyle, { seed: email, backgroundType: ['gradientLinear'] }); - // 返回SVG数据URI + // Return SVG data URI return avatar.toDataUri(); } /** - * 带有授权头的请求 + * Request with authorization headers */ export const fetchWithAuth = async (url: string, options: RequestInit = {}) => { const session = typeof window !== "undefined" ? localStorage.getItem(STORAGE_KEYS.SESSION) : null; @@ -46,7 +46,7 @@ export const fetchWithAuth = async (url: string, options: RequestInit = {}) => { ...options.headers, }; - // 使用带错误处理的请求拦截器 + // Use request interceptor with error handling return fetchWithErrorHandling(url, { ...options, headers, @@ -54,7 +54,7 @@ export const fetchWithAuth = async (url: string, options: RequestInit = {}) => { }; /** - * 保存会话到本地存储 + * Save session to local storage */ export const saveSessionToStorage = (session: Session) => { if (typeof window !== "undefined") { diff --git a/frontend/lib/avatar.tsx b/frontend/lib/avatar.tsx index 7a0390572..6b92ab7b0 100644 --- a/frontend/lib/avatar.tsx +++ b/frontend/lib/avatar.tsx @@ -4,30 +4,30 @@ import * as iconStyle from '@dicebear/icons'; import type { AppConfig } from '../types/modelConfig'; import { presetIcons } from "@/const/avatar" -// 基于种子的随机数生成器 +// Seeded random number generator class SeededRandom { private seed: number; constructor(seed: string) { - // 将字符串转换为数字种子 + // Convert string to numeric seed this.seed = Array.from(seed).reduce((acc, char) => { return acc + char.charCodeAt(0); }, 0); } - // 生成0到1之间的随机数 + // Generate random number between 0 and 1 random(): number { const x = Math.sin(this.seed++) * 10000; return x - Math.floor(x); } - // 生成指定范围内的随机整数 + // Generate random integer within specified range randomInt(min: number, max: number): number { return Math.floor(this.random() * (max - min + 1)) + min; } } -// 直接生成头像 URI 并返回 +// Directly generate avatar URI and return export const generateAvatarUri = (icon: string, color: string, size: number = 30, scale: number = 80): string => { const selectedIcon = presetIcons.find(preset => preset.key === icon) || presetIcons[0]; const mainColor = color.replace("#", ""); @@ -52,10 +52,10 @@ export const getAvatarUrl = (config: AppConfig, size: number = 30, scale: number // Return custom image URL return config.customIconUrl; } else if (config.avatarUri) { - // 如果存在预生成的 URI,直接返回 + // If pre-generated URI exists, return directly return config.avatarUri; } else { - // 默认返回第一个预设图标 + // Default return first preset icon const defaultIcon = presetIcons[0]; const mainColor = "2689cb"; const secondaryColor = generateComplementaryColor(mainColor); @@ -75,39 +75,39 @@ export const getAvatarUrl = (config: AppConfig, size: number = 30, scale: number }; /** - * 根据主色生成随机的配色 - * @param mainColor 主色(十六进制颜色值,可带可不带#前缀) - * @returns 生成的副色(十六进制颜色值,不带#前缀) + * Generate random complementary color based on main color + * @param mainColor Main color (hex color value, with or without # prefix) + * @returns Generated secondary color (hex color value, without # prefix) */ export const generateComplementaryColor = (mainColor: string): string => { - // 移除可能存在的#前缀 + // Remove possible # prefix const colorHex = mainColor.replace('#', ''); - // 将十六进制颜色转换为RGB + // Convert hex color to RGB const r = parseInt(colorHex.substring(0, 2), 16); const g = parseInt(colorHex.substring(2, 4), 16); const b = parseInt(colorHex.substring(4, 6), 16); - // 使用颜色值作为随机数种子 + // Use color value as random number seed const random = new SeededRandom(colorHex); - // 生成随机变化方向(几种常见的变化模式) + // Generate random variation direction (several common variation patterns) const variation = random.randomInt(0, 3); let newR = r, newG = g, newB = b; switch(variation) { - case 0: // 调暗 - 生成更深的颜色 + case 0: // Darken - generate darker color newR = Math.max(0, r - 40 - random.randomInt(0, 30)); newG = Math.max(0, g - 40 - random.randomInt(0, 30)); newB = Math.max(0, b - 40 - random.randomInt(0, 30)); break; - case 1: // 调亮 - 生成更亮的颜色 + case 1: // Brighten - generate brighter color newR = Math.min(255, r + 40 + random.randomInt(0, 30)); newG = Math.min(255, g + 40 + random.randomInt(0, 30)); newB = Math.min(255, b + 40 + random.randomInt(0, 30)); break; - case 2: // 相似色 - 微调RGB中的一个或两个通道 + case 2: // Similar color - fine-tune one or two RGB channels const channel = random.randomInt(0, 2); if (channel === 0) { newR = Math.min(255, Math.max(0, r + random.randomInt(0, 120) - 60)); @@ -117,9 +117,9 @@ export const generateComplementaryColor = (mainColor: string): string => { newB = Math.min(255, Math.max(0, b + random.randomInt(0, 120) - 60)); } break; - case 3: // HSL调整 - 转HSL后调整色相 + case 3: // HSL adjustment - convert to HSL then adjust hue const [h, s, l] = rgbToHsl(r, g, b); - const newH = (h + 0.05 + random.random() * 0.2) % 1; // 调整色相±30-90度 + const newH = (h + 0.05 + random.random() * 0.2) % 1; // Adjust hue ±30-90 degrees const [adjR, adjG, adjB] = hslToRgb(newH, s, l); newR = adjR; newG = adjG; @@ -127,16 +127,16 @@ export const generateComplementaryColor = (mainColor: string): string => { break; } - // 确保RGB值在有效范围内 + // Ensure RGB values are within valid range newR = Math.min(255, Math.max(0, Math.round(newR))); newG = Math.min(255, Math.max(0, Math.round(newG))); newB = Math.min(255, Math.max(0, Math.round(newB))); - // 转回十六进制 + // Convert back to hexadecimal return ((1 << 24) + (newR << 16) + (newG << 8) + newB).toString(16).slice(1); } -// 辅助函数: RGB转HSL +// Helper function: RGB to HSL function rgbToHsl(r: number, g: number, b: number): [number, number, number] { r /= 255; g /= 255; @@ -162,12 +162,12 @@ function rgbToHsl(r: number, g: number, b: number): [number, number, number] { return [h, s, l]; } -// 辅助函数: HSL转RGB +// Helper function: HSL to RGB function hslToRgb(h: number, s: number, l: number): [number, number, number] { let r, g, b; if (s === 0) { - r = g = b = l; // 灰色 + r = g = b = l; // Gray } else { const hue2rgb = (p: number, q: number, t: number) => { if (t < 0) t += 1; @@ -190,39 +190,39 @@ function hslToRgb(h: number, s: number, l: number): [number, number, number] { } /** - * 从Dicebear生成的Data URI中提取主色和次色,预留给app名称使用 - * @param dataUri Dicebear生成的头像data URI - * @returns 包含mainColor和secondaryColor的对象,颜色值不含#前缀 + * Extract main and secondary colors from Dicebear generated Data URI, reserved for app name use + * @param dataUri Dicebear generated avatar data URI + * @returns Object containing mainColor and secondaryColor, color values without # prefix */ export const extractColorsFromUri = (dataUri: string): { mainColor: string | null, secondaryColor: string | null } => { - // 默认返回值 + // Default return value const result = { mainColor: "", secondaryColor: "" }; try { - // 检查是否是Data URI + // Check if it's a Data URI if (!dataUri || !dataUri.startsWith('data:')) { return result; } - // 提取Base64或URL编码的内容 + // Extract Base64 or URL encoded content let svgContent = ''; if (dataUri.includes('base64')) { - // 处理Base64编码 + // Handle Base64 encoding const base64Content = dataUri.split(',')[1]; - svgContent = atob(base64Content); // 解码Base64 + svgContent = atob(base64Content); // Decode Base64 } else { - // 处理URL编码 + // Handle URL encoding const uriContent = dataUri.split(',')[1]; svgContent = decodeURIComponent(uriContent); } - // 查找线性渐变定义 + // Find linear gradient definition const gradientMatch = svgContent.match(/]*>([\s\S]*?)<\/linearGradient>/); if (!gradientMatch) { - // 如果没有渐变,查找背景填充色 + // If no gradient, find background fill color const fillMatch = svgContent.match(/fill="(#[0-9a-fA-F]{6})"/); if (fillMatch && fillMatch[1]) { result.mainColor = fillMatch[1].replace('#', ''); @@ -230,7 +230,7 @@ export const extractColorsFromUri = (dataUri: string): { mainColor: string | nul return result; } - // 提取渐变中的颜色 + // Extract colors from gradient const stopMatches = svgContent.matchAll(/]*stop-color="(#[0-9a-fA-F]{6})"[^>]*>/g); const colors: string[] = []; @@ -240,7 +240,7 @@ export const extractColorsFromUri = (dataUri: string): { mainColor: string | nul } } - // 通常第一个是主色,第二个是次色 + // Usually first is main color, second is secondary color if (colors.length >= 1) { result.mainColor = colors[0]; } @@ -249,7 +249,7 @@ export const extractColorsFromUri = (dataUri: string): { mainColor: string | nul } } catch (error) { - console.error('提取颜色时出错:', error); + console.error('Error extracting colors:', error); } return result; diff --git a/frontend/lib/config.ts b/frontend/lib/config.ts index e1fb1859c..0bc22f7dd 100644 --- a/frontend/lib/config.ts +++ b/frontend/lib/config.ts @@ -38,7 +38,7 @@ class ConfigStoreClass { } } - // 深度合并配置 + // Deep merge configuration private deepMerge(target: T, source: Partial): T { if (!source) return target; @@ -58,7 +58,7 @@ class ConfigStoreClass { return result; } - // 从存储加载配置 + // Load configuration from storage private loadFromStorage(): GlobalConfig { try { // Check if we're in browser environment @@ -112,7 +112,7 @@ class ConfigStoreClass { } } - // 保存配置到存储 + // Save configuration to storage private saveToStorage(): void { try { if (typeof window === 'undefined' || !this.config) return; @@ -124,53 +124,53 @@ class ConfigStoreClass { } } - // 确保配置已初始化 + // Ensure configuration is initialized private ensureConfig(): void { if (!this.config) { this.initializeConfig(); } } - // 获取完整配置 + // Get complete configuration getConfig(): GlobalConfig { this.ensureConfig(); return this.config!; } - // 更新完整配置 + // Update complete configuration updateConfig(partial: Partial): void { this.ensureConfig(); this.config = this.deepMerge(this.config!, partial); this.saveToStorage(); } - // 获取应用配置 + // Get application configuration getAppConfig(): AppConfig { this.ensureConfig(); return this.config!.app; } - // 更新应用配置 + // Update application configuration updateAppConfig(partial: Partial): void { this.ensureConfig(); this.config!.app = this.deepMerge(this.config!.app, partial); this.saveToStorage(); } - // 获取模型配置 + // Get model configuration getModelConfig(): ModelConfig { this.ensureConfig(); return this.config!.models; } - // 更新模型配置 + // Update model configuration updateModelConfig(partial: Partial): void { this.ensureConfig(); this.config!.models = this.deepMerge(this.config!.models, partial); this.saveToStorage(); } - // 清除所有配置 + // Clear all configuration clearConfig(): void { if (typeof window !== 'undefined') { localStorage.removeItem(APP_CONFIG_KEY); @@ -179,9 +179,9 @@ class ConfigStoreClass { this.config = JSON.parse(JSON.stringify(defaultConfig)); } - // 新增:后端配置转前端localStorage结构 + // New: Backend configuration to frontend localStorage structure static transformBackend2Frontend(backendConfig: any): GlobalConfig { - // 适配 app 字段 + // Adapt app field const app = backendConfig.app ? { appName: backendConfig.app.name || "", @@ -198,7 +198,7 @@ class ConfigStoreClass { avatarUri: null }; - // 适配 models 字段 + // Adapt models field const models = backendConfig.models ? { llm: { modelName: backendConfig.models.llm?.name || "", @@ -270,7 +270,7 @@ class ConfigStoreClass { } as GlobalConfig; } - // 新增:从localStorage重新加载配置并触发configChanged事件 + // New: Reload configuration from localStorage and trigger configChanged event reloadFromStorage(): void { this.config = this.loadFromStorage(); if (typeof window !== 'undefined') { @@ -282,8 +282,8 @@ class ConfigStoreClass { } // TODO: Why not use just one singleton pattern? -// 导出类作为 ConfigStore +// Export class as ConfigStore export const ConfigStore = ConfigStoreClass; -// 导出单例 +// Export singleton export const configStore = ConfigStoreClass.getInstance(); \ No newline at end of file diff --git a/frontend/services/agentConfigService.ts b/frontend/services/agentConfigService.ts index 0e11ca88e..c8e3cc6b0 100644 --- a/frontend/services/agentConfigService.ts +++ b/frontend/services/agentConfigService.ts @@ -14,7 +14,7 @@ export const fetchTools = async () => { headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -26,7 +26,7 @@ export const fetchTools = async () => { source: tool.source, is_available: tool.is_available, create_time: tool.create_time, - usage: tool.usage, // 新增:处理usage字段 + usage: tool.usage, // New: handle usage field initParams: tool.params.map((param: any) => { return { name: param.name, @@ -44,7 +44,7 @@ export const fetchTools = async () => { message: "", }; } catch (error) { - console.error("获取工具列表出错:", error); + console.error("Error fetching tool list:", error); return { success: false, data: [], @@ -63,7 +63,7 @@ export const fetchAgentList = async () => { headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -82,7 +82,7 @@ export const fetchAgentList = async () => { message: "", }; } catch (error) { - console.error("获取 agent 列表失败:", error); + console.error("Failed to fetch agent list:", error); return { success: false, data: [], @@ -104,7 +104,7 @@ export const getCreatingSubAgentId = async () => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -127,7 +127,7 @@ export const getCreatingSubAgentId = async () => { message: "", }; } catch (error) { - console.error("获取创建子代理ID失败:", error); + console.error("Failed to get creating sub agent ID:", error); return { success: false, data: null, @@ -163,21 +163,21 @@ export const updateToolConfig = async ( }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); return { success: true, data: data, - message: "工具配置更新成功", + message: "Tool configuration updated successfully", }; } catch (error) { - console.error("更新工具配置失败:", error); + console.error("Failed to update tool configuration:", error); return { success: false, data: null, - message: "更新工具配置失败,请稍后重试", + message: "Failed to update tool configuration, please try again later", }; } }; @@ -200,7 +200,7 @@ export const searchToolConfig = async (toolId: number, agentId: number) => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -213,24 +213,24 @@ export const searchToolConfig = async (toolId: number, agentId: number) => { message: "", }; } catch (error) { - console.error("搜索工具配置失败:", error); + console.error("Failed to search tool configuration:", error); return { success: false, data: null, - message: "搜索工具配置失败,请稍后重试", + message: "Failed to search tool configuration, please try again later", }; } }; /** - * 更新 Agent 信息 + * Update Agent information * @param agentId agent id - * @param name agent 名称 - * @param description agent 描述 - * @param modelName 模型名称 - * @param maxSteps 最大步骤数 - * @param provideRunSummary 是否提供运行摘要 - * @returns 更新结果 + * @param name agent name + * @param description agent description + * @param modelName model name + * @param maxSteps maximum steps + * @param provideRunSummary whether to provide run summary + * @returns update result */ export const updateAgent = async ( agentId: number, @@ -267,29 +267,29 @@ export const updateAgent = async ( }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); return { success: true, data: data, - message: "Agent 更新成功", + message: "Agent updated successfully", }; } catch (error) { - console.error("更新 Agent 失败:", error); + console.error("Failed to update Agent:", error); return { success: false, data: null, - message: "更新 Agent 失败,请稍后重试", + message: "Failed to update Agent, please try again later", }; } }; /** - * 删除 Agent + * Delete Agent * @param agentId agent id - * @returns 删除结果 + * @returns delete result */ export const deleteAgent = async (agentId: number) => { try { @@ -300,18 +300,18 @@ export const deleteAgent = async (agentId: number) => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } return { success: true, - message: "Agent 删除成功", + message: "Agent deleted successfully", }; } catch (error) { - console.error("删除 Agent 失败:", error); + console.error("Failed to delete Agent:", error); return { success: false, - message: "删除 Agent 失败,请稍后重试", + message: "Failed to delete Agent, please try again later", }; } }; @@ -330,7 +330,7 @@ export const exportAgent = async (agentId: number) => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -345,15 +345,15 @@ export const exportAgent = async (agentId: number) => { return { success: false, data: null, - message: data.message || "导出失败", + message: data.message || "Export failed", }; } } catch (error) { - console.error("导出 Agent 失败:", error); + console.error("Failed to export Agent:", error); return { success: false, data: null, - message: "导出失败,请稍后重试", + message: "Export failed, please try again later", }; } }; @@ -375,21 +375,21 @@ export const importAgent = async (agentInfo: any) => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); return { success: true, data: data, - message: "Agent 导入成功", + message: "Agent imported successfully", }; } catch (error) { - console.error("导入 Agent 失败:", error); + console.error("Failed to import Agent:", error); return { success: false, data: null, - message: "导入 Agent 失败,请稍后重试", + message: "Failed to import Agent, please try again later", }; } }; @@ -408,7 +408,7 @@ export const searchAgentInfo = async (agentId: number) => { }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -428,7 +428,7 @@ export const searchAgentInfo = async (agentId: number) => { provide_run_summary: data.provide_run_summary, enabled: data.enabled, is_available: data.is_available, - sub_agent_id_list: data.sub_agent_id_list || [], // 添加sub_agent_id_list + sub_agent_id_list: data.sub_agent_id_list || [], // Add sub_agent_id_list tools: data.tools ? data.tools.map((tool: any) => { const params = @@ -441,7 +441,7 @@ export const searchAgentInfo = async (agentId: number) => { description: tool.description, source: tool.source, is_available: tool.is_available, - usage: tool.usage, // 新增:处理usage字段 + usage: tool.usage, // New: handle usage field initParams: Array.isArray(params) ? params.map((param: any) => ({ name: param.name, @@ -462,7 +462,7 @@ export const searchAgentInfo = async (agentId: number) => { message: "", }; } catch (error) { - console.error("获取Agent详情失败:", error); + console.error("Failed to get Agent details:", error); return { success: false, data: null, @@ -481,7 +481,7 @@ export const fetchAllAgents = async () => { headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -500,7 +500,7 @@ export const fetchAllAgents = async () => { message: "", }; } catch (error) { - console.error("获取所有Agent列表失败:", error); + console.error("Failed to get all Agent list:", error); return { success: false, data: [], @@ -535,12 +535,12 @@ export const addRelatedAgent = async ( return { success: true, data: data, - message: data[0] || "添加关联Agent成功", + message: data[0] || "Successfully added related Agent", status: response.status, }; } else { const errorMessage = - data.detail || data[0] || `添加关联Agent失败: ${response.statusText}`; + data.detail || data[0] || `Failed to add related Agent: ${response.statusText}`; return { success: false, data: null, @@ -549,11 +549,11 @@ export const addRelatedAgent = async ( }; } } catch (error) { - console.error("添加关联Agent失败:", error); + console.error("Failed to add related Agent:", error); return { success: false, data: null, - message: "添加关联Agent失败,请稍后重试", + message: "Failed to add related Agent, please try again later", status: 500, // or a custom error code }; } @@ -580,7 +580,7 @@ export const deleteRelatedAgent = async ( }); if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); + throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); @@ -591,11 +591,11 @@ export const deleteRelatedAgent = async ( message: "", }; } catch (error) { - console.error("删除关联Agent失败:", error); + console.error("Failed to delete related Agent:", error); return { success: false, data: null, - message: "删除关联Agent失败,请稍后重试", + message: "Failed to delete related Agent, please try again later", }; } }; diff --git a/frontend/services/api.ts b/frontend/services/api.ts index 562741651..0b135c4ba 100644 --- a/frontend/services/api.ts +++ b/frontend/services/api.ts @@ -145,79 +145,79 @@ export class ApiError extends Error { } } -// API请求拦截器 +// API request interceptor export const fetchWithErrorHandling = async (url: string, options: RequestInit = {}) => { try { const response = await fetch(url, options); - // 处理HTTP错误 + // Handle HTTP errors if (!response.ok) { - // 检查是否为会话过期错误 (401) + // Check if it's a session expired error (401) if (response.status === 401) { handleSessionExpired(); - throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "登录已过期,请重新登录"); + throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "Login expired, please login again"); } - // 处理自定义499错误码 (客户端关闭连接) + // Handle custom 499 error code (client closed connection) if (response.status === 499) { handleSessionExpired(); - throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "连接已断开,会话可能已过期"); + throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "Connection disconnected, session may have expired"); } - // 其他HTTP错误 + // Other HTTP errors const errorText = await response.text(); - throw new ApiError(response.status, errorText || `请求失败: ${response.status}`); + throw new ApiError(response.status, errorText || `Request failed: ${response.status}`); } return response; } catch (error) { - // 处理网络错误 + // Handle network errors if (error instanceof TypeError && error.message.includes('NetworkError')) { - console.error('网络错误:', error); - throw new ApiError(STATUS_CODES.SERVER_ERROR, "网络连接错误,请检查网络连接"); + console.error('Network error:', error); + throw new ApiError(STATUS_CODES.SERVER_ERROR, "Network connection error, please check your network connection"); } - // 处理连接重置错误 + // Handle connection reset errors if (error instanceof TypeError && error.message.includes('Failed to fetch')) { - console.error('连接错误:', error); + console.error('Connection error:', error); - // 对于用户管理相关的请求,可能是登录过期 + // For user management related requests, it might be login expiration if (url.includes('/user/session') || url.includes('/user/current_user_id')) { handleSessionExpired(); - throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "连接已断开,会话可能已过期"); + throw new ApiError(STATUS_CODES.TOKEN_EXPIRED, "Connection disconnected, session may have expired"); } else { - throw new ApiError(STATUS_CODES.SERVER_ERROR, "服务器连接错误,请稍后重试"); + throw new ApiError(STATUS_CODES.SERVER_ERROR, "Server connection error, please try again later"); } } - // 重新抛出其他错误 + // Re-throw other errors throw error; } }; -// 处理会话过期的方法 +// Method to handle session expiration function handleSessionExpired() { - // 防止重复触发 + // Prevent duplicate triggers if (window.__isHandlingSessionExpired) { return; } - // 标记正在处理中 + // Mark as processing window.__isHandlingSessionExpired = true; - // 清除本地存储的会话信息 + // Clear locally stored session information if (typeof window !== "undefined") { localStorage.removeItem("session"); - // 使用自定义事件通知应用中的其他组件(如SessionExpiredListener) + // Use custom events to notify other components in the app (such as SessionExpiredListener) if (window.dispatchEvent) { - // 确保使用与EVENTS.SESSION_EXPIRED常量一致的事件名 + // Ensure using event name consistent with EVENTS.SESSION_EXPIRED constant window.dispatchEvent(new CustomEvent('session-expired', { - detail: { message: "登录已过期,请重新登录" } + detail: { message: "Login expired, please login again" } })); } - // 300ms后重置标记,允许将来再次触发 + // Reset flag after 300ms to allow future triggers setTimeout(() => { window.__isHandlingSessionExpired = false; }, 300); diff --git a/frontend/services/authService.ts b/frontend/services/authService.ts index 60b6f3ed1..38c8d61d2 100644 --- a/frontend/services/authService.ts +++ b/frontend/services/authService.ts @@ -1,5 +1,5 @@ /** - * 认证服务 + * Authentication service */ import { USER_ROLES } from "@/const/modelConfig"; import { API_ENDPOINTS } from "@/services/api"; diff --git a/frontend/services/configService.ts b/frontend/services/configService.ts index 054b2e818..f32b7f905 100644 --- a/frontend/services/configService.ts +++ b/frontend/services/configService.ts @@ -19,7 +19,7 @@ export class ConfigService { if (!response.ok) { const errorData = await response.json(); - console.error('保存配置失败:', errorData); + console.error('Failed to save configuration:', errorData); return false; } @@ -27,7 +27,7 @@ export class ConfigService { const result = await response.json(); return true; } catch (error) { - console.error('保存配置请求异常:', error); + console.error('Save configuration request exception:', error); return false; } } @@ -41,7 +41,7 @@ export class ConfigService { }); if (!response.ok) { const errorData = await response.json(); - console.error('加载配置失败:', errorData); + console.error('Failed to load configuration:', errorData); return false; } const result = await response.json(); @@ -58,7 +58,7 @@ export class ConfigService { localStorage.setItem('model', JSON.stringify(frontendConfig.models)); } - // 触发配置重新加载并派发事件 + // Trigger configuration reload and dispatch event if (typeof window !== 'undefined') { const configStore = ConfigStore.getInstance(); configStore.reloadFromStorage(); @@ -68,7 +68,7 @@ export class ConfigService { } return false; } catch (error) { - console.error('加载配置请求异常:', error); + console.error('Load configuration request exception:', error); return false; } } diff --git a/frontend/services/knowledgeBaseService.ts b/frontend/services/knowledgeBaseService.ts index aa87cded5..7113d2dab 100644 --- a/frontend/services/knowledgeBaseService.ts +++ b/frontend/services/knowledgeBaseService.ts @@ -14,7 +14,7 @@ class KnowledgeBaseService { // Check Elasticsearch health (force refresh, no caching for setup page) async checkHealth(): Promise { try { - // 在 setup 页面中强制刷新,不使用缓存 + // Force refresh in setup page, no caching const response = await fetch(API_ENDPOINTS.knowledgeBase.health, { headers: getAuthHeaders() }); @@ -22,12 +22,12 @@ class KnowledgeBaseService { const isHealthy = data.status === "healthy" && data.elasticsearch === "connected"; - // 不再更新缓存,每次都获取最新状态 + // No longer update cache, get latest status every time return isHealthy; } catch (error) { - console.error("Elasticsearch健康检查失败:", error); - // 不再缓存错误状态 + console.error("Elasticsearch health check failed:", error); + // No longer cache error status return false; } } @@ -168,9 +168,9 @@ class KnowledgeBaseService { }); const result = await response.json(); - // 修改判断逻辑,后端返回status字段而不是success字段 + // Modify judgment logic, backend returns status field instead of success field if (result.status !== "success") { - throw new Error(result.message || "创建知识库失败"); + throw new Error(result.message || "Failed to create knowledge base"); } // Create a full KnowledgeBase object with default values @@ -378,7 +378,7 @@ class KnowledgeBaseService { throw new Error('Response body is null'); } - // 处理流式响应 + // Handle streaming response const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let summary = ''; @@ -387,23 +387,23 @@ class KnowledgeBaseService { const { done, value } = await reader.read(); if (done) break; - // 解码二进制数据为文本 + // Decode binary data to text const chunk = decoder.decode(value, { stream: true }); - // 处理SSE格式的数据 + // Handle SSE format data const lines = chunk.split('\n\n'); for (const line of lines) { if (line.trim().startsWith('data:')) { try { - // 提取JSON数据 + // Extract JSON data const jsonStr = line.substring(line.indexOf('{')); const data = JSON.parse(jsonStr); if (data.status === 'success') { - // 累加消息部分到摘要 + // Accumulate message part to summary summary += data.message; - // 如果提供了进度回调,则调用它 + // If progress callback is provided, call it if (onProgress) { onProgress(data.message); } @@ -411,7 +411,7 @@ class KnowledgeBaseService { throw new Error(data.message); } } catch (e) { - console.error('解析SSE数据失败:', e, line); + console.error('Failed to parse SSE data:', e, line); } } } diff --git a/frontend/services/mcpService.ts b/frontend/services/mcpService.ts index 050a4ac53..bb331eea8 100644 --- a/frontend/services/mcpService.ts +++ b/frontend/services/mcpService.ts @@ -22,7 +22,7 @@ const getAuthHeaders = () => { }; /** - * 获取MCP服务器列表 + * Get MCP server list */ export const getMcpServerList = async () => { try { @@ -34,7 +34,7 @@ export const getMcpServerList = async () => { if (response.ok && data.status === 'success') { - // 转换后端字段名称为前端期望的格式 + // Convert backend field names to frontend expected format const formattedData = (data.remote_mcp_server_list || []).map((server: any) => { return { service_name: server.remote_mcp_server_name, @@ -49,7 +49,7 @@ export const getMcpServerList = async () => { message: '' }; } else { - // 根据HTTP状态码处理具体的错误信息 + // Handle specific error information based on HTTP status code let errorMessage = data.message || t('mcpService.message.getServerListFailed'); switch (response.status) { @@ -80,7 +80,7 @@ export const getMcpServerList = async () => { }; /** - * 添加MCP服务器 + * Add MCP server */ export const addMcpServer = async (mcpUrl: string, serviceName: string) => { try { @@ -101,7 +101,7 @@ export const addMcpServer = async (mcpUrl: string, serviceName: string) => { message: data.message || t('mcpService.message.addServerSuccess') }; } else { - // 处理具体的错误状态码和错误信息 + // Handle specific error status codes and error information let errorMessage = data.message || t('mcpService.message.addServerFailed'); if (response.status === 409) { @@ -129,7 +129,7 @@ export const addMcpServer = async (mcpUrl: string, serviceName: string) => { }; /** - * 删除MCP服务器 + * Delete MCP server */ export const deleteMcpServer = async (mcpUrl: string, serviceName: string) => { try { @@ -150,7 +150,7 @@ export const deleteMcpServer = async (mcpUrl: string, serviceName: string) => { message: data.message || t('mcpService.message.deleteServerSuccess') }; } else { - // 根据HTTP状态码处理具体的错误信息 + // Handle specific error information based on HTTP status code let errorMessage = data.message || t('mcpService.message.deleteServerFailed'); switch (response.status) { @@ -178,7 +178,7 @@ export const deleteMcpServer = async (mcpUrl: string, serviceName: string) => { }; /** - * 获取远程MCP服务器的工具列表 + * Get tool list from remote MCP server */ export const getMcpTools = async (serviceName: string, mcpUrl: string) => { try { @@ -199,7 +199,7 @@ export const getMcpTools = async (serviceName: string, mcpUrl: string) => { message: '' }; } else { - // 根据HTTP状态码处理具体的错误信息 + // Handle specific error information based on HTTP status code let errorMessage = data.message || t('mcpService.message.getToolsFailed'); switch (response.status) { @@ -247,7 +247,7 @@ export const updateToolList = async () => { message: data.message || t('mcpService.message.updateToolListSuccess') }; } else { - // 根据HTTP状态码处理具体的错误信息 + // Handle specific error information based on HTTP status code let errorMessage = data.message || t('mcpService.message.updateToolListFailed'); switch (response.status) { diff --git a/frontend/services/memoryService.ts b/frontend/services/memoryService.ts index 164fcf813..ea15eb971 100644 --- a/frontend/services/memoryService.ts +++ b/frontend/services/memoryService.ts @@ -21,7 +21,7 @@ function getFriendlyErrorMessage(raw: string): string { // ignore JSON parse errors } - // 关键字映射到用户友好的中文提示 + // Keyword mapping to user-friendly Chinese prompts if (/AuthenticationException/i.test(msg)) { return "ElasticSearch数据库鉴权失败" } else if (/ConnectionTimeout/i.test(msg)) { @@ -215,13 +215,13 @@ export async function fetchTenantSharedGroup(): Promise { } export async function fetchAgentSharedGroups(): Promise { - // 并行请求:记忆列表 + 全量 Agent 清单 + // Parallel requests: memory list + full Agent list const [{ items }, agentsRes] = await Promise.all([ listMemories("agent"), fetchAllAgents(), ]) - // 先把有记忆的结果按照 agent_id 分组 + // First group results with memories by agent_id const groupMap: Record = {} items.forEach((item) => { if (!item.agent_id) return @@ -229,12 +229,12 @@ export async function fetchAgentSharedGroups(): Promise { groupMap[item.agent_id].push(item) }) - // 后续需要补全“无记忆”的 Agent 分组 + // Need to complete "no memory" Agent groups later const agentList: Array<{ agent_id: string; name?: string; display_name?: string }> = (agentsRes as any)?.success ? (agentsRes as any).data : [] const groups: MemoryGroup[] = [] - // 按照 Agent 清单顺序构建分组,保证完整性 + // Build groups in Agent list order to ensure completeness agentList.forEach((agent) => { const agentId = agent.agent_id const list = groupMap[agentId] || [] @@ -245,7 +245,7 @@ export async function fetchAgentSharedGroups(): Promise { }) }) - // 若依然没有任何 Agent 信息,则返回占位分组 + // If still no Agent information, return placeholder group if (groups.length === 0) { return [ { @@ -269,7 +269,7 @@ export async function fetchUserPersonalGroup(): Promise { } export async function fetchUserAgentGroups(): Promise { - // 并行请求:用户记忆 + 全量 Agent 清单 + // Parallel requests: user memory + full Agent list const [{ items }, agentsRes] = await Promise.all([ listMemories("user_agent"), fetchAllAgents(), diff --git a/frontend/services/modelEngineService.ts b/frontend/services/modelEngineService.ts index 8bb314359..f29936027 100644 --- a/frontend/services/modelEngineService.ts +++ b/frontend/services/modelEngineService.ts @@ -36,8 +36,8 @@ const modelEngineService = { status = CONNECTION_STATUS.ERROR } } catch (parseError) { - // JSON parsing failed,视为连接失败 - console.error("响应数据解析失败:", parseError) + // JSON parsing failed, treat as connection failure + console.error("Response data parsing failed:", parseError) status = CONNECTION_STATUS.ERROR } } else { @@ -49,7 +49,7 @@ const modelEngineService = { lastChecked: new Date().toLocaleTimeString() } } catch (error) { - console.error("检查ModelEngine连接状态失败:", error) + console.error("Failed to check ModelEngine connection status:", error) return { status: CONNECTION_STATUS.ERROR, lastChecked: new Date().toLocaleTimeString() diff --git a/frontend/services/uploadService.ts b/frontend/services/uploadService.ts index 5c69c75df..5ea4e8dfa 100644 --- a/frontend/services/uploadService.ts +++ b/frontend/services/uploadService.ts @@ -6,23 +6,23 @@ import { AbortableError } from '../types/knowledgeBase'; import '../app/[locale]/i18n'; -// 新的检查知识库名称状态的方法 +// New method to check knowledge base name status export const checkKnowledgeBaseName = async ( knowledgeBaseName: string, t: TFunction ): Promise<{status: string, action?: string}> => { try { - // 调用新的service方法 + // Call new service method return await knowledgeBaseService.checkKnowledgeBaseName(knowledgeBaseName); } catch (error) { console.error(t('knowledgeBase.check.nameError'), error); - // 返回一个表示检查失败的状态 + // Return a status indicating check failure return { status: NAME_CHECK_STATUS.CHECK_FAILED }; } }; -// 获取知识库文档信息 +// Get knowledge base document information export const fetchKnowledgeBaseInfo = async ( indexName: string, abortController: AbortController, @@ -46,7 +46,7 @@ export const fetchKnowledgeBaseInfo = async ( } }; -// 文件类型验证 +// File type validation export const validateFileType = (file: File, t: TFunction, message: any): boolean => { const validTypes = [ 'application/pdf', @@ -59,10 +59,10 @@ export const validateFileType = (file: File, t: TFunction, message: any): boolea 'application/csv' ]; - // 先判断 MIME type + // First check MIME type let isValidType = validTypes.includes(file.type); - // 如果 MIME type 为空或不在列表里,再根据文件名后缀判断 + // If MIME type is empty or not in the list, check by file extension if (!isValidType) { const name = file.name.toLowerCase(); if ( diff --git a/frontend/services/userConfigService.ts b/frontend/services/userConfigService.ts index 2d20f3f87..eb49cdc4b 100644 --- a/frontend/services/userConfigService.ts +++ b/frontend/services/userConfigService.ts @@ -6,7 +6,7 @@ import { fetchWithAuth, getAuthHeaders } from '@/lib/auth'; const fetch = fetchWithAuth; export class UserConfigService { - // 获取用户选中的知识库列表 + // Get user selected knowledge base list async loadKnowledgeList(): Promise { try { const response = await fetch(API_ENDPOINTS.tenantConfig.loadKnowledgeList, { @@ -28,7 +28,7 @@ export class UserConfigService { } } - // 更新用户选中的知识库列表 + // Update user selected knowledge base list async updateKnowledgeList(knowledgeList: string[]): Promise { try { const response = await fetch(API_ENDPOINTS.tenantConfig.updateKnowledgeList, { @@ -49,5 +49,5 @@ export class UserConfigService { } } -// 导出单例实例 +// Export singleton instance export const userConfigService = new UserConfigService(); \ No newline at end of file diff --git a/frontend/styles/globals.css b/frontend/styles/globals.css index 1b8a8aa7a..f644119a2 100644 --- a/frontend/styles/globals.css +++ b/frontend/styles/globals.css @@ -138,7 +138,7 @@ scrollbar-color: rgba(0, 0, 0, 0.4) transparent; } -/* Tool Pool Tabs 滚动修复 */ +/* Tool Pool Tabs scroll fix */ .tool-pool-tabs .ant-tabs-content-holder { overflow: hidden; height: 100%; @@ -153,13 +153,13 @@ overflow: hidden; } -/* 确保工具池tabs内容区域可以滚动 */ +/* Ensure tool pool tabs content area can scroll */ .tool-pool-tabs .ant-tabs-content-holder .ant-tabs-content .ant-tabs-tabpane > div { height: 100%; max-height: 100%; } -/* 调整tabs内容区域的左右内边距 - 使用更强的选择器 */ +/* Adjust tabs content area left and right padding - use stronger selector */ .tool-pool-tabs.ant-tabs.ant-tabs-left > .ant-tabs-content-holder { padding-left: 12px !important; padding-right: 9px !important; @@ -175,7 +175,7 @@ padding-left: 0 !important; } -/* 缩短标签的内外边距,使标签更紧凑 */ +/* Reduce tab inner and outer margins to make tabs more compact */ .tool-pool-tabs .ant-tabs-tab { padding: 12px 6px !important; margin: 4px 2px !important; diff --git a/frontend/styles/milkdown-nord.css b/frontend/styles/milkdown-nord.css index c75833de6..a501853ae 100644 --- a/frontend/styles/milkdown-nord.css +++ b/frontend/styles/milkdown-nord.css @@ -297,8 +297,8 @@ white-space: pre-wrap; outline: none; padding: 0.75rem; - height: 100%; /* 填满父容器 */ - overflow-y: auto; /* 支持垂直滚动 */ + height: 100%; /* Fill parent container */ + overflow-y: auto; /* Support vertical scrolling */ } /* Focus and Selection Styles */ diff --git a/frontend/types/agentConfig.ts b/frontend/types/agentConfig.ts index 0c8a0b1ad..4c31bfa7d 100644 --- a/frontend/types/agentConfig.ts +++ b/frontend/types/agentConfig.ts @@ -63,7 +63,7 @@ export interface ToolGroup { tools: Tool[]; } -// 树形结构节点类型 +// Tree structure node type export interface TreeNodeDatum { name: string; type?: string; diff --git a/frontend/types/auth.ts b/frontend/types/auth.ts index 4cc381cb5..5e5808b9f 100644 --- a/frontend/types/auth.ts +++ b/frontend/types/auth.ts @@ -21,6 +21,7 @@ export interface ErrorResponse { data?: any; } +// Authorization context type // Auth form values interface export interface AuthFormValues { email: string;