This commit is contained in:
吴红兵
2025-12-02 10:37:49 +08:00
commit 1f645dad3e
1183 changed files with 147673 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
<template>
<el-dialog title="批量新增AI模型" v-model="visible" :close-on-click-modal="false" draggable width="600px">
<el-form ref="dataFormRef" :model="form" :rules="dataRules" v-loading="loading">
<el-form-item prop="apiKey" class="relative">
<el-input
v-model="form.apiKey"
placeholder="请输入API Key格式sk-开头长度51位"
type="textarea"
show-word-limit
maxlength="51"
rows="2"
/>
<div class="absolute -bottom-5 right-2 text-xs z-10 bg-white/90 px-2 py-1 rounded">
<a href="https://cloud.siliconflow.cn/i/YKcJJTYP" target="_blank" class="text-blue-500 hover:text-blue-700 no-underline text-xs">获取硅基流动 免费 ApiKey</a>
</div>
</el-form-item>
<el-form-item>
<el-card class="mt-4">
<template #header>
<span>将要创建的模型{{ previewModels.length }}</span>
</template>
<div class="flex flex-wrap gap-2">
<el-tag v-for="model in previewModels" :key="model.type" class="mb-2 mr-2" type="primary">
{{ model.typeLabel }}: {{ model.modelName }}
</el-tag>
</div>
</el-card>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading">批量创建</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BatchAiModelDialog">
import { useMessage } from '/@/hooks/message';
import { addObj, details } from '/@/api/knowledge/aiModel';
import { ref, reactive, nextTick } from 'vue';
const emit = defineEmits(['refresh']);
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
// 提交表单数据
const form = reactive({
apiKey: ''
});
// 定义校验规则
const dataRules = ref({
apiKey: [
{ required: true, message: '请输入API Key', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error('请输入API Key'));
return;
}
if (!value.startsWith('sk-')) {
callback(new Error('API Key必须以sk-开头'));
return;
}
if (value.length !== 51) {
callback(new Error('API Key长度必须为51位'));
return;
}
callback();
},
trigger: 'blur'
}
]
});
// 预览要创建的模型
const previewModels = ref([
{
type: 'Chat',
typeLabel: '聊天',
modelName: 'moonshotai/Kimi-K2-Instruct',
name: 'kimi-k2',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Chat',
typeLabel: '聊天',
modelName: 'deepseek-ai/DeepSeek-V3',
name: 'deepseek-chat',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Embedding',
typeLabel: '向量',
modelName: 'Qwen/Qwen3-Embedding-8B',
name: 'qwen-embedding',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Reranker',
typeLabel: '排序',
modelName: 'Qwen/Qwen3-Reranker-8B',
name: 'qwen-reranker',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Image',
typeLabel: '图片',
modelName: 'Kwai-Kolors/Kolors',
name: 'kolors-image',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Reason',
typeLabel: '推理',
modelName: 'MiniMaxAI/MiniMax-M1-80k',
name: 'minimax-reason',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Vision',
typeLabel: '视觉',
modelName: 'Qwen/Qwen2.5-VL-72B-Instruct',
name: 'qwen-vision',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Video',
typeLabel: '视频',
modelName: 'Wan-AI/Wan2.1-I2V-14B-720P',
name: 'wan-video',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
},
{
type: 'Voice',
typeLabel: '音频',
modelName: 'RVC-Boss/GPT-SoVITS',
name: 'gpt-sovits',
provider: 'Siliconflow',
baseUrl: 'https://api.siliconflow.cn/v1'
}
]);
// 检查模型名称是否存在
const checkModelExists = async (name: string): Promise<boolean> => {
try {
const response = await details({ name });
return response.data !== null && response.data.length > 0;
} catch (error) {
return false;
}
};
// 打开弹窗
const openDialog = () => {
visible.value = true;
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
form.apiKey = '';
});
};
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false;
try {
loading.value = true;
let successCount = 0;
let failedCount = 0;
let skipCount = 0;
const errors: string[] = [];
const skipped: string[] = [];
// 使用单个API Key为每个模型创建
for (const model of previewModels.value) {
try {
// 先检查模型名称是否存在
const exists = await checkModelExists(model.name);
if (exists) {
skipCount++;
skipped.push(`${model.typeLabel}模型(${model.name})已存在,跳过创建`);
continue;
}
const modelData = {
modelType: model.type,
modelName: model.modelName,
name: model.name,
provider: model.provider,
baseUrl: model.baseUrl,
apiKey: form.apiKey.trim(),
defaultModel: '1', // 非默认模型
extData: '{}',
// 为Chat类型设置默认参数
...(model.type === 'Chat' && {
responseLimit: 2048,
temperature: 0.4,
topP: 0.7
}),
// 为Image类型设置默认参数
...(model.type === 'Image' && {
imageSize: '1024x1024',
imageQuality: 'standard',
imageStyle: 'natural'
})
};
await addObj(modelData);
successCount++;
} catch (err: any) {
failedCount++;
errors.push(`${model.typeLabel}模型(${model.modelName})创建失败: ${err.msg || '未知错误'}`);
}
}
// 显示结果
const resultMessages = [];
if (successCount > 0) {
resultMessages.push(`成功创建${successCount}个AI模型`);
}
if (skipCount > 0) {
resultMessages.push(`跳过${skipCount}个已存在的模型`);
}
if (failedCount > 0) {
resultMessages.push(`失败${failedCount}`);
}
if (resultMessages.length > 0) {
useMessage().success(resultMessages.join(''));
}
// 显示跳过的模型信息
if (skipped.length > 0 && skipped.length <= 3) {
skipped.forEach(msg => useMessage().warning(msg));
} else if (skipped.length > 3) {
useMessage().warning(`共跳过${skipCount}个已存在的模型`);
}
// 显示错误信息
if (errors.length > 0 && errors.length <= 3) {
errors.forEach(error => useMessage().error(error));
} else if (errors.length > 3) {
useMessage().error(`批量创建完成,有${failedCount}个模型创建失败`);
}
if (successCount > 0 || skipCount > 0) {
visible.value = false;
emit('refresh');
}
} catch (err: any) {
useMessage().error(err.msg || '批量创建失败');
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>

View File

@@ -0,0 +1,392 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="供应商" prop="provider">
<el-select v-model="form.provider" placeholder="请选择供应商" @change="handleProviderChange">
<el-option v-for="provider in providers" :key="provider.value" :label="provider.label" :value="provider.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="类型" prop="modelType">
<el-select v-model="form.modelType" placeholder="请选择类型">
<el-option v-for="type in modelTypes" :key="type.value" :label="type.label" :value="type.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item prop="modelName">
<template #label>模型<tip content="模型平台的标准名称,请选择对应模型"></tip> </template>
<el-select v-model="form.modelName" placeholder="请选择模型名称" filterable allow-create>
<el-option v-for="model in filteredModels" :key="model.model" :label="model.model" :value="model.model"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item prop="name">
<template #label
>别名
<tip content="模型别名,方便本平台后续调用"></tip>
</template>
<el-input v-model="form.name" placeholder="请输入模型别名" :disabled="form.id !== ''" />
</el-form-item>
</el-col>
<template v-if="form.modelType === 'Image'">
<el-col :span="12" class="mb20">
<el-form-item label="图片大小" prop="imageSize">
<el-select v-model="form.imageSize" placeholder="请选择图片大小">
<el-option v-for="size in imageSizes" :key="size" :label="size" :value="size"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="图片质量" prop="imageQuality">
<el-input v-model="form.imageQuality" placeholder="请入图片质量" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="图片风格" prop="imageStyle">
<el-input v-model="form.imageStyle" placeholder="请输入图片风格" />
</el-form-item>
</el-col>
</template>
<el-col :span="12" class="mb20">
<el-form-item label="apiKey" prop="apiKey">
<template #label
>apiKey
<tip content="Olama 随意填写API KEY但不能为空"></tip>
</template>
<el-input v-model="form.apiKey" placeholder="请输入 apiKey" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="baseUrl" prop="baseUrl">
<el-input v-model="form.baseUrl" placeholder="请输入 baseUrl" />
</el-form-item>
</el-col>
<template v-if="form.modelType === 'Chat'">
<el-col :span="12" class="mb20">
<el-form-item label="响应限制" prop="responseLimit">
<div class="w-full form-control">
<input type="range" min="0" max="4096" v-model="form.responseLimit" class="range range-primary" />
<div class="flex justify-between px-2 w-full text-xs">
<span>0</span>
<span>4096</span>
</div>
<div class="mt-2 text-center">
<span class="badge">{{ form.responseLimit }}</span>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="随机性" prop="temperature">
<div class="w-full form-control">
<input type="range" min="0" max="1" step="0.1" v-model="form.temperature" class="range range-primary" />
<div class="flex justify-between px-2 w-full text-xs">
<span>0</span>
<span>1</span>
</div>
<div class="mt-2 text-center">
<span class="badge">{{ form.temperature }}</span>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="顶层概率" prop="topP">
<div class="w-full form-control">
<input type="range" min="0" max="1" step="0.1" v-model="form.topP" class="range range-primary" />
<div class="flex justify-between px-2 w-full text-xs">
<span>0</span>
<span>0.5</span>
<span>1</span>
</div>
<div class="mt-2 text-center">
<span class="badge">{{ form.topP }}</span>
</div>
</div>
</el-form-item>
</el-col>
</template>
<template v-if="form.modelType === 'Embedding' && availableDimensions.length > 0">
<el-col :span="12" class="mb20">
<el-form-item label="维度" prop="dimensions">
<el-select v-model="form.dimensions" placeholder="请选择维度">
<el-option v-for="dimension in availableDimensions" :key="dimension" :label="dimension" :value="dimension"></el-option>
</el-select>
</el-form-item>
</el-col>
</template>
</el-row>
<el-row>
<el-col :span="24" class="mb20">
<el-form-item prop="extData">
<template #label
>特殊参数
<tip content="部分模型参考文档配置" />
</template>
<json-editor ref="jsonEditorRef" v-model="form.extData" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="AiModelDialog">
import { useMessage } from '/@/hooks/message';
import { getObj, addObj, putObj, validateExist } from '/@/api/knowledge/aiModel';
import { ref, reactive, watch, nextTick, computed } from 'vue';
import { rule } from '/@/utils/validate';
import { modelTypes, providers, providerModels, providerBaseURLMap } from './model';
// @ts-ignore
import JsonEditor from '@axolo/json-editor-vue';
const emit = defineEmits(['refresh']);
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
// 提交表单数据
const form = reactive({
id: '',
modelType: '',
modelName: '',
provider: '',
name: '',
responseLimit: 2048,
temperature: 0.4,
topP: 0.7,
apiKey: '',
baseUrl: '',
secretKey: '',
endpoint: '',
azureDeploymentName: '',
geminiProject: '',
geminiLocation: '',
imageSize: '',
imageQuality: '',
imageStyle: '',
defaultModel: '1',
extData: `{}`,
dimensions: '',
});
// 可用模型列表
const availableModels = ref<{ type: string; model: string; dimensions?: number[] }[]>([]);
// 计算当前选择模型的可用维度
const availableDimensions = computed(() => {
if (form.modelType !== 'Embedding' || !form.modelName) {
return [];
}
const currentModel = availableModels.value.find(model =>
model.model === form.modelName && model.type === 'Embedding'
);
return currentModel?.dimensions || [];
});
// 处理供应商变化
const handleProviderChange = (provider: string) => {
availableModels.value = providerModels[provider as keyof typeof providerModels] || [];
};
// 监听 provider 变化,自动更新 baseURL
watch(
() => form.provider,
(newProvider) => {
form.baseUrl = providerBaseURLMap[newProvider as keyof typeof providerBaseURLMap] || '';
handleProviderChange(newProvider);
}
);
// 定义校验规则
const dataRules = ref({
modelType: [
{ required: true, message: '请输入模型类型', trigger: 'blur' },
{ validator: rule.overLength, trigger: 'blur' },
],
modelName: [
{ required: true, message: '请输入模型名称', trigger: 'blur' },
{ validator: rule.overLength, trigger: 'blur' },
],
provider: [
{ required: true, message: '请输入供应商', trigger: 'blur' },
{ validator: rule.overLength, trigger: 'blur' },
],
name: [
{ required: true, message: '请输入模型别名', trigger: 'blur' },
{ max: 100, message: '模型别名最多100个字符', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => validateExist(rule, value, callback, !!form.id),
trigger: 'blur',
},
],
apiKey: [
{ required: true, message: '请输入apiKey', trigger: 'blur' },
{ validator: rule.noChinese, trigger: 'blur' },
{ max: 255, message: 'ApiKey最多255个字符', trigger: 'blur' },
],
baseUrl: [
{ required: true, message: '请输入baseUrl', trigger: 'blur' },
{ validator: rule.overLength, trigger: 'blur' },
],
imageSize: [{ required: true, message: '请选择图片大小', trigger: 'change' }],
dimensions: [
{
validator: (rule: any, value: any, callback: any) => {
// 只有当显示维度下拉框时才验证必填
if (form.modelType === 'Embedding' && availableDimensions.value.length > 0) {
if (!value) {
callback(new Error('请选择维度'));
return;
}
}
callback();
},
trigger: 'change'
}
],
extData: [{ validator: rule.json, trigger: 'blur' }],
// ... 其他字段的验证规则 ...
});
// 打开弹窗
const openDialog = (id?: string) => {
visible.value = true;
form.id = '';
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
availableModels.value = []; // 重置可用模型列表
});
// 获取aiModel信息
if (id) {
form.id = id;
getAiModelData(id);
} else {
// Reset the name field when adding a new model
form.name = '';
form.provider = '';
form.modelName = '';
form.modelType = '';
form.dimensions = '';
}
};
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false;
try {
loading.value = true;
if (form.apiKey?.includes('***')) form.apiKey = '';
form.id ? await putObj(form) : await addObj(form);
useMessage().success(form.id ? '修改成功' : '添加成功');
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg);
} finally {
loading.value = false;
}
};
// 初始化表单数据
const getAiModelData = (id: string) => {
// 获取数据
loading.value = true;
getObj(id)
.then((res: any) => {
// 先设置 provider
form.provider = res.data.provider;
// 设置可用模型列表
handleProviderChange(form.provider);
// 设置 modelType
form.modelType = res.data.modelType;
// 确保 filteredModels 已更新
nextTick(() => {
// 然后设置其他表单数据,包括 modelName
Object.assign(form, res.data);
});
})
.finally(() => {
loading.value = false;
});
};
// 暴露变量
defineExpose({
openDialog,
});
// Add this to your existing constants
const imageSizes = ['1024x1024', '512x1024', '768x512', '768x1024', '1024x576', '576x1024'];
// Add a computed property for filtered models based on selected type
const filteredModels = computed(() => {
return availableModels.value.filter((model) => model.type === form.modelType);
});
// Add a watcher for modelType to reset modelName when type changes
watch(
() => form.modelType,
() => {
form.modelName = '';
form.dimensions = '';
}
);
// 监听模型名称变化,自动设置默认维度
watch(
() => form.modelName,
() => {
if (form.modelType === 'Embedding') {
const currentModel = availableModels.value.find(model =>
model.model === form.modelName && model.type === 'Embedding'
);
if (currentModel?.dimensions && currentModel.dimensions.length > 0) {
form.dimensions = currentModel.dimensions[0].toString();
} else {
form.dimensions = '';
}
} else {
form.dimensions = '';
}
}
);
</script>

View File

@@ -0,0 +1,211 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row v-show="showSearch">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item label="类型" prop="title">
<el-select placeholder="请选择类型" v-model="state.queryForm.modelType">
<el-option :key="index" :label="item.label" :value="item.value" v-for="(item, index) in modelTypes">
{{ item.label }}
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList"> 查询</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row>
<div class="mb8" style="width: 100%">
<el-button icon="plus" type="primary" class="ml10" @click="batchFormDialogRef.openDialog()" v-auth="'knowledge_aiModel_add'">
批量新增
</el-button>
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()" v-auth="'knowledge_aiModel_add'">
</el-button>
<el-button plain icon="Refresh" type="primary" @click="handleRefresh"> 刷新 </el-button>
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'knowledge_aiModel_del'" @click="handleDelete(selectObjs)">
删除
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
:export="'knowledge_aiModel_export'"
@exportExcel="exportExcel"
class="ml10 mr20"
style="float: right"
@queryTable="getDataList"
></right-toolbar>
</div>
</el-row>
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@selection-change="selectionChangHandle"
@sort-change="sortChangeHandle"
>
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" label="#" width="60" />
<el-table-column prop="provider" label="供应商" show-overflow-tooltip width="200">
<template #default="scope">
{{ getProviderLabel(scope.row.provider) }}
</template>
</el-table-column>
<el-table-column prop="modelType" label="类型" show-overflow-tooltip width="100">
<template #default="scope">
<el-tag>
{{ getModelTypeLabel(scope.row.modelType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" show-overflow-tooltip width="300" />
<el-table-column prop="modelName" label="模型" show-overflow-tooltip />
<el-table-column prop="defaultModel">
<template #header>
默认模型
<tip content="若调用不传递模型,则使用【默认模型】"></tip>
</template>
<template #default="scope">
<el-switch v-model="scope.row.defaultModel" @change="changeSwitch(scope.row)" active-value="1" inactive-value="0"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button icon="edit-pen" text type="primary" v-auth="'knowledge_aiModel_edit'" @click="formDialogRef.openDialog(scope.row.id)"
>编辑</el-button
>
<el-button icon="delete" text type="primary" v-auth="'knowledge_aiModel_del'" @click="handleDelete([scope.row.id])">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
<!-- 批量新增 -->
<batch-form-dialog ref="batchFormDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="systemAiModel">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObjs, putObj, sync } from '/@/api/knowledge/aiModel';
import { useMessage, useMessageBox } from '/@/hooks/message';
import { modelTypes, providers } from './model';
// 定义AiModel接口
interface AiModel {
id: string;
provider: string;
modelType: string;
name: string;
modelName: string;
defaultModel: string;
}
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const BatchFormDialog = defineAsyncComponent(() => import('./batch-form.vue'));
// 定义查询字典
// 定义变量内容
const formDialogRef = ref();
const batchFormDialogRef = ref();
// 搜索变量
const queryRef = ref();
const showSearch = ref(true);
// 多选变量
const selectObjs = ref([]) as any;
const multiple = ref(true);
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: fetchList,
});
// table hook
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(state);
// 刷新按钮的处理函数
const handleRefresh = async () => {
try {
// 调用list接口获取最新数据
await sync();
useMessage().success('数据已同步');
} catch (error) {
useMessage().error('数据同步失败');
}
};
// Update the changeSwitch function
const changeSwitch = async (row: AiModel) => {
try {
await putObj({
id: row.id,
modelType: row.modelType,
defaultModel: row.defaultModel,
});
useMessage().success('更新成功');
await getDataList();
} catch (error) {
useMessage().error('更新失败');
}
};
// 多选事件
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id);
multiple.value = !objs.length;
};
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm('此操作将永久删除');
} catch {
return;
}
try {
await delObjs(ids);
getDataList();
useMessage().success('删除成功');
} catch (err: any) {
useMessage().error(err.msg);
}
};
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields();
state.queryForm = {};
// 清空多选
selectObjs.value = [];
getDataList();
};
// 导出excel
const exportExcel = () => {
downBlobFile('/knowledge/aiModel/export', Object.assign(state.queryForm, { ids: selectObjs }), 'aiModel.xlsx');
};
// 获取模型类型的标签
const getModelTypeLabel = (value: string) => {
const modelType = modelTypes.find((type) => type.value === value);
return modelType ? modelType.label : value;
};
// 获取供应商的标签
const getProviderLabel = (value: string) => {
const provider = providers.find((p) => p.value === value);
return provider ? provider.label : value;
};
</script>

View File

@@ -0,0 +1,148 @@
// 供应商
export const providers = [
{ label: 'OpenAI协议', value: 'OpenAI' },
{ label: '阿里百炼', value: 'Aliyun' },
{ label: '火山方舟', value: 'Ark' },
{ label: 'DeepSeek', value: 'DeepSeek' },
{ label: '智谱清言', value: 'ChatGLM' },
{ label: '硅基流动', value: 'Siliconflow' },
{ label: 'Gitee', value: 'Gitee' },
{ label: 'Ollama', value: 'Ollama' },
{ label: 'Kimi', value: 'Kimi' },
{ label: '搜索服务', value: 'BoCha' },
{ label: 'Jina', value: 'Jina' },
];
// 模型类型
export const modelTypes = [
{ label: '聊天', value: 'Chat' },
{ label: '推理', value: 'Reason' },
{ label: '向量', value: 'Embedding' },
{ label: '排序', value: 'Reranker' },
{ label: '图片', value: 'Image' },
{ label: '视频', value: 'Video' },
{ label: '视觉', value: 'Vision' },
{ label: '音频', value: 'Voice' },
{ label: '搜索', value: 'Search' },
{ label: '解析', value: 'Parse' },
];
// 各供应商的模型映射
export const providerModels = {
Aliyun: [
{ type: 'Chat', model: 'qwen-max-latest', json: true },
{ type: 'Chat', model: 'qwen-plus' },
{ type: 'Chat', model: 'qwen3-235b-a22b' },
{ type: 'Vision', model: 'qwen-vl-plus-latest' },
{ type: 'Vision', model: 'qwen-vl-max-latest' },
{ type: 'Vision', model: 'qwen-vl-ocr' },
{ type: 'Embedding', model: 'text-embedding-v4', dimensions: [2048, 1536, 1024, 768, 512, 256, 128, 64] },
{ type: 'Embedding', model: 'text-embedding-v3', dimensions: [1024, 768, 512, 256, 128, 64] },
{ type: 'Reranker', model: 'gte-rerank-v2' },
{ type: 'Image', model: 'flux-schnell' },
{ type: 'Voice', model: 'paraformer-v2' },
{ type: 'Voice', model: 'cosyvoice-v1' },
],
Ark: [
{ type: 'Chat', model: 'deepseek-v3-250324', json: true },
{ type: 'Chat', model: 'doubao-1-5-pro-32k-250115' },
{ type: 'Reason', model: 'deepseek-r1-250120' },
{ type: 'Vision', model: 'doubao-1-5-vision-pro-32k-250115' },
{ type: 'Embedding', model: 'doubao-embedding-large-text-240915' },
],
DeepSeek: [
{ type: 'Chat', model: 'deepseek-chat', json: true },
{ type: 'Reason', model: 'deepseek-reasoner' },
],
ChatGLM: [
{ type: 'Chat', model: 'glm-4-flash' },
{ type: 'Chat', model: 'glm-4-plus' },
{ type: 'Reason', model: 'glm-z1-air' },
{ type: 'Reason', model: 'glm-z1-airx' },
{ type: 'Reason', model: 'glm-z1-flash' },
{ type: 'Vision', model: 'glm-4v-flash' },
{ type: 'Vision', model: 'glm-4v-plus' },
{ type: 'Embedding', model: 'embedding-3' },
],
OpenAI: [
{ type: 'Chat', model: 'gpt-4.1-nano', json: true },
{ type: 'Chat', model: 'gpt-4.1-mini', json: true },
{ type: 'Chat', model: 'gpt-4.1', json: true },
{ type: 'Embedding', model: 'text-embedding-3-small' },
{ type: 'Embedding', model: 'text-embedding-3-large' },
],
Siliconflow: [
{ type: 'Chat', model: 'deepseek-ai/DeepSeek-V3', json: true },
{ type: 'Chat', model: 'moonshotai/Kimi-K2-Instruct' },
{ type: 'Image', model: 'black-forest-labs/FLUX.1-schnell' },
{ type: 'Image', model: 'stabilityai/stable-diffusion-3-5-large' },
{ type: 'Image', model: 'Kwai-Kolors/Kolors' },
{ type: 'Video', model: 'Wan-AI/Wan2.1-I2V-14B-720P' },
{ type: 'Video', model: 'Wan-AI/Wan2.1-I2V-14B-720P-Turbo' },
{ type: 'Voice', model: 'FunAudioLLM/CosyVoice2-0.5B' },
{ type: 'Voice', model: 'RVC-Boss/GPT-SoVITS' },
{ type: 'Reason', model: 'deepseek-ai/DeepSeek-R1' },
{ type: 'Reason', model: 'MiniMaxAI/MiniMax-M1-80k' },
{ type: 'Vision', model: 'Qwen/Qwen2.5-VL-72B-Instruct' },
{ type: 'Vision', model: 'Qwen/Qwen2.5-VL-32B-Instruct' },
{ type: 'Embedding', model: 'Qwen/Qwen3-Embedding-8B' },
{ type: 'Embedding', model: 'Qwen/Qwen3-Embedding-4B' },
{ type: 'Embedding', model: 'BAAI/bge-m3' },
{ type: 'Embedding', model: 'BAAI/bge-large-zh-v1.5' },
{ type: 'Reranker', model: 'Qwen/Qwen3-Reranker-8B' },
{ type: 'Reranker', model: 'Qwen/Qwen3-Reranker-4B' },
{ type: 'Reranker', model: 'BAAI/bge-reranker-v2-m3' },
{ type: 'Reranker', model: 'netease-youdao/bce-reranker-base_v1' },
],
Ollama: [
{ type: 'Chat', model: 'qwen3:14b' },
{ type: 'Chat', model: 'qwen3:30b' },
{ type: 'Chat', model: 'qwen3:32b' },
{ type: 'Chat', model: 'qwen3:256b' },
{ type: 'Chat', model: 'qwen2.5:14b' },
{ type: 'Chat', model: 'qwen2.5:32b' },
{ type: 'Chat', model: 'qwen2.5:72b' },
{ type: 'Embedding', model: 'bge-m3:latest' },
{ type: 'Embedding', model: 'shaw/dmeta-embedding-zh' },
{ type: 'Vision', model: 'minicpm-v:latest' },
{ type: 'Vision', model: 'qwen2.5vl:7b' },
{ type: 'Vision', model: 'qwen2.5vl:32b' },
{ type: 'Vision', model: 'qwen2.5vl:72b' },
{ type: 'Reason', model: 'deepseek-r1:8b' },
{ type: 'Reason', model: 'deepseek-r1:14b' },
],
Kimi: [
{ type: 'Vision', model: 'kimi-latest' },
{ type: 'Reason', model: 'kimi-thinking-preview' },
{ type: 'Chat', model: 'kimi-k2-0711-preview', json: true },
],
BoCha: [
{ type: 'Search', model: 'bocha-web-search' },
{ type: 'Search', model: 'sear-xng' },
],
Jina: [
{ type: 'Parse', model: 'jina-reader' },
{ type: 'Reranker', model: 'jina-reranker-m0' },
],
Gitee: [
{ type: 'Embedding', model: 'Qwen3-Embedding-8B' },
{ type: 'Reranker', model: 'Qwen3-Reranker-8B' },
],
};
// 默认的 baseURL 的映射
export const providerBaseURLMap = {
OpenAI: 'https://api.openai-hk.com/v1',
Ark: 'https://ark.cn-beijing.volces.com/api/v3',
Aliyun: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
DeepSeek: 'https://api.deepseek.com/v1',
Ollama: 'http://localhost:11434/v1',
Siliconflow: 'https://api.siliconflow.cn/v1',
ChatGLM: 'https://open.bigmodel.cn/api/paas/v4',
MiniMax: 'https://api.minimax.chat/v1',
Claude: 'https://api.anthropic.com/v1',
BoCha: 'https://api.bochaai.com/v1/',
Jina: 'https://r.jina.ai/',
Gitee: 'https://ai.gitee.com/v1',
Kimi: 'https://api.moonshot.cn/v1',
};