feat(frontend): 澄清模型映射适用范围

This commit is contained in:
MMEXA
2026-07-14 00:30:39 +08:00
parent cfc4894dab
commit f10d631a9c
11 changed files with 657 additions and 111 deletions
@@ -38,21 +38,21 @@
>
<!-- 分组头部(可点击展开) -->
<div
class="flex items-center justify-between px-4 py-3 hover:bg-muted/20 cursor-pointer"
class="flex items-start justify-between px-4 py-3 hover:bg-muted/20 cursor-pointer"
@click="toggleAliasGroupExpand(getAliasGroupKey(group))"
>
<div class="flex items-center gap-2 flex-1 min-w-0">
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1.5">
<!-- 展开/收起图标 -->
<ChevronRight
class="w-4 h-4 text-muted-foreground shrink-0 transition-transform"
:class="{ 'rotate-90': expandedAliasGroups.has(getAliasGroupKey(group)) }"
/>
<!-- 模型名称 -->
<span class="font-semibold text-sm truncate">
<span class="min-w-0 flex-[1_1_12rem] truncate text-sm font-semibold">
{{ group.model.global_model_display_name || group.model.provider_model_name }}
</span>
<!-- 作用域标签 -->
<div class="flex items-center gap-1 shrink-0">
<div class="flex min-w-0 max-w-full flex-wrap items-center gap-1">
<Badge
v-if="group.apiFormats.length === 0"
variant="outline"
@@ -76,11 +76,10 @@
{{ getEndpointScopeLabel(group) }}
</Badge>
<Badge
v-if="group.operations.length > 0"
variant="outline"
class="text-xs"
class="min-w-0 max-w-full text-xs"
>
{{ getOperationScopeLabel(group) }}
<span class="truncate">{{ getOperationScopeLabel(group) }}</span>
</Badge>
</div>
<!-- 映射数量 -->
@@ -214,10 +213,16 @@ import {
type ProviderModelAlias
} from '@/api/endpoints'
import { updateModel } from '@/api/endpoints/models'
import { useI18n } from '@/i18n'
import { parseApiError, parseTestModelError } from '@/utils/errorParser'
import { formatApiFormat } from '@/api/endpoints/types/api-format'
import { buildExactModelMappingTestRequest } from './model-test-request'
import type { ProviderWithEndpointsSummary } from '@/api/endpoints'
import {
formatModelMappingRequestScope,
modelMappingOperationsKey,
normalizeModelMappingOperations,
} from '../../utils/modelMappingScope'
const props = defineProps<{
provider: ProviderWithEndpointsSummary
@@ -228,6 +233,7 @@ const emit = defineEmits<{
}>()
const { error: showError, success: showSuccess } = useToast()
const { t } = useI18n()
// 状态
const loading = ref(false)
@@ -278,21 +284,31 @@ function getEndpointIdsKey(endpointIds: string[] | undefined): string {
}
function getOperationsKey(operations: string[] | undefined): string {
return getScopeKey(operations)
return modelMappingOperationsKey(operations)
}
const requestScopeLabels = computed(() => ({
allRequests: t('providers.modelMapping.scope.allRequests'),
sessionCompactionOnly: t('providers.modelMapping.scope.sessionCompactionOnly'),
customOperations: (operations: string[]) => t(
'providers.modelMapping.scope.customOperations',
{ operations: operations.join(', ') },
),
}))
function getAliasGroupKey(group: AliasGroup): string {
return `${group.model.id}-${group.apiFormatsKey}-${group.endpointIdsKey}-${group.operationsKey}`
}
function getEndpointScopeLabel(group: AliasGroup): string {
if (!group.endpointIds || group.endpointIds.length === 0) return '全部端点'
return `${group.endpointIds.length} 端点`
if (!group.endpointIds || group.endpointIds.length === 0) {
return t('providers.modelMapping.scope.allEndpoints')
}
return t('providers.modelMapping.scope.endpointCount', { count: group.endpointIds.length })
}
function getOperationScopeLabel(group: AliasGroup): string {
if (group.operations.length === 1 && group.operations[0] === 'compact') return '压缩'
return `${group.operations.length} 项操作`
return formatModelMappingRequestScope(group.operations, requestScopeLabels.value)
}
// 按"模型+作用域"分组的映射列表
@@ -317,7 +333,7 @@ const aliasGroups = computed<AliasGroup[]>(() => {
endpointIdsKey,
endpointIds: normalizeStringList(alias.endpoint_ids),
operationsKey,
operations: normalizeStringList(alias.operations),
operations: normalizeModelMappingOperations(alias.operations),
aliases: []
}
groupMap.set(groupKey, group)
@@ -39,10 +39,10 @@
>
<!-- 行头部(可点击展开) -->
<div
class="flex items-center justify-between px-4 py-3 hover:bg-muted/20 cursor-pointer"
class="flex items-start justify-between px-4 py-3 hover:bg-muted/20 cursor-pointer"
@click="toggleExpand(item.key)"
>
<div class="flex items-center gap-2 flex-1 min-w-0">
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1.5">
<!-- 展开/收起图标 -->
<ChevronRight
class="w-4 h-4 text-muted-foreground shrink-0 transition-transform self-start mt-0.5"
@@ -50,7 +50,7 @@
/>
<!-- 精确映射 -->
<template v-if="item.type === 'exact'">
<div class="flex flex-col min-w-0">
<div class="flex min-w-0 flex-[1_1_12rem] flex-col">
<span class="font-semibold text-sm truncate">
{{ item.targetModelName }}
</span>
@@ -75,21 +75,23 @@
<Badge
v-if="item.group"
variant="outline"
class="text-xs shrink-0"
class="min-w-0 max-w-full text-xs"
:title="getGroupEndpointScopeTitle(item.group)"
>
{{ getGroupEndpointScopeLabel(item.group) }}
<span class="truncate">{{ getGroupEndpointScopeLabel(item.group) }}</span>
</Badge>
<Badge
v-if="item.group && item.group.operations.length > 0"
v-if="item.group"
variant="outline"
class="text-xs shrink-0"
class="min-w-0 max-w-full text-xs"
:title="getGroupOperationScopeLabel(item.group)"
>
{{ getGroupOperationScopeLabel(item.group) }}
<span class="truncate">{{ getGroupOperationScopeLabel(item.group) }}</span>
</Badge>
</template>
<!-- 正则映射 -->
<template v-else>
<div class="flex flex-col min-w-0">
<div class="flex min-w-0 flex-[1_1_12rem] flex-col">
<span class="font-semibold text-sm truncate">
{{ item.targetModelName }}
</span>
@@ -372,8 +374,15 @@ import {
} from '@/api/endpoints'
import { type EndpointAPIKey } from '@/api/endpoints/keys'
import { updateModel } from '@/api/endpoints/models'
import { useI18n } from '@/i18n'
import { parseApiError } from '@/utils/errorParser'
import type { ProviderWithEndpointsSummary } from '@/api/endpoints'
import {
formatModelMappingEndpointLabel,
formatModelMappingRequestScope,
modelMappingOperationsKey,
normalizeModelMappingOperations,
} from '../../utils/modelMappingScope'
import {
buildDefaultModelTestRequestHeaders,
buildDefaultModelTestRequestBody,
@@ -424,6 +433,7 @@ const emit = defineEmits<{
}>()
const { error: showError, success: showSuccess } = useToast()
const { t } = useI18n()
// 模型测试 composable
const modelTest = useModelTest({ providerId: () => props.provider.id })
@@ -501,17 +511,46 @@ function getEndpointIdsKey(endpointIds: string[] | undefined): string {
}
function getOperationsKey(operations: string[] | undefined): string {
return getScopeKey(operations)
return modelMappingOperationsKey(operations)
}
const requestScopeLabels = computed(() => ({
allRequests: t('providers.modelMapping.scope.allRequests'),
sessionCompactionOnly: t('providers.modelMapping.scope.sessionCompactionOnly'),
customOperations: (operations: string[]) => t(
'providers.modelMapping.scope.customOperations',
{ operations: operations.join(', ') },
),
}))
function getGroupEndpointScopeLabel(group: AliasGroup): string {
if (!group.endpointIds || group.endpointIds.length === 0) return '全部端点'
return `${group.endpointIds.length} 端点`
if (!group.endpointIds || group.endpointIds.length === 0) {
return t('providers.modelMapping.scope.allEndpoints')
}
const labels = getGroupEndpointScopeLabels(group)
return labels.length === 1
? labels[0]
: t('providers.modelMapping.scope.endpointCount', { count: labels.length })
}
function getGroupEndpointScopeTitle(group: AliasGroup): string {
if (!group.endpointIds || group.endpointIds.length === 0) {
return t('providers.modelMapping.scope.allEndpoints')
}
return getGroupEndpointScopeLabels(group).join('、')
}
function getGroupEndpointScopeLabels(group: AliasGroup): string[] {
const endpoints = props.endpoints ?? []
return group.endpointIds.map((endpointId) => {
const endpoint = endpoints.find(item => item.id === endpointId)
if (!endpoint) return endpointId
return formatModelMappingEndpointLabel(endpoint, endpoints)
})
}
function getGroupOperationScopeLabel(group: AliasGroup): string {
if (group.operations.length === 1 && group.operations[0] === 'compact') return '压缩'
return `${group.operations.length} 项操作`
return formatModelMappingRequestScope(group.operations, requestScopeLabels.value)
}
// 精确映射分组(来自 provider_model_mappings)
@@ -536,7 +575,7 @@ const exactMappingGroups = computed<AliasGroup[]>(() => {
endpointIdsKey,
endpointIds: normalizeStringList(alias.endpoint_ids),
operationsKey,
operations: normalizeStringList(alias.operations),
operations: normalizeModelMappingOperations(alias.operations),
aliases: []
}
groupMap.set(groupKey, group)