From c967346eeb7312e3467591372d02e06ae8c23340 Mon Sep 17 00:00:00 2001 From: wangdan-fit2cloud Date: Sat, 5 Sep 2026 00:30:05 +0800 Subject: [PATCH] feat: default mode --- ui/src/api/API_README.md | 2 + ui/src/api/types/application.ts | 3 + ui/src/api/types/model.ts | 9 + ui/src/components/COMPONENT_README.md | 15 +- .../business/model-select/index.vue | 123 ++++++--- ui/src/constants/model.ts | 14 +- ui/src/router/ROUTE_README.md | 1 + ui/src/router/admin/workflow/index.ts | 4 +- ui/src/styles/element-plus.scss | 9 + ui/src/views/VIEW_README.md | 59 +++- ui/src/views/model/ModelView.vue | 11 +- .../model/create-model/CreateModelDrawer.vue | 28 +- .../model/create-model/ModelCreateButton.vue | 22 +- .../shared-resources/SharedModelview.vue | 2 +- .../workflow/ApplicationWorkflowView.vue | 87 ++++-- ui/src/views/workflow/ToolWorkflowView.vue | 20 +- .../components/DefaultModelSetting.vue | 255 ----------------- .../DefaultModelSettingButton.vue | 55 ++++ .../DefaultModelSettingDrawer.vue | 256 ++++++++++++++++++ ui/src/workflow-canvas/WORKFLOW_README.md | 15 +- ui/src/workflow-canvas/core/workflow-node.ts | 5 + ui/src/workflow-canvas/index.vue | 4 + .../node-menu/ResourceNodeMenu.vue | 2 + .../model-setting/ModelParamsDialog.vue | 78 ------ .../component/model-setting/index.vue | 76 ++---- .../nodes/ai-chat-node/index.vue | 149 +++------- .../LongTermSettingDialog.vue | 2 +- .../component/speech-input/index.vue | 18 +- .../component/speech-playback/index.vue | 51 ++-- .../workflow-canvas/nodes/base-node/index.vue | 7 +- .../workflow-canvas/nodes/base-node/types.ts | 1 + .../nodes/image-generate-node/index.vue | 44 +-- .../nodes/image-understand-node/index.vue | 68 ++--- .../nodes/intent-node/index.vue | 50 +--- .../nodes/parameter-extraction-node/index.vue | 23 +- .../nodes/question-node/index.vue | 51 +--- .../nodes/speech-to-text-node/index.vue | 30 +- .../nodes/text-to-speech-node/index.vue | 8 +- .../nodes/text-to-video-node/index.vue | 44 +-- 39 files changed, 814 insertions(+), 887 deletions(-) delete mode 100644 ui/src/views/workflow/components/DefaultModelSetting.vue create mode 100644 ui/src/views/workflow/components/default-model-setting/DefaultModelSettingButton.vue create mode 100644 ui/src/views/workflow/components/default-model-setting/DefaultModelSettingDrawer.vue delete mode 100644 ui/src/workflow-canvas/nodes/ai-chat-node/component/model-setting/ModelParamsDialog.vue diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 7e4fdc5612e..22abb3cb4a7 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -114,6 +114,8 @@ API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文 - 同一业务边界内的重复类型提取到最近共同目录的 `common.ts`;`common.ts` 不得成为无关类型 的集合。 - API 与 View 或 Component 使用同一业务类型时只保留一份声明,不得在两层分别定义。 +- 智能体详情及保存参数中的 `default_model_setting` 共用模型类别 `DefaultModelType` 和单项 + 配置 `ModelConfig`(`types/model.ts`),工作流页面及设置抽屉从 `@/api/types` 引用。 - 名称相同但业务含义或字段约束不同的类型不要强行合并,应使用明确的领域名称区分。 - 使用 `interface` 描述对象结构,使用 `type` 描述联合类型、交叉类型、工具类型结果或别名。 - 类型名称必须体现业务含义,避免使用 `Data`、`Item`、`Info` 等脱离领域后含义不清的名称。 diff --git a/ui/src/api/types/application.ts b/ui/src/api/types/application.ts index 7b6f89a7249..36e7f93549e 100644 --- a/ui/src/api/types/application.ts +++ b/ui/src/api/types/application.ts @@ -2,10 +2,12 @@ import type LogicFlow from '@logicflow/core' import { APPLICATION_TYPE } from '@/api/enums' +import type { DefaultModelType, ModelConfig } from './model' export type ApplicationType = (typeof APPLICATION_TYPE)[keyof typeof APPLICATION_TYPE] export interface ApplicationDetail { + default_model_setting?: Partial> create_time?: string desc?: string | null folder?: string @@ -27,6 +29,7 @@ export interface ApplicationDetail { } export interface ApplicationFormPayload { + default_model_setting?: ApplicationDetail['default_model_setting'] name?: string desc?: string model_id?: string diff --git a/ui/src/api/types/model.ts b/ui/src/api/types/model.ts index 09f660e6a92..7f1cc7f46e9 100644 --- a/ui/src/api/types/model.ts +++ b/ui/src/api/types/model.ts @@ -1,9 +1,18 @@ /** Workspace 模型与工具页面共用的业务类型。 */ import { MODEL_STATUS } from '@/api/enums' +import type { Dict } from './common' export type ModelStatus = (typeof MODEL_STATUS)[keyof typeof MODEL_STATUS] +/** 智能体默认模型配置支持的模型类别。 */ +export type DefaultModelType = 'LLM' | 'TTS' | 'STT' | 'IMAGE' | 'TTI' | 'TTV' | 'ITV' | 'RERANKER' + +export interface ModelConfig { + model_id?: string + model_params_setting?: Dict +} + export interface ModelProviderItem { icon: string name: string diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index c50e3dc7554..da0a933fe72 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -1053,11 +1053,22 @@ import FolderTree from '@/components/business/folder-tree/index.vue' 按供应商分组展示模型,通过 `v-model` 控制模型 ID,并在选择变化时触发 `change`。`options` 为 `ModelItem[]`,`providerOptions` 为 `ModelProviderItem[]`,模型列表和供应商列表均由使用方查询。 -`showModelParams` 默认为 `false`,设为 `true` 时显示参数按钮,设为 `false` 时隐藏。 +`canEditParams` 默认为 `false`,设为 `true` 时显示参数按钮,设为 `false` 时隐藏。 +参数按钮位于选择框右侧内部,与选择框共用外边框,并通过短竖线与下拉箭头分隔。 未选择模型或传入 `disabled` 时按钮禁用;点击打开组件内的 `ModelParamsDialog`。 通过 `v-model:model-params` 绑定参数:切换模型时加载默认值,清空模型时清空参数,弹窗确认后 回写配置,取消不修改已保存参数。参数表单沿用上层提供的 `getModelParamsForm(modelId)` 注入方法; 隐藏参数入口时,组件只处理模型选择,不加载或修改参数。 +使用方开启参数入口后应绑定对应参数字段,并移除独立的参数按钮、弹窗和默认参数请求。 +通过 Props 与事件回写设置的子组件,应分别发送模型 ID 和参数的局部更新,由父级合并,避免 +同次交互连续更新时使用旧 Props 覆盖刚选中的模型。 + +`canAdd` 默认为 `false`,开启后在下拉列表底部显示“添加模型”,复用 `ModelCreateButton` 的 +供应商选择和模型创建流程。组件通过 `isWorkspaceResource()`、`isSystemSharedResource()` 读取 +`resourceScope`:Workspace 传入 `ModelApi`,System 共享资源传入 `SystemSharedModelApi`; +其他范围暂不展示创建入口。创建成功后触发 `refresh`,使用方重新加载原业务范围的模型选项, +不自动替换当前选中模型。`footer` 插槽仍可覆盖默认创建入口。 + ```vue - + + diff --git a/ui/src/constants/model.ts b/ui/src/constants/model.ts index e1d73bbbd07..26193938a03 100644 --- a/ui/src/constants/model.ts +++ b/ui/src/constants/model.ts @@ -1,11 +1,11 @@ export const MODEL_TYPE_LABELS: Record = { + LLM: 'AI 模型', EMBEDDING: '向量模型', - IMAGE: '图像模型', - ITV: '图生视频', - LLM: '大语言模型', + IMAGE: '视觉模型', + ITV: '图生视频模型', + STT: '语音识别模型', + TTI: '图片生成模型', + TTS: '语音合成模型', + TTV: '文生视频模型', RERANKER: '重排模型', - STT: '语音识别', - TTI: '文生图', - TTS: '语音合成', - TTV: '文生视频', } diff --git a/ui/src/router/ROUTE_README.md b/ui/src/router/ROUTE_README.md index 06bc90ae3b5..482e084f54b 100644 --- a/ui/src/router/ROUTE_README.md +++ b/ui/src/router/ROUTE_README.md @@ -67,6 +67,7 @@ Login、Workflow 路由没有挂载 `AppLayout`,因此不会出现在左侧导 - `login`:登录及账户相关独立页面。 - `workflow`:集中维护各业务的全屏画布路由,当前包含智能体和工具工作流,后续知识库等画布 继续在 `admin/workflow/index.ts` 中注册。 +- Workspace 的全屏 Workflow 路由显式设置 `resourceScope: 'workspace'`,使模型创建等资源操作使用 Workspace API。 - Workflow 路由保留当前 Workspace 和资源标识;智能体工作流地址为 `/workspace/:workspaceId/application/:applicationId/workflow`,工具工作流地址为 `/workspace/:workspaceId/tool/:toolId/workflow`。两个页面均可从资源操作入口进入,按住 Ctrl 或 diff --git a/ui/src/router/admin/workflow/index.ts b/ui/src/router/admin/workflow/index.ts index bb32098a67e..bab28c97b50 100644 --- a/ui/src/router/admin/workflow/index.ts +++ b/ui/src/router/admin/workflow/index.ts @@ -6,12 +6,12 @@ export const workflowRoutes: RouteRecordRaw[] = [ path: '/workspace/:workspaceId/application/:applicationId/workflow', name: 'workflow-application', component: () => import('@/views/workflow/ApplicationWorkflowView.vue'), - meta: { title: '智能体工作流' }, + meta: { title: '智能体工作流', resourceScope: 'workspace' }, }, { path: '/workspace/:workspaceId/tool/:toolId/workflow', name: 'workflow-tool', component: () => import('@/views/workflow/ToolWorkflowView.vue'), - meta: { title: '工具工作流' }, + meta: { title: '工具工作流', resourceScope: 'workspace' }, }, ] diff --git a/ui/src/styles/element-plus.scss b/ui/src/styles/element-plus.scss index d527afaa193..3767a6d2795 100644 --- a/ui/src/styles/element-plus.scss +++ b/ui/src/styles/element-plus.scss @@ -417,6 +417,8 @@ /* drawer */ .el-drawer { + box-shadow: -6px 0px 24px 0px rgb(var(--mk-N900-rgb) / 8%); + &__header { position: relative; border-bottom: 1px solid var(--el-border-color); @@ -779,6 +781,13 @@ } } +.el-select-group { + &__title { + color: var(--mk-N500); + font-size: var(--mk-font-size-base); + } +} + /* steps */ .el-steps { align-items: center; diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 174fea5632d..8b80daf677e 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -286,15 +286,50 @@ Dialog。新增或重命名文件时,应同步更新所有导入和页面功 ## 工作流默认模型设置 -`workflow/components/DefaultModelSetting.vue` 维护默认模型面板,按模型类型组织本地暂存配置。 -调用方通过 `show` 控制显示,通过 `v-model` 接收保存后的配置,通过 `save` 执行页面持久化, -通过 `close` 关闭面板。只读场景传入 `readonly`。编辑与参数弹窗不直接修改父级配置;有修改时 -点击面板外会区分保存、丢弃和取消。 - -模型查询通过必填的完整 `modelApi` 对象执行(类型使用 `typeof ModelApi`),可用 `modelQuery` -补充资源范围需要的查询参数;调用方负责选择 API,组件不从 URL 推断资源范围。供应商列表通过 -当前公共供应商接口查询。模型选择及参数设置复用 `ModelSelect`,重排序模型隐藏参数入口。 - -传入 `workflowRef` 后支持“应用到所有节点”:只切换节点的模型来源,保留自定义模型 ID 和参数, -递归处理循环体,并保留基本信息节点中关闭的语音/长期记忆配置及浏览器语音播放模式。 -该操作修改画布,由页面原有保存流程持久化,不自动提交面板内暂存的默认模型设置。 +`workflow/components/default-model-setting/DefaultModelSettingButton.vue` 负责顶部“默认模型设置”按钮与抽屉按需挂载, +由 `ApplicationWorkflowView` 在 `WorkflowViewLayout` 的 `actions` 插槽中接入。同目录的 +`DefaultModelSettingDrawer.vue` 负责抽屉、模型查询、暂存表单及应用与关闭确认。入口挂载后 +通过组件 Ref 调用 `open(settings)`;抽屉内部管理显隐,打开时先重置再回填配置,关闭动画结束后 +清理状态并触发 `closed`,入口据此卸载。保存事件由入口转发。抽屉复用 `MkDrawer`, +顶部使用 `top-header` 留出页面头栏,高度使用 +`h-layout-content`,不显示遮罩,也不阻挡头栏和左侧画布交互。表单由抽屉统一滚动,页脚固定显示 +应用到所有节点、取消和保存。 + +通过 `modelValue` 接收详情配置;`disabled` 控制顶部按钮。 +编辑及参数弹窗只修改抽屉副本,通过 `save(settings)` 提交副本;取消或关闭时选择不保存, +直接丢弃副本,详情中的原配置不受影响。API 与组件共用的模型类别 +`DefaultModelType` 和单项配置 `ModelConfig` 维护在 `api/types/model.ts`;按类别组织的配置直接使用 +`Partial>`,不再声明与组件名称相近的配置集合类型。 +`ApplicationWorkflowView` 用 `applicationDetail` 保存完整详情,用 `defaultModelSetting` 单独承接 +模型配置,在详情加载或保存成功后从接口的 `default_model_setting` 深拷贝回填,缺省为空对象。 +抽屉的 `save(settings)` 调用页面 `handleSaveDefaultModelSetting(settings)`,先暂存到 +`defaultModelSetting`,再复用 `handleSave()` 和 `saveApplication` 将配置与 `work_flow` 一起提交。 +页面顶部保存使用当前 `defaultModelSetting`;保存成功更新详情,失败则从详情深拷贝回滚配置, +提示错误并继续抛出异常,阻止保存并退出、保存后调试等成功分支。 +点击保存后抽屉保持打开;`modelValue` 更新时刷新比较基准,`hasChanges` 自动重新计算。 +提交时比较基准会暂时更新,失败回滚后恢复;抽屉编辑副本不被覆盖,保存期间继续编辑的内容仍保留。 +页面将 `defaultModelSetting` 通过画布的 `defaultModelSettings` Props 传入,AI 对话节点通过节点 model +获取当前 `LLM` 模型及参数;提交期间使用暂存配置,失败后随页面回滚,不通过 `provide` 同步抽屉草稿。 + +模型查询通过必填的完整 `modelApi` 对象执行(类型使用 `typeof ModelApi`);调用方负责选择 API, +组件不从 URL 推断资源范围。每次打开或刷新只查询一次模型列表,保存在 `models` 中,由 +`getModelOptions(type)` 按 `model_type` 过滤。默认模型类型保留页面展示顺序,标签复用 +`MODEL_TYPE_LABELS`。供应商列表通过当前公共供应商接口查询。模型选择及参数设置复用 +`ModelSelect`,通过 `canEditParams` 控制参数入口,重排序模型隐藏该入口。 +默认模型抽屉开启 `canAdd`,创建成功后通过 `refresh` 重新加载模型列表。 + +入口接收 `getGraphData` 函数并传给抽屉。“应用到所有节点”确认后,抽屉调用该函数获取最新图数据, +克隆后切换节点的模型来源,保留自定义模型 ID 和参数,递归处理循环体,并保留基本信息节点中 +关闭的语音/长期记忆配置及浏览器语音播放模式。存在变更时触发 `applyToAll(graphData)`,入口 +转发给 `ApplicationWorkflowView`,页面只负责通过自己的画布 Ref 渲染结果,抽屉不接收画布实例。 +`disabled` 同时控制顶部入口和应用操作,确认完成后再次检查,避免保存期间修改画布。 +该操作修改画布,由页面原有保存流程持久化,不自动提交抽屉内暂存的默认模型设置。 + +## 模型创建入口 + +`model/create-model/ModelCreateButton.vue` 和 `CreateModelDrawer.vue` 通过必填的 `api` 接收完整 +模型 API 对象。Workspace 模型页传入 `ModelApi`,System 共享模型页传入 `SystemSharedApi`; +创建抽屉只调用传入的 API,不再自行判断资源范围。 +`ModelCreateButton` 默认渲染主按钮,也支持通过默认作用域插槽的 `open()` 定制触发按钮, +`ModelSelect` 的下拉页脚使用该插槽复用同一创建流程。创建成功后保留基础资料刷新,再触发 +`refresh` 通知调用方重新加载模型列表。 diff --git a/ui/src/views/model/ModelView.vue b/ui/src/views/model/ModelView.vue index 27b1dd12fa1..821ae9a0ba4 100644 --- a/ui/src/views/model/ModelView.vue +++ b/ui/src/views/model/ModelView.vue @@ -92,13 +92,20 @@ onMounted(() => {

{{ currentProvider.name }}

- +
diff --git a/ui/src/workflow-canvas/nodes/base-node/index.vue b/ui/src/workflow-canvas/nodes/base-node/index.vue index a653c5c8f6d..c351c115b56 100644 --- a/ui/src/workflow-canvas/nodes/base-node/index.vue +++ b/ui/src/workflow-canvas/nodes/base-node/index.vue @@ -55,6 +55,7 @@ const defaultForm: BaseNodeForm = { stt_autosend: false, stt_model_enable: false, stt_model_id: '', + stt_model_params_setting: {}, stt_model_id_type: 'default', tts_autoplay: false, tts_model_enable: false, @@ -69,6 +70,7 @@ function normalizeForm(data: Partial) { normalized.file_upload_setting = { ...cloneDeep(defaultFileUploadSetting), ...(savedData.file_upload_setting ?? {}) } normalized.long_term_trigger_setting = savedData.long_term_trigger_setting ?? { rounds: 10 } normalized.long_term_model_params_setting = savedData.long_term_model_params_setting ?? {} + normalized.stt_model_params_setting = savedData.stt_model_params_setting ?? {} normalized.tts_model_params_setting = savedData.tts_model_params_setting ?? {} if (!normalized.stt_model_id_type) normalized.stt_model_id_type = 'default' if (!normalized.long_term_model_id_type) normalized.long_term_model_id_type = 'default' @@ -109,6 +111,7 @@ const speechInputSetting = computed(() => ({ stt_autosend: formData.value.stt_autosend, stt_model_enable: formData.value.stt_model_enable, stt_model_id: formData.value.stt_model_id, + stt_model_params_setting: formData.value.stt_model_params_setting, stt_model_id_type: formData.value.stt_model_id_type, })) const speechPlaybackSetting = computed(() => ({ @@ -189,11 +192,11 @@ function updateConversationVariables(fields: ChatInputField[]) { model.graphModel.eventCenter.emit('chatFieldList', undefined) } -function updateSpeechInput(setting: SpeechInputSetting) { +function updateSpeechInput(setting: Partial) { Object.assign(formData.value, setting) } -function updateSpeechPlayback(setting: SpeechPlaybackSetting) { +function updateSpeechPlayback(setting: Partial) { Object.assign(formData.value, setting) } diff --git a/ui/src/workflow-canvas/nodes/base-node/types.ts b/ui/src/workflow-canvas/nodes/base-node/types.ts index afebc0f58c1..6c415b432b5 100644 --- a/ui/src/workflow-canvas/nodes/base-node/types.ts +++ b/ui/src/workflow-canvas/nodes/base-node/types.ts @@ -37,6 +37,7 @@ export interface SpeechInputSetting { stt_autosend: boolean stt_model_enable: boolean stt_model_id: string + stt_model_params_setting: Record stt_model_id_type: 'custom' | 'default' } diff --git a/ui/src/workflow-canvas/nodes/image-generate-node/index.vue b/ui/src/workflow-canvas/nodes/image-generate-node/index.vue index 6eb8bdc8eab..ddcc6946b7c 100644 --- a/ui/src/workflow-canvas/nodes/image-generate-node/index.vue +++ b/ui/src/workflow-canvas/nodes/image-generate-node/index.vue @@ -75,13 +75,7 @@ onMounted(() => {