Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,7 @@ def execute(
model_params_setting = reference_data.get("model_params_setting")
elif model_id_type == "default":
default_setting = self.workflow_manage.get_default_model_setting("LLM")
if default_setting.get("model_id"):
model_id = default_setting.get("model_id")
model_id = default_setting.get("model_id")
model_params_setting = default_setting.get("model_params_setting", model_params_setting)
if model_id is None or model_id == "":
raise Exception(_("Model is not allowed to be empty"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_t
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('TTI')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

if model_id is None or model_id == '':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_t
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('ITV')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)
if model_id is None or model_id == '':
raise Exception(_('Model is not allowed to be empty'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,7 @@ def execute(self, model_id, system, prompt, dialogue_number, dialogue_type, hist
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('IMAGE')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

if model_id is None or model_id == '':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ def execute(self, model_id, dialogue_number, history_chat_record, user_input, br
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('LLM')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)
if not model_id:
raise Exception(_('Model is not allowed to be empty'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ def _run(self):
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('LLM')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

input_variable = self.workflow_manage.get_reference_field(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ def execute(self, model_id, system, prompt, dialogue_number, history_chat_record
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('LLM')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)
if not model_id:
raise Exception(_('Model is not allowed to be empty'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ def _run(self):
reference_data.get('model_id', reranker_model_id))
elif reranker_model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('RERANKER')
if default_setting.get('model_id'):
reranker_model_id = default_setting.get('model_id')
reranker_model_id = default_setting.get('model_id')
if reranker_model_id is None or reranker_model_id == '':
raise Exception(_('Model is not allowed to be empty'))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def execute(self, stt_model_id, audio, model_params_setting=None, stt_model_id_t
model_params_setting = reference_data.get('model_params_setting')
elif stt_model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('STT')
if default_setting.get('model_id'):
stt_model_id = default_setting.get('model_id')
stt_model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

from django.utils.translation import gettext_lazy as _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def execute(self, tts_model_id,
model_params_setting = reference_data.get('model_params_setting')
elif tts_model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('TTS')
if default_setting.get('model_id'):
tts_model_id = default_setting.get('model_id')
tts_model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

from django.utils.translation import gettext_lazy as _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_t
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('TTV')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

if model_id is None or model_id == '':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ def execute(self, model_id, system, prompt, dialogue_number, dialogue_type, hist
model_params_setting = reference_data.get('model_params_setting')
elif model_id_type == 'default':
default_setting = self.workflow_manage.get_default_model_setting('IMAGE')
if default_setting.get('model_id'):
model_id = default_setting.get('model_id')
model_id = default_setting.get('model_id')
model_params_setting = default_setting.get('model_params_setting', model_params_setting)

from django.utils.translation import gettext_lazy as _
Expand Down
24 changes: 16 additions & 8 deletions apps/application/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
from langchain_mcp_adapters.client import MultiServerMCPClient
from maxkb.conf import PROJECT_DIR
from maxkb.const import CONFIG
from models_provider.base_model_provider import ModelTypeConst
from models_provider.models import Model
from models_provider.tools import get_model_instance_by_model_workspace_id
from rest_framework import serializers, status
Expand Down Expand Up @@ -97,6 +98,17 @@ def _walk_workflow_nodes(work_flow, collector):
}


def _default_model_not_configured_message(node_name: str, model_type: str) -> str:
"""节点选了默认模型但该类别默认模型未配置时的报错,与前端「{节点名称}节点,{模型类别}的默认模型未配置」文案一致。"""
try:
model_type_label = str(ModelTypeConst[model_type].value['message'])
except Exception:
model_type_label = model_type
return _(
"{node_name} Node, the default model for {model_type_label} is not configured"
).format(node_name=node_name, model_type_label=model_type_label)


def validate_workflow_default_models(work_flow, default_model_setting):
"""发布前校验:节点选择「默认模型」但对应类别默认模型未配置时,禁止发布并定位。"""
if not work_flow:
Expand All @@ -117,10 +129,7 @@ def validate_workflow_default_models(work_flow, default_model_setting):
and not ((default_model_setting or {}).get(default_model_type, {}) or {}).get('model_id')):
raise AppApiException(
500,
_(
"{node_name} selected the default model, but the default model "
"of this type is not configured."
).format(node_name=node_name),
_default_model_not_configured_message(node_name, default_model_type),
)
if model_type is not None:
# 取该节点实际使用的 model_id_type 字段(custom/reference/default)
Expand All @@ -142,10 +151,7 @@ def validate_workflow_default_models(work_flow, default_model_setting):
).get("model_id"):
raise AppApiException(
500,
_(
"{node_name} selected the default model, but the default model "
"of this type is not configured."
).format(node_name=node_name),
_default_model_not_configured_message(node_name, model_type),
)
if node_type == "loop-node":
validate_workflow_default_models(node_data.get("loop_body"), default_model_setting)
Expand Down Expand Up @@ -465,6 +471,7 @@ class WorkflowRequest(serializers.Serializer):
required=False, allow_null=True, allow_blank=True, max_length=102400, label=_("Opening remarks")
)
folder_id = serializers.CharField(required=True, label=_("folder id"))
default_model_setting = serializers.DictField(required=False, label=_("Default model settings"))

@staticmethod
def to_application_model(user_id: str, workspace_id: str, application: Dict):
Expand Down Expand Up @@ -497,6 +504,7 @@ def to_application_model(user_id: str, workspace_id: str, application: Dict):
file_upload_enable=application.get("file_upload_enable", False),
file_upload_setting=application.get("file_upload_setting", {}),
work_flow=default_workflow,
default_model_setting=application.get("default_model_setting") or {},
)

class SimplateRequest(serializers.Serializer):
Expand Down
3 changes: 3 additions & 0 deletions apps/locales/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8691,6 +8691,9 @@ msgstr ""
msgid "{node_name} selected the default model, but the default model of this type is not configured."
msgstr ""

msgid "{node_name} Node, the default model for {model_type_label} is not configured"
msgstr ""

msgid "Authentication failed. Please verify that the parameters are correct"
msgstr ""

Expand Down
3 changes: 3 additions & 0 deletions apps/locales/zh_CN/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8815,6 +8815,9 @@ msgstr "图生视频"
msgid "{node_name} selected the default model, but the default model of this type is not configured."
msgstr "{node_name}选择了默认模型,但未配置该类默认模型"

msgid "{node_name} Node, the default model for {model_type_label} is not configured"
msgstr "{node_name} 节点,{model_type_label}的默认模型未配置"

msgid "Authentication failed. Please verify that the parameters are correct"
msgstr "认证失败,请检查参数是否正确"

Expand Down
3 changes: 3 additions & 0 deletions apps/locales/zh_Hant/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8815,6 +8815,9 @@ msgstr "圖生視頻"
msgid "{node_name} selected the default model, but the default model of this type is not configured."
msgstr "{node_name}選擇了預設模型,但未配置該類預設模型"

msgid "{node_name} Node, the default model for {model_type_label} is not configured"
msgstr "{node_name} 節點,{model_type_label}的預設模型未配置"

msgid "Authentication failed. Please verify that the parameters are correct"
msgstr "認證失敗,請檢查參數是否正確"

Expand Down
7 changes: 6 additions & 1 deletion apps/tools/serializers/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,12 @@ def insert(self, instance, with_valid=True):
}
).auth_resource(str(tool_id))
if instance.get("tool_type") == ToolType.WORKFLOW:
ToolWorkflow(id=uuid.uuid7(), tool_id=tool_id, work_flow=instance.get("work_flow", {})).save()
ToolWorkflow(
id=uuid.uuid7(),
tool_id=tool_id,
work_flow=instance.get("work_flow", {}),
default_model_setting=instance.get("default_model_setting") or {},
).save()
# 如果是SKILL类型的工具,修改file表中对应的记录
if instance.get("tool_type") == ToolType.SKILL:
file_id = instance.get("code")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
v-show="show"
v-if="show"
v-click-outside="handleClickOutside"
class="default-model-setting-menu workflow-dropdown-menu border border-r-6 white-bg"
>
Expand All @@ -18,11 +18,12 @@
@change="(val: any) => handleModelChange(item.type, val)"
showFooter
clearable
:disabled="readonly"
style="flex: 1; min-width: 0"
/>
<el-button
class="ml-8"
:disabled="!modelSetting[item.type].model_id"
:disabled="readonly || !modelSetting[item.type].model_id || item.type === 'RERANKER'"
@click="openModelParam(item)"
icon="Operation"
/>
Expand All @@ -31,10 +32,10 @@
</el-form>
</el-scrollbar>
<div class="flex-between p-12">
<el-button @click="applyDefaultModelToAll">
<el-button :disabled="readonly" @click="applyDefaultModelToAll">
{{ $t('workflow.setting.applyToAll') }}
</el-button>
<el-button type="primary" @click="handleSave">
<el-button type="primary" :disabled="readonly || !hasChanges" @click="handleSave">
{{ $t('common.save') }}
</el-button>
</div>
Expand All @@ -54,6 +55,7 @@ const props = defineProps({
modelValue: { type: Object, default: () => ({}) },
show: { type: Boolean, default: false },
workflowRef: { type: Object, default: null },
readonly: { type: Boolean, default: false },
})
const emit = defineEmits(['update:modelValue', 'save', 'close'])

Expand Down Expand Up @@ -99,6 +101,8 @@ watch(
loadModelOptions(type)
}
},
// 面板改为 v-if 挂载:每次打开都是全新实例,watch 需 immediate 在挂载时即同步暂存副本
{ immediate: true },
)

function loadModelOptions(type: string) {
Expand All @@ -115,9 +119,8 @@ function loadModelOptions(type: string) {

function handleModelChange(type: string, _val: any) {
modelSetting.value[type].model_params_setting = {}
// 改动实时同步到父层 detail.default_model_setting,使「未点面板保存就发布」也能带上本次编辑;
// 节点上的 DefaultModelDisplay 也会随之实时更新
emit('update:modelValue', { ...modelSetting.value })
// 只改动面板内的暂存副本 modelSetting,不实时同步到父层 detail.default_model_setting;
// 未点「保存」前,节点默认模型模式不会取到未持久化的配置(发布校验/调试行为一致)
}

function openModelParam(item: { type: string }) {
Expand All @@ -131,12 +134,40 @@ function refreshModelParam(paramData: any) {
const type = currentModelType.value
if (modelSetting.value[type]?.model_id) {
modelSetting.value[type].model_params_setting = paramData
emit('update:modelValue', { ...modelSetting.value })
}
}

function handleSave() {
// 暂存副本与已持久化的 detail.default_model_setting(props.modelValue)是否有实质差异。
// modelSetting 固定含全部 8 个类别(未配置为 {}),而 props.modelValue 仅含已配置类别,
// 故不能直接整对象序列化比较;按类别比较 model_id 与参数即可。
// 只读时无任何可编辑操作,hasChanges 恒为 false。
const hasChanges = computed(
() =>
!props.readonly &&
defaultModelTypes.some(({ type }) => {
const cur = modelSetting.value[type] || {}
const base = (props.modelValue || {})[type] || {}
return (
(cur.model_id || '') !== (base.model_id || '') ||
JSON.stringify(cur.model_params_setting || {}) !== JSON.stringify(base.model_params_setting || {})
)
}),
)

function commitSetting() {
emit('update:modelValue', { ...modelSetting.value })
}

// 将暂存副本重置为已持久化的配置(用于「不保存」丢弃暂存)。
function resetSetting() {
modelSetting.value = { ...EMPTY_SETTING }
for (const { type } of defaultModelTypes) {
modelSetting.value[type] = JSON.parse(JSON.stringify((props.modelValue as any)?.[type] || {}))
}
}

function handleSave() {
commitSetting()
emit('save')
}

Expand Down Expand Up @@ -197,13 +228,45 @@ function applyDefaultModelToAll() {
})
}

function handleClickOutside(e: MouseEvent, _e2?: MouseEvent) {
async function handleClickOutside(e: MouseEvent, _e2?: MouseEvent) {
// 面板用 v-show 隐藏(未卸载),v-click-outside 的监听仍常驻 document;
// 隐藏后点击任意处不应再触发关闭/确认逻辑。
if (!props.show) return
const target = e.target as HTMLElement | null
// ModelSelect 下拉(popper-class select-model)与参数弹窗(el-dialog append-to-body 及其 .el-overlay 遮罩)
// 都 teleport 到 body,不在面板 DOM 树内,v-click-outside 会误判为"面板外点击"。
// 此处显式排除:点击这两类浮层内(含弹窗遮罩)视为面板内交互,不关闭。
// 此处显式排除:点击这两类浮层内(含弹窗遮罩、确认弹窗)视为面板内交互,不关闭。
if (target && (target.closest('.select-model') || target.closest('.el-overlay'))) return
emit('close')
if (!hasChanges.value) {
// 无未保存修改,直接关闭
emit('close')
return
}
// 有未保存修改,弹确认:「保存修改」/「不保存」/「取消(关闭弹窗)」
try {
const action = await MsgConfirm(
t('common.tip'),
t('workflow.setting.defaultModelSettingUnsaved'),
{
type: 'warning',
confirmButtonText: t('workflow.setting.saveChanges'),
cancelButtonText: t('workflow.setting.discardChanges'),
distinguishCancelAndClose: true,
},
)
// 保存修改并关闭
if (action === 'confirm') {
commitSetting()
emit('save')
emit('close')
}
} catch (action: any) {
// cancel → 选择「不保存」,丢弃暂存修改,关闭面板;close → 取消,保持面板打开
if (action === 'close') return
// 丢弃暂存,重置为已持久化配置,避免隐藏后 hasChanges 残留导致再次点击又弹确认
resetSetting()
emit('close')
}
}
</script>
<style lang="scss">
Expand Down
1 change: 1 addition & 0 deletions ui/src/locales/lang/en-US/views/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ After disabling, the long-term memory of the conversation users will be cleared,
defaultModel: 'Default Model',
custom: 'Custom',
modelRequiredMessage: 'Please select a long-term memory model',
modelSettingTip: 'The model can be configured in "Long-Term Memory Settings"',
},
form: {
appName: {
Expand Down
Loading
Loading