feat: 新增统一模型网关与模型管理工作区
- 新增 OpenAI 兼容统一模型调用链路、模型发布配置与批量发布能力 - 重构模型管理页面入口与统一网关工作区,更新服务商 logo 资源与模型 ID 文案 - 收口全新库初始化脚本,仅保留服务商种子并整理统一网关 migration
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, ref} from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import {$t} from '@easyflow/locales';
|
||||
import { $t } from '@easyflow/locales';
|
||||
|
||||
import {Delete, Edit, Plus, Select, Setting} from '@element-plus/icons-vue';
|
||||
import { Delete, Edit, Plus, Select, Setting } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElButton,
|
||||
ElEmpty,
|
||||
@@ -15,15 +15,15 @@ import {
|
||||
ElTag,
|
||||
} from 'element-plus';
|
||||
|
||||
import {getLlmProviderList} from '#/api/ai/llm.js';
|
||||
import {api} from '#/api/request.js';
|
||||
import { getLlmProviderList } from '#/api/ai/llm.js';
|
||||
import { api } from '#/api/request.js';
|
||||
import ListPageShell from '#/components/page/ListPageShell.vue';
|
||||
import ActiveModelWorkspace from '#/views/ai/model/ActiveModelWorkspace.vue';
|
||||
import AddModelModal from '#/views/ai/model/AddModelModal.vue';
|
||||
import AddModelProviderModal from '#/views/ai/model/AddModelProviderModal.vue';
|
||||
import ModelProviderBadge from '#/views/ai/model/ModelProviderBadge.vue';
|
||||
import {getProviderPresetByValue} from '#/views/ai/model/modelUtils/defaultIcon';
|
||||
import {modelTypes} from '#/views/ai/model/modelUtils/modelTypes';
|
||||
import { getProviderPresetByValue } from '#/views/ai/model/modelUtils/defaultIcon';
|
||||
import { modelTypes } from '#/views/ai/model/modelUtils/modelTypes';
|
||||
import {
|
||||
createProviderDraft,
|
||||
getProviderConfigMetrics,
|
||||
@@ -31,8 +31,9 @@ import {
|
||||
} from '#/views/ai/model/modelUtils/providerDraft';
|
||||
import ModelVerifyConfig from '#/views/ai/model/ModelVerifyConfig.vue';
|
||||
import ModelViewItemOperation from '#/views/ai/model/ModelViewItemOperation.vue';
|
||||
import UnifiedGatewayWorkspace from '#/views/ai/model/UnifiedGatewayWorkspace.vue';
|
||||
|
||||
type ModelWorkspaceView = 'active' | 'provider';
|
||||
type ModelWorkspaceView = 'active' | 'gateway' | 'provider';
|
||||
|
||||
interface ModelGroup {
|
||||
groupName: string;
|
||||
@@ -59,6 +60,7 @@ const addLlmProviderRef = ref();
|
||||
const llmVerifyConfigRef = ref();
|
||||
const addLlmRef = ref();
|
||||
const activeWorkspaceRef = ref();
|
||||
const unifiedGatewayWorkspaceRef = ref();
|
||||
|
||||
const selectedProvider = computed(() =>
|
||||
providers.value.find((item) => item.id === selectedProviderId.value),
|
||||
@@ -194,7 +196,10 @@ const loadProviderDetail = async (
|
||||
isDetailLoading.value = true;
|
||||
|
||||
try {
|
||||
const res = await api.get(`/api/v1/model/getList?providerId=${providerId}`, {});
|
||||
const res = await api.get(
|
||||
`/api/v1/model/getList?providerId=${providerId}`,
|
||||
{},
|
||||
);
|
||||
|
||||
if (res.errorCode === 0) {
|
||||
syncGroupedModels(res.data);
|
||||
@@ -282,15 +287,30 @@ const switchWorkspaceView = async (target: ModelWorkspaceView) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await confirmDiscardProviderDraft())) {
|
||||
if (
|
||||
workspaceView.value === 'provider' &&
|
||||
!(await confirmDiscardProviderDraft())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (workspaceView.value === 'gateway') {
|
||||
const canLeaveGateway =
|
||||
(await unifiedGatewayWorkspaceRef.value?.confirmBeforeLeave?.()) ?? true;
|
||||
if (!canLeaveGateway) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
workspaceView.value = target;
|
||||
|
||||
if (target === 'active') {
|
||||
await activeWorkspaceRef.value?.reloadData?.();
|
||||
}
|
||||
|
||||
if (target === 'gateway') {
|
||||
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
|
||||
}
|
||||
};
|
||||
|
||||
const selectProvider = async (provider: any) => {
|
||||
@@ -357,6 +377,7 @@ const handleDeleteProvider = async (provider: any) => {
|
||||
const remaining = providers.value.find((item) => item.id !== provider.id);
|
||||
await loadProviders(remaining?.id, true);
|
||||
await activeWorkspaceRef.value?.reloadData?.();
|
||||
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -375,6 +396,7 @@ const openEditProviderDialog = () => {
|
||||
const handleProviderModalReload = async () => {
|
||||
await loadProviders(selectedProviderId.value, true);
|
||||
await activeWorkspaceRef.value?.reloadData?.();
|
||||
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
|
||||
};
|
||||
|
||||
const handleAddLlm = (modelType = activeModelType.value) => {
|
||||
@@ -444,6 +466,7 @@ const handleModelDataReload = async () => {
|
||||
keepDraft: isProviderDirty.value,
|
||||
});
|
||||
await activeWorkspaceRef.value?.reloadData?.();
|
||||
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
|
||||
await refreshProviderCounts(providers.value);
|
||||
};
|
||||
|
||||
@@ -481,6 +504,14 @@ onMounted(() => {
|
||||
>
|
||||
已配置模型
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="workspace-switch__item"
|
||||
:class="{ 'is-active': workspaceView === 'gateway' }"
|
||||
@click="switchWorkspaceView('gateway')"
|
||||
>
|
||||
统一网关
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -770,7 +801,7 @@ onMounted(() => {
|
||||
</section>
|
||||
|
||||
<ActiveModelWorkspace
|
||||
v-else
|
||||
v-else-if="workspaceView === 'active'"
|
||||
ref="activeWorkspaceRef"
|
||||
:providers="providers"
|
||||
@create-model="handleAddLlm"
|
||||
@@ -778,6 +809,12 @@ onMounted(() => {
|
||||
@refresh-provider-stats="handleActiveProviderStatsRefresh"
|
||||
/>
|
||||
|
||||
<UnifiedGatewayWorkspace
|
||||
v-else
|
||||
ref="unifiedGatewayWorkspaceRef"
|
||||
@updated="handleModelDataReload"
|
||||
/>
|
||||
|
||||
<AddModelProviderModal
|
||||
ref="addLlmProviderRef"
|
||||
@reload="handleProviderModalReload"
|
||||
|
||||
Reference in New Issue
Block a user