style(web): translate the comments in the src/views directory into English

This commit is contained in:
zhaoying
2026-02-03 18:38:04 +08:00
parent a191e32f71
commit 9e195ea63b
155 changed files with 4169 additions and 586 deletions

View File

@@ -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 (
<div className={clsx(className, "rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
@@ -40,6 +52,12 @@ const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className})
</div>
)
}
/**
* 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 (
<div className={clsx(className, "rb:text-[14px] rb:font-medium rb:leading-5")}>
@@ -48,6 +66,13 @@ const LabelWrapper: FC<{title: string, className?: string; children?: ReactNode}
</div>
)
}
/**
* 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[];
</div>
)
}
/**
* 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<AgentRef>((_props, ref) => {
const { t } = useTranslation()
const { id } = useParams();
@@ -103,7 +139,7 @@ const Agent = forwardRef<AgentRef>((_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<AgentRef>((_props, ref) => {
getData()
}, [])
/**
* Fetch agent configuration data
*/
const getData = () => {
setLoading(true)
getApplicationConfig(id as string).then(res => {
@@ -147,6 +186,11 @@ const Agent = forwardRef<AgentRef>((_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<AgentRef>((_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<AgentRef>((_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<AgentRef>((_props, ref) => {
setModelList(response.items)
})
}
/**
* Add new model for debugging
*/
const handleAddModel = () => {
modelConfigModalRef.current?.handleOpen('chat')
}
@@ -268,9 +328,16 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
}))
const aiPromptModalRef = useRef<AiPromptModalRef>(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<AgentRef>((_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<ChatVariableConfigModalRef>(null)
const [chatVariables, setChatVariables] = useState<Variable[]>([])
/**
* 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)
}

View File

@@ -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<ApiKeyConfigModalRef>(null);
const [apiKeyList, setApiKeyList] = useState<ApiKey[]>([])
/**
* 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 (
<div className="rb:w-250 rb:mt-5 rb:pb-5 rb:mx-auto">

View File

@@ -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<ClusterRef>((_props, ref) => {
const { t } = useTranslation()
const { message } = App.useApp()
@@ -41,6 +52,11 @@ const Cluster = forwardRef<ClusterRef>((_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<ClusterRef>((_props, ref) => {
getData()
}, [id])
/**
* Fetch cluster configuration data
*/
const getData = () => {
if (!id) {
return
@@ -113,9 +132,17 @@ const Cluster = forwardRef<ClusterRef>((_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<ClusterRef>((_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<ClusterRef>((_props, ref) => {
}))
const modelConfigModalRef = useRef<ModelConfigModalRef>(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

View File

@@ -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<Release['tagKey'], TagProps['color']> = {
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(() => {

View File

@@ -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<string, keyof StatisticsData> = {
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<StatisticsData>({
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({

View File

@@ -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<AiPromptModalRef, AiPromptModalProps>(({
refresh,
defaultModel,
@@ -42,7 +63,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
const values = Form.useWatch([], form)
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset state */
const handleClose = () => {
setVisible(false);
setLoading(false)
@@ -54,6 +75,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
})
};
/** Open modal and create new prompt session */
const handleOpen = () => {
createPromptSessions()
.then(res => {
@@ -66,6 +88,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
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<AiPromptModalRef, AiPromptModalProps>(({
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<AiPromptModalRef, AiPromptModalProps>(({
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<AiPromptModalRef, AiPromptModalProps>(({
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<AiPromptModalRef, AiPromptModalProps>(({
handleClose()
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
}));

View File

@@ -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<AiPromptVariableModalRef, AiPromptVariableModalProps>(({
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<AiPromptVariableModalRef, AiPromptVaria
label: `{{${key}}}`
})))
}, [variables])
/** Search and filter variables */
const handleSearch = (value: string) => {
const filterKeys = variables?.filter(key => key.includes(value))
@@ -47,18 +65,19 @@ const AiPromptVariableModal = forwardRef<AiPromptVariableModalRef, AiPromptVaria
}
}
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
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<AiPromptVariableModalRef, AiPromptVaria
handleClose()
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose

View File

@@ -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<ApiKeyConfigModalRef, ApiKeyConfigModalProps>(({
/**
* Modal for configuring API key limits
*/const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProps>(({
refresh
}, ref) => {
const { t } = useTranslation();
@@ -20,7 +38,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
const values = Form.useWatch<ApiKey>([], form)
const [editVo, setEditVo] = useState<ApiKey | null>(null)
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset state */
const handleClose = () => {
form.resetFields();
setLoading(false)
@@ -28,6 +46,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
setVisible(false);
};
/** Open modal with API key data */
const handleOpen = (apiKey: ApiKey) => {
setVisible(true);
setEditVo(apiKey)
@@ -36,7 +55,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
rate_limit: apiKey.rate_limit
});
};
// 封装保存方法,添加提交逻辑
/** Save API key configuration */
const handleSave = () => {
if (!editVo?.id) return
form.validateFields()
@@ -52,7 +71,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -73,7 +92,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
className="rb:px-2.5!"
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
>
{/* QPS 限制(每秒请求数) */}
{/* QPS limit (requests per second) */}
<>
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
{t(`application.qpsLimit`)}({t('application.qpsLimitTip')})
@@ -98,7 +117,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
</div>
</div>
</>
{/* 日调用量限制 */}
{/* Daily usage limit */}
<>
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
{t(`application.dailyUsageLimit`)}

View File

@@ -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<ApiKeyModalRef, ApiKeyModalProps>(({
refresh,
application
@@ -24,18 +43,19 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
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<ApiKeyModalRef, ApiKeyModalProps>(({
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -78,7 +98,7 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
layout="vertical"
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
>
{/* Key 名称 */}
{/* Key name */}
<FormItem
name="name"
label={t('application.apiKeyName')}
@@ -89,7 +109,7 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
>
<Input placeholder={t('application.apiKeyNamePlaceholder')} />
</FormItem>
{/* 描述 */}
{/* Description */}
<FormItem
name="description"
label={t('application.description')}

View File

@@ -1,13 +1,31 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:27:31
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:27:31
*/
import { type FC, type ReactNode } from 'react'
import RbCard from '@/components/RbCard/Card'
/**
* Props for Card component
*/
interface CardProps {
/** Card title */
title?: string | ReactNode;
/** Card subtitle */
subTitle?: string | ReactNode;
/** Card content */
children: ReactNode;
/** Extra content in header */
extra?: ReactNode;
}
/**
* Card component wrapper
* Styled card with left border accent for application configuration sections
*/
const Card: FC<CardProps> = ({
title,
subTitle,

View File

@@ -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<React.SetStateAction<ChatData[]>>;
/** Save configuration before running */
handleSave: (flag?: boolean) => Promise<unknown>;
/** 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<ChatProps> = ({ chatList, data, updateChatList, handleSave, source = 'agent' }) => {
const { t } = useTranslation();
const [form] = Form.useForm<{ message: string }>()
@@ -31,6 +57,7 @@ const Chat: FC<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ 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<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
})
}
/** Delete chat configuration from list */
const handleDelete = (index: number) => {
updateChatList(chatList.filter((_, voIndex) => voIndex !== index))
}

View File

@@ -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<ChatVariableConfigModalRef, VariableEditModalProps>(({
refresh,
}, ref) => {
@@ -19,20 +37,21 @@ const ChatVariableConfigModal = forwardRef<ChatVariableConfigModalRef, VariableE
const [loading, setLoading] = useState(false)
const [initialValues, setInitialValues] = useState<Variable[]>([])
// 封装取消方法,添加关闭弹窗逻辑
/** 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<ChatVariableConfigModalRef, VariableE
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose

View File

@@ -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<string, string> = {
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<WorkflowRef>
/** App component ref (Agent/Cluster/Workflow) */
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
}
/**
* Configuration header component
* Displays application name, tabs, and action buttons
*/
const ConfigHeader: FC<ConfigHeaderProps> = ({
application, activeTab, handleChangeTab, refresh,
workflowRef,
@@ -42,12 +71,18 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
const applicationModalRef = useRef<ApplicationModalRef>(null);
const copyModalRef = useRef<CopyModalRef>(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<ConfigHeaderProps> = ({
onClick: handleClick
}
}
/**
* Handle menu item click
*/
const handleClick: MenuProps['onClick'] = ({ key }) => {
switch (key) {
case 'edit':
@@ -74,6 +112,9 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
break;
}
}
/**
* Delete application with confirmation
*/
const handleDelete = () => {
if (!id) {
return
@@ -86,21 +127,36 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
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()
}

View File

@@ -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<CopyModalRef, CopyModalProps>(({
data
}, ref) => {
@@ -23,17 +41,18 @@ const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
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<CopyModalRef, CopyModalProps>(({
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -68,7 +87,7 @@ const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
form={form}
layout="vertical"
>
{/* 应用名 */}
{/* Application name */}
<FormItem
name="new_name"
label={t('application.applicationName')}

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:25:17
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:25:17
*/
/**
* Rich text editor component using Lexical framework
* Provides text editing with insert, append, clear, and scroll capabilities
*/
import {forwardRef, useImperativeHandle } from 'react';
import clsx from 'clsx';
import { LexicalComposer } from '@lexical/react/LexicalComposer';
@@ -6,25 +17,44 @@ import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { $getSelection, $getRoot, $createParagraphNode, $createTextNode, $isParagraphNode, $isTextNode } from 'lexical';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import InitialValuePlugin from './plugin/InitialValuePlugin'
import LineBreakPlugin from './plugin/LineBreakPlugin';
import InsertTextPlugin from './plugin/InsertTextPlugin';
/**
* Editor ref methods exposed to parent components
*/
export interface EditorRef {
/** Insert text at current cursor position */
insertText: (text: string) => 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<EditorRef, LexicalEditorProps>(({
className = '',
value,
@@ -41,6 +74,13 @@ const EditorContent = forwardRef<EditorRef, LexicalEditorProps>(({
}, 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<EditorRef, LexicalEditorProps>(({
);
});
/**
* Main editor wrapper component
* Initializes Lexical composer with configuration
*/
const Editor = forwardRef<EditorRef, LexicalEditorProps>((props, ref) => {
const initialConfig = {
namespace: 'Editor',

View File

@@ -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<string | undefined>(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 {
// valueundefined或空时,创建一个空段落
// When value is undefined or empty, create an empty paragraph
const paragraph = $createParagraphNode();
root.append(paragraph);
}

View File

@@ -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<EditorRef>((_, ref) => {
/**
* Plugin to insert text at cursor position
*/
const InsertTextPlugin = forwardRef<{ insertText: (text: string) => void; }>((_, ref) => {
const [editor] = useLexicalComposerContext();
useImperativeHandle(ref, () => ({

View File

@@ -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);
});

View File

@@ -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<KnowledgeModalRef>(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]

View File

@@ -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<KnowledgeConfigModalRef, KnowledgeConfigModalProps>(({
refresh,
}, ref) => {
@@ -24,13 +47,14 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
const values = Form.useWatch<KnowledgeConfigForm>([], 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<KnowledgeConfigModalRef, KnowledgeConfig
setData({...data})
setVisible(true);
};
// 封装保存方法,添加提交逻辑
/** Save knowledge configuration */
const handleSave = () => {
form
.validateFields()
@@ -57,7 +81,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
});
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -94,7 +118,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
</div>
)}
<FormItem name="kb_id" hidden />
{/* 检索模式 */}
{/* Retrieval mode */}
<FormItem
name="retrieve_type"
label={t('application.retrieve_type')}
@@ -124,7 +148,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
onChange={(value) => form.setFieldValue('top_k', value)}
/>
</FormItem>
{/* 语义相似度阈值 similarity_threshold */}
{/* Semantic similarity threshold */}
{values?.retrieve_type === 'semantic' && (
<FormItem
name="similarity_threshold"
@@ -139,7 +163,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
/>
</FormItem>
)}
{/* 分词匹配度阈值 vector_similarity_weight */}
{/* Word segmentation matching threshold */}
{values?.retrieve_type === 'participle' && (
<FormItem
name="vector_similarity_weight"
@@ -154,7 +178,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
/>
</FormItem>
)}
{/* 混合检索权重 */}
{/* Hybrid retrieval weight */}
{values?.retrieve_type === 'hybrid' && (
<>
<FormItem

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:25:42
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:25:42
*/
/**
* Knowledge Global Configuration Modal
* Configures global reranker settings for all knowledge bases
*/
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Form, InputNumber, Switch } from 'antd';
import { useTranslation } from 'react-i18next';
@@ -9,11 +20,19 @@ import { getModelListUrl } from '@/api/models'
const FormItem = Form.Item;
/**
* Component props
*/
interface KnowledgeGlobalConfigModalProps {
/** Current reranker configuration */
data: RerankerConfig;
/** Callback to update reranker configuration */
refresh: (values: RerankerConfig, type: 'rerankerConfig') => void;
}
/**
* Modal for configuring global reranker settings
*/
const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, KnowledgeGlobalConfigModalProps>(({
refresh,
data,
@@ -23,17 +42,18 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
const [form] = Form.useForm<RerankerConfig>();
const values = Form.useWatch<RerankerConfig>([], form);
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
setVisible(false);
form.resetFields();
};
/** Open modal with current configuration */
const handleOpen = () => {
form.setFieldsValue({ ...data, rerank_model: !!data?.reranker_id })
setVisible(true);
};
// 封装保存方法,添加提交逻辑
/** Save reranker configuration */
const handleSave = () => {
form
.validateFields()
@@ -54,7 +74,7 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
}
}, [values?.rerank_model])
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
}));
@@ -73,7 +93,7 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
>
<div className="rb:text-[#5B6167] rb:mb-6">{t('application.globalConfigDesc')}</div>
{/* 结果重排 */}
{/* Result reranking */}
<div className="rb:flex rb:items-center rb:justify-between rb:my-6">
<div className="rb:text-[14px] rb:font-medium rb:leading-5">
{t('application.rerankModel')}

View File

@@ -1,7 +1,19 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:25:49
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:25:49
*/
/**
* Knowledge List Modal
* Displays and allows selection of knowledge bases to associate with the application
*/
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { Space, List } from 'antd';
import { useTranslation } from 'react-i18next';
import clsx from 'clsx'
import type { KnowledgeModalRef, KnowledgeBase } from './types'
import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types'
import RbModal from '@/components/RbModal'
@@ -9,11 +21,19 @@ import { getKnowledgeBaseList } from '@/api/knowledgeBase'
import SearchInput from '@/components/SearchInput'
import Empty from '@/components/Empty'
import { formatDateTime } from '@/utils/format';
/**
* Component props
*/
interface KnowledgeModalProps {
/** Callback to add selected knowledge bases */
refresh: (rows: KnowledgeBase[], type: 'knowledge') => void;
/** Currently selected knowledge bases */
selectedList: KnowledgeBase[];
}
/**
* Modal for selecting knowledge bases
*/
const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
refresh,
selectedList
@@ -26,7 +46,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
const [selectedIds, setSelectedIds] = useState<string[]>([])
const [selectedRows, setSelectedRows] = useState<KnowledgeBase[]>([])
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset state */
const handleClose = () => {
setVisible(false);
setQuery({})
@@ -34,6 +54,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
setSelectedRows([])
};
/** Open modal */
const handleOpen = () => {
setVisible(true);
setQuery({})
@@ -46,6 +67,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
getList()
}
}, [query.keywords, visible])
/** Fetch knowledge base list */
const getList = () => {
getKnowledgeBaseList(undefined, {
...query,
@@ -58,7 +80,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
setList(response.items || [])
})
}
// 封装保存方法,添加提交逻辑
/** Save selected knowledge bases */
const handleSave = () => {
refresh(selectedRows.map(item => ({
...item,
@@ -72,16 +94,18 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
setVisible(false);
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
}));
/** Search knowledge bases */
const handleSearch = (value?: string) => {
setQuery({keywords: value})
setSelectedIds([])
setSelectedRows([])
}
/** Toggle knowledge base selection */
const handleSelect = (item: KnowledgeBase) => {
const index = selectedIds.indexOf(item.id)
if (index === -1) {

View File

@@ -1,30 +1,87 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:25:53
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:25:53
*/
/**
* Type definitions for knowledge base configuration in application settings
*/
import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types'
/**
* Reranker configuration for knowledge retrieval
*/
export interface RerankerConfig {
/** Whether to enable rerank model */
rerank_model?: boolean | undefined;
/** Reranker model ID */
reranker_id?: string | undefined;
/** Top K results for reranking */
reranker_top_k?: number | undefined;
}
/**
* Knowledge retrieval type
* - participle: Word segmentation based retrieval
* - semantic: Semantic similarity based retrieval
* - hybrid: Combination of both methods
*/
export type RetrieveType = 'participle' | 'semantic' | 'hybrid'
/**
* Knowledge base configuration form data
*/
export interface KnowledgeConfigForm {
/** Knowledge base ID */
kb_id?: string;
/** Similarity threshold for retrieval (0-1) */
similarity_threshold?: number;
/** Weight for vector similarity in hybrid mode (0-1) */
vector_similarity_weight?: number;
/** Number of top results to retrieve */
top_k?: number;
/** Retrieval strategy type */
retrieve_type?: RetrieveType;
}
/**
* Knowledge base with configuration
*/
export interface KnowledgeBase extends KnowledgeBaseListItem, KnowledgeConfigForm {
/** Additional configuration object */
config?: KnowledgeConfigForm
}
/**
* Complete knowledge configuration including reranker settings
*/
export interface KnowledgeConfig extends RerankerConfig {
/** List of configured knowledge bases */
knowledge_bases: KnowledgeBase[];
}
/**
* Modal ref for individual knowledge base configuration
*/
export interface KnowledgeConfigModalRef {
/** Open modal with knowledge base data */
handleOpen: (data: KnowledgeBase) => void;
}
/**
* Modal ref for global knowledge configuration
*/
export interface KnowledgeGlobalConfigModalRef {
/** Open global configuration modal */
handleOpen: () => void;
}
/**
* Modal ref for knowledge base selection
*/
export interface KnowledgeModalRef {
/** Open modal with optional existing configuration */
handleOpen: (config?: KnowledgeConfig[]) => void;
}

View File

@@ -1,18 +1,38 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:03
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:28:03
*/
/**
* Line chart card component for displaying statistics
* Uses ECharts to render time-series data with gradient area fill
*/
import { type FC, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import ReactEcharts from 'echarts-for-react';
import * as echarts from 'echarts';
import Empty from '@/components/Empty'
import Empty from '@/components/Empty'
import Card from './Card'
import type { StatisticsItem } from '../types'
/**
* Component props
*/
interface LineCardProps {
/** Chart data points */
chartData: StatisticsItem[];
/** Statistics type key */
type: string;
/** Total count to display */
total: number;
}
/**
* ECharts series configuration
*/
const SeriesConfig = {
type: 'line',
stack: 'Total',
@@ -30,6 +50,9 @@ const SeriesConfig = {
},
}
/**
* Color mapping for different statistic types
*/
const ColorObj: Record<string, string> = {
daily_conversations: '#FFB048',
daily_new_users: '#4DA8FF',
@@ -37,6 +60,10 @@ const ColorObj: Record<string, string> = {
daily_tokens: '#AD88FF'
}
/**
* Line chart card component
* Displays time-series statistics with gradient area chart
*/
const LineCard: FC<LineCardProps> = ({ chartData, type, total }) => {
const { t } = useTranslation()
const chartRef = useRef<ReactEcharts>(null);

View File

@@ -1,3 +1,15 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:07
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:28:07
*/
/**
* Model Configuration Modal
* Allows configuring model parameters like temperature, max_tokens, top_p, etc.
* Supports different sources: model, chat, and multi_agent
*/
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Form, Select } from 'antd';
import { useTranslation } from 'react-i18next';
@@ -9,12 +21,24 @@ import RbSlider from '@/components/RbSlider'
const FormItem = Form.Item;
/**
* Component props
*/
interface ModelConfigModalProps {
/** List of available models */
modelList?: ModelListItem[];
/** Callback to update model configuration */
refresh: (values: ModelConfig, type: Source) => void;
/** Application configuration data */
data: Config;
}
/**
* Modal for configuring model parameters
*/
/**
* Model parameter configuration fields
*/
const configFields = [
{ key: 'temperature', max: 2, min: 0, step: 0.1, defaultValue: 0.7 },
{ key: 'max_tokens', max: 32000, min: 256, step: 1, defaultValue: 2000 },
@@ -36,12 +60,13 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
const values = Form.useWatch([], form);
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
setVisible(false);
form.resetFields();
};
/** Open modal with configuration source */
const handleOpen = (source: Source, model?: any) => {
setSource(source)
if (source === 'model') {
@@ -64,7 +89,7 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
}
setVisible(true);
};
// 封装保存方法,添加提交逻辑
/** Save model configuration */
const handleSave = () => {
form
.validateFields()
@@ -76,13 +101,14 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
console.log('err', err)
});
}
/** Handle model selection change */
const handleChange = (_value: string, option: ModelListItem | ModelListItem[] | undefined) => {
if (source === 'chat') {
form.setFieldValue('label', (option as ModelListItem).name)
}
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:11
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:28:11
*/
/**
* Release Modal
* Allows publishing a new version of the application
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Form, Input } from 'antd';
import { useTranslation } from 'react-i18next';
@@ -9,11 +20,19 @@ import type { Application } from '@/views/ApplicationManagement/types'
const FormItem = Form.Item;
/**
* Component props
*/
interface ReleaseModalProps {
/** Callback to refresh release list */
refreshTable: () => void;
/** Application data */
data: Application
}
/**
* Modal for publishing new application versions
*/
const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
refreshTable,
data
@@ -23,17 +42,18 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
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);
};
// 封装保存方法,添加提交逻辑
/** Publish new release */
const handleSave = () => {
form.validateFields().then(() => {
setLoading(true)
@@ -49,7 +69,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -69,7 +89,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
form={form}
layout="vertical"
>
{/* 版本名 */}
{/* Version name */}
<FormItem
name="version_name"
label={t('application.versionName')}
@@ -80,7 +100,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
>
<Input placeholder={t('common.enter')} />
</FormItem>
{/* 版本描述 */}
{/* Version description */}
<FormItem
name="release_notes"
label={t('application.versionDescription')}

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:46
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:28:46
*/
/**
* Release Share Modal
* Generates and displays a shareable link for a specific application version
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Button, App } from 'antd';
import { ExclamationCircleFilled } from '@ant-design/icons';
@@ -9,10 +20,17 @@ import RbModal from '@/components/RbModal'
import { shareRelease } from '@/api/application'
import RbAlert from '@/components/RbAlert'
/**
* Component props
*/
interface ReleaseShareModalProps {
/** Version to share */
version: Release | null
}
/**
* Modal for sharing application versions
*/
const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProps>(({
version
}, ref) => {
@@ -22,12 +40,13 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
const [loading, setLoading] = useState(false)
const [shareLink, setShareLink] = useState<string | null>(null)
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal */
const handleClose = () => {
setVisible(false);
setLoading(false)
};
/** Open modal and generate share link */
const handleOpen = () => {
if (!version) {
return
@@ -45,13 +64,14 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
setLoading(false)
})
};
/** Copy share link to clipboard */
const handleCopy = () => {
if (!shareLink) return
copy(shareLink)
message.success(t('common.copySuccess'))
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -65,9 +85,9 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
footer={false}
>
<>
<div className="rb:leading-[20px] rb:mb-[8px]">{t('application.shareLink')}</div>
<div className="rb:mb-[12px] rb:flex rb:items-center rb:gap-[10px] rb:justify-between">
<div className="rb:overflow-hidden rb:whitespace-nowrap rb:text-ellipsis rb:cursor-pointer rb:h-[32px] rb:p-[6px_10px] rb:bg-[#FFFFFF] rb:border rb:border-[#EBEBEB] rb:rounded-[6px] rb:leading-[20px]">{shareLink}</div>
<div className="rb:leading-5 rb:mb-2">{t('application.shareLink')}</div>
<div className="rb:mb-3 rb:flex rb:items-center rb:gap-2.5 rb:justify-between">
<div className="rb:overflow-hidden rb:whitespace-nowrap rb:text-ellipsis rb:cursor-pointer rb:h-8 rb:p-[6px_10px] rb:bg-[#FFFFFF] rb:border rb:border-[#EBEBEB] rb:rounded-md rb:leading-5">{shareLink}</div>
<Button type="primary" loading={loading} disabled={!shareLink} onClick={handleCopy}>
{t('common.copy')}

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:28:51
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:28:51
*/
/**
* Sub-Agent Modal
* Allows adding or editing sub-agents in multi-agent cluster configuration
*/
import { forwardRef, useImperativeHandle, useState, type Key } from 'react';
import { Form, Select, Input } from 'antd';
import type { DefaultOptionType } from 'antd/es/select'
@@ -10,10 +21,17 @@ import { getApplicationListUrl } from '@/api/application';
const FormItem = Form.Item;
/**
* Component props
*/
interface SubAgentModalProps {
/** Callback to update sub-agent */
refresh: (agent: SubAgentItem) => void;
}
/**
* Modal for managing sub-agents
*/
const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
refresh,
}, ref) => {
@@ -24,19 +42,20 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
const [editVo, setEditVo] = useState<SubAgentItem>()
const values = Form.useWatch([], form)
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
setVisible(false);
form.resetFields();
setLoading(false)
};
/** Open modal with optional agent data */
const handleOpen = (agent?: SubAgentItem) => {
setVisible(true);
form.setFieldsValue(agent)
setEditVo(agent)
};
// 封装保存方法,添加提交逻辑
/** Save sub-agent configuration */
const handleSave = () => {
form.validateFields().then(() => {
setLoading(false)
@@ -47,6 +66,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
handleClose()
})
}
/** Handle agent selection change */
const handleChange = (value: Key, option?: DefaultOptionType | DefaultOptionType[] | undefined) => {
console.log(value, option)
if (option && !Array.isArray(option)) {
@@ -54,7 +74,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
}
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
@@ -73,7 +93,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
form={form}
layout="vertical"
>
{/* Agent名称 */}
{/* Agent name */}
<FormItem
name="agent_id"
label={t('application.agentName')}
@@ -93,14 +113,14 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
/>
</FormItem>
<FormItem name="name" hidden />
{/* 描述 */}
{/* Description */}
<FormItem
name="role"
label={t('application.description')}
>
<Input.TextArea placeholder={t('common.pleaseEnter')} />
</FormItem>
{/* 关键词 */}
{/* Keywords */}
<FormItem
name="capabilities"
label={t('application.capabilities')}

View File

@@ -1,11 +1,31 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:17
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:29:17
*/
/**
* Tag Component
* Displays colored status tags with different color schemes
*/
import { type FC, type ReactNode } from 'react'
/**
* Tag component props
*/
export interface TagProps {
/** Tag color scheme */
color?: 'processing' | 'warning' | 'default' | 'success';
/** Tag content */
children: ReactNode;
/** Additional CSS classes */
className?: string;
}
/**
* Color scheme mapping
*/
const colors = {
processing: 'rb:text-[#155EEF] rb:border-[rgba(21,94,239,0.25)] rb:bg-[rgba(21,94,239,0.06)]',
warning: 'rb:text-[#FF5D34] rb:border-[rgba(255,93,52,0.08)] rb:bg-[rgba(255,93,52,0.08)]',
@@ -13,9 +33,12 @@ const colors = {
success: 'rb:text-[#369F21] rb:border-[rgba(54,159,33,0.30)] rb:bg-[rgba(54,159,33,0.08)]',
}
/**
* Tag component for displaying status labels
*/
const Tag: FC<TagProps> = ({ color = 'processing', children, className }) => {
return (
<span className={`rb:inline-block rb:px-[8px] rb:py-[2px] rb:rounded-[11px] rb:text-[12px] rb:font-regular rb:leading-[16px] rb:border-[1px] ${colors[color]} ${className || ''}`}>
<span className={`rb:inline-block rb:px-2 rb:py-0.5 rb:rounded-[11px] rb:text-[12px] rb:font-regular rb:leading-4 rb:border ${colors[color]} ${className || ''}`}>
{children}
</span>
)

View File

@@ -1,6 +1,19 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:03
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:03
*/
/**
* Tool List Component
* Manages tool configurations for the application
* Allows adding, removing, and enabling/disabling tools
*/
import { type FC, useRef, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { Space, Button, List, Switch } from 'antd'
import Card from '../Card'
import type {
ToolModalRef,
@@ -10,6 +23,11 @@ import Empty from '@/components/Empty'
import ToolModal from './ToolModal'
import { getToolMethods, getToolDetail } from '@/api/tools'
/**
* Tool list management component
* @param value - Current tool configurations
* @param onChange - Callback when tools change
*/
const ToolList: FC<{ value?: ToolOption[]; onChange?: (config: ToolOption[]) => void}> = ({value, onChange}) => {
const { t } = useTranslation()
const toolModalRef = useRef<ToolModalRef>(null)
@@ -79,19 +97,23 @@ const ToolList: FC<{ value?: ToolOption[]; onChange?: (config: ToolOption[]) =>
}
}, [value])
/** Open tool selection modal */
const handleAddTool = () => {
toolModalRef.current?.handleOpen()
}
/** Add new tool to list */
const updateTools = (tool: ToolOption) => {
const list = [...toolList, tool]
setToolList(list)
onChange && onChange(list)
}
/** Remove tool from list */
const handleDeleteTool = (index: number) => {
const list = toolList.filter((_item, idx) => idx !== index)
setToolList([...list])
onChange && onChange(list)
}
/** Toggle tool enabled state */
const handleChangeEnabled = (index: number) => {
const list = toolList.map((item, idx) => {
if (idx === index) {

View File

@@ -1,18 +1,37 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:06
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:06
*/
/**
* Tool Selection Modal
* Provides cascading selection of tools by type, tool, and method
* Supports MCP, builtin, and custom tool types
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Form, Cascader, type CascaderProps } from 'antd';
import { useTranslation } from 'react-i18next';
import type { ToolModalRef, ToolOption } from '../types'
import type { ToolModalRef, ToolOption } from './types'
import RbModal from '@/components/RbModal'
import { getToolMethods, getTools } from '@/api/tools'
import type { ToolType, ToolItem } from '@/views/ToolManagement/types'
const FormItem = Form.Item;
/**
* Component props
*/
interface ToolModalProps {
/** Callback to add selected tool */
refresh: (tool: ToolOption) => void;
}
/**
* Modal for selecting tools
*/
const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
refresh,
}, ref) => {
@@ -27,7 +46,7 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
])
const [selectdTools, setSelectedTools] = useState<ToolOption[]>([])
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset state */
const handleClose = () => {
setVisible(false);
form.resetFields();
@@ -35,12 +54,13 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
setSelectedTools([])
};
/** Open modal */
const handleOpen = () => {
setVisible(true);
form.resetFields();
setSelectedTools([])
};
// 封装保存方法,添加提交逻辑
/** Save selected tool */
const handleSave = () => {
form.validateFields().then(() => {
setLoading(false)
@@ -64,6 +84,7 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
handleClose()
})
}
/** Load cascader options dynamically */
const loadData = (selectedOptions: ToolOption[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
if (selectedOptions.length === 1) {
@@ -98,12 +119,13 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
}
};
/** Handle cascader selection change */
const handleChange: CascaderProps<ToolOption>['onChange'] = (_value, selectedOptions) => {
console.log('selectedOptions', selectedOptions)
setSelectedTools(selectedOptions)
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose

View File

@@ -1,26 +1,67 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:10
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:10
*/
/**
* Type definitions for tool configuration in application settings
*/
/**
* Tool option for cascader selection
*/
export interface ToolOption {
/** Option value */
value?: string | number | null;
/** Display label */
label?: React.ReactNode;
/** Tool description */
description?: string;
/** Child options for nested selection */
children?: ToolOption[];
/** Whether this is a leaf node */
isLeaf?: boolean;
/** Method ID for API operations */
method_id?: string;
/** Operation name */
operation?: string;
/** Method parameters */
parameters?: Parameter[];
/** Tool ID */
tool_id?: string;
/** Whether tool is enabled */
enabled?: boolean;
}
/**
* Parameter definition for tool methods
*/
export interface Parameter {
/** Parameter name */
name: string;
/** Parameter data type */
type: string;
/** Parameter description */
description: string;
/** Whether parameter is required */
required: boolean;
/** Default value */
default: any;
/** Enum values if applicable */
enum: null | string[];
/** Minimum value for numeric types */
minimum: number;
/** Maximum value for numeric types */
maximum: number;
/** Regex pattern for validation */
pattern: null | string;
}
/**
* Modal ref for tool selection
*/
export interface ToolModalRef {
/** Open tool selection modal */
handleOpen: () => void;
}

View File

@@ -1,3 +1,14 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:18
*/
/**
* API Extension Modal
* Allows configuring external API endpoints for dynamic variable options
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Form, Input } from 'antd';
import { useTranslation } from 'react-i18next';
@@ -7,10 +18,17 @@ import RbModal from '@/components/RbModal'
const FormItem = Form.Item;
/**
* Component props
*/
interface ApiExtensionModalProps {
/** Callback to refresh API extension list */
refresh: () => void;
}
/**
* Modal for adding API extensions
*/
const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProps>(({
refresh
}, ref) => {
@@ -21,18 +39,19 @@ const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProp
const values = Form.useWatch([], form);
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
setVisible(false);
form.resetFields();
setLoading(false)
};
/** Open modal */
const handleOpen = () => {
form.resetFields();
setVisible(true);
};
// 封装保存方法,添加提交逻辑
/** Save API extension configuration */
const handleSave = () => {
form
.validateFields()
@@ -46,7 +65,7 @@ const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProp
});
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose

View File

@@ -1,3 +1,15 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:27
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:27
*/
/**
* Variable Edit Modal
* Allows creating and editing application input variables
* Supports multiple variable types: text, paragraph, number, dropdown, checkbox, API variable
*/
import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
import { Form, Input, Select, InputNumber, Checkbox, Tag, Divider, Button } from 'antd';
import { useTranslation } from 'react-i18next';
@@ -9,10 +21,17 @@ import ApiExtensionModal from './ApiExtensionModal'
const FormItem = Form.Item;
/**
* Component props
*/
interface VariableEditModalProps {
/** Callback to update variable */
refreshTable: (values: Variable) => void;
}
/**
* Supported variable types
*/
const types = [
'text',
'paragraph',
@@ -21,6 +40,9 @@ const types = [
// 'checkbox',
// 'apiVariable'
]
/**
* Variable type to data type mapping
*/
const variableType = {
text: 'string',
paragraph: 'string',
@@ -30,6 +52,9 @@ const variableType = {
apiVariable: 'string',
}
/**
* Modal for editing variables
*/
const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProps>(({
refreshTable
}, ref) => {
@@ -42,7 +67,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
const values = Form.useWatch([], form);
// 封装取消方法,添加关闭弹窗逻辑
/** Close modal and reset form */
const handleClose = () => {
setVisible(false);
form.resetFields();
@@ -50,6 +75,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
setEditVo(null)
};
/** Open modal with optional variable data */
const handleOpen = (variable?: Variable) => {
setVisible(true);
if (variable) {
@@ -59,7 +85,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
form.resetFields();
}
};
// 封装保存方法,添加提交逻辑
/** Save variable configuration */
const handleSave = () => {
form.validateFields().then((values) => {
refreshTable({
@@ -70,12 +96,12 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
})
}
// 暴露给父组件的方法
/** Expose methods to parent component */
useImperativeHandle(ref, () => ({
handleOpen,
handleClose
}));
// 变量类型改变时,更新初始化其他字段值
/** Handle variable type change */
const handleChangeType = (value: Variable['type']) => {
if (value) {
form.setFieldsValue({
@@ -90,7 +116,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
})
}
}
// 添加 API 扩展
/** Add API extension */
const addApiExtension = () => {
apiExtensionModalRef.current?.handleOpen()
}

View File

@@ -1,6 +1,19 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:32
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:32
*/
/**
* Variable List Component
* Manages application input variables configuration
* Allows adding, editing, and removing variables
*/
import { type FC, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { Space, Button, Switch, Form } from 'antd'
import variablesEmpty from '@/assets/images/application/variablesEmpty.svg'
import Card from '../Card'
import Table from '@/components/Table';
@@ -8,17 +21,27 @@ import type { Variable, VariableEditModalRef } from './types'
import Empty from '@/components/Empty'
import VariableEditModal from './VariableEditModal'
/**
* Component props
*/
interface VariableListProps {
/** Current variable list */
value?: Variable[];
/** Callback when variables change */
onChange?: (value: Variable[]) => void;
}
const VariableList: FC<VariableListProps> = ({value = [], onChange}) => {
/**
* Variable list management component
*/const VariableList: FC<VariableListProps> = ({value = [], onChange}) => {
const { t } = useTranslation()
const variableEditModalRef = useRef<VariableEditModalRef>(null)
/** Open variable edit modal */
const handleAddVariable = () => {
variableEditModalRef.current?.handleOpen()
}
/** Save variable changes */
const handleSaveVariable = (variable: Variable) => {
const newList = [...(value || [])]
if (variable.index !== undefined && variable.index >= 0) {

View File

@@ -1,28 +1,69 @@
export interface Variable {
index?: number;
name: string;
display_name: string;
type: string;
required: boolean;
max_length?: number;
description?: string;
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:26:21
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:26:21
*/
/**
* Type definitions for variable configuration in application settings
*/
/**
* Variable definition for application input
*/
export interface Variable {
/** Variable index in list */
index?: number;
/** Variable name (identifier) */
name: string;
/** Display name shown to users */
display_name: string;
/** Variable data type (string, number, select, etc.) */
type: string;
/** Whether variable is required */
required: boolean;
/** Maximum length for string types */
max_length?: number;
/** Variable description */
description?: string;
/** Unique key for React rendering */
key?: string;
/** Default value */
default_value?: string;
/** Options for select type variables */
options?: string[];
/** API extension for dynamic options */
api_extension?: string;
/** Whether variable is hidden from UI */
hidden?: boolean;
/** Current value */
value?: any;
}
/**
* Modal ref for variable editing
*/
export interface VariableEditModalRef {
/** Open modal with optional existing variable data */
handleOpen: (values?: Variable) => void;
}
/**
* API extension configuration data
*/
export interface ApiExtensionModalData {
/** Extension name */
name: string;
/** API endpoint URL */
apiEndpoint: string;
/** API authentication key */
apiKey: string;
}
/**
* Modal ref for API extension configuration
*/
export interface ApiExtensionModalRef {
/** Open API extension modal */
handleOpen: () => void;
}

View File

@@ -1,5 +1,12 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:37
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:29:37
*/
import React, { useEffect, useState, useRef } from 'react';
import { useParams } from 'react-router-dom';
import ConfigHeader from './components/ConfigHeader'
import type { AgentRef, ClusterRef, WorkflowRef } from './types'
import type { Application } from '@/views/ApplicationManagement/types'
@@ -11,14 +18,28 @@ import { getApplication } from '@/api/application'
import Workflow from '@/views/Workflow';
import Statistics from './Statistics'
/**
* Application configuration page component
* Main container for configuring agents, workflows, multi-agent clusters
* Manages tabs for arrangement, API, release, and statistics
*/
const ApplicationConfig: React.FC = () => {
// Hooks
const { id } = useParams();
// Refs for different application types
const agentRef = useRef<AgentRef>(null)
const clusterRef = useRef<ClusterRef>(null)
const workflowRef = useRef<WorkflowRef>(null)
// State
const [application, setApplication] = useState<Application | null>(null);
const [activeTab, setActiveTab] = useState('arrangement');
/**
* Handle tab change with auto-save for arrangement tab
* @param key - New tab key
*/
const handleChangeTab = async (key: string) => {
if (activeTab === 'arrangement' && application?.type === 'agent' && agentRef.current) {
agentRef.current.handleSave(false)
@@ -44,6 +65,9 @@ const ApplicationConfig: React.FC = () => {
getApplicationInfo()
}, [id])
/**
* Fetch application information
*/
const getApplicationInfo = () => {
if (!id) {
return

View File

@@ -1,3 +1,9 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:49
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:29:49
*/
import type { KnowledgeConfig } from './components/Knowledge/types'
import type { Variable } from './components/VariableList/types'
import type { ToolOption } from './components/ToolList/types'
@@ -5,164 +11,389 @@ import type { ChatItem } from '@/components/Chat/types'
import type { GraphRef } from '@/views/Workflow/types';
import type { ApiKey } from '@/views/ApiKeyManagement/types'
/**
* Model configuration parameters
*/
export interface ModelConfig {
/** Model label */
label?: string;
/** Default model configuration ID */
default_model_config_id?: string;
/** Temperature for response randomness (0-2) */
temperature: number;
/** Maximum tokens in response */
max_tokens: number;
/** Top-p sampling parameter */
top_p: number;
/** Frequency penalty */
frequency_penalty: number;
/** Presence penalty */
presence_penalty: number;
/** Number of completions to generate */
n: number;
/** Stop sequences */
stop?: string;
}
/**
* Memory configuration
*/
export interface MemoryConfig {
/** Whether memory is enabled */
enabled: boolean;
/** Memory content */
memory_content?: string;
/** Maximum history length */
max_history?: number | string;
}
/**
* Application configuration
*/
export interface Config extends MultiAgentConfig {
/** Configuration ID */
id: string;
/** Application ID */
app_id: string;
/** System prompt */
system_prompt: string;
/** Default model configuration ID */
default_model_config_id?: string;
/** Model parameters */
model_parameters: ModelConfig;
/** Knowledge retrieval configuration */
knowledge_retrieval: KnowledgeConfig | null;
/** Memory configuration */
memory?: MemoryConfig;
/** Variables list */
variables: Variable[];
/** Tools list */
tools: ToolOption[];
/** Whether configuration is active */
is_active: boolean;
/** Creation timestamp */
created_at: number;
/** Last update timestamp */
updated_at: number;
}
/**
* Multi-agent configuration
*/
export interface MultiAgentConfig {
/** Configuration ID */
id: string;
/** Application ID */
app_id: string;
/** Default model configuration ID */
default_model_config_id?: string;
/** Model parameters */
model_parameters: ModelConfig;
/** Sub-agents list */
sub_agents?: SubAgentItem[];
/** Routing rules */
routing_rules: null;
/** Orchestration mode */
orchestration_mode: 'supervisor' | 'collaboration';
/** Execution configuration */
execution_config: {
/** Sub-agent execution mode */
sub_agent_execution_mode: 'sequential' | 'parallel';
};
/** Aggregation strategy */
aggregation_strategy: 'merge' | 'vote' | 'priority'
}
// 创建表单数据类型
/**
* Application modal form data
*/
export interface ApplicationModalData {
/** Application name */
name: string;
/** Application type */
type: string;
/** Application icon */
icon: string;
}
// 定义组件暴露的方法接口
/**
* Agent component ref methods
*/
export interface AgentRef {
/**
* Save agent configuration
* @param flag - Whether to show success message
*/
handleSave: (flag?: boolean) => Promise<unknown>;
}
/**
* Cluster component ref methods
*/
export interface ClusterRef {
/**
* Save cluster configuration
* @param flag - Whether to show success message
*/
handleSave: (flag?: boolean) => Promise<unknown>;
}
/**
* Workflow component ref methods
*/
export interface WorkflowRef {
/**
* Save workflow configuration
* @param flag - Whether to show success message
*/
handleSave: (flag?: boolean) => Promise<unknown>;
/** Run workflow */
handleRun: () => void;
/** Graph reference */
graphRef: GraphRef;
/** Add variable */
addVariable: () => void;
}
/**
* Application modal ref methods
*/
export interface ApplicationModalRef {
/**
* Open application modal
* @param application - Optional application data for edit mode
*/
handleOpen: (application?: Config) => void;
}
/**
* Model configuration source type
*/
export type Source = 'chat' | 'model' | 'multi_agent'
/**
* Model configuration modal ref methods
*/
export interface ModelConfigModalRef {
/**
* Open model configuration modal
* @param source - Configuration source
* @param model - Optional model data
*/
handleOpen: (source: Source, model?: any) => void;
}
/**
* Model configuration modal form data
*/
export interface ModelConfigModalData {
/** Model identifier */
model: string;
/** Additional configuration fields */
[key: string]: string;
}
/**
* AI prompt modal ref methods
*/
export interface AiPromptModalRef {
handleOpen: () => void;
}
export interface ChatData {
label?: string;
model_config_id?: string;
model_parameters?: ModelConfig;
list?: ChatItem[];
conversation_id?: string | null;
}
export interface Release {
id: string;
app_id: string;
version: string;
release_notes: string;
name: string;
description?: string;
icon: string;
icon_type?: string;
type: string;
visibility: string;
config: Config;
default_model_config_id?: string;
published_by?: string;
published_at: number;
publisher_name?: string;
is_active?: boolean;
created_at?: number;
updated_at?: number;
status?: string;
version_name?: string;
tagKey: 'current' | 'rolledBack' | 'history';
}
export interface ReleaseModalRef {
handleOpen: () => void;
}
export interface ReleaseShareModalRef {
handleOpen: () => void;
}
export interface CopyModalRef {
handleOpen: () => void;
}
export interface SubAgentItem {
agent_id: string;
name: string;
role: string;
capabilities: string[];
is_active?: boolean;
}
export interface SubAgentModalRef {
handleOpen: (agent?: SubAgentItem) => void;
}
export interface ApiKeyModalRef {
handleOpen: () => void;
}
export interface ApiKeyConfigModalRef {
handleOpen: (apiKey: ApiKey) => void;
}
export interface AiPromptVariableModalRef {
/** Open AI prompt modal */
handleOpen: () => void;
}
/**
* Chat data structure
*/
export interface ChatData {
/** Chat label */
label?: string;
/** Model configuration ID */
model_config_id?: string;
/** Model parameters */
model_parameters?: ModelConfig;
/** Chat messages list */
list?: ChatItem[];
/** Conversation ID */
conversation_id?: string | null;
}
/**
* Release version data
*/
export interface Release {
/** Release ID */
id: string;
/** Application ID */
app_id: string;
/** Version number */
version: string;
/** Release notes */
release_notes: string;
/** Release name */
name: string;
/** Release description */
description?: string;
/** Application icon */
icon: string;
/** Icon type */
icon_type?: string;
/** Application type */
type: string;
/** Visibility setting */
visibility: string;
/** Configuration snapshot */
config: Config;
/** Default model configuration ID */
default_model_config_id?: string;
/** Publisher user ID */
published_by?: string;
/** Publication timestamp */
published_at: number;
/** Publisher name */
publisher_name?: string;
/** Whether release is active */
is_active?: boolean;
/** Creation timestamp */
created_at?: number;
/** Last update timestamp */
updated_at?: number;
/** Release status */
status?: string;
/** Version name */
version_name?: string;
/** Tag key for UI display */
tagKey: 'current' | 'rolledBack' | 'history';
}
/**
* Release modal ref methods
*/
export interface ReleaseModalRef {
/** Open release modal */
handleOpen: () => void;
}
/**
* Release share modal ref methods
*/
export interface ReleaseShareModalRef {
/** Open release share modal */
handleOpen: () => void;
}
/**
* Copy modal ref methods
*/
export interface CopyModalRef {
/** Open copy modal */
handleOpen: () => void;
}
/**
* Sub-agent item data
*/
export interface SubAgentItem {
/** Agent ID */
agent_id: string;
/** Agent name */
name: string;
/** Agent role */
role: string;
/** Agent capabilities */
capabilities: string[];
/** Whether agent is active */
is_active?: boolean;
}
/**
* Sub-agent modal ref methods
*/
export interface SubAgentModalRef {
/**
* Open sub-agent modal
* @param agent - Optional agent data for edit mode
*/
handleOpen: (agent?: SubAgentItem) => void;
}
/**
* API key modal ref methods
*/
export interface ApiKeyModalRef {
/** Open API key modal */
handleOpen: () => void;
}
/**
* API key configuration modal ref methods
*/
export interface ApiKeyConfigModalRef {
/**
* Open API key configuration modal
* @param apiKey - API key data
*/
handleOpen: (apiKey: ApiKey) => void;
}
/**
* AI prompt variable modal ref methods
*/
export interface AiPromptVariableModalRef {
/** Open AI prompt variable modal */
handleOpen: () => void;
}
/**
* AI prompt form data
*/
export interface AiPromptForm {
/** Model ID */
model_id?: string;
/** Message content */
message?: string;
/** Current prompt */
current_prompt?: string;
}
/**
* Chat variable configuration modal ref methods
*/
export interface ChatVariableConfigModalRef {
/**
* Open chat variable configuration modal
* @param values - Variables list
*/
handleOpen: (values: Variable[]) => void;
}
/**
* Statistics item data
*/
export interface StatisticsItem {
/** Count value */
count: number;
/** Date string */
date: string;
}
/**
* Statistics data structure
*/
export interface StatisticsData {
/** Daily conversations statistics */
daily_conversations: StatisticsItem[];
/** Daily new users statistics */
daily_new_users: StatisticsItem[];
/** Daily API calls statistics */
daily_api_calls: StatisticsItem[];
/** Daily tokens usage statistics */
daily_tokens: StatisticsItem[];
/** Total conversations count */
total_conversations: number;
/** Total new users count */
total_new_users: number;
/** Total API calls count */
total_api_calls: number;
/** Total tokens used */
total_tokens: number;
}