parent
f62e8d4cea
commit
eb5af0eec6
|
@ -117,7 +117,10 @@ export default defineComponent({
|
|||
const getElFormRef = (): ComponentRef<typeof ElForm> => {
|
||||
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
||||
}
|
||||
|
||||
const clearForm = () => {
|
||||
formModel.value = {}
|
||||
getElFormRef().resetFields()
|
||||
}
|
||||
expose({
|
||||
setValues,
|
||||
formModel,
|
||||
|
@ -125,7 +128,8 @@ export default defineComponent({
|
|||
delSchema,
|
||||
addSchema,
|
||||
setSchema,
|
||||
getElFormRef
|
||||
getElFormRef,
|
||||
clearForm
|
||||
})
|
||||
|
||||
// 监听表单结构化数组,重新生成formModel
|
||||
|
|
|
@ -116,3 +116,9 @@ export const isImgPath = (path: string): boolean => {
|
|||
export const isEmptyVal = (val: any): boolean => {
|
||||
return val === '' || val === null || val === undefined
|
||||
}
|
||||
export function isWhitespace(val: unknown) {
|
||||
return val === '';
|
||||
}
|
||||
export function isNullOrWhitespace(val: unknown) {
|
||||
return isNullOrUnDef(val) || isWhitespace(val);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// import { FormSchema } from '@/components/Form';
|
||||
import { LLMProviders } from './consts';
|
||||
import { getModels, ProviderEnum } from './provider';
|
||||
// import { ModelTypeEnum } from '@/api/models';
|
||||
import {FormSchema} from "@/types/form";
|
||||
import { isNullOrWhitespace } from '@/utils/is';
|
||||
|
||||
const baseSchemas: FormSchema[] = [
|
||||
// {
|
||||
// field: 'id',
|
||||
// label: 'ID',
|
||||
// component: 'Input',
|
||||
// },
|
||||
// {
|
||||
// field: 'type',
|
||||
// label: 'type',
|
||||
// component: 'Input',
|
||||
// // value: ModelTypeEnum.CHAT,
|
||||
// },
|
||||
{
|
||||
field: 'provider',
|
||||
label: 'LLM供应商',
|
||||
component: 'Select',
|
||||
// isHidden: true,
|
||||
componentProps: {
|
||||
placeholder: 'LLM供应商',
|
||||
options: LLMProviders,
|
||||
labelField: 'name',
|
||||
valueField: 'model',
|
||||
},
|
||||
// rules: [{ required: true, message: '请选择LLM供应商', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '模型别名',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [{ required: true, message: '请输入模型别名', trigger: ['blur'] }]
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入模型别名',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'apiKey',
|
||||
label: 'Api Key',
|
||||
labelMessage: '模型的ApiKey',
|
||||
component: 'Input',
|
||||
// rules: [{ required: true, message: '请输入API Key', trigger: ['blur'] }],
|
||||
componentProps: {
|
||||
placeholder: '请输入Api Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'responseLimit',
|
||||
label: '回复上限',
|
||||
labelMessage: '控制模型输出的Tokens长度上限。通常 100 Tokens 约等于150个中文汉字',
|
||||
component: 'Slider',
|
||||
formItemProps:{
|
||||
rules: [{ type: 'number', required: true, message: '请输入回复上限', trigger: ['blur'] }]
|
||||
},
|
||||
componentProps: {
|
||||
showTooltip: true,
|
||||
value: 2000,
|
||||
step: 1,
|
||||
min: 1,
|
||||
max: 8192,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'temperature',
|
||||
label: '生成随机性',
|
||||
labelMessage: '调高参数会使得模型的输出更多样性和创新性,反之降低参数将会减少多样性',
|
||||
component: 'Slider',
|
||||
formItemProps: {
|
||||
rules: [{ type: 'number', required: true, message: '请输入生成随机性', trigger: ['blur'] }]
|
||||
},
|
||||
value:0.2,
|
||||
componentProps: {
|
||||
showTooltip: true,
|
||||
step: 0.05,
|
||||
min: 0,
|
||||
max: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'topP',
|
||||
label: 'Top P',
|
||||
labelMessage:
|
||||
'模型在生成输出时会从概率最高的词汇开始选择,直到这些词汇的总概率累积达到Top p值。这样可以限制模型只选择这些高概率的词汇,从而控制输出内容的多样性。建议不要与“生成随机性“同时调整',
|
||||
component: 'Slider',
|
||||
formItemProps: {
|
||||
rules: [{ type: 'number', required: true, message: '请输入', trigger: ['blur'] }]
|
||||
},
|
||||
componentProps: {
|
||||
showTooltip: true,
|
||||
value: 0.8,
|
||||
step: 0.1,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function getSchemas(provider: string) {
|
||||
const list = JSON.parse(JSON.stringify(baseSchemas));
|
||||
console.log(provider);
|
||||
const modelSchema: any = {
|
||||
field: 'model',
|
||||
label: '模型版本',
|
||||
labelMessage: '该LLM供应商对应的模型版本号',
|
||||
component: 'Select',
|
||||
rules: [{ required: true, message: '请选择模型', trigger: ['blur'] }],
|
||||
componentProps: {
|
||||
placeholder: '请选择模型版本(可以手动输入)',
|
||||
filterable: true,
|
||||
tag: true,
|
||||
options: getModels(provider, LLMProviders),
|
||||
},
|
||||
};
|
||||
list.splice(1, 0, modelSchema);
|
||||
|
||||
let value: any = undefined;
|
||||
let labelMessage: any = '模型的基础请求URL地址(或中转地址)';
|
||||
let disabled = false;
|
||||
switch (provider) {
|
||||
case ProviderEnum.GITEEAI:
|
||||
disabled = true;
|
||||
value = 'https://ai.gitee.com/v1';
|
||||
labelMessage = '对于Gitee AI,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.DEEPSEEK:
|
||||
disabled = true;
|
||||
value = 'https://api.deepseek.com/v1';
|
||||
labelMessage = '对于DeepSeek模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.SILICON:
|
||||
disabled = true;
|
||||
value = 'https://api.siliconflow.cn/v1';
|
||||
labelMessage = '对于硅基流动模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.DOUYIN:
|
||||
disabled = true;
|
||||
value = 'https://ark.cn-beijing.volces.com/api/v3';
|
||||
labelMessage = '对于抖音豆包模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.YI:
|
||||
disabled = true;
|
||||
value = 'https://api.lingyiwanwu.com/v1';
|
||||
labelMessage = '对于零一模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.SPARK:
|
||||
disabled = true;
|
||||
value = 'https://spark-api-open.xf-yun.com/v1';
|
||||
labelMessage = '对于讯飞星火大模型,此Url固定不可修改';
|
||||
break;
|
||||
}
|
||||
const baseUlrSchema: any = {
|
||||
field: 'baseUrl',
|
||||
label: 'Base Url',
|
||||
labelMessage,
|
||||
component: 'Input',
|
||||
value,
|
||||
componentProps: {
|
||||
disabled,
|
||||
placeholder: '请输入BaseUrl',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
trigger: ['blur'],
|
||||
validator: (_, value: string) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const urlRegex =
|
||||
/^(https?:\/\/)?((([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|localhost|(\d{1,3}\.){3}\d{1,3})(:\d{1,5})?(\/.*)?)$/;
|
||||
if (isNullOrWhitespace(value) || urlRegex.test(value)) {
|
||||
return true;
|
||||
}
|
||||
return new Error('URL格式错误');
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
list.splice(3, 0, baseUlrSchema);
|
||||
return list;
|
||||
}
|
|
@ -1,9 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
import {Form} from '@/components/Form'
|
||||
import {ref} from 'vue'
|
||||
import {FormSchema} from "@/types/form";
|
||||
import {getSchemas} from "@/views/ai/model/chatModel/composables/schemas";
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = ref({})
|
||||
const formRef = ref()
|
||||
const schemas = ref([])
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
formRef.value.clearForm()
|
||||
}
|
||||
const show = async (data: object) => {
|
||||
visible.value = true
|
||||
await nextTick()
|
||||
formRef.value.setValues(data)
|
||||
schemas.value = (getSchemas(data.provider) as FormSchema[]).splice(1);
|
||||
formRef.value.setSchema(schemas.value)
|
||||
// models.value = getModels(data.provider, LLMProviders)
|
||||
}
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<el-dialog v-model="visible" draggable title="编辑" width="800px" @close="close">
|
||||
<Form ref="formRef" :model="formData" :schema="schemas" />
|
||||
<template #footer>
|
||||
<el-button type="primary">确认</el-button>
|
||||
<el-button @click="close">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { Table } from '@/components/Table';
|
||||
import { Delete, Edit, Plus } from '@element-plus/icons-vue';
|
||||
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 {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 {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {FormSchema} from "@/types/form";
|
||||
// import { ModelTypeEnum } from '@/api/models';
|
||||
import { ProviderEnum } from './composables/provider.ts';
|
||||
import {getModels, ProviderEnum} from './composables/provider.ts';
|
||||
|
||||
const formData = ref({
|
||||
provider: ProviderEnum.OPENAI
|
||||
});
|
||||
|
@ -19,65 +20,75 @@ 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),
|
||||
},
|
||||
],
|
||||
});
|
||||
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(() => {
|
||||
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 });
|
||||
// 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) {
|
||||
|
@ -106,20 +117,63 @@ function handleDel(record: any) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<content-wrap>
|
||||
<Search :schema="shema" :model="formData" @search="({ provider}) => formData.provider = provider"/>
|
||||
</content-wrap>
|
||||
<!-- <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-alert
|
||||
class="w-full mb-10px min-alert"
|
||||
title="对于完全适配OpenAI接口格式的模型都可在OpenAI中配置(只需要定义BaseUrl)"
|
||||
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"/>
|
||||
<editCom ref="editRef" :provider="provider" @reload="reloadTable" />
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<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;
|
||||
|
||||
& > 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,34 @@
|
|||
import {ProviderEnum} from "@/views/ai/model/chatModel/composables/provider";
|
||||
|
||||
export 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'],
|
||||
},
|
||||
];
|
|
@ -1,49 +1,20 @@
|
|||
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 formData = ref({
|
||||
provider: ProviderEnum.OPENAI
|
||||
})
|
||||
const editRef = ref()
|
||||
const baseColumns = [
|
||||
{
|
||||
label: '模型别名',
|
||||
field: 'name',
|
||||
field: 'name',
|
||||
},
|
||||
{
|
||||
label: '模型版本',
|
||||
field: 'model',
|
||||
field: 'model',
|
||||
width: '160',
|
||||
},
|
||||
{
|
||||
|
@ -65,38 +36,22 @@ export default function () {
|
|||
},
|
||||
{
|
||||
label: 'Api Key',
|
||||
field: 'apiKey',
|
||||
field: 'apiKey',
|
||||
},
|
||||
{
|
||||
label: 'Base Url',
|
||||
field: 'baseUrl',
|
||||
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,
|
||||
})),
|
||||
},
|
||||
|
||||
}
|
||||
])
|
||||
const tableData = ref([])
|
||||
const open = () => {
|
||||
editRef.value.show({provider: formData.value.provider});
|
||||
}
|
||||
return {
|
||||
baseColumns,
|
||||
shema,
|
||||
tableData
|
||||
tableData,
|
||||
formData,
|
||||
editRef,
|
||||
open
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
|
||||
import {FormSchema} from "@/types/form";
|
||||
// import { ModelTypeEnum } from '@/api/models';
|
||||
import {getModels, ProviderEnum} from "@/views/ai/model/chatModel/composables/provider";
|
||||
import {LLMProviders} from "@/views/ai/model/embedding/composables/consts";
|
||||
// import { LLMProviders } from './consts';
|
||||
const baseSchemas: FormSchema[] = [
|
||||
// {
|
||||
// field: 'id',
|
||||
// label: 'ID',
|
||||
// component: 'Input',
|
||||
// isHidden: true,
|
||||
// },
|
||||
// {
|
||||
// field: 'type',
|
||||
// label: 'type',
|
||||
// component: 'Input',
|
||||
// isHidden: true,
|
||||
// value: ModelTypeEnum.EMBEDDING,
|
||||
// },
|
||||
{
|
||||
field: 'provider',
|
||||
label: 'LLM供应商',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择LLM供应商',
|
||||
options: LLMProviders,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '模型别名',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [{ required: true, message: '请输入模型别名', trigger: ['blur'] }]
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入模型别名',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'apiKey',
|
||||
label: 'Api Key',
|
||||
labelMessage: '模型的ApiKey',
|
||||
component: 'Input',
|
||||
// rules: [{ required: true, message: '请输入ApiKey', trigger: ['blur'] }],
|
||||
componentProps: {
|
||||
placeholder: '请输入ApiKey',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'dimension',
|
||||
label: '向量纬度',
|
||||
component: 'Select',
|
||||
value: 1024,
|
||||
labelMessage: '慎重修改此参数,纬度高会消耗更多的算力,但纬度高并不代表搜索更精确',
|
||||
componentProps: {
|
||||
placeholder: '请输入向量纬度',
|
||||
options: [
|
||||
{
|
||||
label: '512',
|
||||
value: 512,
|
||||
},
|
||||
{
|
||||
label: '768',
|
||||
value: 768,
|
||||
},
|
||||
{
|
||||
label: '1024',
|
||||
value: 1024,
|
||||
},
|
||||
{
|
||||
label: '1536',
|
||||
value: 1536,
|
||||
},
|
||||
],
|
||||
},
|
||||
formItemProps:{
|
||||
rules: [{ type: 'number', required: true, message: '请输入向量纬度', trigger: ['blur'] }]
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function getSchemas(provider: string) {
|
||||
const list = JSON.parse(JSON.stringify(baseSchemas));
|
||||
|
||||
const modelSchema: any = {
|
||||
field: 'model',
|
||||
label: '模型版本',
|
||||
labelMessage: '该LLM供应商对应的模型版本号',
|
||||
component: 'Select',
|
||||
rules: [{ required: true, message: '请选择模型', trigger: ['blur'] }],
|
||||
componentProps: {
|
||||
placeholder: '请选择模型版本',
|
||||
filterable: true,
|
||||
tag: true,
|
||||
options: getModels(provider, LLMProviders),
|
||||
},
|
||||
};
|
||||
list.splice(list.length, 0, modelSchema);
|
||||
|
||||
let value: any = undefined;
|
||||
let labelMessage: any = '模型的基础请求URL地址(或中转地址)';
|
||||
let disabled = false;
|
||||
switch (provider) {
|
||||
case ProviderEnum.DOUYIN:
|
||||
disabled = true;
|
||||
value = 'https://ark.cn-beijing.volces.com/api/v3';
|
||||
labelMessage = '对于抖音豆包模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.Q_FAN:
|
||||
disabled = true;
|
||||
labelMessage = '对于百度千帆模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.Q_WEN:
|
||||
disabled = true;
|
||||
labelMessage = '对于阿里千问模型,此Url固定不可修改';
|
||||
break;
|
||||
case ProviderEnum.ZHIPU:
|
||||
disabled = true;
|
||||
labelMessage = '对于智谱清言模型,此Url固定不可修改';
|
||||
break;
|
||||
}
|
||||
const baseUlrSchema: any = {
|
||||
field: 'baseUrl',
|
||||
label: 'Base Url',
|
||||
labelMessage,
|
||||
component: 'Input',
|
||||
value,
|
||||
componentProps: {
|
||||
disabled,
|
||||
placeholder: '请输入BaseUrl',
|
||||
},
|
||||
};
|
||||
list.splice(list.length, 0, baseUlrSchema);
|
||||
return list;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
import {Form} from '@/components/Form'
|
||||
import {ref} from 'vue'
|
||||
import {FormSchema} from "@/types/form";
|
||||
import {getSchemas} from "@/views/ai/model/embedding/composables/schemas";
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = ref({})
|
||||
const formRef = ref()
|
||||
const schemas = ref([])
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
formRef.value.clearForm()
|
||||
}
|
||||
const show = async (data: object) => {
|
||||
visible.value = true
|
||||
await nextTick()
|
||||
formRef.value.setValues(data)
|
||||
schemas.value = (getSchemas(data.provider) as FormSchema[]).splice(1);
|
||||
formRef.value.setSchema(schemas.value)
|
||||
// models.value = getModels(data.provider, LLMProviders)
|
||||
}
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog v-model="visible" draggable title="编辑" width="800px" @close="close">
|
||||
<Form ref="formRef" :model="formData" :schema="schemas" />
|
||||
<template #footer>
|
||||
<el-button type="primary">确认</el-button>
|
||||
<el-button @click="close">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
|
@ -2,25 +2,68 @@
|
|||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
const {shema, baseColumns: columns, tableData} = usePage()
|
||||
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>
|
||||
<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"/>
|
||||
<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,22 @@
|
|||
export enum ProviderEnum {
|
||||
OPENAI = 'OPENAI',
|
||||
AZURE_OPENAI = 'AZURE_OPENAI',
|
||||
ZHIPU = 'ZHIPU',
|
||||
}
|
||||
export 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'],
|
||||
},
|
||||
]
|
|
@ -1,49 +1,12 @@
|
|||
export default function () {
|
||||
enum ProviderEnum {
|
||||
OPENAI = 'OPENAI',
|
||||
AZURE_OPENAI = 'AZURE_OPENAI',
|
||||
ZHIPU = 'ZHIPU',
|
||||
}
|
||||
import {ProviderEnum} from "@/views/ai/model/image/composables/consts";
|
||||
|
||||
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'],
|
||||
},
|
||||
]
|
||||
export default function () {
|
||||
|
||||
const editRef= ref()
|
||||
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 = [
|
||||
{
|
||||
|
@ -100,11 +63,14 @@ export default function () {
|
|||
nextTick();
|
||||
return getColumns(formData.value.provider)
|
||||
});
|
||||
const open = () => {
|
||||
editRef.value.show({provider: formData.value.provider});
|
||||
}
|
||||
return {
|
||||
LLMProviders,
|
||||
columns,
|
||||
tableData,
|
||||
shema,
|
||||
formData
|
||||
formData,
|
||||
editRef,
|
||||
open
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {FormSchema} from '@/types/form';
|
||||
// import { LLMProviders, ProviderEnum } from './data';
|
||||
// import { ModelTypeEnum } from '@/api/models';
|
||||
import {isNullOrWhitespace} from '@/utils/is';
|
||||
import {LLMProviders, ProviderEnum} from "@/views/ai/model/image/composables/consts";
|
||||
|
||||
const baseHeadSchemas: FormSchema[] = [
|
||||
// {
|
||||
// field: 'id',
|
||||
// label: 'ID',
|
||||
// component: 'Input',
|
||||
// isHidden: true,
|
||||
// },
|
||||
// {
|
||||
// field: 'type',
|
||||
// label: 'type',
|
||||
// component: 'Input',
|
||||
// isHidden: true,
|
||||
// defaultValue: ModelTypeEnum.TEXT_IMAGE,
|
||||
// },
|
||||
{
|
||||
field: 'provider',
|
||||
label: 'LLM供应商',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: LLMProviders,
|
||||
labelField: 'name',
|
||||
valueField: 'model',
|
||||
},
|
||||
formItemProps: {rules: [{required: true, message: '请选择LLM供应商', trigger: ['blur']}]},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '模型别名',
|
||||
component: 'Input',
|
||||
formItemProps: {rules: [{required: true, message: '请输入模型别名', trigger: ['blur']}]},
|
||||
},
|
||||
];
|
||||
const keySchemas: FormSchema[] = [
|
||||
{
|
||||
field: 'apiKey',
|
||||
label: 'Api Key',
|
||||
labelMessage: '模型链接的秘钥,注意有些模型例如Gemini是本地认证方式,则不是通过这种方式',
|
||||
component: 'Input',
|
||||
formItemProps: {rules: [{required: true, message: '请输入API Key', trigger: ['blur']}]},
|
||||
},
|
||||
{
|
||||
field: 'baseUrl',
|
||||
label: 'Base Url',
|
||||
labelMessage: '注意对于大多数模型此参数仅代表中转地址,但是对于Ollama这类本地模型则是必填的',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
trigger: ['blur'],
|
||||
validator: (_, value: string) => {
|
||||
const urlRegex = /^(https?:\/\/)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(\/.*)?$/;
|
||||
if (isNullOrWhitespace(value) || urlRegex.test(value)) {
|
||||
return true;
|
||||
}
|
||||
return new Error('URL格式错误');
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const openaiSchemas: FormSchema[] = [
|
||||
...baseHeadSchemas,
|
||||
{
|
||||
field: 'model',
|
||||
label: '模型',
|
||||
labelMessage: '该LLM供应商对应的模型版本号',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择模型', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
filterable: true,
|
||||
options: getModels(ProviderEnum.OPENAI),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageSize',
|
||||
label: '图片大小',
|
||||
labelMessage: '生成图片的大小尺寸',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择图片大小', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '1024x1024',
|
||||
value: '1024x1024',
|
||||
},
|
||||
{
|
||||
label: '1024x1792',
|
||||
value: '1024x1792',
|
||||
},
|
||||
{
|
||||
label: '1792x1024',
|
||||
value: '1792x1024',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageQuality',
|
||||
label: '图片质量',
|
||||
labelMessage: '生成图片的质量',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择图片的质量', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'standard',
|
||||
value: 'standard',
|
||||
},
|
||||
{
|
||||
label: 'hd',
|
||||
value: 'hd',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageStyle',
|
||||
label: '图片风格',
|
||||
labelMessage: '生成图片的风格',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择图片的风格', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'vivid',
|
||||
value: 'vivid',
|
||||
},
|
||||
{
|
||||
label: 'natural',
|
||||
value: 'natural',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...keySchemas,
|
||||
];
|
||||
|
||||
export const azureOpenaiSchemas: FormSchema[] = [
|
||||
...baseHeadSchemas,
|
||||
{
|
||||
field: 'model',
|
||||
label: '模型',
|
||||
labelMessage: '该LLM供应商对应的模型版本号',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择模型', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
filterable: true,
|
||||
options: getModels(ProviderEnum.AZURE_OPENAI),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageSize',
|
||||
label: '图片大小',
|
||||
labelMessage: '生成图片的大小尺寸',
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [{required: true, message: '请选择图片大小', trigger: ['blur']}]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '1024x1024',
|
||||
value: '1024x1024',
|
||||
},
|
||||
{
|
||||
label: '1024x1792',
|
||||
value: '1024x1792',
|
||||
},
|
||||
{
|
||||
label: '1792x1024',
|
||||
value: '1792x1024',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageQuality',
|
||||
label: '图片质量',
|
||||
labelMessage: '生成图片的质量',
|
||||
component: 'Select',
|
||||
formItemProps: {rules: [{required: true, message: '请选择图片的质量', trigger: ['blur']}]},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'standard',
|
||||
value: 'standard',
|
||||
},
|
||||
{
|
||||
label: 'hd',
|
||||
value: 'hd',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'imageStyle',
|
||||
label: '图片风格',
|
||||
labelMessage: '生成图片的风格',
|
||||
component: 'Select',
|
||||
formItemProps: {rules: [{required: true, message: '请选择图片的风格', trigger: ['blur']}]},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'vivid',
|
||||
value: 'vivid',
|
||||
},
|
||||
{
|
||||
label: 'natural',
|
||||
value: 'natural',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...keySchemas,
|
||||
];
|
||||
|
||||
export const zhipuSchemas: FormSchema[] = [
|
||||
...baseHeadSchemas,
|
||||
{
|
||||
field: 'model',
|
||||
label: '模型',
|
||||
labelMessage: '该LLM供应商对应的模型版本号',
|
||||
component: 'Select',
|
||||
formItemProps: {rules: [{required: true, message: '请选择模型', trigger: ['blur']}]},
|
||||
componentProps: {
|
||||
filterable: true,
|
||||
options: getModels(ProviderEnum.ZHIPU),
|
||||
},
|
||||
},
|
||||
...keySchemas,
|
||||
];
|
||||
|
||||
export function getSchemas(provider: string) {
|
||||
switch (provider) {
|
||||
case ProviderEnum.OPENAI: {
|
||||
return openaiSchemas;
|
||||
}
|
||||
case ProviderEnum.AZURE_OPENAI: {
|
||||
return azureOpenaiSchemas;
|
||||
}
|
||||
case ProviderEnum.ZHIPU: {
|
||||
return zhipuSchemas;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getModels(provider: string) {
|
||||
const arr = LLMProviders.filter((i) => i.model === provider);
|
||||
if (arr.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return arr[0].models.map((i) => {
|
||||
return {
|
||||
label: i,
|
||||
value: i,
|
||||
};
|
||||
});
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
import {Form} from '@/components/Form'
|
||||
import {ref} from 'vue'
|
||||
import {FormSchema} from "@/types/form";
|
||||
import {getSchemas} from "@/views/ai/model/image/composables/schemas";
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = ref({})
|
||||
const formRef = ref()
|
||||
const schemas = ref([])
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
formRef.value.clearForm()
|
||||
}
|
||||
const show = async (data: object) => {
|
||||
visible.value = true
|
||||
await nextTick()
|
||||
formRef.value.setValues(data)
|
||||
schemas.value = (getSchemas(data.provider) as FormSchema[]);
|
||||
// formRef.value.setSchema(schemas.value)
|
||||
// models.value = getModels(data.provider, LLMProviders)
|
||||
}
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog v-model="visible" draggable title="编辑" width="800px" @close="close">
|
||||
<Form ref="formRef" :model="formData" :schema="schemas" />
|
||||
<template #footer>
|
||||
<el-button type="primary">确认</el-button>
|
||||
<el-button @click="close">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
|
@ -2,25 +2,70 @@
|
|||
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import usePage from './composables/index'
|
||||
const {shema, columns, tableData, formData} = usePage()
|
||||
import {LLMProviders} from "@/views/ai/model/image/composables/consts";
|
||||
import Edit from './edit.vue'
|
||||
|
||||
const {columns, tableData, formData, editRef, open} = 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"/>
|
||||
<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