parent
92eaf7af82
commit
17e4a42500
|
@ -1,11 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
|
@ -1,125 +1,185 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { Table } from '@/components/Table';
|
||||
import { Delete, Edit, Plus } from '@element-plus/icons-vue';
|
||||
import editCom from './edit.vue';
|
||||
import { computed, h, nextTick, reactive, ref } from 'vue';
|
||||
import { getColumns } from './composables/columns.ts';
|
||||
import { LLMProviders } from './composables/consts.ts';
|
||||
// import { del, list as getModels } from '@/api/aigc/model';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {FormSchema} from "@/types/form";
|
||||
// import { ModelTypeEnum } from '@/api/models';
|
||||
import { ProviderEnum } from './composables/provider.ts';
|
||||
const formData = ref({
|
||||
provider: ProviderEnum.OPENAI
|
||||
});
|
||||
const message = ElMessage;
|
||||
const dialog = ElMessageBox;
|
||||
const actionRef = ref();
|
||||
const editRef = ref();
|
||||
const tableData = ref([
|
||||
])
|
||||
const shema = ref([
|
||||
{
|
||||
label: '模型名称',
|
||||
field: 'provider',
|
||||
component: 'Select',
|
||||
colProps: {
|
||||
span: 6
|
||||
},
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '150px',
|
||||
},
|
||||
options: LLMProviders.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.model,
|
||||
})),
|
||||
},
|
||||
|
||||
}
|
||||
]) as FormSchema[]
|
||||
const actionColumn = reactive({
|
||||
width: 100,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render(record: any) {
|
||||
return h(TableAction as any, {
|
||||
style: 'text',
|
||||
actions: [
|
||||
{
|
||||
type: 'info',
|
||||
icon: Edit,
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
icon: Delete,
|
||||
onClick: handleDel.bind(null, record),
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const columns = computed(() => {
|
||||
console.log(formData.value.provider);
|
||||
nextTick();
|
||||
return getColumns(formData.value.provider);
|
||||
});
|
||||
const loadDataTable = async (params: any) => {
|
||||
if (formData.value.provider === '') {
|
||||
formData.value.provider = LLMProviders[0].model;
|
||||
}
|
||||
return await getModels({ ...params, provider: formData.value.provider, type: ModelTypeEnum.CHAT });
|
||||
};
|
||||
async function handleAdd() {
|
||||
editRef.value.show({ provider: formData.value.provider });
|
||||
}
|
||||
|
||||
function handleEdit(record: any) {
|
||||
editRef.value.show(record);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function handleDel(record: any) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
message: `你确定删除 [${record.name}] 模型吗?删除之后不可再用该模型对话`,
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '不确定',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await del(record.id);
|
||||
reloadTable();
|
||||
message.success('模型删除成功');
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<content-wrap>
|
||||
<Search :schema="shema" :model="formData" @search="({ provider}) => formData.provider = provider"/>
|
||||
</content-wrap>
|
||||
<ContentWrap>
|
||||
<el-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
title="对于完全适配OpenAI接口格式的模型都可在OpenAI中配置(只需要定义BaseUrl)"
|
||||
type="info"
|
||||
show-icon
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="模型名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入模型名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
<el-button class="my-10px" type="primary" :icon="Plus">新增模型</el-button>
|
||||
<Table height="cacl(100% - 400px)" border :columns="columns" :data="tableData.concat(tableData)" :pagination="false"/>
|
||||
<editCom ref="editRef" :provider="provider" @reload="reloadTable" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型标识" prop="model">
|
||||
<el-input
|
||||
v-model="queryParams.model"
|
||||
placeholder="请输入模型标识"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型平台" prop="platform">
|
||||
<el-input
|
||||
v-model="queryParams.platform"
|
||||
placeholder="请输入模型平台"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['ai:chat-model:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="所属平台" align="center" prop="platform">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模型名字" align="center" prop="name" />
|
||||
<el-table-column label="模型标识" align="center" prop="model" />
|
||||
<el-table-column label="API 秘钥" align="center" prop="keyId" min-width="140">
|
||||
<template #default="scope">
|
||||
<span>{{ apiKeyList.find((item) => item.id === scope.row.keyId)?.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sort" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="温度参数" align="center" prop="temperature" />
|
||||
<el-table-column label="回复数 Token 数" align="center" prop="maxTokens" min-width="140" />
|
||||
<el-table-column label="上下文数量" align="center" prop="maxContexts" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['ai:chat-model:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['ai:chat-model:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ChatModelForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<script setup lang="ts">
|
||||
import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
|
||||
import ChatModelForm from './ChatModelForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey'
|
||||
|
||||
/** API 聊天模型 列表 */
|
||||
defineOptions({ name: 'AiChatModel' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<ChatModelVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
model: undefined,
|
||||
platform: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ChatModelApi.getChatModelPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ChatModelApi.deleteChatModel(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
getList()
|
||||
// 获得下拉数据
|
||||
apiKeyList.value = await ApiKeyApi.getApiKeySimpleList()
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
import {ElTag} from "element-plus";
|
||||
import {ref} from "vue";
|
||||
import {ProviderEnum} from "@/views/ai/model/chatModel/composables/provider";
|
||||
import {FormSchema} from "@/types/form";
|
||||
|
||||
export default function () {
|
||||
const LLMProviders: any[] = [
|
||||
{
|
||||
model: ProviderEnum.OPENAI,
|
||||
name: 'OpenAI',
|
||||
models: ['text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.Q_FAN,
|
||||
name: '百度千帆',
|
||||
models: ['bge-large-zh', 'bge-large-en', 'tao-8k'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.Q_WEN,
|
||||
name: '阿里百炼',
|
||||
models: ['text-embedding-v3'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.ZHIPU,
|
||||
name: '智谱清言',
|
||||
models: ['embedding-2', 'embedding-3'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.DOUYIN,
|
||||
name: '抖音豆包',
|
||||
models: ['text-240715', 'text-240515'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.OLLAMA,
|
||||
name: 'Ollama',
|
||||
models: ['text2vec-bge-large-chinese:latest'],
|
||||
},
|
||||
];
|
||||
const baseColumns = [
|
||||
{
|
||||
label: '模型别名',
|
||||
field: 'name',
|
||||
},
|
||||
{
|
||||
label: '模型版本',
|
||||
field: 'model',
|
||||
width: '160',
|
||||
},
|
||||
{
|
||||
label: '向量纬度',
|
||||
field: 'dimension',
|
||||
align: 'center',
|
||||
width: '100',
|
||||
render(row) {
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
default: () => row.dimension,
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Api Key',
|
||||
field: 'apiKey',
|
||||
},
|
||||
{
|
||||
label: 'Base Url',
|
||||
field: 'baseUrl',
|
||||
},
|
||||
];
|
||||
const tableData = ref([
|
||||
])
|
||||
const shema = ref<FormSchema[]>([
|
||||
{
|
||||
label: '模型名称',
|
||||
field: 'name',
|
||||
component: 'Select',
|
||||
colProps: {
|
||||
span: 6
|
||||
},
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '150px',
|
||||
},
|
||||
options: LLMProviders.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.model,
|
||||
})),
|
||||
},
|
||||
|
||||
}
|
||||
])
|
||||
return {
|
||||
baseColumns,
|
||||
shema,
|
||||
tableData
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
const {shema, baseColumns: columns, tableData} = usePage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<content-wrap>
|
||||
<Search :schema="shema"/>
|
||||
</content-wrap>
|
||||
<ContentWrap>
|
||||
<el-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
title="注意:为了实现向量数据库的动态切换,这里Embedding供应商统一选择支持1024纬度的模型"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<el-button class="my-10px" type="primary" :icon="Plus">新增向量模型</el-button>
|
||||
<Table height="cacl(100% - 400px)" border :columns="columns" :data="tableData.concat(tableData)" :pagination="false"/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
|
@ -1,110 +0,0 @@
|
|||
export default function () {
|
||||
enum ProviderEnum {
|
||||
OPENAI = 'OPENAI',
|
||||
AZURE_OPENAI = 'AZURE_OPENAI',
|
||||
ZHIPU = 'ZHIPU',
|
||||
}
|
||||
|
||||
const LLMProviders: any[] = [
|
||||
{
|
||||
model: ProviderEnum.OPENAI,
|
||||
name: 'OpenAI',
|
||||
models: ['dall-e-2', 'dall-e-3'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.AZURE_OPENAI,
|
||||
name: 'Azure OpenAI',
|
||||
models: ['dall-e-2', 'dall-e-3'],
|
||||
},
|
||||
{
|
||||
model: ProviderEnum.ZHIPU,
|
||||
name: '智谱清言',
|
||||
models: ['cogview-3'],
|
||||
},
|
||||
]
|
||||
const formData = ref({
|
||||
provider: ProviderEnum.OPENAI,
|
||||
});
|
||||
const tableData = ref([])
|
||||
const shema = ref([
|
||||
{
|
||||
field: 'provider',
|
||||
label: '模型别名',
|
||||
component: 'Select',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: '请输入模型别名',
|
||||
style: {
|
||||
width: '180px',
|
||||
},
|
||||
options: LLMProviders.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.model,
|
||||
})),
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const baseColumns = [
|
||||
{
|
||||
label: '模型别名',
|
||||
field: 'name',
|
||||
},
|
||||
{
|
||||
label: '模型版本',
|
||||
field: 'model',
|
||||
},
|
||||
];
|
||||
|
||||
const openaiColumns = [
|
||||
...baseColumns,
|
||||
{
|
||||
label: 'Api Key',
|
||||
field: 'apiKey',
|
||||
},
|
||||
];
|
||||
|
||||
const azureOpenaiColumns = [
|
||||
...baseColumns,
|
||||
{
|
||||
label: 'Api Key',
|
||||
field: 'apiKey',
|
||||
},
|
||||
{
|
||||
label: 'Endpoint',
|
||||
field: 'endpoint',
|
||||
},
|
||||
{
|
||||
label: 'Deployment Name',
|
||||
field: 'azureDeploymentName',
|
||||
},
|
||||
];
|
||||
|
||||
const zhipuColumns = [...baseColumns];
|
||||
function getColumns(provider: string) {
|
||||
console.log(provider);
|
||||
switch (provider) {
|
||||
case ProviderEnum.OPENAI: {
|
||||
return openaiColumns;
|
||||
}
|
||||
case ProviderEnum.AZURE_OPENAI: {
|
||||
return azureOpenaiColumns;
|
||||
}
|
||||
case ProviderEnum.ZHIPU: {
|
||||
return zhipuColumns;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
const columns = computed(() => {
|
||||
nextTick();
|
||||
return getColumns(formData.value.provider)
|
||||
});
|
||||
return {
|
||||
LLMProviders,
|
||||
columns,
|
||||
tableData,
|
||||
shema,
|
||||
formData
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
const {shema, columns, tableData, formData} = usePage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<content-wrap>
|
||||
<Search :schema="shema" :model="formData" @search="(model) => formData.provider = model.provider"/>
|
||||
</content-wrap>
|
||||
<ContentWrap>
|
||||
<el-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
title="鉴于很多模型的文生图效果很差甚至没有,这里只建议使用OpenAI的DALL-E模型"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<el-button class="my-10px" type="primary" :icon="Plus">新增向量模型</el-button>
|
||||
<Table height="cacl(100% - 400px)" border :columns="columns" :data="tableData.concat(tableData)" :pagination="false"/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
Loading…
Reference in New Issue