|
|
|
@@ -129,21 +129,21 @@
|
|
|
|
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
|
|
|
|
|
<el-form-item label="输入 Token 路径">
|
|
|
|
|
<el-select v-model="formData.tokenMapping.promptTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
|
|
|
|
|
<el-option v-for="item in tokenMappingOptions" :key="item.path" :label="`${item.path} — ${item.label}`" :value="item.path" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
|
|
|
|
|
<el-form-item label="输出 Token 路径">
|
|
|
|
|
<el-select v-model="formData.tokenMapping.completionTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
|
|
|
|
|
<el-option v-for="item in tokenMappingOptions" :key="item.path" :label="`${item.path} — ${item.label}`" :value="item.path" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
|
|
|
|
|
<el-form-item label="总 Token 路径">
|
|
|
|
|
<el-select v-model="formData.tokenMapping.totalTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
|
|
|
|
|
<el-option v-for="item in tokenMappingOptions" :key="item.path" :label="`${item.path} — ${item.label}`" :value="item.path" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
@@ -236,7 +236,7 @@
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- JSON 编辑器弹窗 -->
|
|
|
|
|
<el-dialog v-model="jsonEditorVisible" :title="jsonEditorTitle" width="1000px" top="5vh" :close-on-click-modal="false" destroy-on-close append-to-body>
|
|
|
|
|
<el-dialog v-model="jsonEditorVisible" :title="jsonEditorTitle" width="1200px" top="5vh" :close-on-click-modal="false" destroy-on-close append-to-body>
|
|
|
|
|
<template #header>
|
|
|
|
|
<span>{{ jsonEditorTitle }}</span>
|
|
|
|
|
<div class="json-editor-toolbar">
|
|
|
|
@@ -276,7 +276,8 @@ import { JsonEditor } from '/@/components/json-schema-editor';
|
|
|
|
|
import {
|
|
|
|
|
createModelManage,
|
|
|
|
|
updateModelManage,
|
|
|
|
|
getModelSupplierList,
|
|
|
|
|
getModelManage,
|
|
|
|
|
getModelSupplierList,
|
|
|
|
|
type ModelManageItem,
|
|
|
|
|
type ModelTypeTreeNode,
|
|
|
|
|
type ModelSupplierItem,
|
|
|
|
@@ -385,12 +386,48 @@ watch(isReasoningModel, (val) => {
|
|
|
|
|
if (!val) formData.chatModel = false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** 根据返回类型获取 Token 映射下拉选项(响应映射的 key 列表) */
|
|
|
|
|
|
|
|
|
|
/** 递归提取 { type, attrs/value } 包裹结构中的所有叶子字段 */
|
|
|
|
|
function flattenWrappedFields(obj: any, basePath: string = ''): { path: string; label: string }[] {
|
|
|
|
|
const results: { path: string; label: string }[] = [];
|
|
|
|
|
if (!obj || typeof obj !== 'object') return results;
|
|
|
|
|
|
|
|
|
|
// 检测 { type, attrs/value } 包裹格式
|
|
|
|
|
if (typeof obj.type === 'string' && ['string','number','boolean','null','object','array'].includes(obj.type)) {
|
|
|
|
|
const jtype = obj.type;
|
|
|
|
|
const dataKey = (jtype === 'object' || jtype === 'array') ? 'attrs' : 'value';
|
|
|
|
|
|
|
|
|
|
if (jtype === 'object' && dataKey in obj && obj[dataKey] && typeof obj[dataKey] === 'object' && !Array.isArray(obj[dataKey])) {
|
|
|
|
|
for (const [key, val] of Object.entries(obj[dataKey])) {
|
|
|
|
|
const childPath = basePath ? basePath + '.attrs.' + key : key;
|
|
|
|
|
results.push(...flattenWrappedFields(val, childPath));
|
|
|
|
|
}
|
|
|
|
|
} else if (jtype === 'array' && dataKey in obj && Array.isArray(obj[dataKey])) {
|
|
|
|
|
obj[dataKey].forEach((item: any, idx: number) => {
|
|
|
|
|
results.push(...flattenWrappedFields(item, basePath + '.attrs[' + idx + ']'));
|
|
|
|
|
});
|
|
|
|
|
} else if (['string', 'number', 'boolean', 'null'].includes(jtype)) {
|
|
|
|
|
results.push({ path: basePath, label: obj.label || '' });
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 普通对象(非包裹格式)
|
|
|
|
|
if (!Array.isArray(obj)) {
|
|
|
|
|
for (const [key, val] of Object.entries(obj)) {
|
|
|
|
|
const childPath = basePath ? basePath + '.' + key : key;
|
|
|
|
|
results.push(...flattenWrappedFields(val, childPath));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 根据返回类型获取 Token 映射下拉选项(响应映射的 key — 路径) */
|
|
|
|
|
const tokenMappingOptions = computed(() => {
|
|
|
|
|
const source = formData.responseType === 2
|
|
|
|
|
? formData.asyncTaskMapping.responseMapping
|
|
|
|
|
: formData.responseMapping;
|
|
|
|
|
return source ? Object.keys(source) : [];
|
|
|
|
|
return source ? flattenWrappedFields(source) : [];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** 是否有可用的映射配置 */
|
|
|
|
@@ -514,32 +551,41 @@ const resetForm = () => {
|
|
|
|
|
formData.lastFrame = '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const openDialog = (type: 'add' | 'edit', row?: ModelManageItem) => {
|
|
|
|
|
const openDialog = async (type: 'add' | 'edit', id?: string) => {
|
|
|
|
|
resetForm();
|
|
|
|
|
isEdit.value = type === 'edit';
|
|
|
|
|
detailLoading.value = false;
|
|
|
|
|
detailLoading.value = type === 'edit';
|
|
|
|
|
loadSupplierList();
|
|
|
|
|
|
|
|
|
|
if (type === 'edit' && row) {
|
|
|
|
|
editId.value = row.id;
|
|
|
|
|
formData.modelName = row.modelName;
|
|
|
|
|
formData.modelType = [row.modelType];
|
|
|
|
|
formData.modelSupplier = Number(row.modelSupplier) || undefined;
|
|
|
|
|
formData.baseUrl = row.baseUrl;
|
|
|
|
|
formData.apiKey = row.apiKey || '';
|
|
|
|
|
// 兼容新旧字段名
|
|
|
|
|
formData.responseType = row.responseType ?? row.invokeType;
|
|
|
|
|
formData.enabled = row.enabled ?? true;
|
|
|
|
|
formData.chatModel = row.ChatModel ?? false;
|
|
|
|
|
formData.maxConcurrency = row.maxConcurrency || 10;
|
|
|
|
|
formData.maxTokens = row.maxTokens || 4096;
|
|
|
|
|
formData.tokenPredictPrice = row.tokenPredictPrice ?? 0;
|
|
|
|
|
formData.requestHeadMapping = row.requestHeadMapping || row.requestMapping || {};
|
|
|
|
|
formData.requestBodyMapping = row.requestBodyMapping || {};
|
|
|
|
|
formData.responseMapping = row.responseMapping || {};
|
|
|
|
|
formData.tokenMapping = row.tokenMapping ? { ...defaultTokenMapping, ...row.tokenMapping } : { ...defaultTokenMapping };
|
|
|
|
|
formData.asyncTaskMapping = row.asyncTaskMapping ? { ...defaultAsyncTaskMapping, ...row.asyncTaskMapping } : { ...defaultAsyncTaskMapping };
|
|
|
|
|
formData.lastFrame = row.lastFrame || '';
|
|
|
|
|
if (type === 'edit' && id) {
|
|
|
|
|
editId.value = id;
|
|
|
|
|
try {
|
|
|
|
|
const res = await getModelManage(id);
|
|
|
|
|
const row = res.data.modelManage;
|
|
|
|
|
formData.modelName = row.modelName;
|
|
|
|
|
formData.modelType = [row.modelType];
|
|
|
|
|
formData.modelSupplier = Number(row.modelSupplier) || undefined;
|
|
|
|
|
formData.baseUrl = row.baseUrl;
|
|
|
|
|
formData.apiKey = row.apiKey || '';
|
|
|
|
|
formData.responseType = row.responseType ?? row.invokeType;
|
|
|
|
|
formData.enabled = row.enabled ?? true;
|
|
|
|
|
formData.chatModel = row.ChatModel ?? false;
|
|
|
|
|
formData.maxConcurrency = row.maxConcurrency || 10;
|
|
|
|
|
formData.maxTokens = row.maxTokens || 4096;
|
|
|
|
|
formData.tokenPredictPrice = row.tokenPredictPrice ?? 0;
|
|
|
|
|
formData.requestHeadMapping = row.requestHeadMapping || row.requestMapping || {};
|
|
|
|
|
formData.requestBodyMapping = row.requestBodyMapping || {};
|
|
|
|
|
formData.responseMapping = row.responseMapping || {};
|
|
|
|
|
formData.tokenMapping = row.tokenMapping ? { ...defaultTokenMapping, ...row.tokenMapping } : { ...defaultTokenMapping };
|
|
|
|
|
formData.asyncTaskMapping = row.asyncTaskMapping ? { ...defaultAsyncTaskMapping, ...row.asyncTaskMapping } : { ...defaultAsyncTaskMapping };
|
|
|
|
|
formData.lastFrame = row.lastFrame || '';
|
|
|
|
|
} catch {
|
|
|
|
|
// 关闭弹窗,错误由全局处理
|
|
|
|
|
dialogVisible.value = false;
|
|
|
|
|
return;
|
|
|
|
|
} finally {
|
|
|
|
|
detailLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dialogVisible.value = true;
|
|
|
|
|