Normalize canonical API formats

This commit is contained in:
fawney19
2026-04-29 10:20:41 +08:00
parent 02ad67fe33
commit 07a319259b
171 changed files with 2460 additions and 1962 deletions
@@ -264,7 +264,7 @@ async function handleTestModel(modelName: string) {
provider_id: props.providerId,
model_name: modelName,
api_key_id: props.keyId,
api_format: 'gemini:chat',
api_format: 'gemini:generate_content',
})
if (result.success) {
@@ -1338,10 +1338,6 @@ function hasDefaultBodyRules(apiFormat: string): boolean {
function normalizeLegacyOpenAIFormatAlias(apiFormat: string): string {
switch (apiFormat.trim().toLowerCase()) {
case 'openai:cli':
return 'openai:responses'
case 'openai:compact':
return 'openai:responses:compact'
default:
return apiFormat.trim().toLowerCase()
}
@@ -1385,10 +1381,10 @@ function getDefaultPath(apiFormat: string, baseUrl?: string): string {
const providerType = (props.provider?.provider_type || '').toLowerCase()
const normalizedApiFormat = normalizeLegacyOpenAIFormatAlias(apiFormat)
if (providerType === 'vertex_ai') {
if (normalizedApiFormat === 'gemini:chat') {
if (normalizedApiFormat === 'gemini:generate_content') {
return '/v1/publishers/google/models/{model}:{action}'
}
if (normalizedApiFormat === 'claude:chat') {
if (normalizedApiFormat === 'claude:messages') {
return '/v1/projects/{project_id}/locations/{region}/publishers/anthropic/models/{model}:{action}'
}
}
@@ -350,7 +350,7 @@ import {
type CapabilityDefinition,
type ProviderType
} from '@/api/endpoints'
import { formatApiFormat } from '@/api/endpoints/types/api-format'
import { formatApiFormat, normalizeApiFormatAlias } from '@/api/endpoints/types/api-format'
const props = defineProps<{
open: boolean
@@ -370,13 +370,13 @@ const { success, error: showError } = useToast()
function getVertexAllowedFormatsByAuth(authType: 'api_key' | 'service_account'): Set<string> {
if (authType === 'api_key') {
return new Set(['gemini:chat'])
return new Set(['gemini:generate_content'])
}
return new Set(['gemini:chat', 'claude:chat'])
return new Set(['gemini:generate_content', 'claude:messages'])
}
function normalizeApiFormat(format: string): string {
return String(format || '').trim().toLowerCase()
return normalizeApiFormatAlias(format).trim().toLowerCase()
}
function getAvailableApiFormatSet(): Set<string> {
@@ -529,7 +529,7 @@ function handleDialogUpdate(value: boolean) {
// 主 Tab 状态
const activeMainTab = ref<'provider' | 'key'>('provider')
const activeFormatTab = ref<string>('claude:chat')
const activeFormatTab = ref<string>('claude:messages')
// 提供商排序状态
const sortedProviders = ref<ProviderWithEndpointsSummary[]>([])
@@ -614,17 +614,16 @@ async function loadBalances() {
}
const LEGACY_API_FORMAT_MAP: Record<string, string> = {
CLAUDE: 'claude:chat',
CLAUDE_CLI: 'claude:cli',
CLAUDE: 'claude:messages',
CLAUDE_MESSAGES: 'claude:messages',
OPENAI: 'openai:chat',
OPENAI_CLI: 'openai:responses',
OPENAI_COMPACT: 'openai:responses:compact',
OPENAI_RESPONSES: 'openai:responses',
OPENAI_RESPONSES_COMPACT: 'openai:responses:compact',
OPENAI_VIDEO: 'openai:video',
GEMINI: 'gemini:chat',
GEMINI_CLI: 'gemini:cli',
GEMINI: 'gemini:generate_content',
GEMINI_GENERATE_CONTENT: 'gemini:generate_content',
GEMINI_VIDEO: 'gemini:video',
GEMINI_FILES: 'gemini:files',
}
function normalizeApiFormatKey(value: string | null | undefined): string {
@@ -635,6 +634,12 @@ function normalizeApiFormatKey(value: string | null | undefined): string {
const [family, kind] = raw.split(':', 2)
const familyNorm = family?.trim().toLowerCase()
const kindNorm = kind?.trim().toLowerCase()
if (familyNorm === 'claude' && ['chat', 'cli', 'messages'].includes(kindNorm)) {
return 'claude:messages'
}
if (familyNorm === 'gemini' && ['chat', 'cli', 'generate_content'].includes(kindNorm)) {
return 'gemini:generate_content'
}
if (familyNorm && kindNorm) return `${familyNorm}:${kindNorm}`
}
@@ -4,15 +4,14 @@ import type { EndpointHealthDetail } from '@/api/endpoints'
export type EndpointStatus = 'disabled' | 'no_keys' | 'keys_disabled' | 'available'
const ENDPOINT_SORT_ORDER = [
'claude:chat',
'claude:cli',
'claude:messages',
'openai:chat',
'openai:responses',
'openai:responses:compact',
'gemini:chat',
'gemini:cli',
'gemini:generate_content',
'openai:video',
'gemini:video',
'gemini:files',
]
/**
@@ -23,13 +23,11 @@ export function useProviderFilters(
const apiFormatFilters: FilterOption[] = [
{ value: 'all', label: '全部格式' },
{ value: 'claude:chat', label: 'Claude Chat' },
{ value: 'claude:cli', label: 'Claude CLI' },
{ value: 'claude:messages', label: 'Claude Messages' },
{ value: 'openai:chat', label: 'OpenAI Chat' },
{ value: 'openai:responses', label: 'OpenAI Responses' },
{ value: 'openai:responses:compact', label: 'OpenAI Responses Compact' },
{ value: 'gemini:chat', label: 'Gemini Chat' },
{ value: 'gemini:cli', label: 'Gemini CLI' },
{ value: 'gemini:generate_content', label: 'Gemini Generate Content' },
]
const modelFilters = computed<FilterOption[]>(() => {