/* * @Author: ZhaoYing * @Date: 2026-02-03 16:29:21 * @Last Modified by: ZhaoYing * @Last Modified time: 2026-02-06 11:20:14 */ 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' import ModelConfigModal from './components/ModelConfigModal' import type { ModelConfigModalRef, ChatData, Config, ModelConfig, AgentRef, MemoryConfig, AiPromptModalRef, Source, ChatVariableConfigModalRef } from './types' import type { Variable } from './components/VariableList/types' import type { KnowledgeConfig } from './components/Knowledge/types' import type { ModelListItem } from '@/views/ModelManagement/types' import { getModelList } from '@/api/models'; import { saveAgentConfig } from '@/api/application' import Knowledge from './components/Knowledge/Knowledge' import VariableList from './components/VariableList/VariableList' import { getApplicationConfig } from '@/api/application' import { memoryConfigListUrl } from '@/api/memory' import CustomSelect from '@/components/CustomSelect' import aiPrompt from '@/assets/images/application/aiPrompt.png' import AiPromptModal from './components/AiPromptModal' import ToolList from './components/ToolList/ToolList' import SkillList from './components/Skill' import ChatVariableConfigModal from './components/ChatVariableConfigModal'; import type { Skill } from '@/views/Skills/types' /** * Description wrapper component * @param desc - Description text * @param className - Additional CSS classes */ const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => { return (
{desc}
) } /** * 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 (
{title} {children}
) } /** * 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 (
{desc && }
) } /** * 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 ( <> ) } /** * Agent configuration component * Manages single agent configuration including prompts, knowledge, memory, variables, and tools */ const Agent = forwardRef((_props, ref) => { const { t } = useTranslation() const { id } = useParams(); const { message } = App.useApp() const [form] = Form.useForm() const [loading, setLoading] = useState(false) const [data, setData] = useState(null); const modelConfigModalRef = useRef(null) const [modelList, setModelList] = useState([]) const [defaultModel, setDefaultModel] = useState(null) const [chatList, setChatList] = useState([]) const values = Form.useWatch([], form) const [isSave, setIsSave] = useState(false) const initialized = useRef(false) console.log('chatList', chatList) // Initialization flag useEffect(() => { if (data) { initialized.current = true } }, [data]) useEffect(() => { if (!initialized.current) return if (isSave) return setIsSave(true) }, [values]) useEffect(() => { getModels() getData() }, []) /** * Fetch agent configuration data */ const getData = () => { setLoading(true) getApplicationConfig(id as string).then(res => { const response = res as Config const { skills } = response let allSkills = Array.isArray(skills?.skill_ids) ? skills?.skill_ids.map(vo => ({ id: vo })) : [] let allTools = Array.isArray(response.tools) ? response.tools : [] const memoryContent = response.memory?.memory_config_id const parsedMemoryContent = memoryContent === null || memoryContent === '' ? undefined : !isNaN(Number(memoryContent)) ? Number(memoryContent) : memoryContent form.setFieldsValue({ ...response, tools: allTools, memory: { ...response.memory, memory_config_id: parsedMemoryContent }, skills: { ...skills, skill_ids: allSkills } }) setData({ ...response, tools: allTools }) }).finally(() => { setLoading(false) }) } /** * 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 form.setFieldsValue({ default_model_config_id, model_parameters: {...rest} }) if (default_model_config_id === values?.default_model_config_id) { setChatList([{ label: vo.label || '', model_config_id: default_model_config_id || '', model_parameters: {...rest}, list: [] }]) } } else if (type === 'chat') { if (chatList.length >= 4) { message.warning(t('application.maxChatCount')) return } const { label, default_model_config_id, ...reset } = vo setChatList((prev: ChatData[]) => { const newChatItem: ChatData = { label, model_config_id: default_model_config_id || '', model_parameters: {...reset}, list: [] }; return [ ...(prev || []).map(item => ({ ...item, conversation_id: undefined, list: [] })), newChatItem ]; }) } } /** * Open model configuration modal */ const handleModelConfig = () => { modelConfigModalRef.current?.handleOpen('model') } /** * Clear all debugging chat sessions */ const handleClearDebugging = () => { setChatList([]) } /** * 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, skills, ...rest } = values const { knowledge_bases = [], ...knowledgeRest } = knowledge_retrieval || {} const { memory_config_id } = memory || {} // Get other necessary properties of memory from original data const originalMemory = data.memory || ({} as MemoryConfig) const params: Config = { ...data, ...rest, memory: { ...originalMemory, ...memory, memory_config_id: memory_config_id ? String(memory_config_id) : '', }, knowledge_retrieval: knowledge_bases.length > 0 ? { ...data.knowledge_retrieval, ...knowledgeRest, knowledge_bases: knowledge_bases.map(item => ({ kb_id: item.kb_id || item.id, ...(item.config || {}) })) } as KnowledgeConfig : null, tools: tools.map(vo => ({ tool_id: vo.tool_id, operation: vo.operation, enabled: vo.enabled })), skills: { ...skills, skill_ids: (skills?.skill_ids as Skill[])?.map(vo => vo.id) } } return new Promise((resolve, reject) => { saveAgentConfig(data.app_id, params) .then((res) => { if (flag) { message.success(t('common.saveSuccess')) } setIsSave(false) resolve(res) }).catch(error => { reject(error) }) }) } /** * Fetch available models list */ const getModels = () => { getModelList({ type: 'llm,chat', pagesize: 100, page: 1, is_active: true }) .then(res => { const response = res as { items: ModelListItem[] } setModelList(response.items) }) } /** * Add new model for debugging */ const handleAddModel = () => { modelConfigModalRef.current?.handleOpen('chat') } useEffect(() => { if (values?.default_model_config_id && modelList.length > 0) { const filterValue = modelList.find(item => item.id === values.default_model_config_id) setDefaultModel(filterValue as ModelListItem | null) setChatList([{ label: filterValue?.name || '', model_config_id: filterValue?.id || '', model_parameters: {...(filterValue?.config || {})} as unknown as ModelConfig, list: [] }]) } }, [modelList, values?.default_model_config_id]) useImperativeHandle(ref, () => ({ handleSave })) const aiPromptModalRef = useRef(null) /** * Open AI prompt generation modal */ const handlePrompt = () => { aiPromptModalRef.current?.handleOpen() } /** * Update prompt and extract variables * @param value - New prompt value */ const updatePrompt = (value: string) => { form.setFieldValue('system_prompt', value) const variables = value.match(/\{\{([^}]+)\}\}/g)?.map(match => match.slice(2, -2)) || [] const uniqueVariables = [...new Set(variables)] const newVariableList: Variable[] = uniqueVariables.map((name, index) => ({ index, type: 'text', name, display_name: name, required: false })) updateVariableList(newVariableList) } /** * Update variable list * @param list - New variable list */ const updateVariableList = (list: Variable[]) => { form.setFieldValue('variables', [...list]) setChatVariables([...list]) } const chatVariableConfigModalRef = useRef(null) const [chatVariables, setChatVariables] = useState([]) /** * Open chat variable configuration modal */ const handleOpenVariableConfig = () => { chatVariableConfigModalRef.current?.handleOpen(chatVariables) } /** * Save chat variable configuration * @param values - Variable values */ const handleSaveChatVariable = (values: Variable[]) => { setChatVariables(values) } console.log('values', values) return ( <> {loading && }
{t('application.configuration')} ({t('application.configurationDesc')})
{/* Memory Configuration */} {/* Tool Configuration */}
{t('application.debuggingAndPreview')}
); }); export default Agent;