From ef89bcd90a14ba05e884bb71f13980aa1ebd083b Mon Sep 17 00:00:00 2001 From: hexiaonan-800 Date: Thu, 10 Sep 2026 18:12:34 +0800 Subject: [PATCH] feat: tool workflow default model setting --- .../tool_workflow_lib_node.py | 1 + ...workflow_default_model_setting_and_more.py | 55 +++++++++++++++++++ apps/tools/models/tool_workflow.py | 20 ++++--- apps/tools/serializers/tool_workflow.py | 5 ++ ui/src/api/types/tool.ts | 2 + .../views/workflow/tool/ToolWorkflowView.vue | 49 +++++++++++++++-- 6 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 apps/tools/migrations/0008_toolworkflow_default_model_setting_and_more.py diff --git a/apps/application/workflow/nodes/tool_workflow_lib_node/tool_workflow_lib_node.py b/apps/application/workflow/nodes/tool_workflow_lib_node/tool_workflow_lib_node.py index f7dff66e098..93e4004abc8 100644 --- a/apps/application/workflow/nodes/tool_workflow_lib_node/tool_workflow_lib_node.py +++ b/apps/application/workflow/nodes/tool_workflow_lib_node/tool_workflow_lib_node.py @@ -104,6 +104,7 @@ def execute(self): "position": position.get("children") if position else None, "chunk_id": workflow_params.get("chunk_id"), "form_data": workflow_params.get("form_data"), + "default_model_setting": tool_workflow_version.default_model_setting or {}, **parameters, } diff --git a/apps/tools/migrations/0008_toolworkflow_default_model_setting_and_more.py b/apps/tools/migrations/0008_toolworkflow_default_model_setting_and_more.py new file mode 100644 index 00000000000..9082399b91b --- /dev/null +++ b/apps/tools/migrations/0008_toolworkflow_default_model_setting_and_more.py @@ -0,0 +1,55 @@ +# Generated by Django 6.1 on 2026-09-10 07:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("tools", "0007_alter_tool_tool_type_toolworkflow_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="toolworkflow", + name="default_model_setting", + field=models.JSONField(default=dict, verbose_name="默认模型"), + ), + migrations.AddField( + model_name="toolworkflowversion", + name="default_model_setting", + field=models.JSONField(default=dict, verbose_name="默认模型"), + ), + migrations.AlterField( + model_name="tool", + name="tool_type", + field=models.CharField( + choices=[ + ("INTERNAL", "内置"), + ("CUSTOM", "自定义"), + ("SKILL", "技能"), + ("MCP", "MCP工具"), + ("DATA_SOURCE", "数据源"), + ("WORKFLOW", "工作流"), + ], + db_index=True, + default="CUSTOM", + max_length=20, + verbose_name="工具类型", + ), + ), + migrations.AlterField( + model_name="toolrecord", + name="source_type", + field=models.CharField( + choices=[ + ("APPLICATION", "Application"), + ("KNOWLEDGE", "Knowledge"), + ("TOOL", "Tool"), + ("TRIGGER", "Trigger"), + ], + default="APPLICATION", + max_length=256, + verbose_name="触发器任务类型", + ), + ), + ] diff --git a/apps/tools/models/tool_workflow.py b/apps/tools/models/tool_workflow.py index 4f070cebd6c..8ca45c8c549 100644 --- a/apps/tools/models/tool_workflow.py +++ b/apps/tools/models/tool_workflow.py @@ -1,11 +1,12 @@ # coding=utf-8 """ - @project: MaxKB - @Author:虎虎 - @file: tool_workflow.py - @date:2026/3/3 13:59 - @desc: +@project: MaxKB +@Author:虎虎 +@file: tool_workflow.py +@date:2026/3/3 13:59 +@desc: """ + from django.db import models from common.mixins.app_model_mixin import AppModelMixin @@ -18,13 +19,16 @@ class ToolWorkflow(AppModelMixin): """ 知识库工作流表 """ + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") - tool = models.OneToOneField(Tool, on_delete=models.CASCADE, verbose_name="工具", - db_constraint=False, related_name='workflow') + tool = models.OneToOneField( + Tool, on_delete=models.CASCADE, verbose_name="工具", db_constraint=False, related_name="workflow" + ) workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) work_flow = models.JSONField(verbose_name="工作流数据", default=dict) is_publish = models.BooleanField(verbose_name="是否发布", default=False, db_index=True) publish_time = models.DateTimeField(verbose_name="发布时间", null=True, blank=True) + default_model_setting = models.JSONField(verbose_name="默认模型", default=dict) class Meta: db_table = "tool_workflow" @@ -34,6 +38,7 @@ class ToolWorkflowVersion(AppModelMixin): """ 知识库工作流版本表 - 记录工作流历史版本 """ + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") tool = models.ForeignKey(Tool, on_delete=models.CASCADE, verbose_name="工具", db_constraint=False) workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) @@ -41,6 +46,7 @@ class ToolWorkflowVersion(AppModelMixin): work_flow = models.JSONField(verbose_name="工作流数据", default=dict) publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True) publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="") + default_model_setting = models.JSONField(verbose_name="默认模型", default=dict) class Meta: db_table = "tool_workflow_version" diff --git a/apps/tools/serializers/tool_workflow.py b/apps/tools/serializers/tool_workflow.py index d49dc491c42..2862f0b3f62 100644 --- a/apps/tools/serializers/tool_workflow.py +++ b/apps/tools/serializers/tool_workflow.py @@ -170,6 +170,7 @@ def debug(self, instance: Dict, user, with_valid=True): self.is_valid(raise_exception=True) tool_workflow = QuerySet(ToolWorkflow).filter(tool_id=self.data.get("tool_id")).first() workspace_id = tool_workflow.workspace_id + default_model_setting = tool_workflow.default_model_setting tool_record_id = instance.get("chat_record_id") or str(uuid.uuid7()) # 表单节点等断点续跑:position 指向要从其恢复执行的节点,机制与 chat 一致 position = instance.get("position") @@ -184,6 +185,7 @@ def debug(self, instance: Dict, user, with_valid=True): "debug": True, "workspace_id": workspace_id, "user_id": self.data.get("user_id"), + "default_model_setting": default_model_setting, **{k: v for k, v in instance.items() if k not in identity_keys}, } @@ -356,6 +358,7 @@ def publish(self, with_valid=True): publish_user_id=user_id, publish_user_name=user.username, workspace_id=workspace_id, + default_model_setting=tool_workflow.default_model_setting, ) work_flow_version.save() QuerySet(ToolWorkflow).filter(tool_id=self.data.get("tool_id")).update( @@ -444,11 +447,13 @@ def edit(self, instance: Dict): "tool_id": self.data.get("tool_id"), "workspace_id": workflow_id, "work_flow": instance.get("work_flow", {}), + "default_model_setting": instance.get("default_model_setting", {}), }, defaults={ "tool_id": self.data.get("tool_id"), "workspace_id": workflow_id, "work_flow": instance.get("work_flow"), + "default_model_setting": instance.get("default_model_setting", {}), }, ) # 当前用户可修改关联的知识库列表 diff --git a/ui/src/api/types/tool.ts b/ui/src/api/types/tool.ts index 5ab5abaf58b..9edd5f07692 100644 --- a/ui/src/api/types/tool.ts +++ b/ui/src/api/types/tool.ts @@ -3,6 +3,7 @@ import type LogicFlow from '@logicflow/core' import { TOOL_SCOPE, TOOL_TYPE } from '@/api/enums' import type { DynamicFormField } from './common' +import type { DefaultModelSettingPayload } from '@/api/types/model.ts' export type ToolScope = (typeof TOOL_SCOPE)[keyof typeof TOOL_SCOPE] export type ToolType = (typeof TOOL_TYPE)[keyof typeof TOOL_TYPE] @@ -71,6 +72,7 @@ export interface ToolItem { } export interface ToolWorkflowDetail { + default_model_setting?: DefaultModelSettingPayload create_time?: string id: string is_publish: boolean diff --git a/ui/src/views/workflow/tool/ToolWorkflowView.vue b/ui/src/views/workflow/tool/ToolWorkflowView.vue index 0daf8ed30e6..bd1f081875e 100644 --- a/ui/src/views/workflow/tool/ToolWorkflowView.vue +++ b/ui/src/views/workflow/tool/ToolWorkflowView.vue @@ -7,12 +7,13 @@ import { cloneDeep } from 'lodash' import ModelApi from '@/api/admin/workspace/model/model' import ToolApi from '@/api/admin/workspace/tool/tool' import ToolWorkflowApi from '@/api/admin/workspace/tool/workflow' -import type { ToolItem, ToolWorkflowDetail } from '@/api/types' -import { MsgConfirm, MsgSuccess } from '@/utils/message' +import type { DefaultModelSettingPayload, ToolItem, ToolWorkflowDetail } from '@/api/types' +import { MsgConfirm, MsgSuccess, MsgError } from '@/utils/message' import WorkflowCanvas from '@/workflow-canvas/index.vue' import { defaultToolNodes } from '@/workflow-canvas/config/node-mapping' import { WorkflowMode } from '@/workflow-canvas/types' import WorkflowViewLayout from '../components/WorkflowViewLayout.vue' +import DefaultModelSettingButton from '@/views/workflow/components/default-model-setting/DefaultModelSettingButton.vue' defineOptions({ name: 'ToolWorkflowView' }) @@ -35,6 +36,7 @@ const workflowRef = useTemplateRef>('workflo const toolDetail = ref() const loading = ref(false) const saving = ref(false) +const publishing = ref(false) const savedWorkflow = ref() const saveTime = ref() @@ -57,8 +59,9 @@ function saveToolWorkflow(graphData = getGraphData(), showMessage = false) { if (!graphData) return Promise.resolve(undefined) saving.value = true - return ToolWorkflowApi.putToolWorkflow(toolId, { work_flow: graphData }) + return ToolWorkflowApi.putToolWorkflow(toolId, { work_flow: graphData, default_model_setting: cloneDeep(defaultModelSetting.value) }) .then((toolWorkflow) => { + defaultModelSetting.value = cloneDeep(toolWorkflow.default_model_setting ?? {}) saveTime.value = toolWorkflow.update_time || new Date() setSavedWorkflow(graphData) if (showMessage) MsgSuccess('保存成功') @@ -69,15 +72,43 @@ function saveToolWorkflow(graphData = getGraphData(), showMessage = false) { }) } +/* 应用默认模型设置:抽屉提交后暂存,保存失败时从详情回滚。 */ +const defaultModelSetting = ref({}) + +function handleApplyDefaultModelToAll(graphData: LogicFlow.GraphData) { + workflowRef.value?.renderGraphData(graphData) +} + +function handleSaveDefaultModelSetting(settings: DefaultModelSettingPayload) { + defaultModelSetting.value = cloneDeep(settings) + return handleSave() +} + function handleSave() { saveToolWorkflow(undefined, true) } +function handlePublish() { + if (!workflowRef.value) return + + publishing.value = true + workflowRef.value + .validate() + .then(() => saveToolWorkflow()) // 先保存未落库的画布改动 + .then(() => ToolWorkflowApi.putToolWorkflowPublish(toolId)) + .then(() => MsgSuccess('发布成功')) + .catch(() => MsgError('发布失败')) + .finally(() => { + publishing.value = false + }) +} + function loadToolWorkflow() { loading.value = true return Promise.all([ToolApi.getToolDetail(toolId), ToolWorkflowApi.getToolWorkflow(toolId)]) .then(([tool, toolWorkflow]) => { toolDetail.value = tool + defaultModelSetting.value = cloneDeep(toolWorkflow.default_model_setting ?? {}) saveTime.value = toolWorkflow.update_time const workflow = toolWorkflow.work_flow?.nodes?.length ? toolWorkflow.work_flow : DEFAULT_WORKFLOW @@ -133,9 +164,19 @@ onMounted(() => {