From 67d0b196b8202215e5178baa8877d58268eda33e Mon Sep 17 00:00:00 2001 From: zhaoying Date: Fri, 16 Jan 2026 13:56:36 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(web):=20loop=E3=80=81iteration=20sub=20?= =?UTF-8?q?node=20move=20bugfix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/Workflow/hooks/useWorkflowGraph.ts | 90 +++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/web/src/views/Workflow/hooks/useWorkflowGraph.ts b/web/src/views/Workflow/hooks/useWorkflowGraph.ts index 77ea56ca..75bd2517 100644 --- a/web/src/views/Workflow/hooks/useWorkflowGraph.ts +++ b/web/src/views/Workflow/hooks/useWorkflowGraph.ts @@ -666,6 +666,77 @@ export const useWorkflowGraph = ({ graphRef.current.resize(containerRef.current.offsetWidth, containerRef.current.offsetHeight); } }; + + const nodeChangePosition = ({ node, options }: { node: Node; options: { skipParentHandler?: boolean } }) => { + const embedPadding = 50; // Define the embed padding constant + if (options.skipParentHandler) { + return + } + + const children = node.getChildren() + if (children && children.length) { + node.prop('originPosition', node.getPosition()) + } + + const parent = node.getParent() + if (parent && parent.isNode()) { + let originSize = parent.prop('originSize') + if (originSize == null) { + originSize = parent.getSize() + parent.prop('originSize', originSize) + } + + let originPosition = parent.prop('originPosition') + if (originPosition == null) { + originPosition = parent.getPosition() + parent.prop('originPosition', originPosition) + } + + let x = originPosition.x + let y = originPosition.y + let cornerX = originPosition.x + originSize.width + let cornerY = originPosition.y + originSize.height + let hasChange = false + + const children = parent.getChildren() + if (children) { + children.forEach((child) => { + const bbox = child.getBBox().inflate(embedPadding) + const corner = bbox.getCorner() + + if (bbox.x < x) { + x = bbox.x + hasChange = true + } + + if (bbox.y < y) { + y = bbox.y + hasChange = true + } + + if (corner.x > cornerX) { + cornerX = corner.x + hasChange = true + } + + if (corner.y > cornerY) { + cornerY = corner.y + hasChange = true + } + }) + } + + if (hasChange) { + parent.prop( + { + position: { x, y }, + size: { width: cornerX - x, height: cornerY - y }, + }, + { skipParentHandler: true }, + ) + } + } + } // 初始化 const init = () => { @@ -764,10 +835,7 @@ export const useWorkflowGraph = ({ }, }, embedding: { - enabled: true, - validate (this) { - return false - } + enabled: false, }, translating: { restrict(view) { @@ -783,6 +851,17 @@ export const useWorkflowGraph = ({ return null }, }, + highlighting: { + embedding: { + name: 'stroke', + args: { + padding: -1, + attrs: { + stroke: '#73d13d', + }, + }, + }, + }, }); // 使用插件 setupPlugins(); @@ -824,7 +903,8 @@ export const useWorkflowGraph = ({ // 监听缩放事件 graphRef.current.on('scale', scaleEvent); // 监听节点移动事件 - graphRef.current.on('node:moved', nodeMoved); + // graphRef.current.on('node:moved', nodeMoved); + graphRef.current.on('node:change:position', nodeChangePosition); // 监听画布变化事件 const events = [ From a6a18b73046a66a16690260db42f4635b77df7af Mon Sep 17 00:00:00 2001 From: zhaoying Date: Fri, 16 Jan 2026 13:57:46 +0800 Subject: [PATCH 2/3] feat(web): menu order adjustment --- web/src/store/menu.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/web/src/store/menu.json b/web/src/store/menu.json index b49788a8..62f6c13c 100644 --- a/web/src/store/menu.json +++ b/web/src/store/menu.json @@ -332,21 +332,6 @@ } ] }, - { - "id": 19, - "parent": 0, - "code": "member", - "label": "成员管理", - "i18nKey": "menu.memberManagement", - "path": "/member", - "enable": true, - "display": true, - "level": 1, - "sort": 0, - "icon": null, - "iconActive": null, - "subs": null - }, { "id": 10, "parent": 0, @@ -377,6 +362,21 @@ "iconActive": null, "subs": null }, + { + "id": 19, + "parent": 0, + "code": "member", + "label": "成员管理", + "i18nKey": "menu.memberManagement", + "path": "/member", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, { "id": 12, "parent": 0, From c2c2b306a28eade36e8391d53d6aff3d43a5902c Mon Sep 17 00:00:00 2001 From: zhaoying Date: Fri, 16 Jan 2026 15:48:02 +0800 Subject: [PATCH 3/3] refactor: agent config refactor --- web/src/i18n/zh.ts | 2 +- web/src/views/ApplicationConfig/Agent.tsx | 209 ++++++------------ .../ApplicationConfig/components/Card.tsx | 3 + .../components/ChatVariableConfigModal.tsx | 101 +++++++++ .../components/{ => Knowledge}/Knowledge.tsx | 105 +++++---- .../{ => Knowledge}/KnowledgeConfigModal.tsx | 39 ++-- .../KnowledgeGlobalConfigModal.tsx | 19 +- .../{ => Knowledge}/KnowledgeListModal.tsx | 17 +- .../components/Knowledge/types.ts | 30 +++ .../components/{ => ToolList}/ToolList.tsx | 19 +- .../components/{ => ToolList}/ToolModal.tsx | 0 .../components/ToolList/types.ts | 26 +++ .../components/VariableList.tsx | 131 ----------- .../{ => VariableList}/ApiExtensionModal.tsx | 2 +- .../{ => VariableList}/VariableEditModal.tsx | 11 +- .../components/VariableList/VariableList.tsx | 110 +++++++++ .../components/VariableList/types.ts | 28 +++ web/src/views/ApplicationConfig/types.ts | 92 +------- 18 files changed, 501 insertions(+), 443 deletions(-) create mode 100644 web/src/views/ApplicationConfig/components/ChatVariableConfigModal.tsx rename web/src/views/ApplicationConfig/components/{ => Knowledge}/Knowledge.tsx (54%) rename web/src/views/ApplicationConfig/components/{ => Knowledge}/KnowledgeConfigModal.tsx (76%) rename web/src/views/ApplicationConfig/components/{ => Knowledge}/KnowledgeGlobalConfigModal.tsx (86%) rename web/src/views/ApplicationConfig/components/{ => Knowledge}/KnowledgeListModal.tsx (88%) create mode 100644 web/src/views/ApplicationConfig/components/Knowledge/types.ts rename web/src/views/ApplicationConfig/components/{ => ToolList}/ToolList.tsx (93%) rename web/src/views/ApplicationConfig/components/{ => ToolList}/ToolModal.tsx (100%) create mode 100644 web/src/views/ApplicationConfig/components/ToolList/types.ts delete mode 100644 web/src/views/ApplicationConfig/components/VariableList.tsx rename web/src/views/ApplicationConfig/components/{ => VariableList}/ApiExtensionModal.tsx (99%) rename web/src/views/ApplicationConfig/components/{ => VariableList}/VariableEditModal.tsx (96%) create mode 100644 web/src/views/ApplicationConfig/components/VariableList/VariableList.tsx create mode 100644 web/src/views/ApplicationConfig/components/VariableList/types.ts diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 028202d1..6b46084f 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -658,8 +658,8 @@ export const zh = { priority: '结构化整合', addTool: '添加工具', tool: '工具', + variableConfig: '配置变量' }, - // 角色管理相关翻译 role: { roleManagement: '角色管理', roleId: '角色ID', diff --git a/web/src/views/ApplicationConfig/Agent.tsx b/web/src/views/ApplicationConfig/Agent.tsx index 92170d55..9aab1110 100644 --- a/web/src/views/ApplicationConfig/Agent.tsx +++ b/web/src/views/ApplicationConfig/Agent.tsx @@ -13,26 +13,25 @@ import type { Config, ModelConfig, AgentRef, - KnowledgeBase, - KnowledgeConfig, - Variable, MemoryConfig, AiPromptModalRef, Source, - ToolOption + ChatVariableConfigModalRef } from './types' +import type { Variable } from './components/VariableList/types' +import type { KnowledgeConfig } from './components/Knowledge/types' import type { Model } from '@/views/ModelManagement/types' import { getModelList } from '@/api/models'; import { saveAgentConfig } from '@/api/application' -import Knowledge from './components/Knowledge' -import VariableList from './components/VariableList' +import Knowledge from './components/Knowledge/Knowledge' +import VariableList from './components/VariableList/VariableList' import { getApplicationConfig } from '@/api/application' -import { getKnowledgeBaseList } from '@/api/knowledgeBase' 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' +import ToolList from './components/ToolList/ToolList' +import ChatVariableConfigModal from './components/ChatVariableConfigModal'; const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => { return ( @@ -66,7 +65,7 @@ const SwitchWrapper: FC<{ title: string, desc?: string, name: string | string[]; ) } -const SelectWrapper: FC<{ title: string, desc: string, name: string, url: string }> = ({ title, desc, name, url }) => { +const SelectWrapper: FC<{ title: string, desc: string, name: string | string[], url: string }> = ({ title, desc, name, url }) => { const { t } = useTranslation(); return ( <> @@ -77,6 +76,7 @@ const SelectWrapper: FC<{ title: string, desc: string, name: string, url: string className="rb:mb-0!" > ((_props, ref) => { const [modelList, setModelList] = useState([]) const [defaultModel, setDefaultModel] = useState(null) const [chatList, setChatList] = useState([]) - const [formData, setFormData] = useState<{ - default_model_config_id?: string, - model_parameters?: Config['model_parameters'], - tools: ToolOption[], - } | null>(null) - const values = Form.useWatch<{ - memoryEnabled: boolean; - memory_content?: string | number; - } & Config>([], form) - - const [knowledgeConfig, setKnowledgeConfig] = useState({ knowledge_bases: [] }) - const [variableList, setVariableList] = useState([]) + const values = Form.useWatch([], form) const [isSave, setIsSave] = useState(false) const initialized = useRef(false) - const [toolList, setToolList] = useState([]) // 初始化完成标记 useEffect(() => { - if (data && values && formData) { + if (data) { initialized.current = true } - }, [data, values, formData]) + }, [data]) - useEffect(() => { - if (!initialized.current) return - if (isSave) return - setIsSave(true) - }, [knowledgeConfig]) - useEffect(() => { - if (!initialized.current) return - if (isSave) return - setIsSave(true) - }, [variableList]) - useEffect(() => { - if (!initialized.current) return - if (isSave) return - setIsSave(true) - }, [formData]) useEffect(() => { if (!initialized.current) return if (isSave) return setIsSave(true) }, [values]) - useEffect(() => { - if (!initialized.current) return - if (isSave) return - setIsSave(true) - }, [toolList]) useEffect(() => { getModels() @@ -157,68 +125,19 @@ const Agent = forwardRef((_props, ref) => { setLoading(true) getApplicationConfig(id as string).then(res => { const response = res as Config - setData({ - ...response, - tools: Array.isArray(response.tools) ? response.tools : [] - }) - const { memory, tools } = response + let allTools = Array.isArray(response.tools) ? response.tools : [] form.setFieldsValue({ ...response, - memoryEnabled: memory?.enabled || false, - memory_content: memory?.memory_content ? Number(memory?.memory_content) : undefined, - tools: Array.isArray(tools) ? tools : [] + tools: allTools }) - setFormData({ - default_model_config_id: response.default_model_config_id, - model_parameters: response.model_parameters || {}, - tools: Array.isArray(tools) ? tools : [] + setData({ + ...response, + tools: allTools }) - if (response?.knowledge_retrieval?.knowledge_bases?.length) { - getDefaultKnowledgeList(response) - } - if (response?.tools?.length) { - setToolList(response?.tools) - } }).finally(() => { setLoading(false) }) } - const getDefaultKnowledgeList = (data: Config) => { - if (!data || !data.knowledge_retrieval || !data.knowledge_retrieval?.knowledge_bases?.length) { - return - } - const initialList = [...(data?.knowledge_retrieval?.knowledge_bases || [])] - getKnowledgeBaseList(undefined, { - kb_ids: initialList.map(vo => vo.kb_id).join(','), - page: 1, - pagesize: 100, - }) - .then(res => { - const list = res.items || [] - const knowledge_bases: KnowledgeBase[] = list.map(item => { - const filterItem = initialList.find(vo => vo.kb_id === item.id) - return { - ...item, - ...filterItem - } - }) - setKnowledgeConfig(prev => ({ - ...prev, - knowledge_bases: [...knowledge_bases] - })) - setData((prev) => { - prev = prev as Config - const knowledge_retrieval: KnowledgeConfig = { - ...(prev?.knowledge_retrieval || {}), - knowledge_bases: [...knowledge_bases] - } - return { - ...(prev || {}), - knowledge_retrieval - } - }) - }) - } const refresh = (vo: ModelConfig, type: Source) => { if (type === 'model') { @@ -227,15 +146,7 @@ const Agent = forwardRef((_props, ref) => { default_model_config_id, model_parameters: {...rest} }) - setFormData((prevState) => { - const prev = prevState as Config - return { - ...(prev || {}), - default_model_config_id, - model_parameters: {...rest} - }; - }) - if (default_model_config_id === formData?.default_model_config_id) { + if (default_model_config_id === values?.default_model_config_id) { setChatList([{ label: vo.label || '', model_config_id: default_model_config_id || '', @@ -279,24 +190,20 @@ const Agent = forwardRef((_props, ref) => { // 保存Agent配置 const handleSave = (flag = true) => { if (!isSave || !data) return Promise.resolve() - const { memoryEnabled, memory_content, ...rest } = values - const { knowledge_bases = [], ...knowledgeRest } = knowledgeConfig || {} - - + const { memory, knowledge_retrieval, tools, ...rest } = values + const { knowledge_bases = [], ...knowledgeRest } = knowledge_retrieval || {} + const { memory_content } = memory || {} // 从原数据中获取memory的其他必要属性 const originalMemory = data.memory || ({} as MemoryConfig) const params: Config = { ...data, ...rest, - ...(formData || {}), memory: { ...originalMemory, - enabled: memoryEnabled, + ...memory, memory_content: memory_content ? String(memory_content) : '', - max_history: originalMemory.max_history || '', }, - variables: variableList || [], knowledge_retrieval: knowledge_bases.length > 0 ? { ...data.knowledge_retrieval, ...knowledgeRest, @@ -305,14 +212,12 @@ const Agent = forwardRef((_props, ref) => { ...(item.config || {}) })) } as KnowledgeConfig : null, - tools: toolList.map(vo => ({ + tools: tools.map(vo => ({ tool_id: vo.tool_id, operation: vo.operation, enabled: vo.enabled })) } - - console.log('params', rest, params) return new Promise((resolve, reject) => { saveAgentConfig(data.app_id, params) @@ -338,8 +243,8 @@ const Agent = forwardRef((_props, ref) => { modelConfigModalRef.current?.handleOpen('chat') } useEffect(() => { - if (formData?.default_model_config_id && modelList.length > 0) { - const filterValue = modelList.find(item => item.id === formData.default_model_config_id) + if (values?.default_model_config_id && modelList.length > 0) { + const filterValue = modelList.find(item => item.id === values.default_model_config_id) setDefaultModel(filterValue as Model | null) setChatList([{ label: filterValue?.name || '', @@ -348,7 +253,7 @@ const Agent = forwardRef((_props, ref) => { list: [] }]) } - }, [modelList, formData?.default_model_config_id]) + }, [modelList, values?.default_model_config_id]) useImperativeHandle(ref, () => ({ handleSave @@ -360,8 +265,31 @@ const Agent = forwardRef((_props, ref) => { } 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) } + const updateVariableList = (list: Variable[]) => { + form.setFieldValue('variables', [...list]) + setChatVariables([...list]) + } + const chatVariableConfigModalRef = useRef(null) + const [chatVariables, setChatVariables] = useState([]) + const handleOpenVariableConfig = () => { + chatVariableConfigModalRef.current?.handleOpen(chatVariables) + } + const handleSaveChatVariable = (values: Variable[]) => { + setChatVariables(values) + } + console.log('values', values) return ( <> {loading && } @@ -379,8 +307,9 @@ const Agent = forwardRef((_props, ref) => {
+ + - {/* 提示词 */}
@@ -406,36 +335,31 @@ const Agent = forwardRef((_props, ref) => { - {/* 知识库 */} - + + + {/* 记忆配置 */} - + - {/* 变量配置 */} - + + + {/* 工具配置 */} - + + + @@ -444,6 +368,9 @@ const Agent = forwardRef((_props, ref) => { {t('application.debuggingAndPreview')} + @@ -463,7 +390,7 @@ const Agent = forwardRef((_props, ref) => { @@ -472,6 +399,10 @@ const Agent = forwardRef((_props, ref) => { defaultModel={defaultModel} refresh={updatePrompt} /> + ); }); diff --git a/web/src/views/ApplicationConfig/components/Card.tsx b/web/src/views/ApplicationConfig/components/Card.tsx index 7d9328ea..f414848f 100644 --- a/web/src/views/ApplicationConfig/components/Card.tsx +++ b/web/src/views/ApplicationConfig/components/Card.tsx @@ -3,18 +3,21 @@ import RbCard from '@/components/RbCard/Card' interface CardProps { title?: string | ReactNode; + subTitle?: string | ReactNode; children: ReactNode; extra?: ReactNode; } const Card: FC = ({ title, + subTitle, children, extra, }) => { return ( void; +} + +const ChatVariableConfigModal = forwardRef(({ + refresh, +}, ref) => { + const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + const [form] = Form.useForm<{variables: Variable[]}>(); + const [loading, setLoading] = useState(false) + const [initialValues, setInitialValues] = useState([]) + + // 封装取消方法,添加关闭弹窗逻辑 + const handleClose = () => { + setVisible(false); + form.resetFields(); + setLoading(false) + }; + + const handleOpen = (values: Variable[]) => { + console.log('values', values) + setVisible(true); + form.setFieldsValue({variables: values}) + setInitialValues([...values]) + }; + // 封装保存方法,添加提交逻辑 + const handleSave = () => { + form.validateFields().then((values) => { + refresh([ + ...(values?.variables ?? []), + ]) + handleClose() + }) + } + + // 暴露给父组件的方法 + useImperativeHandle(ref, () => ({ + handleOpen, + handleClose + })); + + console.log(form.getFieldValue('variables')) + + return ( + +
+ + {(fields) => ( + <> + {fields.map(({ name }, index) => { + const field = initialValues[index] + return ( + + { + field.type === 'text' && + } + { + field.type === 'number' && form.setFieldValue(['variables', name, 'value'], value)} /> + } + { + field.type === 'paragraph' && + } + + ) + })} + + )} + +
+
+ ); +}); + +export default ChatVariableConfigModal; \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/Knowledge.tsx b/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx similarity index 54% rename from web/src/views/ApplicationConfig/components/Knowledge.tsx rename to web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx index bc1207e4..1e59f26d 100644 --- a/web/src/views/ApplicationConfig/components/Knowledge.tsx +++ b/web/src/views/ApplicationConfig/components/Knowledge/Knowledge.tsx @@ -2,7 +2,6 @@ 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 Card from './Card' import type { KnowledgeConfigForm, KnowledgeConfig, @@ -11,14 +10,16 @@ import type { KnowledgeModalRef, KnowledgeConfigModalRef, KnowledgeGlobalConfigModalRef, -} from '../types' +} from './types' import Empty from '@/components/Empty' import KnowledgeListModal from './KnowledgeListModal' import KnowledgeConfigModal from './KnowledgeConfigModal' import KnowledgeGlobalConfigModal from './KnowledgeGlobalConfigModal' import Tag from '@/components/Tag' +import { getKnowledgeBaseList } from '@/api/knowledgeBase' +import Card from '../Card' -const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) => void}> = ({data, onUpdate}) => { +const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfig) => void}> = ({value = {knowledge_bases: []}, onChange}) => { const { t } = useTranslation() const knowledgeModalRef = useRef(null) const knowledgeConfigModalRef = useRef(null) @@ -27,12 +28,31 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) const [editConfig, setEditConfig] = useState({} as KnowledgeConfig) useEffect(() => { - if (data) { - setEditConfig({ ...(data || {}) }) - const knowledge_bases = [...(data.knowledge_bases || [])] - setKnowledgeList(knowledge_bases) + if (value && JSON.stringify(value) !== JSON.stringify(editConfig)) { + setEditConfig({ ...(value || {}) }) + const knowledge_bases = [...(value.knowledge_bases || [])] + + // 检查是否有knowledge_bases缺少name字段 + const basesWithoutName = knowledge_bases.filter(base => !base.name) + if (basesWithoutName.length > 0) { + // 调用接口获取完整的知识库信息 + getKnowledgeBaseList().then(res => { + const fullBases = knowledge_bases.map(base => { + if (!base.name) { + const fullBase = res.items.find((item: any) => item.id === base.kb_id) + return fullBase ? { ...base, ...fullBase } : base + } + return base + }) + setKnowledgeList(fullBases) + }).catch(() => { + setKnowledgeList(knowledge_bases) + }) + } else { + setKnowledgeList(knowledge_bases) + } } - }, [data]) + }, [value]) const handleKnowledgeConfig = () => { knowledgeGlobalConfigModalRef.current?.handleOpen() @@ -43,7 +63,7 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) const handleDeleteKnowledge = (id: string) => { const list = knowledgeList.filter(item => item.id !== id) setKnowledgeList([...list]) - onUpdate({ + onChange && onChange({ ...editConfig, knowledge_bases: [...list], }) @@ -65,7 +85,7 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) list = [...values as KnowledgeBase[]] } setKnowledgeList([...list]) - onUpdate({ + onChange && onChange({ ...editConfig, knowledge_bases: [...list], }) @@ -77,14 +97,14 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) config: {...values as KnowledgeConfigForm} } setKnowledgeList([...list]) - onUpdate({ + onChange && onChange({ ...editConfig, knowledge_bases: [...list], }) } else if (type === 'rerankerConfig') { const rerankerValues = values as RerankerConfig setEditConfig(prev => ({ ...prev, ...rerankerValues })) - onUpdate({ + onChange && onChange({ ...editConfig, ...rerankerValues, reranker_id: rerankerValues.rerank_model ? rerankerValues.reranker_id : undefined, @@ -93,55 +113,54 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig) } } return ( - handleKnowledgeConfig()}>{t('application.globalConfig')} + + + + } > -
-
{t('application.associatedKnowledgeBase')}
- -
- {knowledgeList.length === 0 ? : ( - -
-
- {item.name} - - {item.status === 1 ? t('common.enable') : item.status === 0 ? t('common.disabled') : t('common.deleted')} - -
{t('application.contains', {include_count: item.doc_num})}
+ renderItem={(item) => { + if (!item.id) return null + return ( + +
+
+ {item.name} + + {item.status === 1 ? t('common.enable') : item.status === 0 ? t('common.disabled') : t('common.deleted')} + +
{t('application.contains', {include_count: item.doc_num})}
+
+ +
handleEditKnowledge(item)} + >
+
handleDeleteKnowledge(item.id)} + >
+
- -
handleEditKnowledge(item)} - >
-
handleDeleteKnowledge(item.id)} - >
-
-
- - )} + + ) + }} /> } - {/* 全局设置 */} - {/* 知识库列表 */} void; } -const retrieveTypes = ['participle', 'semantic', 'hybrid'] +const retrieveTypes: RetrieveType[] = ['participle', 'semantic', 'hybrid'] const KnowledgeConfigModal = forwardRef(({ refresh, @@ -33,8 +33,11 @@ const KnowledgeConfigModal = forwardRef { form.setFieldsValue({ - retrieve_type: retrieveTypes[0], + retrieve_type: data?.config?.retrieve_type || retrieveTypes[0], kb_id: data.id, + top_k: data?.config?.top_k || 5, + similarity_threshold: data?.config?.similarity_threshold || 0.5, + vector_similarity_weight: data?.config?.vector_similarity_weight || 0.5, ...(data || {}), ...(data?.config || {}), }) @@ -62,12 +65,10 @@ const KnowledgeConfigModal = forwardRef { if (values?.retrieve_type) { - const initialValues = Object.keys(values).map(key => { - return { - [key as keyof KnowledgeConfigForm]: (key === 'kb_id' || key === 'retrieve_type') ? values[key] : undefined - } - }) - form.resetFields(initialValues) + const fieldsToReset = Object.keys(values).filter(key => + key !== 'kb_id' && key !== 'retrieve_type' + ) as (keyof KnowledgeConfigForm)[]; + form.resetFields(fieldsToReset); } }, [values?.retrieve_type]) @@ -84,12 +85,12 @@ const KnowledgeConfigModal = forwardRef {data && ( -
-
+
+
{data.name} -
{t('application.contains', {include_count: data.doc_num})}
+
{t('application.contains', {include_count: data.doc_num})}
-
{formatDateTime(data.updated_at, 'YYYY-MM-DD HH:mm:ss')}
+
{formatDateTime(data.updated_at, 'YYYY-MM-DD HH:mm:ss')}
)} {/* 语义相似度阈值 similarity_threshold */} {values?.retrieve_type === 'semantic' && ( @@ -123,6 +130,7 @@ const KnowledgeConfigModal = forwardRef -
{t('application.globalConfigDesc')}
+
{t('application.globalConfigDesc')}
{/* 结果重排 */} -
-
+
+
{t('application.rerankModel')} -
{t('application.rerankModelDesc')}
+
{t('application.rerankModelDesc')}
@@ -110,7 +110,12 @@ const KnowledgeGlobalConfigModal = forwardRef - + form.setFieldValue('reranker_top_k', value)} + /> } diff --git a/web/src/views/ApplicationConfig/components/KnowledgeListModal.tsx b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx similarity index 88% rename from web/src/views/ApplicationConfig/components/KnowledgeListModal.tsx rename to web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx index 0c7b47b2..f1ebd516 100644 --- a/web/src/views/ApplicationConfig/components/KnowledgeListModal.tsx +++ b/web/src/views/ApplicationConfig/components/Knowledge/KnowledgeListModal.tsx @@ -2,7 +2,7 @@ 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 { KnowledgeModalRef, KnowledgeBase } from './types' import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types' import RbModal from '@/components/RbModal' import { getKnowledgeBaseList } from '@/api/knowledgeBase' @@ -39,12 +39,13 @@ const KnowledgeListModal = forwardRef(({ setQuery({}) setSelectedIds([]) setSelectedRows([]) - getList() }; useEffect(() => { - getList() - }, [query.keywords]) + if (visible) { + getList() + } + }, [query.keywords, visible]) const getList = () => { getKnowledgeBaseList(undefined, { ...query, @@ -124,15 +125,15 @@ const KnowledgeListModal = forwardRef(({ dataSource={filterList} renderItem={(item: KnowledgeBase) => ( -
handleSelect(item)}> -
+
{item.name} -
{t('application.contains', {include_count: item.doc_num})}
+
{t('application.contains', {include_count: item.doc_num})}
-
{formatDateTime(item.created_at, 'YYYY-MM-DD HH:mm:ss')}
+
{formatDateTime(item.created_at, 'YYYY-MM-DD HH:mm:ss')}
)} diff --git a/web/src/views/ApplicationConfig/components/Knowledge/types.ts b/web/src/views/ApplicationConfig/components/Knowledge/types.ts new file mode 100644 index 00000000..f4f9ed17 --- /dev/null +++ b/web/src/views/ApplicationConfig/components/Knowledge/types.ts @@ -0,0 +1,30 @@ +import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types' +export interface RerankerConfig { + rerank_model?: boolean | undefined; + reranker_id?: string | undefined; + reranker_top_k?: number | undefined; +} +export type RetrieveType = 'participle' | 'semantic' | 'hybrid' +export interface KnowledgeConfigForm { + kb_id?: string; + similarity_threshold?: number; + vector_similarity_weight?: number; + top_k?: number; + retrieve_type?: RetrieveType; +} +export interface KnowledgeBase extends KnowledgeBaseListItem, KnowledgeConfigForm { + config?: KnowledgeConfigForm +} +export interface KnowledgeConfig extends RerankerConfig { + knowledge_bases: KnowledgeBase[]; +} + +export interface KnowledgeConfigModalRef { + handleOpen: (data: KnowledgeBase) => void; +} +export interface KnowledgeGlobalConfigModalRef { + handleOpen: () => void; +} +export interface KnowledgeModalRef { + handleOpen: (config?: KnowledgeConfig[]) => void; +} \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/ToolList.tsx b/web/src/views/ApplicationConfig/components/ToolList/ToolList.tsx similarity index 93% rename from web/src/views/ApplicationConfig/components/ToolList.tsx rename to web/src/views/ApplicationConfig/components/ToolList/ToolList.tsx index fde7286b..e914d879 100644 --- a/web/src/views/ApplicationConfig/components/ToolList.tsx +++ b/web/src/views/ApplicationConfig/components/ToolList/ToolList.tsx @@ -1,22 +1,22 @@ 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 Card from '../Card' import type { ToolModalRef, ToolOption -} from '../types' +} from './types' import Empty from '@/components/Empty' import ToolModal from './ToolModal' import { getToolMethods, getToolDetail } from '@/api/tools' -const ToolList: FC<{ data: ToolOption[]; onUpdate: (config: ToolOption[]) => void}> = ({data, onUpdate}) => { +const ToolList: FC<{ value?: ToolOption[]; onChange?: (config: ToolOption[]) => void}> = ({value, onChange}) => { const { t } = useTranslation() const toolModalRef = useRef(null) const [toolList, setToolList] = useState([]) useEffect(() => { - if (data) { - const processedData = data.map(async (item) => { + if (value) { + const processedData = value.map(async (item) => { if (!item.label && item.tool_id) { try { const [toolDetail, methods] = await Promise.all([ @@ -77,7 +77,7 @@ const ToolList: FC<{ data: ToolOption[]; onUpdate: (config: ToolOption[]) => voi Promise.all(processedData).then(setToolList) } - }, [data]) + }, [value]) const handleAddTool = () => { toolModalRef.current?.handleOpen() @@ -85,12 +85,12 @@ const ToolList: FC<{ data: ToolOption[]; onUpdate: (config: ToolOption[]) => voi const updateTools = (tool: ToolOption) => { const list = [...toolList, tool] setToolList(list) - onUpdate(list) + onChange && onChange(list) } const handleDeleteTool = (index: number) => { const list = toolList.filter((_item, idx) => idx !== index) setToolList([...list]) - onUpdate(list) + onChange && onChange(list) } const handleChangeEnabled = (index: number) => { const list = toolList.map((item, idx) => { @@ -103,7 +103,7 @@ const ToolList: FC<{ data: ToolOption[]; onUpdate: (config: ToolOption[]) => voi return item }) setToolList([...list]) - onUpdate(list) + onChange && onChange(list) } return ( voi } > - {toolList.length === 0 ? : diff --git a/web/src/views/ApplicationConfig/components/ToolModal.tsx b/web/src/views/ApplicationConfig/components/ToolList/ToolModal.tsx similarity index 100% rename from web/src/views/ApplicationConfig/components/ToolModal.tsx rename to web/src/views/ApplicationConfig/components/ToolList/ToolModal.tsx diff --git a/web/src/views/ApplicationConfig/components/ToolList/types.ts b/web/src/views/ApplicationConfig/components/ToolList/types.ts new file mode 100644 index 00000000..142ffe26 --- /dev/null +++ b/web/src/views/ApplicationConfig/components/ToolList/types.ts @@ -0,0 +1,26 @@ +export interface ToolOption { + value?: string | number | null; + label?: React.ReactNode; + description?: string; + children?: ToolOption[]; + isLeaf?: boolean; + method_id?: string; + operation?: string; + parameters?: Parameter[]; + tool_id?: string; + enabled?: boolean; +} +export interface Parameter { + name: string; + type: string; + description: string; + required: boolean; + default: any; + enum: null | string[]; + minimum: number; + maximum: number; + pattern: null | string; +} +export interface ToolModalRef { + handleOpen: () => void; +} \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/VariableList.tsx b/web/src/views/ApplicationConfig/components/VariableList.tsx deleted file mode 100644 index fbadf2ea..00000000 --- a/web/src/views/ApplicationConfig/components/VariableList.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { type FC, useRef, useState, useEffect } from 'react' -import { useTranslation } from 'react-i18next' -import { Space, Button, Switch } from 'antd' -import variablesEmpty from '@/assets/images/application/variablesEmpty.svg' -import Card from './Card' -import Table from '@/components/Table'; -import type { Variable, VariableEditModalRef } from '../types' -import Empty from '@/components/Empty' -import VariableEditModal from './VariableEditModal' - -interface VariableListProps { - data?: Variable[]; - onUpdate: (data: Variable[]) => void; -} -const VariableList: FC = ({data = [], onUpdate}) => { - const { t } = useTranslation() - const variableEditModalRef = useRef(null) - const [variableList, setVariableList] = useState([]) - const [maxIndex, setMaxIndex] = useState(0) - - useEffect(() => { - if (!data || data.length === 0) return - const list = data.map((item, index) => ({ - ...item, - index - })) - setVariableList(list) - onUpdate(list) - setMaxIndex(list.length) - }, [data]) - - const handleAddVariable = () => { - variableEditModalRef.current?.handleOpen() - } - const handleSaveVariable = (value: Variable) => { - if (value.index !== undefined && value.index >= 0) { - const index = variableList.findIndex(item => item.index === value.index) - if (index !== -1) { - const newData = [...variableList] - newData[index] = value - setVariableList([...newData]) - onUpdate([...newData]) - } - } else { - const list = [...variableList, { - index: maxIndex + 1, - ...value - }] - setVariableList(list) - onUpdate([...list]) - setMaxIndex(maxIndex + 1) - } - } - const handleDeleteVariable = (index: number) => { - const list = variableList.filter((_, i) => i !== index) - setVariableList(list) - onUpdate([...list]) - } - return ( - -
-
- {t('application.VariableManagement')} - ({t('application.VariableManagementDesc')}) -
- -
- - {/* List */} - {variableList.length > 0 - ? ( -
- t(`application.${type}`) - }, - { - title: t('application.variableKey'), - dataIndex: 'name', - key: 'name', - }, - { - title: t('application.variableName'), - dataIndex: 'display_name', - key: 'display_name', - }, - { - title: t('application.optional'), - dataIndex: 'required', - key: 'required', - render: (required) => - }, - { - title: t('common.operation'), - key: 'action', - render: (_, record, index: number) => ( - - - - - ), - }, - ]} - initialData={variableList as unknown as Record[]} - emptySize={88} - /> - - ) - : - } - - - ) -} -export default VariableList \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/ApiExtensionModal.tsx b/web/src/views/ApplicationConfig/components/VariableList/ApiExtensionModal.tsx similarity index 99% rename from web/src/views/ApplicationConfig/components/ApiExtensionModal.tsx rename to web/src/views/ApplicationConfig/components/VariableList/ApiExtensionModal.tsx index b1c7450a..4f4f9047 100644 --- a/web/src/views/ApplicationConfig/components/ApiExtensionModal.tsx +++ b/web/src/views/ApplicationConfig/components/VariableList/ApiExtensionModal.tsx @@ -2,7 +2,7 @@ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input } from 'antd'; import { useTranslation } from 'react-i18next'; -import type { ApiExtensionModalData, ApiExtensionModalRef } from '../types' +import type { ApiExtensionModalData, ApiExtensionModalRef } from './types' import RbModal from '@/components/RbModal' const FormItem = Form.Item; diff --git a/web/src/views/ApplicationConfig/components/VariableEditModal.tsx b/web/src/views/ApplicationConfig/components/VariableList/VariableEditModal.tsx similarity index 96% rename from web/src/views/ApplicationConfig/components/VariableEditModal.tsx rename to web/src/views/ApplicationConfig/components/VariableList/VariableEditModal.tsx index 3efd721c..69e213fb 100644 --- a/web/src/views/ApplicationConfig/components/VariableEditModal.tsx +++ b/web/src/views/ApplicationConfig/components/VariableList/VariableEditModal.tsx @@ -2,7 +2,7 @@ import { forwardRef, useImperativeHandle, useState, useRef } from 'react'; import { Form, Input, Select, InputNumber, Checkbox, Tag, Divider, Button } from 'antd'; import { useTranslation } from 'react-i18next'; -import type { ApiExtensionModalRef, Variable, VariableEditModalRef } from '../types' +import type { ApiExtensionModalRef, Variable, VariableEditModalRef } from './types' import RbModal from '@/components/RbModal' import SortableList from '@/components/SortableList' import ApiExtensionModal from './ApiExtensionModal' @@ -137,7 +137,14 @@ const VariableEditModal = forwardRef - + { + if (!form.getFieldValue('display_name')) { + form.setFieldValue('display_name', e.target.value) + } + }} + /> {/* 显示名称 */} void; +} +const VariableList: FC = ({value = [], onChange}) => { + const { t } = useTranslation() + const variableEditModalRef = useRef(null) + + const handleAddVariable = () => { + variableEditModalRef.current?.handleOpen() + } + const handleSaveVariable = (variable: Variable) => { + const newList = [...(value || [])] + if (variable.index !== undefined && variable.index >= 0) { + const index = newList.findIndex(item => item.index === variable.index) + if (index !== -1) { + newList[index] = variable + } + } else { + newList.push({ ...variable, index: Date.now() }) + } + onChange?.(newList) + } + return ( + + {t('application.variableConfiguration')} + ({t('application.VariableManagementDesc')}) + } + extra={} + > + + {(fields, { remove }) => { + return ( + <> + {fields.length > 0 ? ( +
+
t(`application.${type}`) + }, + { + title: t('application.variableKey'), + dataIndex: 'name', + key: 'name', + }, + { + title: t('application.variableName'), + dataIndex: 'display_name', + key: 'display_name', + }, + { + title: t('application.optional'), + dataIndex: 'required', + key: 'required', + render: (required) => + }, + { + title: t('common.operation'), + key: 'action', + render: (_, record, index: number) => ( + + + + + ), + }, + ]} + initialData={value as unknown as Record[]} + emptySize={88} + /> + + ) : ( + + )} + + ) + }} + + + + ) +} +export default VariableList \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/VariableList/types.ts b/web/src/views/ApplicationConfig/components/VariableList/types.ts new file mode 100644 index 00000000..f262dda1 --- /dev/null +++ b/web/src/views/ApplicationConfig/components/VariableList/types.ts @@ -0,0 +1,28 @@ +export interface Variable { + index?: number; + name: string; + display_name: string; + type: string; + required: boolean; + max_length?: number; + description?: string; + + key?: string; + default_value?: string; + options?: string[]; + api_extension?: string; + hidden?: boolean; + value?: any; +} +export interface VariableEditModalRef { + handleOpen: (values?: Variable) => void; +} + +export interface ApiExtensionModalData { + name: string; + apiEndpoint: string; + apiKey: string; +} +export interface ApiExtensionModalRef { + handleOpen: () => void; +} \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/types.ts b/web/src/views/ApplicationConfig/types.ts index 6eb97f22..6f641ebb 100644 --- a/web/src/views/ApplicationConfig/types.ts +++ b/web/src/views/ApplicationConfig/types.ts @@ -1,4 +1,6 @@ -import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types' +import type { KnowledgeConfig } from './components/Knowledge/types' +import type { Variable } from './components/VariableList/types' +import type { ToolOption } from './components/ToolList/types' import type { ChatItem } from '@/components/Chat/types' import type { GraphRef } from '@/views/Workflow/types'; import type { ApiKey } from '@/views/ApiKeyManagement/types' @@ -14,55 +16,6 @@ export interface ModelConfig { n: number; stop?: string; } - -/*************** 知识库相关 ******************/ -export interface RerankerConfig { - rerank_model?: boolean | undefined; - reranker_id?: string | undefined; - reranker_top_k?: number | undefined; -} -export interface KnowledgeConfigForm { - kb_id?: string; - similarity_threshold?: number; - vector_similarity_weight?: number; - top_k?: number; - retrieve_type?: 'participle' | 'semantic' | 'hybrid'; -} -export interface KnowledgeBase extends KnowledgeBaseListItem, KnowledgeConfigForm { - config?: KnowledgeConfigForm -} -export interface KnowledgeConfig extends RerankerConfig { - knowledge_bases: KnowledgeBase[]; -} - -export interface KnowledgeConfigModalRef { - handleOpen: (data: KnowledgeBase) => void; -} -export interface KnowledgeGlobalConfigModalRef { - handleOpen: () => void; -} -/*********** end 知识库相关 ******************/ - -/*************** 变量相关 ******************/ -export interface Variable { - index?: number; - name: string; - display_name: string; - type: string; - required: boolean; - max_length?: number; - description?: string; - - key: string; - default_value?: string; - options?: string[]; - api_extension?: string; - hidden?: boolean; -} -export interface VariableEditModalRef { - handleOpen: (values?: Variable) => void; -} -/*********** end 变量相关 ******************/ export interface MemoryConfig { enabled: boolean; memory_content?: string; @@ -131,17 +84,6 @@ export interface ModelConfigModalData { export interface AiPromptModalRef { handleOpen: () => void; } -export interface KnowledgeModalRef { - handleOpen: (config?: KnowledgeConfig[]) => void; -} -export interface ApiExtensionModalData { - name: string; - apiEndpoint: string; - apiKey: string; -} -export interface ApiExtensionModalRef { - handleOpen: () => void; -} export interface ChatData { label?: string; model_config_id?: string; @@ -206,30 +148,6 @@ export interface AiPromptForm { message?: string; current_prompt?: string; } -export interface ToolModalRef { - handleOpen: () => void; -} - -export interface ToolOption { - value?: string | number | null; - label?: React.ReactNode; - description?: string; - children?: ToolOption[]; - isLeaf?: boolean; - method_id?: string; - operation?: string; - parameters?: Parameter[]; - tool_id?: string; - enabled?: boolean; -} -export interface Parameter { - name: string; - type: string; - description: string; - required: boolean; - default: any; - enum: null | string[]; - minimum: number; - maximum: number; - pattern: null | string; +export interface ChatVariableConfigModalRef { + handleOpen: (values: Variable[]) => void; } \ No newline at end of file