更新创作模型配置功能
- 在创作页面中新增会话ID和流ID字段,增强执行流的管理能力。 - 在模型配置页面中添加模型类型字段,提升模型管理的灵活性。 - 优化模型选择器,更新API Key配置弹窗,改善用户交互体验。
This commit is contained in:
@@ -76,6 +76,8 @@ export interface ExecutionItem {
|
||||
|
||||
export interface ExecutionFlowItem {
|
||||
flowName: string;
|
||||
flowId?: number | string;
|
||||
sessionId?: string;
|
||||
items: ExecutionItem[];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface ModelModuleListParams {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
modelName?: string;
|
||||
modelsType?: number | string;
|
||||
}
|
||||
|
||||
export interface ModelFormItem {
|
||||
|
||||
@@ -222,7 +222,7 @@ const handleSelectModel = (model: ModelItem) => {
|
||||
if (model.tenantId === 1) {
|
||||
// 系统模型,需要用户配置 API Key
|
||||
systemModelToClone.value = model;
|
||||
apiKeyForm.modelName = `${model.modelName} - 副本`;
|
||||
apiKeyForm.modelName = model.modelName;
|
||||
apiKeyForm.apiKey = '';
|
||||
apiKeyDialogVisible.value = true;
|
||||
} else {
|
||||
|
||||
@@ -341,8 +341,23 @@
|
||||
<!-- 输入框 -->
|
||||
<el-input v-model="userInput" placeholder="说点什么..." class="chat-input" @keydown.enter="sendMessage" />
|
||||
|
||||
<!-- 右侧发送按钮 -->
|
||||
<el-button type="primary" :icon="Promotion" :loading="isCreating" @click="sendMessage" class="send-btn" circle />
|
||||
<!-- 右侧发送/暂停按钮 -->
|
||||
<el-button
|
||||
v-if="!isCreating"
|
||||
type="primary"
|
||||
:icon="Promotion"
|
||||
@click="sendMessage"
|
||||
class="send-btn"
|
||||
circle
|
||||
/>
|
||||
<el-button
|
||||
v-else
|
||||
type="danger"
|
||||
:icon="VideoPause"
|
||||
@click="stopExecution"
|
||||
class="send-btn"
|
||||
circle
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 已选技能标签 -->
|
||||
@@ -556,7 +571,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Document, Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck } from '@element-plus/icons-vue';
|
||||
import { Document, Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck, VideoPause } from '@element-plus/icons-vue';
|
||||
import LogicFlow from '@logicflow/core';
|
||||
import { Control, SelectionSelect } from '@logicflow/extension';
|
||||
import '@logicflow/core/dist/index.css';
|
||||
@@ -1287,8 +1302,22 @@ const sendMessage = async () => {
|
||||
isCreating.value = false;
|
||||
}
|
||||
};
|
||||
// 终止执行(接口预留)
|
||||
const stopExecution = async () => {
|
||||
try {
|
||||
// TODO: 调用终止执行的接口
|
||||
// await stopExecutionApi({ sessionId: getSessionId() });
|
||||
|
||||
ElMessage.warning('终止执行功能开发中...');
|
||||
|
||||
// 暂时直接停止加载状态
|
||||
isCreating.value = false;
|
||||
} catch (error) {
|
||||
ElMessage.error('终止执行失败');
|
||||
}
|
||||
};
|
||||
// 判断节点是否有可见字段
|
||||
const hasVisibleFields = (node: any) => {
|
||||
const _hasVisibleFields = (node: any) => {
|
||||
if (!node.config) return false;
|
||||
const excludeKeys = ['nodeCode', 'width', 'height', 'x', 'y', 'formConfig', 'inputSource', 'fieldMetadata', 'selectedModel'];
|
||||
return Object.keys(node.config).some((key) => !excludeKeys.includes(key));
|
||||
@@ -1305,7 +1334,7 @@ const handleTreeNodeClick = async (data: TreeNode) => {
|
||||
if (data.nodeType === 'contentType' && data.flowId) {
|
||||
try {
|
||||
// 调用详情接口获取工作流数据,使用 flowId
|
||||
const res = await getWorkflowDetail(data.flowId);
|
||||
const res = await getWorkflowDetail(String(data.flowId));
|
||||
if (res.data) {
|
||||
// 设置当前会话的 sessionId(从工作空间进入)
|
||||
currentSessionId.value = data.sessionId || null;
|
||||
|
||||
@@ -35,17 +35,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="httpMethod" label="请求方式" width="100"></el-table-column>
|
||||
<el-table-column label="会话模型" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-if="isInferenceModel(row.modelsType)"
|
||||
size="small"
|
||||
:model-value="Number(row.isChatModel) === 1"
|
||||
:before-change="() => onChatModelSwitchRequest(row)"
|
||||
/>
|
||||
<span v-else style="color: #999;">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="enabled" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.enabled === 1 ? 'success' : 'danger'">{{ scope.row.enabled === 1 ? '启用' : '禁用' }}</el-tag>
|
||||
@@ -56,10 +45,29 @@
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip width="160"></el-table-column>
|
||||
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip width="160"></el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<el-table-column label="操作" width="300" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onOpenEditModule('edit', scope.row)">修改</el-button>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)">删除</el-button>
|
||||
<div class="action-buttons">
|
||||
<el-button size="small" text type="primary" @click="onOpenEditModule('edit', scope.row)">修改</el-button>
|
||||
<el-button
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) !== 1"
|
||||
size="small"
|
||||
text
|
||||
type="warning"
|
||||
@click="onSetChatModel(scope.row)"
|
||||
>
|
||||
设为会话模型
|
||||
</el-button>
|
||||
<el-tag
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) === 1"
|
||||
type="success"
|
||||
effect="dark"
|
||||
size="default"
|
||||
>
|
||||
✓ 当前会话模型
|
||||
</el-tag>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -78,19 +86,45 @@
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
<EditModule ref="editModuleRef" :model-types="state.modelTypes" @refresh="getTableData()" />
|
||||
|
||||
<!-- 系统模型 API Key 输入弹窗 -->
|
||||
<el-dialog v-model="apiKeyDialogVisible" title="配置系统模型" width="500px" :close-on-click-modal="false">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 16px">
|
||||
<template #title>
|
||||
<div style="line-height: 1.6">
|
||||
您选择的是系统模型,需要配置您自己的 API Key。<br />
|
||||
系统将为您创建一个模型副本并设置为会话模型。
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form :model="apiKeyForm" :rules="apiKeyRules" ref="apiKeyFormRef" label-width="100px">
|
||||
<el-form-item label="模型名称" prop="modelName">
|
||||
<el-input v-model="apiKeyForm.modelName" placeholder="请输入模型名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="API Key" prop="apiKey">
|
||||
<el-input v-model="apiKeyForm.apiKey" type="password" show-password placeholder="请输入您的 API Key" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="apiKeyDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleCreatePrivateModelAndSetChat" :loading="creatingModel">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="digitalHumanModelModule">
|
||||
import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { ElMessageBox, ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import {
|
||||
getModelModuleList,
|
||||
getModelTypeList,
|
||||
deleteModelModule,
|
||||
normalizeModelTypeOptions,
|
||||
updateChatModel,
|
||||
addModelModule,
|
||||
} from '/@/api/digitalHuman/modelConfig/modelModule/index';
|
||||
import { getApiErrorMessage } from '/@/utils/request';
|
||||
|
||||
const EditModule = defineAsyncComponent(() => import('/@/views/digitalHuman/modelConfig/modelModule/component/editModule.vue'));
|
||||
|
||||
@@ -110,7 +144,21 @@ const state = reactive({
|
||||
},
|
||||
});
|
||||
|
||||
// 判断是否为推理模型(只有推理模型才显示会话模型开关)
|
||||
// 系统模型 API Key 配置
|
||||
const apiKeyDialogVisible = ref(false);
|
||||
const apiKeyFormRef = ref<FormInstance>();
|
||||
const apiKeyForm = reactive({
|
||||
modelName: '',
|
||||
apiKey: '',
|
||||
});
|
||||
const apiKeyRules: FormRules = {
|
||||
modelName: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
|
||||
apiKey: [{ required: true, message: '请输入 API Key', trigger: 'blur' }],
|
||||
};
|
||||
const creatingModel = ref(false);
|
||||
const systemModelToClone = ref<any>(null);
|
||||
|
||||
// 判断是否为推理模型(只有推理模型才能设置为会话模型)
|
||||
const isInferenceModel = (modelsType: number | string | undefined | null) => {
|
||||
if (modelsType === undefined || modelsType === null || modelsType === '') {
|
||||
return false;
|
||||
@@ -120,21 +168,79 @@ const isInferenceModel = (modelsType: number | string | undefined | null) => {
|
||||
return typeInfo?.label === '推理模型' || String(modelsType) === '1';
|
||||
};
|
||||
|
||||
// 会话模型开关切换
|
||||
const onChatModelSwitchRequest = async (row: { id?: number | string; isChatModel?: number }) => {
|
||||
// 设置为会话模型
|
||||
const onSetChatModel = async (row: any) => {
|
||||
// 判断是否是系统模型(tenantId === 1)
|
||||
if (row.tenantId === 1) {
|
||||
// 系统模型,需要用户配置 API Key
|
||||
systemModelToClone.value = row;
|
||||
apiKeyForm.modelName = row.modelName;
|
||||
apiKeyForm.apiKey = '';
|
||||
apiKeyDialogVisible.value = true;
|
||||
} else {
|
||||
// 非系统模型,直接设置为会话模型
|
||||
try {
|
||||
await updateChatModel({
|
||||
id: row.id!,
|
||||
isChatModel: 1,
|
||||
});
|
||||
ElMessage.success('已设置为会话模型');
|
||||
await getTableData();
|
||||
} catch {
|
||||
// 错误已由全局拦截器处理
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建私有模型并设置为会话模型
|
||||
const handleCreatePrivateModelAndSetChat = async () => {
|
||||
if (!apiKeyFormRef.value || !systemModelToClone.value) return;
|
||||
|
||||
try {
|
||||
const newStatus = Number(row.isChatModel) === 1 ? 0 : 1;
|
||||
await updateChatModel({
|
||||
id: row.id!,
|
||||
isChatModel: newStatus as 0 | 1,
|
||||
});
|
||||
ElMessage.success(newStatus === 1 ? '已设置为会话模型' : '已取消会话模型');
|
||||
// 重新获取列表数据
|
||||
await apiKeyFormRef.value.validate();
|
||||
|
||||
creatingModel.value = true;
|
||||
|
||||
// 基于系统模型创建新模型(继承原模型的所有配置,只替换 apiKey)
|
||||
const systemModel = systemModelToClone.value;
|
||||
const createParams = {
|
||||
modelName: apiKeyForm.modelName,
|
||||
modelsType: systemModel.modelsType,
|
||||
baseUrl: systemModel.baseUrl,
|
||||
httpMethod: systemModel.httpMethod || 'POST',
|
||||
headMsg: systemModel.headMsg || '',
|
||||
isPrivate: systemModel.isPrivate ?? 1,
|
||||
enabled: systemModel.enabled ?? 1,
|
||||
isChatModel: 1, // 设置为会话模型
|
||||
apiKey: apiKeyForm.apiKey,
|
||||
form: systemModel.form || [],
|
||||
requestMapping: systemModel.requestMapping || {},
|
||||
responseMapping: systemModel.responseMapping || {},
|
||||
maxConcurrency: systemModel.maxConcurrency || 10,
|
||||
queueLimit: systemModel.queueLimit || 100,
|
||||
timeoutSeconds: systemModel.timeoutSeconds || 30,
|
||||
expectedSeconds: systemModel.expectedSeconds || 15,
|
||||
retryTimes: systemModel.retryTimes || 3,
|
||||
retryQueueMaxSeconds: systemModel.retryQueueMaxSeconds || 60,
|
||||
autoCleanSeconds: systemModel.autoCleanSeconds || 300,
|
||||
remark: systemModel.remark || '',
|
||||
};
|
||||
|
||||
await addModelModule(createParams);
|
||||
|
||||
ElMessage.success('模型创建成功并已设置为会话模型');
|
||||
|
||||
// 关闭对话框
|
||||
apiKeyDialogVisible.value = false;
|
||||
|
||||
// 刷新列表
|
||||
await getTableData();
|
||||
return true;
|
||||
} catch {
|
||||
// 接口错误由 request 全局提示后端 message
|
||||
return false;
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error(getApiErrorMessage(error, '创建模型失败'));
|
||||
}
|
||||
} finally {
|
||||
creatingModel.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -237,4 +343,14 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.el-button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user