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 @@ -43,7 +43,7 @@ function validate(): Promise<void> {
hasError = true
}
if (!cond.compare) {
cond._compareError = '请选择比较方式'
cond._compareError = '请选择'
hasError = true
}
const isEmpty = Array.isArray(cond.value) ? cond.value.length === 0 : !cond.value && cond.value !== 0
Expand Down
2 changes: 1 addition & 1 deletion ui/src/workflow-canvas/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ export const compareList = [
{ value: 'wildcard', label: '通配符匹配' },
]

export const fileTooltip = JSON.stringify([{ name: 'File Name', url: './oss/file/019d8ac3-e2c6-7ff2-8956-c9c98f0e11f4', file_id: '019d8ac3-e2c6-7ff2-8956-c9c98f0e11f4' }], null, 2)
export const fileTooltip = `示例:\n${JSON.stringify([{ name: 'File Name', url: './oss/file/019d8ac3-e2c6-7ff2-8956-c9c98f0e11f4', file_id: '019d8ac3-e2c6-7ff2-8956-c9c98f0e11f4' }], null, 2)}`
8 changes: 2 additions & 6 deletions ui/src/workflow-canvas/nodes/ai-chat-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const model = getModel()
const store = useWorkflowStore(apiType)

const formRef = useTemplateRef<FormInstance>('formRef')
const imageCascaderRef = useTemplateRef<InstanceType<typeof NodeCascader>>('imageCascaderRef')
const promptGenerateDialogRef = useTemplateRef<InstanceType<typeof PromptGenerateDialog>>('promptGenerateDialogRef')
const reasoningSettingDialogRef = useTemplateRef<InstanceType<typeof ReasoningSettingDialog>>('reasoningSettingDialogRef')

Expand Down Expand Up @@ -132,11 +131,8 @@ function validateModel(_rule: unknown, _value: unknown, callback: (error?: Error
callback(model_id ? undefined : new Error(model_id_type === 'default' ? '请在默认模型设置中选择 AI 模型' : '请选择 AI 模型'))
}

function validate() {
// TODO 图片选择需要必填?为什么没有必填标记?
return Promise.all([formData.value.vision ? imageCascaderRef.value?.validate() : Promise.resolve(), formRef.value?.validate()]).catch((error) =>
Promise.reject({ node: model, errMessage: error }),
)
async function validate() {
return formRef.value?.validate().catch((error) => Promise.reject({ node: model, errMessage: error }))
}

onMounted(() => {
Expand Down
144 changes: 91 additions & 53 deletions ui/src/workflow-canvas/nodes/base-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,22 +157,6 @@ const providerOptions = ref<ModelProviderItem[]>([])

// 节点统一校验
function validate() {
// TODO v2没有标记必填 但是需要校验 需要核对一下
if (formData.value.tts_model_enable && formData.value.tts_type === 'CUSTOM' && !formData.value.tts_model_id) {
return Promise.reject({ node: model, errMessage: '请选择语音播放模型' })
}
if (formData.value.tts_model_enable && formData.value.tts_type === 'DEFAULT' && !defaultTtsModelSetting.value?.model_id) {
return Promise.reject({ node: model, errMessage: '请在默认模型设置中选择语音合成模型' })
}
if (formData.value.stt_model_enable && formData.value.stt_model_id_type === 'custom' && !formData.value.stt_model_id) {
return Promise.reject({ node: model, errMessage: '请选择语音输入模型' })
}
if (formData.value.stt_model_enable && formData.value.stt_model_id_type === 'default' && !defaultSttModelSetting.value?.model_id) {
return Promise.reject({ node: model, errMessage: '请在默认模型设置中选择语音识别模型' })
}
if (formData.value.long_term_enable && formData.value.long_term_model_id_type === 'custom' && !formData.value.long_term_model_id) {
return Promise.reject({ node: model, errMessage: '请选择长期记忆模型' })
}
return Promise.all([validateUserFieldReferences(), formRef.value?.validate()]).catch((error) => Promise.reject({ node: model, errMessage: error }))
}

Expand Down Expand Up @@ -211,40 +195,58 @@ onMounted(() => {
</el-form-item>

<!-- 长期记忆 -->
<div class="mb-4 flex-between">
<span class="flex items-center gap-1">
长期记忆
<el-tooltip
content="开启后,从开启时间记录新对话并按周期生成记忆,可通过 {{开始.memory}} 变量在系统提示词中调用。关闭后,将清空对话用户的长期记忆,再次开启将重新从开启时点开始累积。"
placement="right"
>
<MkIcon name="icon_info_outlined" class="text-N600!" />
</el-tooltip>
</span>
<span class="flex items-center gap-2">
<!-- // TODO 长期记忆设置 -->
<el-button v-if="formData.long_term_enable" text type="primary">
<MkIcon name="icon-setting" />
</el-button>
<el-switch :model-value="formData.long_term_enable" size="small" @change="changeLongTermEnabled" />
</span>
</div>
<el-form-item
class="mb-2!"
prop="long_term_model_id"
:rules="{
required: formData.long_term_enable && formData.long_term_model_id_type === 'custom',
message: '请选择长期记忆模型',
trigger: 'change',
}"
>
<template #label>
<div class="flex-between">
<span class="flex items-center gap-1">
<span :class="formData.long_term_enable ? 'mk-required' : ''">长期记忆</span>

<div class="mb-4 flex-between w-full">
<span class="flex items-center gap-1">
文件上传
<el-tooltip content="开启后,问答页面会显示上传文件的按钮。" placement="right">
<MkIcon name="icon_info_outlined" class="text-N600!" />
</el-tooltip>
</span>
<span class="flex items-center gap-2">
<!-- // TODO 文件上传设置 -->
<el-button v-if="formData.file_upload_enable" text type="primary">
<MkIcon name="icon-setting" />
</el-button>
<el-switch :model-value="formData.file_upload_enable" size="small" @change="changeFileUploadEnabled" />
</span>
</div>
<el-tooltip
content="开启后,从开启时间记录新对话并按周期生成记忆,可通过 {{开始.memory}} 变量在系统提示词中调用。关闭后,将清空对话用户的长期记忆,再次开启将重新从开启时点开始累积。"
placement="right"
>
<MkIcon name="icon_info_outlined" class="text-N600!" />
</el-tooltip>
</span>
<span class="flex items-center gap-2">
<!-- // TODO 长期记忆设置 -->
<el-button v-if="formData.long_term_enable" text type="primary">
<MkIcon name="icon-setting" />
</el-button>
<el-switch :model-value="formData.long_term_enable" size="small" @change="changeLongTermEnabled" />
</span>
</div>
</template>
</el-form-item>

<!-- 文件上传 -->
<el-form-item class="mb-2!">
<template #label>
<div class="flex-between w-full">
<span class="flex items-center gap-1">
文件上传
<el-tooltip content="开启后,问答页面会显示上传文件的按钮。" placement="right">
<MkIcon name="icon_info_outlined" class="text-N600!" />
</el-tooltip>
</span>
<span class="flex items-center gap-2">
<!-- // TODO 文件上传设置 -->
<el-button v-if="formData.file_upload_enable" text type="primary">
<MkIcon name="icon-setting" />
</el-button>
<el-switch :model-value="formData.file_upload_enable" size="small" @change="changeFileUploadEnabled" />
</span>
</div>
</template>
</el-form-item>

<!-- 用户输入 -->
<el-form-item>
Expand All @@ -261,13 +263,31 @@ onMounted(() => {

<!-- 语音输入 -->
<div class="flex-between">
<span>语音输入</span>
<span :class="formData.stt_model_enable ? 'mk-required' : ''">语音输入</span>
<span class="flex items-center gap-3">
<el-checkbox v-if="formData.stt_model_enable" v-model="formData.stt_autosend">自动发送</el-checkbox>
<el-switch v-model="formData.stt_model_enable" size="small" @change="changeSpeechInputEnabled" />
</span>
</div>
<el-form-item class="mt-2" v-if="formData.stt_model_enable">
<el-form-item
v-if="formData.stt_model_enable"
class="mt-2"
prop="stt_model_id"
:rules="{
trigger: 'change',
validator: (_rule: unknown, _value: unknown, callback: (error?: Error) => void) => {
if (formData.stt_model_id_type === 'custom' && !formData.stt_model_id) {
callback(new Error('请选择语音输入模型'))
return
}
if (formData.stt_model_id_type === 'default' && !defaultSttModelSetting?.model_id) {
callback(new Error('请在默认模型设置中选择语音识别模型'))
return
}
callback()
},
}"
>
<el-radio-group v-model="formData.stt_model_id_type" class="mb-2">
<el-radio value="default">默认模型</el-radio>
<el-radio value="custom">自定义</el-radio>
Expand All @@ -293,14 +313,32 @@ onMounted(() => {

<!-- 语音播放 -->
<div class="flex-between mt-4">
<span>语音播放</span>
<span :class="formData.tts_model_enable && formData.tts_type !== 'BROWSER' ? 'mk-required' : ''">语音播放</span>
<span class="flex items-center gap-3">
<el-checkbox v-if="formData.tts_model_enable" v-model="formData.tts_autoplay">自动播放</el-checkbox>
<el-switch v-model="formData.tts_model_enable" size="small" @change="changeSpeechPlaybackEnabled" />
</span>
</div>

<el-form-item class="mt-2" v-if="formData.tts_model_enable">
<el-form-item
v-if="formData.tts_model_enable"
class="mt-2"
prop="tts_model_id"
:rules="{
trigger: 'change',
validator: (_rule: unknown, _value: unknown, callback: (error?: Error) => void) => {
if (formData.tts_type === 'CUSTOM' && !formData.tts_model_id) {
callback(new Error('请选择语音播放模型'))
return
}
if (formData.tts_type === 'DEFAULT' && !defaultTtsModelSetting?.model_id) {
callback(new Error('请在默认模型设置中选择语音合成模型'))
return
}
callback()
},
}"
>
<el-radio-group v-model="formData.tts_type" class="mb-2">
<el-radio value="BROWSER">浏览器播放(免费)</el-radio>
<el-radio value="DEFAULT">默认模型</el-radio>
Expand Down
17 changes: 10 additions & 7 deletions ui/src/workflow-canvas/nodes/condition-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ const formData = computed(() => model.properties.node_data as { branch: BranchIt
const conditionNodeFormRef = useTemplateRef<FormInstance>('conditionNodeFormRef')
const nodeCascaderRefs = useTemplateRef<InstanceType<typeof NodeCascader>[]>('nodeCascaderRefs')

function validate() {
return Promise.all([conditionNodeFormRef.value?.validate(), ...(nodeCascaderRefs.value ?? []).map((cascader) => cascader.validate())]).catch(
(error) => Promise.reject({ node: model, errMessage: error }),
)
}

// 新分支插入 ELSE 之前,并保留稳定的锚点 ID。
function addBranch() {
Expand Down Expand Up @@ -103,6 +98,14 @@ const vBranchResize: Directive<HTMLElement, string> = {
},
}


async function validate() {
return conditionNodeFormRef.value?.validate().catch(
(error) => Promise.reject({ node: model, errMessage: error }),
)
}


const anchorGuard = createAnchorGuard(model)
onMounted(() => {
model.validate = validate
Expand Down Expand Up @@ -153,12 +156,12 @@ onBeforeUnmount(() => {
<el-form-item
class="small min-w-0 flex-1"
:prop="`branch.${branchIndex}.conditions.${conditionIndex}.compare`"
:rules="{ required: true, message: '请选择比较符', trigger: 'change' }"
:rules="{ required: true, message: '请选择', trigger: 'change' }"
>
<el-select
v-model="condition.compare"
:teleported="false"
placeholder="请选择比较符"
placeholder="请选择"
clearable
@change="changeComparison(condition)"
@visible-change="anchorGuard.setOverlayVisible(`${branch.id}:${conditionIndex}:compare`, $event)"
Expand Down
14 changes: 13 additions & 1 deletion ui/src/workflow-canvas/nodes/question-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface QuestionNodeForm {
dialogue_number: number
is_result: boolean
model_params_setting: Record<string, unknown>
dialogue_type: 'NODE' | 'WORKFLOW'
}

const formRef = useTemplateRef<FormInstance>('formRef')
Expand All @@ -46,6 +47,7 @@ const defaultForm: QuestionNodeForm = {
dialogue_number: 1,
is_result: false,
model_params_setting: {},
dialogue_type: 'NODE',
}
const savedForm = model.properties.node_data as Partial<QuestionNodeForm> | undefined
model.properties.node_data = {
Expand All @@ -57,6 +59,7 @@ model.properties.node_data = {
system: savedForm?.system ?? defaultForm.system,
prompt: savedForm?.prompt ?? defaultForm.prompt,
dialogue_number: savedForm?.dialogue_number ?? defaultForm.dialogue_number,
dialogue_type: savedForm?.dialogue_type ?? defaultForm.dialogue_type,
is_result: savedForm ? savedForm.is_result : defaultForm.is_result,
}

Expand Down Expand Up @@ -198,7 +201,16 @@ onMounted(() => {
</el-form-item>

<!-- 历史聊天记录 -->
<el-form-item label="历史聊天记录">
<el-form-item>
<template #label>
<div class="flex-between">
<span>历史聊天记录</span>
<el-select v-model="formData.dialogue_type" :teleported="false" class="w-18!" size="small">
<el-option label="节点" value="NODE" />
<el-option label="工作流" value="WORKFLOW" />
</el-select>
</div>
</template>
<el-input-number
v-model="formData.dialogue_number"
:min="0"
Expand Down
Loading