From 9e195ea63b264d2643354c23e8c083a5e4d84d20 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 3 Feb 2026 18:38:04 +0800 Subject: [PATCH 1/2] style(web): translate the comments in the src/views directory into English --- .../components/ApiKeyDetailModal.tsx | 26 +- .../components/ApiKeyModal.tsx | 43 ++- web/src/views/ApiKeyManagement/index.tsx | 37 +- web/src/views/ApiKeyManagement/types.ts | 43 ++- web/src/views/ApiParameters/index.tsx | 75 ---- web/src/views/ApiParameters/types.ts | 22 -- web/src/views/ApplicationConfig/Agent.tsx | 82 +++- web/src/views/ApplicationConfig/Api.tsx | 35 +- web/src/views/ApplicationConfig/Cluster.tsx | 40 +- .../views/ApplicationConfig/ReleasePage.tsx | 22 ++ .../views/ApplicationConfig/Statistics.tsx | 22 ++ .../components/AiPromptModal.tsx | 33 +- .../components/AiPromptVariableModal.tsx | 33 +- .../components/ApiKeyConfigModal.tsx | 31 +- .../components/ApiKeyModal.tsx | 30 +- .../ApplicationConfig/components/Card.tsx | 18 + .../ApplicationConfig/components/Chat.tsx | 36 ++ .../components/ChatVariableConfigModal.tsx | 25 +- .../components/ConfigHeader.tsx | 56 +++ .../components/CopyModal.tsx | 27 +- .../components/Editor/index.tsx | 44 +++ .../Editor/plugin/InitialValuePlugin.tsx | 22 +- .../Editor/plugin/InsertTextPlugin.tsx | 18 +- .../Editor/plugin/LineBreakPlugin.tsx | 18 +- .../components/Knowledge/Knowledge.tsx | 27 +- .../Knowledge/KnowledgeConfigModal.tsx | 38 +- .../Knowledge/KnowledgeGlobalConfigModal.tsx | 28 +- .../Knowledge/KnowledgeListModal.tsx | 30 +- .../components/Knowledge/types.ts | 57 +++ .../ApplicationConfig/components/LineCard.tsx | 29 +- .../components/ModelConfigModal.tsx | 32 +- .../components/ReleaseModal.tsx | 30 +- .../components/ReleaseShareModal.tsx | 30 +- .../components/SubAgentModal.tsx | 32 +- .../ApplicationConfig/components/Tag.tsx | 25 +- .../components/ToolList/ToolList.tsx | 22 ++ .../components/ToolList/ToolModal.tsx | 30 +- .../components/ToolList/types.ts | 41 ++ .../VariableList/ApiExtensionModal.tsx | 25 +- .../VariableList/VariableEditModal.tsx | 36 +- .../components/VariableList/VariableList.tsx | 25 +- .../components/VariableList/types.ts | 57 ++- web/src/views/ApplicationConfig/index.tsx | 24 ++ web/src/views/ApplicationConfig/types.ts | 351 +++++++++++++++--- .../components/ApplicationModal.tsx | 34 +- web/src/views/ApplicationManagement/index.tsx | 20 + web/src/views/ApplicationManagement/types.ts | 104 +++++- web/src/views/Conversation/index.tsx | 29 +- web/src/views/Conversation/types.ts | 32 ++ web/src/views/EmotionEngine/index.tsx | 22 ++ web/src/views/EmotionEngine/types.ts | 61 ++- .../ForgettingEngine/components/LineChart.tsx | 48 ++- web/src/views/ForgettingEngine/index.tsx | 22 ++ web/src/views/ForgettingEngine/types.ts | 69 +++- web/src/views/Home/components/Card.tsx | 15 + web/src/views/Home/components/LineCard.tsx | 27 +- web/src/views/Home/components/PieCard.tsx | 18 +- .../views/Home/components/QuickOperation.tsx | 34 +- .../views/Home/components/RecentActivity.tsx | 43 ++- web/src/views/Home/components/TagList.tsx | 20 +- .../Home/components/TopCardList/index.tsx | 27 +- web/src/views/Home/index.tsx | 48 ++- web/src/views/InviteRegister/index.tsx | 72 ++-- web/src/views/InviteRegister/types.ts | 27 ++ web/src/views/Login/index.tsx | 51 ++- web/src/views/Login/types.ts | 15 + .../components/MemberModal.tsx | 29 +- web/src/views/MemberManagement/index.tsx | 24 +- web/src/views/MemberManagement/types.ts | 32 +- .../MemoryConversation/components/Card.tsx | 9 + web/src/views/MemoryConversation/index.tsx | 45 ++- web/src/views/MemoryConversation/types.ts | 0 .../components/Card.tsx | 17 +- .../components/Result.tsx | 30 +- .../views/MemoryExtractionEngine/constant.ts | 20 +- .../views/MemoryExtractionEngine/index.tsx | 20 +- web/src/views/MemoryExtractionEngine/types.ts | 18 + .../components/MemoryForm.tsx | 23 +- web/src/views/MemoryManagement/index.tsx | 21 +- web/src/views/MemoryManagement/types.ts | 18 +- web/src/views/ModelManagement/Group.tsx | 18 + web/src/views/ModelManagement/List.tsx | 18 + web/src/views/ModelManagement/Square.tsx | 19 + .../components/CustomModelModal.tsx | 20 + .../components/GroupModelModal.tsx | 20 + .../components/KeyConfigModal.tsx | 20 + .../ModelImplement/SubModelModal.tsx | 29 +- .../components/ModelImplement/index.tsx | 26 ++ .../components/ModelImplement/types.ts | 32 ++ .../components/ModelListDetail.tsx | 28 ++ .../components/ModelSquareDetail.tsx | 26 ++ .../components/MultiKeyConfigModal.tsx | 22 ++ web/src/views/ModelManagement/index.tsx | 24 +- web/src/views/ModelManagement/types.ts | 196 +++++++++- web/src/views/ModelManagement/utils.ts | 18 + web/src/views/NoPermission/index.tsx | 12 + web/src/views/NotFound/index.tsx | 12 + .../OrderHistory/components/OrderDetail.tsx | 21 +- web/src/views/OrderHistory/index.tsx | 21 +- web/src/views/OrderHistory/types.ts | 15 + web/src/views/OrderPayment/index.tsx | 20 +- web/src/views/OrderPayment/types.ts | 12 + web/src/views/Pricing/index.tsx | 20 + web/src/views/Prompt/History.tsx | 14 + web/src/views/Prompt/Prompt.tsx | 18 + .../views/Prompt/components/PromptDetail.tsx | 17 +- .../Prompt/components/PromptSaveModal.tsx | 21 +- .../Prompt/components/PromptVariableModal.tsx | 22 +- web/src/views/Prompt/index.tsx | 16 + web/src/views/Prompt/types.ts | 27 ++ web/src/views/SelfReflectionEngine/index.tsx | 17 + web/src/views/SelfReflectionEngine/types.ts | 39 +- web/src/views/SpaceConfig/index.tsx | 13 +- web/src/views/SpaceConfig/types.ts | 12 + .../SpaceManagement/components/SpaceModal.tsx | 25 +- web/src/views/SpaceManagement/index.tsx | 15 + web/src/views/SpaceManagement/types.ts | 23 +- .../UserManagement/components/CreateModal.tsx | 21 +- .../components/ResetPasswordModal.tsx | 22 +- web/src/views/UserManagement/index.tsx | 26 +- web/src/views/UserManagement/types.ts | 25 +- .../views/UserMemory/components/CardList.tsx | 61 --- web/src/views/UserMemory/enum.ts | 0 web/src/views/UserMemory/index.tsx | 19 +- web/src/views/UserMemory/types.ts | 9 + web/src/views/UserMemoryDetail/Neo4j.tsx | 14 + web/src/views/UserMemoryDetail/Rag.tsx | 27 +- .../UserMemoryDetail/components/AboutMe.tsx | 19 +- .../components/ActivationMetricsPieCard.tsx | 17 + .../UserMemoryDetail/components/Card.tsx | 14 + .../components/ConversationMemory.tsx | 16 +- .../components/EmotionLine.tsx | 17 + .../components/EmotionTags.tsx | 23 ++ .../components/EndUserProfile.tsx | 17 + .../components/EndUserProfileModal.tsx | 23 +- .../components/ExplicitDetailModal.tsx | 42 ++- .../components/ForgetRefreshModal.tsx | 31 +- .../UserMemoryDetail/components/Habits.tsx | 24 ++ .../UserMemoryDetail/components/Health.tsx | 20 + .../components/InteractionBar.tsx | 18 + .../components/InterestAreas.tsx | 30 ++ .../components/InterestDistribution.tsx | 13 + .../components/MemoryInsight.tsx | 19 +- .../components/NodeStatistics.tsx | 17 + .../components/PageHeader.tsx | 18 +- .../components/PerceptualLastInfo.tsx | 31 ++ .../UserMemoryDetail/components/Portrait.tsx | 32 ++ .../components/Preferences.tsx | 30 ++ .../components/RecentTrendsLineCard.tsx | 19 +- .../components/RelationshipNetwork.tsx | 20 +- .../components/Suggestions.tsx | 17 + .../UserMemoryDetail/components/Timeline.tsx | 37 +- .../UserMemoryDetail/components/WordCloud.tsx | 17 + web/src/views/UserMemoryDetail/index.tsx | 12 + web/src/views/UserMemoryDetail/types.ts | 59 +++ 155 files changed, 4169 insertions(+), 586 deletions(-) delete mode 100644 web/src/views/ApiParameters/index.tsx delete mode 100644 web/src/views/ApiParameters/types.ts delete mode 100644 web/src/views/MemoryConversation/types.ts delete mode 100644 web/src/views/UserMemory/components/CardList.tsx delete mode 100644 web/src/views/UserMemory/enum.ts diff --git a/web/src/views/ApiKeyManagement/components/ApiKeyDetailModal.tsx b/web/src/views/ApiKeyManagement/components/ApiKeyDetailModal.tsx index 2899a306..1126af4f 100644 --- a/web/src/views/ApiKeyManagement/components/ApiKeyDetailModal.tsx +++ b/web/src/views/ApiKeyManagement/components/ApiKeyDetailModal.tsx @@ -1,7 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 15:52:44 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 15:54:22 + */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Switch, Button, Tooltip } from 'antd'; import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; + import type { ApiKey, ApiKeyModalRef } from '../types'; import RbModal from '@/components/RbModal' import { getApiKey } from '@/api/apiKey'; @@ -9,16 +16,29 @@ import { formatDateTime } from '@/utils/format' import Tag from '@/components/Tag' import { maskApiKeys } from '@/utils/apiKeyReplacer'; +/** + * Modal component for viewing API key details + * Displays read-only information about an API key + */ const ApiKeyDetailModal = forwardRef void }>(({ handleCopy }, ref) => { + // Hooks const { t } = useTranslation(); + + // State const [visible, setVisible] = useState(false); const [data, setData] = useState({} as ApiKey) - // 封装取消方法,添加关闭弹窗逻辑 + /** + * Close the modal + */ const handleClose = () => { setVisible(false); }; + /** + * Open modal and fetch API key details + * @param apiKey - API key item to view + */ const handleOpen = (apiKey?: ApiKey) => { if (apiKey?.id) { getApiKey(apiKey.id) @@ -29,7 +49,9 @@ const ApiKeyDetailModal = forwardRef ({ handleOpen, handleClose diff --git a/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx b/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx index f0bf4e11..9b7bd5f8 100644 --- a/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx +++ b/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx @@ -1,28 +1,48 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 15:52:47 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 15:52:47 + */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, Switch, App, DatePicker } from 'antd'; import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs' + import type { ApiKey, ApiKeyModalRef } from '../types'; import RbModal from '@/components/RbModal' -import dayjs from 'dayjs' import { createApiKey, updateApiKey } from '@/api/apiKey'; const FormItem = Form.Item; +/** + * Props for ApiKeyModal component + */ interface CreateModalProps { + /** Callback to refresh parent list after save */ refresh: () => void; } +/** + * Modal component for creating or editing API keys + * Handles API key configuration including permissions and expiration + */ const ApiKeyModal = forwardRef(({ refresh, }, ref) => { + // Hooks const { t } = useTranslation(); const { message } = App.useApp(); - const [visible, setVisible] = useState(false); const [form] = Form.useForm(); + + // State + const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false); const [editVo, setEditVo] = useState(null); - // 封装取消方法,添加关闭弹窗逻辑 + /** + * Close modal and reset form state + */ const handleClose = () => { setVisible(false); form.resetFields(); @@ -30,10 +50,14 @@ const ApiKeyModal = forwardRef(({ setEditVo(null); }; + /** + * Open modal for creating or editing + * @param apiKey - Optional API key data for edit mode + */ const handleOpen = (apiKey?: ApiKey) => { if (apiKey?.id) { const { scopes = [], expires_at } = apiKey - // 编辑模式,填充表单 + // Edit mode - populate form with existing data form.setFieldsValue({ name: apiKey.name, description: apiKey.description, @@ -46,7 +70,10 @@ const ApiKeyModal = forwardRef(({ setVisible(true); }; - // 封装保存方法,添加提交逻辑 + /** + * Validate and submit form data + * Creates new API key or updates existing one + */ const handleSave = async () => { form.validateFields() .then((values) => { @@ -59,7 +86,7 @@ const ApiKeyModal = forwardRef(({ if (rag) { scopes.push('rag') } - // 准备新的/更新的API Key数据 + // Prepare new/updated API key data const apiKeyData = { ...rest, scopes, @@ -78,7 +105,9 @@ const ApiKeyModal = forwardRef(({ }) } - // 暴露给父组件的方法 + /** + * Expose methods to parent component via ref + */ useImperativeHandle(ref, () => ({ handleOpen, handleClose diff --git a/web/src/views/ApiKeyManagement/index.tsx b/web/src/views/ApiKeyManagement/index.tsx index 168b5e52..7bb417c5 100644 --- a/web/src/views/ApiKeyManagement/index.tsx +++ b/web/src/views/ApiKeyManagement/index.tsx @@ -1,8 +1,16 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 15:52:50 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 15:52:50 + */ import React, { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, App, Space } from 'antd'; import clsx from 'clsx'; import { DeleteOutlined, EditOutlined, EyeOutlined } from '@ant-design/icons'; +import copy from 'copy-to-clipboard' + import type { ApiKey, ApiKeyModalRef } from './types'; import ApiKeyModal from './components/ApiKeyModal'; import ApiKeyDetailModal from './components/ApiKeyDetailModal'; @@ -11,26 +19,49 @@ import { getApiKeyListUrl, deleteApiKey } from '@/api/apiKey'; import PageScrollList, { type PageScrollListRef } from '@/components/PageScrollList' import { formatDateTime } from '@/utils/format'; import Tag from '@/components/Tag' -import copy from 'copy-to-clipboard' import { maskApiKeys } from '@/utils/apiKeyReplacer'; +/** + * API Key Management page component + * Manages service API keys with CRUD operations + */ const ApiKeyManagement: React.FC = () => { + // Hooks const { t } = useTranslation(); const { modal, message } = App.useApp(); + + // Refs const apiKeyModalRef = useRef(null); const apiKeyDetailModalRef = useRef(null) const scrollListRef = useRef(null) + /** + * Refresh the API key list + */ const refresh = () => { scrollListRef.current?.refresh(); } + /** + * Open modal to create or edit API key + * @param item - Optional API key item for edit mode + */ const handleEdit = (item?: ApiKey) => { apiKeyModalRef.current?.handleOpen(item); } + + /** + * Open modal to view API key details + * @param item - API key item to view + */ const handleView = (item: ApiKey) => { apiKeyDetailModalRef.current?.handleOpen(item); } + + /** + * Delete API key with confirmation + * @param item - API key item to delete + */ const handleDelete = (item: ApiKey) => { modal.confirm({ title: t('common.confirmDeleteDesc', { name: item.name }), @@ -46,6 +77,10 @@ const ApiKeyManagement: React.FC = () => { } }) } + /** + * Copy content to clipboard + * @param content - Content to copy + */ const handleCopy = (content: string) => { copy(content) message.success(t('common.copySuccess')) diff --git a/web/src/views/ApiKeyManagement/types.ts b/web/src/views/ApiKeyManagement/types.ts index 2df67193..4ea1de0d 100644 --- a/web/src/views/ApiKeyManagement/types.ts +++ b/web/src/views/ApiKeyManagement/types.ts @@ -1,39 +1,76 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 15:52:53 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 15:52:53 + */ import type { Dayjs } from 'dayjs' import { maskApiKeys } from '@/utils/apiKeyReplacer' +/** + * API Key data structure + */ export interface ApiKey { + /** Unique identifier */ id: string; + /** API key name */ name: string; + /** Optional description */ description?: string; + /** API key type */ type: 'agent' | 'multi_agent' | 'workflow' | 'service'; - scopes?: string[]; // 'memory' | 'rag' | 'app' + /** Permission scopes: 'memory' | 'rag' | 'app' */ + scopes?: string[]; + /** The actual API key string */ api_key: string; + /** Whether the key is active */ is_active: boolean; + /** Whether the key has expired */ is_expired: boolean; + /** Creation timestamp */ created_at: number; + /** Expiration timestamp or Dayjs object */ expires_at?: number | Dayjs; + /** Memory engine permission flag */ memory?: boolean; + /** RAG/Knowledge base permission flag */ rag?: boolean; - + /** Last update timestamp */ updated_at: string; + /** Queries per second limit */ qps_limit?: number; + /** Daily request limit */ daily_request_limit?: number; + /** Rate limit */ rate_limit?: number; + /** Total number of requests made */ total_requests: number; + /** Quota used */ quota_used: number; + /** Quota limit */ quota_limit: number; } +/** + * Ref methods exposed by API Key modal components + */ export interface ApiKeyModalRef { + /** + * Open the modal + * @param apiKey - Optional API key data for edit mode + */ handleOpen: (apiKey?: ApiKey) => void; + /** Close the modal */ handleClose: () => void; } /** - * 获取掩码后的API密钥 + * Get masked API key for display + * @param apiKey - The API key to mask + * @returns Masked API key string */ export const getMaskedApiKey = (apiKey: string): string => { return maskApiKeys(apiKey) diff --git a/web/src/views/ApiParameters/index.tsx b/web/src/views/ApiParameters/index.tsx deleted file mode 100644 index b5ad6b4a..00000000 --- a/web/src/views/ApiParameters/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { type FC, useEffect, useState } from 'react'; -import { Row, Col, Skeleton } from 'antd' -import CodeBlock from '@/components/Markdown/CodeBlock'; -import { getMemoryApi } from '@/api/memory'; -import RbCard from '@/components/RbCard/Card'; -import type { - Data, - Section -} from './types'; -import Empty from '@/components/Empty' - - -const ApiParameters: FC = () => { - const { t } = useTranslation(); - const [loading, setLoading] = useState(false) - // const [data, setData] = useState(null) - const [apiData, setApiData] = useState([]) - - useEffect(() => { - getApiData() - }, []) - const getApiData = () => { - setLoading(true) - getMemoryApi().then((res) => { - const resp = res as Data || {} - // setData(resp) - setApiData(resp.sections || []) - }) - .finally(() => setLoading(false)) - } - - return ( -
-

{t('api.pageTitle')}

-

{t('api.pageSubTitle')}

- - {loading - ? - : apiData.length === 0 - ? - : - {apiData.map((api, index) => ( - - - <> -
- {api.method} - {api.path} -
- {api.desc &&<> -
{t('api.desc')}
-
{api.desc}
- } - - {typeof api.input === 'string' && api.input !== '无' && <> -
{t('api.input')}
- - } - {typeof api.output === 'string' && api.output !== '无' && <> -
{t('api.output')}
- - } - -
- - ))} -
- } -
- ); -}; -export default ApiParameters; \ No newline at end of file diff --git a/web/src/views/ApiParameters/types.ts b/web/src/views/ApiParameters/types.ts deleted file mode 100644 index 56d516be..00000000 --- a/web/src/views/ApiParameters/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface Section { - name: string; - path: string; - method: string; - input: string; - output: string; - desc: string; -} -export interface Data { - title: string; - meta: { - search_switch: { - value: string; - desc: string; - }[]; - status_code: { - code: string; - desc: string; - }[]; - } - sections: Section[] -} \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/Agent.tsx b/web/src/views/ApplicationConfig/Agent.tsx index 0e9e8b44..1cda821d 100644 --- a/web/src/views/ApplicationConfig/Agent.tsx +++ b/web/src/views/ApplicationConfig/Agent.tsx @@ -1,8 +1,15 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:29:21 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:29:21 + */ import { type FC, type ReactNode, useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react'; import clsx from 'clsx' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom'; import { Row, Col, Space, Form, Input, Switch, Button, App, Spin } from 'antd' + import Chat from './components/Chat' import RbCard from '@/components/RbCard/Card' import Card from './components/Card' @@ -33,6 +40,11 @@ import AiPromptModal from './components/AiPromptModal' import ToolList from './components/ToolList/ToolList' import ChatVariableConfigModal from './components/ChatVariableConfigModal'; +/** + * Description wrapper component + * @param desc - Description text + * @param className - Additional CSS classes + */ const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => { return (
@@ -40,6 +52,12 @@ const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className})
) } +/** + * Label wrapper component + * @param title - Label title + * @param className - Additional CSS classes + * @param children - Child elements + */ const LabelWrapper: FC<{title: string, className?: string; children?: ReactNode}> = ({title, className, children}) => { return (
@@ -48,6 +66,13 @@ const LabelWrapper: FC<{title: string, className?: string; children?: ReactNode}
) } +/** + * Switch wrapper component with label and description + * @param title - Switch title + * @param desc - Optional description + * @param name - Form field name + * @param needTransition - Whether to translate text + */ const SwitchWrapper: FC<{ title: string, desc?: string, name: string | string[]; needTransition?: boolean; }> = ({ title, desc, name, needTransition = true }) => { const { t } = useTranslation(); return ( @@ -65,6 +90,13 @@ const SwitchWrapper: FC<{ title: string, desc?: string, name: string | string[]; ) } +/** + * Select wrapper component with label and description + * @param title - Select title + * @param desc - Description text + * @param name - Form field name + * @param url - API URL for options + */ const SelectWrapper: FC<{ title: string, desc: string, name: string | string[], url: string }> = ({ title, desc, name, url }) => { const { t } = useTranslation(); return ( @@ -88,6 +120,10 @@ const SelectWrapper: FC<{ title: string, desc: string, name: string | string[], ) } +/** + * Agent configuration component + * Manages single agent configuration including prompts, knowledge, memory, variables, and tools + */ const Agent = forwardRef((_props, ref) => { const { t } = useTranslation() const { id } = useParams(); @@ -103,7 +139,7 @@ const Agent = forwardRef((_props, ref) => { const [isSave, setIsSave] = useState(false) const initialized = useRef(false) - // 初始化完成标记 + // Initialization flag useEffect(() => { if (data) { initialized.current = true @@ -121,6 +157,9 @@ const Agent = forwardRef((_props, ref) => { getData() }, []) + /** + * Fetch agent configuration data + */ const getData = () => { setLoading(true) getApplicationConfig(id as string).then(res => { @@ -147,6 +186,11 @@ const Agent = forwardRef((_props, ref) => { }) } + /** + * Refresh configuration after model changes + * @param vo - Model configuration + * @param type - Source type (model or chat) + */ const refresh = (vo: ModelConfig, type: Source) => { if (type === 'model') { const { default_model_config_id, ...rest } = vo @@ -188,14 +232,24 @@ const Agent = forwardRef((_props, ref) => { } } + /** + * Open model configuration modal + */ const handleModelConfig = () => { modelConfigModalRef.current?.handleOpen('model') } + /** + * Clear all debugging chat sessions + */ const handleClearDebugging = () => { setChatList([]) } - // 保存Agent配置 + /** + * Save agent configuration + * @param flag - Whether to show success message + * @returns Promise that resolves when save is complete + */ const handleSave = (flag = true) => { if (!isSave || !data) return Promise.resolve() const { memory, knowledge_retrieval, tools, ...rest } = values @@ -240,6 +294,9 @@ const Agent = forwardRef((_props, ref) => { }) }) } + /** + * Fetch available models list + */ const getModels = () => { getModelList({ type: 'llm,chat', pagesize: 100, page: 1, is_active: true }) .then(res => { @@ -247,6 +304,9 @@ const Agent = forwardRef((_props, ref) => { setModelList(response.items) }) } + /** + * Add new model for debugging + */ const handleAddModel = () => { modelConfigModalRef.current?.handleOpen('chat') } @@ -268,9 +328,16 @@ const Agent = forwardRef((_props, ref) => { })) const aiPromptModalRef = useRef(null) + /** + * Open AI prompt generation modal + */ const handlePrompt = () => { aiPromptModalRef.current?.handleOpen() } + /** + * Update prompt and extract variables + * @param value - New prompt value + */ const updatePrompt = (value: string) => { form.setFieldValue('system_prompt', value) const variables = value.match(/\{\{([^}]+)\}\}/g)?.map(match => match.slice(2, -2)) || [] @@ -285,15 +352,26 @@ const Agent = forwardRef((_props, ref) => { updateVariableList(newVariableList) } + /** + * Update variable list + * @param list - New variable list + */ const updateVariableList = (list: Variable[]) => { form.setFieldValue('variables', [...list]) setChatVariables([...list]) } const chatVariableConfigModalRef = useRef(null) const [chatVariables, setChatVariables] = useState([]) + /** + * Open chat variable configuration modal + */ const handleOpenVariableConfig = () => { chatVariableConfigModalRef.current?.handleOpen(chatVariables) } + /** + * Save chat variable configuration + * @param values - Variable values + */ const handleSaveChatVariable = (values: Variable[]) => { setChatVariables(values) } diff --git a/web/src/views/ApplicationConfig/Api.tsx b/web/src/views/ApplicationConfig/Api.tsx index ab33ba19..5623f3d0 100644 --- a/web/src/views/ApplicationConfig/Api.tsx +++ b/web/src/views/ApplicationConfig/Api.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:29:29 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:29:29 + */ import { type FC, useState, useRef, useEffect } from 'react'; import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; @@ -14,6 +20,11 @@ import Tag from '@/components/Tag' import { getApiKeyList, getApiKeyStats, deleteApiKey } from '@/api/apiKey'; import { maskApiKeys } from '@/utils/apiKeyReplacer' +/** + * API configuration page component + * Manages API endpoints and API keys for the application + * @param application - Current application data + */ const Api: FC<{ application: Application | null }> = ({ application }) => { const { t } = useTranslation(); const activeMethods = ['POST']; @@ -23,6 +34,10 @@ const Api: FC<{ application: Application | null }> = ({ application }) => { const apiKeyConfigModalRef = useRef(null); const [apiKeyList, setApiKeyList] = useState([]) + /** + * Copy content to clipboard + * @param content - Content to copy + */ const handleCopy = (content: string) => { copy(content) message.success(t('common.copySuccess')) @@ -31,6 +46,9 @@ const Api: FC<{ application: Application | null }> = ({ application }) => { useEffect(() => { getApiList() }, []) + /** + * Fetch API key list for the application + */ const getApiList = () => { if (!application) { return @@ -48,6 +66,10 @@ const Api: FC<{ application: Application | null }> = ({ application }) => { getAllStats([...list]) }) } + /** + * Fetch statistics for all API keys + * @param list - List of API keys + */ const getAllStats = (list: ApiKey[]) => { const allList: ApiKey[] = [] list.forEach(async item => { @@ -66,12 +88,23 @@ const Api: FC<{ application: Application | null }> = ({ application }) => { }) } + /** + * Open modal to add new API key + */ const handleAdd = () => { apiKeyModalRef.current?.handleOpen() } + /** + * Open modal to edit API key + * @param vo - API key to edit + */ const handleEdit = (vo: ApiKey) => { apiKeyConfigModalRef.current?.handleOpen(vo) } + /** + * Delete API key with confirmation + * @param vo - API key to delete + */ const handleDelete = (vo: ApiKey) => { modal.confirm({ title: t('common.confirmDeleteDesc', { name: vo.name }), @@ -89,7 +122,7 @@ const Api: FC<{ application: Application | null }> = ({ application }) => { }) } - // 计算total_requests总数 + // Calculate total requests across all API keys const totalRequests = apiKeyList.reduce((total, item) => total + item.total_requests, 0); return (
diff --git a/web/src/views/ApplicationConfig/Cluster.tsx b/web/src/views/ApplicationConfig/Cluster.tsx index aa4a5d98..2688eaae 100644 --- a/web/src/views/ApplicationConfig/Cluster.tsx +++ b/web/src/views/ApplicationConfig/Cluster.tsx @@ -1,8 +1,15 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:29:33 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:29:33 + */ import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom'; -import Card from './components/Card' import { Form, Space, Row, Col, Button, Flex, App, Select } from 'antd' + +import Card from './components/Card' import Tag, { type TagProps } from './components/Tag' import CustomSelect from '@/components/CustomSelect'; import { getMultiAgentConfig, saveMultiAgentConfig, getApplicationList } from '@/api/application'; @@ -26,6 +33,10 @@ import type { Application } from '@/views/ApplicationManagement/types' const tagColors = ['processing', 'warning', 'default'] const MAX_LENGTH = 5; +/** + * Multi-agent cluster configuration component + * Manages multi-agent orchestration, sub-agents, and collaboration modes + */ const Cluster = forwardRef((_props, ref) => { const { t } = useTranslation() const { message } = App.useApp() @@ -41,6 +52,11 @@ const Cluster = forwardRef((_props, ref) => { }, ]) + /** + * Save cluster configuration + * @param flag - Whether to show success message + * @returns Promise that resolves when save is complete + */ const handleSave = (flag = true) => { if (!data) return Promise.resolve() if (!values.default_model_config_id && values.orchestration_mode === 'supervisor') { @@ -80,6 +96,9 @@ const Cluster = forwardRef((_props, ref) => { getData() }, [id]) + /** + * Fetch cluster configuration data + */ const getData = () => { if (!id) { return @@ -113,9 +132,17 @@ const Cluster = forwardRef((_props, ref) => { } }) } + /** + * Open sub-agent modal for add or edit + * @param agent - Optional agent data for edit mode + */ const handleSubAgentModal = (agent?: SubAgentItem) => { subAgentModalRef.current?.handleOpen(agent) } + /** + * Refresh sub-agents list after add or edit + * @param agent - Agent data to add or update + */ const refreshSubAgents = (agent: SubAgentItem) => { const index = subAgents.findIndex(item => item.agent_id === agent.agent_id) const newSubAgents = [...subAgents] @@ -130,6 +157,10 @@ const Cluster = forwardRef((_props, ref) => { setSubAgents(newSubAgents) } } + /** + * Delete sub-agent from list + * @param agent - Agent to delete + */ const handleDeleteSubAgent = (agent: SubAgentItem) => { setSubAgents(prev => prev.filter(item => item.agent_id !== agent.agent_id)) } @@ -138,9 +169,16 @@ const Cluster = forwardRef((_props, ref) => { })) const modelConfigModalRef = useRef(null) + /** + * Open model configuration modal + */ const handleEditModelConfig = () => { modelConfigModalRef.current?.handleOpen('multi_agent', values.model_parameters) } + /** + * Save model configuration + * @param values - Model parameters + */ const handleSaveModelConfig = (values: Config['model_parameters']) => { form.setFieldsValue({ model_parameters: values diff --git a/web/src/views/ApplicationConfig/ReleasePage.tsx b/web/src/views/ApplicationConfig/ReleasePage.tsx index ae550d36..b87abb52 100644 --- a/web/src/views/ApplicationConfig/ReleasePage.tsx +++ b/web/src/views/ApplicationConfig/ReleasePage.tsx @@ -1,7 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:29:41 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:29:41 + */ import { type FC, useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import clsx from 'clsx'; import { Button, Space, Input, Form, App } from 'antd'; + import Tag, { type TagProps } from './components/Tag' import RbCard from '@/components/RbCard/Card' import { getReleaseList, rollbackRelease } from '@/api/application' @@ -12,12 +19,21 @@ import type { Application } from '@/views/ApplicationManagement/types' import Empty from '@/components/Empty' import { formatDateTime } from '@/utils/format'; import Markdown from '@/components/Markdown' +/** + * Tag color mapping for release versions + */ const tagColors: Record = { current: 'processing', rolledBack: 'warning', history: 'default', } +/** + * Release page component + * Manages application version releases, rollbacks, and version history + * @param data - Application data + * @param refresh - Function to refresh application data + */ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refresh}) => { const { t } = useTranslation(); const { message } = App.useApp() @@ -30,6 +46,9 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres getData() }, [data.id]) + /** + * Fetch release list data + */ const getData = () => { refresh() getReleaseList(data.id).then(res => { @@ -38,6 +57,9 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres setSelectedVersion(response?.[0]) }) } + /** + * Rollback to selected version + */ const handleRollback = () => { if (!selectedVersion) return rollbackRelease(data.id, selectedVersion.version).then(() => { diff --git a/web/src/views/ApplicationConfig/Statistics.tsx b/web/src/views/ApplicationConfig/Statistics.tsx index 8a76ab06..0c2c4b54 100644 --- a/web/src/views/ApplicationConfig/Statistics.tsx +++ b/web/src/views/ApplicationConfig/Statistics.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:29:45 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:29:45 + */ import { type FC, useState, useEffect } from 'react'; import { Row, Col, Flex, DatePicker } from 'antd'; import type { Dayjs } from 'dayjs' @@ -10,12 +16,21 @@ import { getAppStatistics } from '@/api/application'; import LineCard from './components/LineCard' import type { StatisticsData, StatisticsItem } from './types' +/** + * Mapping of daily statistics keys to total statistics keys + */ const TotalObj: Record = { daily_conversations: 'total_conversations', daily_new_users: 'total_new_users', daily_api_calls: 'total_api_calls', daily_tokens: 'total_tokens', } + +/** + * Statistics page component + * Displays application usage statistics with charts and date range filtering + * @param application - Application data + */ const Statistics: FC<{ application: Application | null }> = ({ application }) => { const [data, setData] = useState({ daily_conversations: [], @@ -35,6 +50,9 @@ const Statistics: FC<{ application: Application | null }> = ({ application }) => useEffect(() => { getData() }, [application, query]) + /** + * Fetch statistics data + */ const getData = () => { if (!application?.id) { return @@ -49,6 +67,10 @@ const Statistics: FC<{ application: Application | null }> = ({ application }) => setData(res as StatisticsData) }) } + /** + * Handle date range change + * @param date - Selected date range + */ const handleChange = (date: [Dayjs | null, Dayjs | null] | null) => { if (!date || !date[0] || !date[1]) return setQuery({ diff --git a/web/src/views/ApplicationConfig/components/AiPromptModal.tsx b/web/src/views/ApplicationConfig/components/AiPromptModal.tsx index 198460eb..c35a2a0f 100644 --- a/web/src/views/ApplicationConfig/components/AiPromptModal.tsx +++ b/web/src/views/ApplicationConfig/components/AiPromptModal.tsx @@ -1,3 +1,15 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:26:44 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:26:44 + */ +/** + * AI Prompt Assistant Modal + * Provides an interactive chat interface to help users optimize their prompts using AI + * Features model selection, chat history, and variable insertion + */ + import { forwardRef, useImperativeHandle, useState, useRef } from 'react'; import { Button, Form, Input, App, Row, Col } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -19,11 +31,20 @@ import AiPromptVariableModal from './AiPromptVariableModal' import { type SSEMessage } from '@/utils/stream' import Editor from './Editor' +/** + * Component props + */ interface AiPromptModalProps { + /** Callback to refresh prompt with optimized value */ refresh: (value: string) => void; + /** Default model to pre-select */ defaultModel: ModelListItem | null; } +/** + * AI Prompt Assistant Modal Component + * Helps users create and optimize prompts through AI-powered conversation + */ const AiPromptModal = forwardRef(({ refresh, defaultModel, @@ -42,7 +63,7 @@ const AiPromptModal = forwardRef(({ const values = Form.useWatch([], form) - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset state */ const handleClose = () => { setVisible(false); setLoading(false) @@ -54,6 +75,7 @@ const AiPromptModal = forwardRef(({ }) }; + /** Open modal and create new prompt session */ const handleOpen = () => { createPromptSessions() .then(res => { @@ -66,6 +88,7 @@ const AiPromptModal = forwardRef(({ setVisible(true); }) }; + /** Send user message and get AI response */ const handleSend = () => { if (!promptSession) return if (!values.model_id) { @@ -115,7 +138,7 @@ const AiPromptModal = forwardRef(({ break; case 'end': setLoading(false) - // 流结束时同步表单值 + // Sync form value when stream ends form.setFieldsValue({ current_prompt: currentPromptValueRef.current }) break } @@ -134,14 +157,17 @@ const AiPromptModal = forwardRef(({ setLoading(false) }) } + /** Copy current prompt to clipboard */ const handleCopy = () => { if (!values.current_prompt || values?.current_prompt?.trim() === '') return copy(values.current_prompt) message.success(t('common.copySuccess')) } + /** Open variable selection modal */ const handleAdd = () => { aiPromptVariableModalRef.current?.handleOpen() } + /** Insert variable into prompt editor */ const handleVariableApply = (value: string) => { if (editorRef.current?.insertText) { editorRef.current.insertText(value) @@ -149,6 +175,7 @@ const AiPromptModal = forwardRef(({ form.setFieldValue('current_prompt', (values.current_prompt || '') + value) } } + /** Apply optimized prompt and close modal */ const handleApply = () => { if (!values.current_prompt) { return @@ -157,7 +184,7 @@ const AiPromptModal = forwardRef(({ handleClose() } - // 暴露给父组件的方法 + /** Expose methods to parent component */ useImperativeHandle(ref, () => ({ handleOpen, })); diff --git a/web/src/views/ApplicationConfig/components/AiPromptVariableModal.tsx b/web/src/views/ApplicationConfig/components/AiPromptVariableModal.tsx index 61847d9a..6e8e617a 100644 --- a/web/src/views/ApplicationConfig/components/AiPromptVariableModal.tsx +++ b/web/src/views/ApplicationConfig/components/AiPromptVariableModal.tsx @@ -1,25 +1,42 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:14 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:14 + */ +/** + * AI Prompt Variable Modal + * Allows users to insert variables into AI-generated prompts + * Supports autocomplete with existing variables + */ + import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; -import { Form, Input, App, Select, AutoComplete, type AutoCompleteProps } from 'antd'; +import { Form, AutoComplete, type AutoCompleteProps } from 'antd'; import { useTranslation } from 'react-i18next'; -import type { Application } from '@/views/ApplicationManagement/types' import type { AiPromptVariableModalRef } from '../types' -import { createApiKey } from '@/api/apiKey'; import RbModal from '@/components/RbModal' const FormItem = Form.Item; +/** + * Component props + */ interface AiPromptVariableModalProps { + /** Callback to insert variable into prompt */ refresh: (value: string) => void; + /** List of available variables */ variables: string[]; } +/** + * Variable selection modal for AI prompt assistant + */ const AiPromptVariableModal = forwardRef(({ refresh, variables }, ref) => { const { t } = useTranslation(); - const { message } = App.useApp(); const [visible, setVisible] = useState(false); const [form] = Form.useForm(); const [loading, setLoading] = useState(false) @@ -31,6 +48,7 @@ const AiPromptVariableModal = forwardRef { const filterKeys = variables?.filter(key => key.includes(value)) @@ -47,18 +65,19 @@ const AiPromptVariableModal = forwardRef { setVisible(false); form.resetFields(); setLoading(false) }; + /** Open modal */ const handleOpen = () => { setVisible(true); form.resetFields(); }; - // 封装保存方法,添加提交逻辑 + /** Apply selected variable */ const handleSave = () => { const variableName = form.getFieldValue('variableName') @@ -68,7 +87,7 @@ const AiPromptVariableModal = forwardRef ({ handleOpen, handleClose diff --git a/web/src/views/ApplicationConfig/components/ApiKeyConfigModal.tsx b/web/src/views/ApplicationConfig/components/ApiKeyConfigModal.tsx index 1b4f3f6e..f4751c88 100644 --- a/web/src/views/ApplicationConfig/components/ApiKeyConfigModal.tsx +++ b/web/src/views/ApplicationConfig/components/ApiKeyConfigModal.tsx @@ -1,3 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:22 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:22 + */ +/** + * API Key Configuration Modal + * Allows configuring rate limits and daily usage limits for API keys + */ + import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Slider } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -7,10 +18,17 @@ import RbModal from '@/components/RbModal' import { updateApiKey } from '@/api/apiKey'; import type { ApiKey } from '@/views/ApiKeyManagement/types' +/** + * Component props + */ interface ApiKeyConfigModalProps { + /** Callback to refresh API key list */ refresh: () => void; } -const ApiKeyConfigModal = forwardRef(({ + +/** + * Modal for configuring API key limits + */const ApiKeyConfigModal = forwardRef(({ refresh }, ref) => { const { t } = useTranslation(); @@ -20,7 +38,7 @@ const ApiKeyConfigModal = forwardRef([], form) const [editVo, setEditVo] = useState(null) - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset state */ const handleClose = () => { form.resetFields(); setLoading(false) @@ -28,6 +46,7 @@ const ApiKeyConfigModal = forwardRef { setVisible(true); setEditVo(apiKey) @@ -36,7 +55,7 @@ const ApiKeyConfigModal = forwardRef { if (!editVo?.id) return form.validateFields() @@ -52,7 +71,7 @@ const ApiKeyConfigModal = forwardRef ({ handleOpen, handleClose @@ -73,7 +92,7 @@ const ApiKeyConfigModal = forwardRef - {/* QPS 限制(每秒请求数) */} + {/* QPS limit (requests per second) */} <>
{t(`application.qpsLimit`)}({t('application.qpsLimitTip')}) @@ -98,7 +117,7 @@ const ApiKeyConfigModal = forwardRef
- {/* 日调用量限制 */} + {/* Daily usage limit */} <>
{t(`application.dailyUsageLimit`)} diff --git a/web/src/views/ApplicationConfig/components/ApiKeyModal.tsx b/web/src/views/ApplicationConfig/components/ApiKeyModal.tsx index 54740436..b43f0e4a 100644 --- a/web/src/views/ApplicationConfig/components/ApiKeyModal.tsx +++ b/web/src/views/ApplicationConfig/components/ApiKeyModal.tsx @@ -1,3 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:25 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:25 + */ +/** + * API Key Creation Modal + * Allows creating new API keys for application access + */ + import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, App } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -9,11 +20,19 @@ import RbModal from '@/components/RbModal' const FormItem = Form.Item; +/** + * Component props + */ interface ApiKeyModalProps { + /** Callback to refresh API key list */ refresh: () => void; + /** Application data */ application?: Application | null; } +/** + * Modal for creating new API keys + */ const ApiKeyModal = forwardRef(({ refresh, application @@ -24,18 +43,19 @@ const ApiKeyModal = forwardRef(({ const [form] = Form.useForm(); const [loading, setLoading] = useState(false) - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset form */ const handleClose = () => { setVisible(false); form.resetFields(); setLoading(false) }; + /** Open modal */ const handleOpen = () => { setVisible(true); form.resetFields(); }; - // 封装保存方法,添加提交逻辑 + /** Create new API key */ const handleSave = () => { if (!application) return form.validateFields() @@ -58,7 +78,7 @@ const ApiKeyModal = forwardRef(({ }) } - // 暴露给父组件的方法 + /** Expose methods to parent component */ useImperativeHandle(ref, () => ({ handleOpen, handleClose @@ -78,7 +98,7 @@ const ApiKeyModal = forwardRef(({ layout="vertical" scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }} > - {/* Key 名称 */} + {/* Key name */} (({ > - {/* 描述 */} + {/* Description */} = ({ title, subTitle, diff --git a/web/src/views/ApplicationConfig/components/Chat.tsx b/web/src/views/ApplicationConfig/components/Chat.tsx index bd826ba1..716f3cc0 100644 --- a/web/src/views/ApplicationConfig/components/Chat.tsx +++ b/web/src/views/ApplicationConfig/components/Chat.tsx @@ -1,7 +1,20 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:39 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:39 + */ +/** + * Chat debugging component for application testing + * Supports both single agent and multi-agent cluster modes + * Provides real-time streaming responses and conversation history + */ + import { type FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import clsx from 'clsx' import { Input, Form } from 'antd' + import ChatIcon from '@/assets/images/application/chat.png' import ChatSendIcon from '@/assets/images/application/chatSend.svg' import DebuggingEmpty from '@/assets/images/application/debuggingEmpty.png' @@ -12,13 +25,26 @@ import ChatContent from '@/components/Chat/ChatContent' import type { ChatItem } from '@/components/Chat/types' import { type SSEMessage } from '@/utils/stream' +/** + * Component props + */ interface ChatProps { + /** List of chat configurations for comparison */ chatList: ChatData[]; + /** Application configuration data */ data: Config; + /** Update chat list state */ updateChatList: React.Dispatch>; + /** Save configuration before running */ handleSave: (flag?: boolean) => Promise; + /** Source type: multi-agent cluster or single agent */ source?: 'multi_agent' | 'agent'; } + +/** + * Chat debugging component + * Allows testing application with different model configurations side-by-side + */ const Chat: FC = ({ chatList, data, updateChatList, handleSave, source = 'agent' }) => { const { t } = useTranslation(); const [form] = Form.useForm<{ message: string }>() @@ -31,6 +57,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc setIsCluster(source === 'multi_agent') }, [source]) + /** Add user message to all chat lists */ const addUserMessage = (message: string) => { const newUserMessage: ChatItem = { role: 'user', @@ -42,6 +69,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc list: [...(item.list || []), newUserMessage] }))) } + /** Add empty assistant message placeholder */ const addAssistantMessage = () => { const assistantMessage: ChatItem = { role: 'assistant', @@ -65,6 +93,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc }))) } } + /** Update assistant message with streaming content */ const updateAssistantMessage = (content?: string, model_config_id?: string, conversation_id?: string) => { if (!content || !model_config_id) return updateChatList(prev => { @@ -92,6 +121,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc return prev; }) } + /** Update assistant message when error occurs */ const updateErrorAssistantMessage = (message_length: number, model_config_id?: string) => { if (message_length > 0 || !model_config_id) return @@ -120,6 +150,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc return prev }) } + /** Send message for agent comparison mode */ const handleSend = () => { if (loading) return setLoading(true) @@ -176,6 +207,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc }) } + /** Add assistant message for cluster mode */ const addClusterAssistantMessage = () => { const assistantMessage: ChatItem = { role: 'assistant', @@ -187,6 +219,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc list: [...(item.list || []), assistantMessage] }))) } + /** Update cluster assistant message with content */ const updateClusterAssistantMessage = (content?: string) => { if (!content) return updateChatList(prev => { @@ -209,6 +242,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc return [...modelChatList] }) } + /** Update cluster message when error occurs */ const updateClusterErrorAssistantMessage = (message_length: number) => { if (message_length > 0) return @@ -232,6 +266,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc return [...modelChatList] }) } + /** Send message for cluster mode */ const handleClusterSend = () => { if (loading) return setLoading(true) @@ -291,6 +326,7 @@ const Chat: FC = ({ chatList, data, updateChatList, handleSave, sourc }) } + /** Delete chat configuration from list */ const handleDelete = (index: number) => { updateChatList(chatList.filter((_, voIndex) => voIndex !== index)) } diff --git a/web/src/views/ApplicationConfig/components/ChatVariableConfigModal.tsx b/web/src/views/ApplicationConfig/components/ChatVariableConfigModal.tsx index abf33cb5..a16840cc 100644 --- a/web/src/views/ApplicationConfig/components/ChatVariableConfigModal.tsx +++ b/web/src/views/ApplicationConfig/components/ChatVariableConfigModal.tsx @@ -1,3 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:44 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:44 + */ +/** + * Chat Variable Configuration Modal + * Allows users to configure variable values before starting a chat session + */ + import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, InputNumber } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -6,10 +17,17 @@ import type { ChatVariableConfigModalRef } from '../types' import type { Variable } from './VariableList/types' import RbModal from '@/components/RbModal' +/** + * Component props + */ interface VariableEditModalProps { + /** Callback to update variables */ refresh: (values: Variable[]) => void; } +/** + * Modal for configuring chat variables + */ const ChatVariableConfigModal = forwardRef(({ refresh, }, ref) => { @@ -19,20 +37,21 @@ const ChatVariableConfigModal = forwardRef([]) - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset form */ const handleClose = () => { setVisible(false); form.resetFields(); setLoading(false) }; + /** Open modal with variable list */ const handleOpen = (values: Variable[]) => { console.log('values', values) setVisible(true); form.setFieldsValue({variables: values}) setInitialValues([...values]) }; - // 封装保存方法,添加提交逻辑 + /** Save variable configuration */ const handleSave = () => { form.validateFields().then((values) => { refresh([ @@ -42,7 +61,7 @@ const ChatVariableConfigModal = forwardRef ({ handleOpen, handleClose diff --git a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx index db1e0fa5..374c87e8 100644 --- a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx +++ b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx @@ -1,8 +1,15 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:52 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:52 + */ import { type FC, useRef } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { Layout, Tabs, Dropdown, Button, Flex } from 'antd'; import type { MenuProps } from 'antd'; import { useTranslation } from 'react-i18next'; + import styles from '../index.module.css' import logoutIcon from '@/assets/images/logout.svg' import editIcon from '@/assets/images/edit_hover.svg' @@ -17,21 +24,43 @@ import CopyModal from './CopyModal' const { Header } = Layout; +/** + * Tab keys for application configuration + */ const tabKeys = ['arrangement', 'api', 'release', 'statistics'] + +/** + * Menu icon mapping + */ const menuIcons: Record = { edit: editIcon, copy: copyIcon, export: exportIcon, delete: deleteIcon } + +/** + * Props for ConfigHeader component + */ interface ConfigHeaderProps { + /** Application data */ application?: Application; + /** Active tab key */ activeTab: string; + /** Tab change handler */ handleChangeTab: (key: string) => void; + /** Refresh application data */ refresh: () => void; + /** Workflow component ref */ workflowRef: React.RefObject + /** App component ref (Agent/Cluster/Workflow) */ appRef?: React.RefObject } + +/** + * Configuration header component + * Displays application name, tabs, and action buttons + */ const ConfigHeader: FC = ({ application, activeTab, handleChangeTab, refresh, workflowRef, @@ -42,12 +71,18 @@ const ConfigHeader: FC = ({ const applicationModalRef = useRef(null); const copyModalRef = useRef(null); + /** + * Format tab items for display + */ const formatTabItems = () => { return tabKeys.map(key => ({ key, label: t(`application.${key}`), })) } + /** + * Format dropdown menu items + */ const formatMenuItems = () => { const items = ['edit', 'copy', 'export', 'delete'].map(key => ({ key, @@ -59,6 +94,9 @@ const ConfigHeader: FC = ({ onClick: handleClick } } + /** + * Handle menu item click + */ const handleClick: MenuProps['onClick'] = ({ key }) => { switch (key) { case 'edit': @@ -74,6 +112,9 @@ const ConfigHeader: FC = ({ break; } } + /** + * Delete application with confirmation + */ const handleDelete = () => { if (!id) { return @@ -86,21 +127,36 @@ const ConfigHeader: FC = ({ console.error('Failed to delete application'); }); } + /** + * Navigate to application list + */ const goToApplication = () => { navigate('/application', { replace: true }) } + /** + * Save workflow configuration + */ const save = () => { workflowRef.current?.handleSave() } + /** + * Run workflow + */ const run = () => { workflowRef.current?.handleSave(false) .then(() => { workflowRef.current?.handleRun() }) } + /** + * Clear workflow canvas + */ const clear = () => { workflowRef?.current?.graphRef?.current?.clearCells() } + /** + * Add variable to workflow + */ const addvariable = () => { workflowRef?.current?.addVariable() } diff --git a/web/src/views/ApplicationConfig/components/CopyModal.tsx b/web/src/views/ApplicationConfig/components/CopyModal.tsx index 0b83e65a..7eaf1497 100644 --- a/web/src/views/ApplicationConfig/components/CopyModal.tsx +++ b/web/src/views/ApplicationConfig/components/CopyModal.tsx @@ -1,3 +1,14 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:27:56 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:27:56 + */ +/** + * Copy Application Modal + * Allows users to duplicate an existing application with a new name + */ + import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -10,10 +21,17 @@ import type { Application } from '@/views/ApplicationManagement/types' const FormItem = Form.Item; +/** + * Component props + */ interface CopyModalProps { + /** Application data to copy */ data: Application } +/** + * Modal for copying applications + */ const CopyModal = forwardRef(({ data }, ref) => { @@ -23,17 +41,18 @@ const CopyModal = forwardRef(({ const [form] = Form.useForm(); const [loading, setLoading] = useState(false) - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset form */ const handleClose = () => { setVisible(false); form.resetFields(); setLoading(false) }; + /** Open modal */ const handleOpen = () => { setVisible(true); }; - // 封装保存方法,添加提交逻辑 + /** Copy application with new name */ const handleSave = () => { setVisible(false); setLoading(true) @@ -48,7 +67,7 @@ const CopyModal = forwardRef(({ }) } - // 暴露给父组件的方法 + /** Expose methods to parent component */ useImperativeHandle(ref, () => ({ handleOpen, handleClose @@ -68,7 +87,7 @@ const CopyModal = forwardRef(({ form={form} layout="vertical" > - {/* 应用名 */} + {/* Application name */} void; + /** Append text to the end of content */ appendText: (text: string) => void; + /** Clear all editor content */ clear: () => void; + /** Scroll editor to bottom */ scrollToBottom: () => void; } +/** + * Editor component props + */ interface LexicalEditorProps { + /** Additional CSS class names */ className?: string; + /** Placeholder text when editor is empty */ placeholder?: string; + /** Initial editor value */ value?: string; + /** Callback when content changes */ onChange?: (value: string) => void; + /** Editor height in pixels */ height?: number; } +/** + * Lexical editor theme configuration + */ const theme = { paragraph: 'editor-paragraph', text: { @@ -33,6 +63,9 @@ const theme = { }, }; +/** + * Editor content component with Lexical context + */ const EditorContent = forwardRef(({ className = '', value, @@ -41,6 +74,13 @@ const EditorContent = forwardRef(({ }, ref) => { const [editor] = useLexicalComposerContext(); + /** + * Expose editor methods to parent component + * - insertText: Insert at cursor position + * - appendText: Append to end of content + * - clear: Clear all content + * - scrollToBottom: Scroll to bottom + */ useImperativeHandle(ref, () => ({ insertText: (text: string) => { editor.update(() => { @@ -109,6 +149,10 @@ const EditorContent = forwardRef(({ ); }); +/** + * Main editor wrapper component + * Initializes Lexical composer with configuration + */ const Editor = forwardRef((props, ref) => { const initialConfig = { namespace: 'Editor', diff --git a/web/src/views/ApplicationConfig/components/Editor/plugin/InitialValuePlugin.tsx b/web/src/views/ApplicationConfig/components/Editor/plugin/InitialValuePlugin.tsx index da373023..dc84074a 100644 --- a/web/src/views/ApplicationConfig/components/Editor/plugin/InitialValuePlugin.tsx +++ b/web/src/views/ApplicationConfig/components/Editor/plugin/InitialValuePlugin.tsx @@ -1,20 +1,34 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:24:59 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:24:59 + */ +/** + * Initial Value Plugin + * Sets the initial content of the Lexical editor + * Only updates when the value prop changes + */ + import { type FC, useEffect, useRef } from 'react'; import { $getRoot, $createParagraphNode, $createTextNode } from 'lexical'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -// 设置初始值的插件 +/** + * Plugin to set initial editor value + */ const InitialValuePlugin: FC<{ value?: string }> = ({ value }) => { const [editor] = useLexicalComposerContext(); const lastValueRef = useRef(undefined); useEffect(() => { - // 只有当value真正发生变化时才更新 + // Only update when value actually changes if (lastValueRef.current !== value) { editor.update(() => { const root = $getRoot(); const currentText = root.getTextContent(); - // 如果当前内容和新值相同,则不更新 + // If current content matches new value, don't update if (currentText === (value || '')) { return; } @@ -26,7 +40,7 @@ const InitialValuePlugin: FC<{ value?: string }> = ({ value }) => { paragraph.append(textNode); root.append(paragraph); } else { - // 当value为undefined或空时,创建一个空段落 + // When value is undefined or empty, create an empty paragraph const paragraph = $createParagraphNode(); root.append(paragraph); } diff --git a/web/src/views/ApplicationConfig/components/Editor/plugin/InsertTextPlugin.tsx b/web/src/views/ApplicationConfig/components/Editor/plugin/InsertTextPlugin.tsx index ca75c393..38c068aa 100644 --- a/web/src/views/ApplicationConfig/components/Editor/plugin/InsertTextPlugin.tsx +++ b/web/src/views/ApplicationConfig/components/Editor/plugin/InsertTextPlugin.tsx @@ -1,10 +1,22 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:25:05 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:25:05 + */ +/** + * Insert Text Plugin + * Provides functionality to insert text at the current cursor position + */ + import { forwardRef, useImperativeHandle } from 'react'; import { $getSelection } from 'lexical'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import type { EditorRef } from '../index' -// 插入文本的插件 -const InsertTextPlugin = forwardRef((_, ref) => { +/** + * Plugin to insert text at cursor position + */ +const InsertTextPlugin = forwardRef<{ insertText: (text: string) => void; }>((_, ref) => { const [editor] = useLexicalComposerContext(); useImperativeHandle(ref, () => ({ diff --git a/web/src/views/ApplicationConfig/components/Editor/plugin/LineBreakPlugin.tsx b/web/src/views/ApplicationConfig/components/Editor/plugin/LineBreakPlugin.tsx index 63d1ffc4..225ba322 100644 --- a/web/src/views/ApplicationConfig/components/Editor/plugin/LineBreakPlugin.tsx +++ b/web/src/views/ApplicationConfig/components/Editor/plugin/LineBreakPlugin.tsx @@ -1,8 +1,22 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:25:09 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:25:09 + */ +/** + * Line Break Plugin + * Handles line breaks and triggers onChange callback when editor content changes + * Converts \n escape sequences to actual line breaks + */ + import { type FC, useEffect } from 'react'; import { $getRoot } from 'lexical'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -// 处理换行的插件 +/** + * Plugin to handle line breaks and content changes + */ const LineBreakPlugin: FC<{ onChange?: (value: string) => void }> = ({ onChange }) => { const [editor] = useLexicalComposerContext(); @@ -11,7 +25,7 @@ const LineBreakPlugin: FC<{ onChange?: (value: string) => void }> = ({ onChange editorState.read(() => { const root = $getRoot(); const textContent = root.getTextContent(); - // 将\n转换为实际换行 + // Convert \n to actual line breaks const processedContent = textContent.replace(/\\n/g, '\n'); onChange?.(processedContent); }); diff --git a/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx b/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx index 1e59f26d..297e9faa 100644 --- a/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx +++ b/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx @@ -1,6 +1,19 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:25:32 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:25:32 + */ +/** + * Knowledge Base Component + * Manages knowledge base associations for the application + * Allows adding, configuring, and removing knowledge bases + */ + import { type FC, useRef, useState, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Space, Button, List } from 'antd' + import knowledgeEmpty from '@/assets/images/application/knowledgeEmpty.svg' import type { KnowledgeConfigForm, @@ -19,6 +32,11 @@ import Tag from '@/components/Tag' import { getKnowledgeBaseList } from '@/api/knowledgeBase' import Card from '../Card' +/** + * Knowledge base management component + * @param value - Current knowledge configuration + * @param onChange - Callback when configuration changes + */ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfig) => void}> = ({value = {knowledge_bases: []}, onChange}) => { const { t } = useTranslation() const knowledgeModalRef = useRef(null) @@ -32,10 +50,10 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi setEditConfig({ ...(value || {}) }) const knowledge_bases = [...(value.knowledge_bases || [])] - // 检查是否有knowledge_bases缺少name字段 + // Check if knowledge_bases are missing name field const basesWithoutName = knowledge_bases.filter(base => !base.name) if (basesWithoutName.length > 0) { - // 调用接口获取完整的知识库信息 + // Call API to get complete knowledge base information getKnowledgeBaseList().then(res => { const fullBases = knowledge_bases.map(base => { if (!base.name) { @@ -54,12 +72,15 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi } }, [value]) + /** Open global knowledge configuration modal */ const handleKnowledgeConfig = () => { knowledgeGlobalConfigModalRef.current?.handleOpen() } + /** Open knowledge base selection modal */ const handleAddKnowledge = () => { knowledgeModalRef.current?.handleOpen() } + /** Remove knowledge base from list */ const handleDeleteKnowledge = (id: string) => { const list = knowledgeList.filter(item => item.id !== id) setKnowledgeList([...list]) @@ -68,9 +89,11 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi knowledge_bases: [...list], }) } + /** Open knowledge base configuration modal */ const handleEditKnowledge = (item: KnowledgeBase) => { knowledgeConfigModalRef.current?.handleOpen(item) } + /** Update knowledge configuration */ const refresh = (values: KnowledgeBase[] | KnowledgeConfigForm | RerankerConfig, type: 'knowledge' | 'knowledgeConfig' | 'rerankerConfig') => { if (type === 'knowledge') { let list = [...knowledgeList] diff --git a/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeConfigModal.tsx b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeConfigModal.tsx index 70b17a11..9adcd168 100644 --- a/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeConfigModal.tsx +++ b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeConfigModal.tsx @@ -1,3 +1,15 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 16:25:37 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 16:25:37 + */ +/** + * Knowledge Configuration Modal + * Configures retrieval settings for individual knowledge bases + * Supports different retrieval modes: participle, semantic, and hybrid + */ + import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { Form, Select, InputNumber } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -9,11 +21,22 @@ import { formatDateTime } from '@/utils/format'; const FormItem = Form.Item; +/** + * Component props + */ interface KnowledgeConfigModalProps { + /** Callback to update knowledge configuration */ refresh: (values: KnowledgeConfigForm, type: 'knowledgeConfig') => void; } + +/** + * Available retrieval types + */ const retrieveTypes: RetrieveType[] = ['participle', 'semantic', 'hybrid'] +/** + * Modal for configuring knowledge base retrieval settings + */ const KnowledgeConfigModal = forwardRef(({ refresh, }, ref) => { @@ -24,13 +47,14 @@ const KnowledgeConfigModal = forwardRef([], form); - // 封装取消方法,添加关闭弹窗逻辑 + /** Close modal and reset form */ const handleClose = () => { setVisible(false); form.resetFields(); setData(null) }; + /** Open modal with knowledge base data */ const handleOpen = (data: KnowledgeBase) => { form.setFieldsValue({ retrieve_type: data?.config?.retrieve_type || retrieveTypes[0], @@ -44,7 +68,7 @@ const KnowledgeConfigModal = forwardRef { form .validateFields() @@ -57,7 +81,7 @@ const KnowledgeConfigModal = forwardRef ({ handleOpen, handleClose @@ -94,7 +118,7 @@ const KnowledgeConfigModal = forwardRef )}