parent
26d2033e20
commit
b0027f3cb2
|
@ -1,185 +1,179 @@
|
|||
|
||||
<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 {getModels, 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([
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
}, {
|
||||
name: '1111'
|
||||
},
|
||||
{
|
||||
name: '1111'
|
||||
}
|
||||
])
|
||||
// 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(() => {
|
||||
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 addModel() {
|
||||
console.log(formData.value.provider);
|
||||
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>-->
|
||||
<!-- <el-button v-for="(item,index) in LLMProviders" :key="index" @click="formData.provider = item.model">{{ item.name }}</el-button>-->
|
||||
<!-- </content-wrap>-->
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<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-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>
|
||||
<div class="flex children">
|
||||
<el-scrollbar class="h-full w-300px pl-10px pr-20px">
|
||||
<div
|
||||
v-for="(item,index) in LLMProviders" :key="index"
|
||||
:class="{active: formData.provider === item.model}" class="menu"
|
||||
@click="formData.provider = item.model">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="h-full flex-1 px-20px">
|
||||
<el-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
title="对于完全适配OpenAI接口格式的模型都可在OpenAI中配置(只需要定义BaseUrl)"
|
||||
type="warning"
|
||||
show-icon
|
||||
/>
|
||||
<el-button :icon="Plus" class="my-10px" type="primary" @click="addModel">新增模型</el-button>
|
||||
<Table class="table-wrapper" height="100%" border :columns="columns" :data="tableData.concat(tableData)" :pagination="false"/>
|
||||
<editCom ref="editRef" @reload="reloadTable"/>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<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'
|
||||
<style lang="scss" scoped>
|
||||
.children {
|
||||
height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - (var(--app-content-padding) * 3)) !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
/** 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
|
||||
& > div:nth-child(2) {
|
||||
width: calc(100% - 300px);
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
.table-wrapper {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
.menu {
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
padding: 12px 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
background-color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
&:hover {
|
||||
&:not(&.active) {
|
||||
background-color: var(--el-color-info-light-7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
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>
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
import Edit from "@/views/ai/model/embedding/edit.vue";
|
||||
import {LLMProviders} from "@/views/ai/model/embedding/composables/consts";
|
||||
import {Table} from "@/components/Table";
|
||||
const { baseColumns: columns, tableData, formData, editRef, open} = usePage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="children flex">
|
||||
<el-scrollbar class="h-full w-300px pl-10px pr-20px">
|
||||
<div
|
||||
v-for="(item,index) in LLMProviders" :key="index"
|
||||
:class="{active: formData.provider === item.model}" class="menu"
|
||||
@click="formData.provider = item.model">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="h-full flex-1 px-20px">
|
||||
<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" @click="open">新增向量模型</el-button>
|
||||
<Table class="table-wrapper" height="100%" border :columns="columns" :data="tableData.concat(tableData)" :pagination="false"/>
|
||||
</div>
|
||||
</div>
|
||||
<Edit ref="editRef" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.children {
|
||||
height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - (var(--app-content-padding) * 3)) !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
& > div:nth-child(2) {
|
||||
width: calc(100% - 300px);
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
|
||||
.menu {
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
padding: 12px 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
background-color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:not(&.active) {
|
||||
background-color: var(--el-color-info-light-7);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,71 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
import {LLMProviders} from "@/views/ai/model/image/composables/consts";
|
||||
import Edit from './edit.vue'
|
||||
|
||||
const {columns, tableData, formData, editRef, open} = usePage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="children flex">
|
||||
<el-scrollbar class="h-full w-300px pl-10px pr-20px">
|
||||
<div
|
||||
v-for="(item,index) in LLMProviders" :key="index"
|
||||
:class="{active: formData.provider === item.model}" class="menu"
|
||||
@click="formData.provider = item.model">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="h-full p-20px">
|
||||
<el-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
show-icon
|
||||
title="鉴于很多模型的文生图效果很差甚至没有,这里只建议使用OpenAI的DALL-E模型"
|
||||
type="info"
|
||||
/>
|
||||
<el-button :icon="Plus" class="my-10px" type="primary" @click="open">新增向量模型
|
||||
</el-button>
|
||||
<Table :columns="columns" :data="tableData.concat(tableData)" :pagination="false" border
|
||||
class="table-wrapper" height="100%"/>
|
||||
</div>
|
||||
</div>
|
||||
<Edit ref="editRef"/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.children {
|
||||
height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - (var(--app-content-padding) * 3)) !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
& > div:nth-child(2) {
|
||||
width: calc(100% - 300px);
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
|
||||
.menu {
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
padding: 12px 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
background-color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:not(&.active) {
|
||||
background-color: var(--el-color-info-light-7);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue