mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: 全局适配 ApiFamily/EndpointKind 结构化标识体系
将新的 (ApiFamily, EndpointKind) / `family:kind` 签名体系应用到整个代码库: - API Handlers: 所有 adapter/handler 使用新的签名格式 - Services: provider, model, usage, cache, auth 等服务层适配 - Database: ProviderEndpoint 新增 api_family/endpoint_kind 字段 - Frontend: Provider 管理、Usage 表格等组件适配 - Tests: 更新所有相关测试用例
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
// API 格式常量
|
||||
export const API_FORMATS = {
|
||||
CLAUDE: 'CLAUDE',
|
||||
CLAUDE_CLI: 'CLAUDE_CLI',
|
||||
OPENAI: 'OPENAI',
|
||||
OPENAI_CLI: 'OPENAI_CLI',
|
||||
GEMINI: 'GEMINI',
|
||||
GEMINI_CLI: 'GEMINI_CLI',
|
||||
// 新模式:endpoint signature key(family:kind,全小写)
|
||||
CLAUDE: 'claude:chat',
|
||||
CLAUDE_CLI: 'claude:cli',
|
||||
OPENAI: 'openai:chat',
|
||||
OPENAI_CLI: 'openai:cli',
|
||||
OPENAI_VIDEO: 'openai:video',
|
||||
GEMINI: 'gemini:chat',
|
||||
GEMINI_CLI: 'gemini:cli',
|
||||
GEMINI_VIDEO: 'gemini:video',
|
||||
} as const
|
||||
|
||||
export type APIFormat = typeof API_FORMATS[keyof typeof API_FORMATS]
|
||||
@@ -16,28 +19,52 @@ export const API_FORMAT_LABELS: Record<string, string> = {
|
||||
[API_FORMATS.CLAUDE_CLI]: 'Claude CLI',
|
||||
[API_FORMATS.OPENAI]: 'OpenAI',
|
||||
[API_FORMATS.OPENAI_CLI]: 'OpenAI CLI',
|
||||
[API_FORMATS.OPENAI_VIDEO]: 'OpenAI Video',
|
||||
[API_FORMATS.GEMINI]: 'Gemini',
|
||||
[API_FORMATS.GEMINI_CLI]: 'Gemini CLI',
|
||||
[API_FORMATS.GEMINI_VIDEO]: 'Gemini Video',
|
||||
// legacy 兼容(仅用于展示历史数据)
|
||||
CLAUDE: 'Claude',
|
||||
CLAUDE_CLI: 'Claude CLI',
|
||||
OPENAI: 'OpenAI',
|
||||
OPENAI_CLI: 'OpenAI CLI',
|
||||
OPENAI_VIDEO: 'OpenAI Video',
|
||||
GEMINI: 'Gemini',
|
||||
GEMINI_CLI: 'Gemini CLI',
|
||||
GEMINI_VIDEO: 'Gemini Video',
|
||||
}
|
||||
|
||||
// API 格式缩写映射(用于空间紧凑的显示场景)
|
||||
export const API_FORMAT_SHORT: Record<string, string> = {
|
||||
[API_FORMATS.OPENAI]: 'O',
|
||||
[API_FORMATS.OPENAI_CLI]: 'OC',
|
||||
[API_FORMATS.OPENAI_VIDEO]: 'OV',
|
||||
[API_FORMATS.CLAUDE]: 'C',
|
||||
[API_FORMATS.CLAUDE_CLI]: 'CC',
|
||||
[API_FORMATS.GEMINI]: 'G',
|
||||
[API_FORMATS.GEMINI_CLI]: 'GC',
|
||||
[API_FORMATS.GEMINI_VIDEO]: 'GV',
|
||||
// legacy 兼容(仅用于展示历史数据)
|
||||
OPENAI: 'O',
|
||||
OPENAI_CLI: 'OC',
|
||||
OPENAI_VIDEO: 'OV',
|
||||
CLAUDE: 'C',
|
||||
CLAUDE_CLI: 'CC',
|
||||
GEMINI: 'G',
|
||||
GEMINI_CLI: 'GC',
|
||||
GEMINI_VIDEO: 'GV',
|
||||
}
|
||||
|
||||
// API 格式排序顺序(统一的显示顺序)
|
||||
export const API_FORMAT_ORDER: string[] = [
|
||||
API_FORMATS.OPENAI,
|
||||
API_FORMATS.OPENAI_CLI,
|
||||
API_FORMATS.OPENAI_VIDEO,
|
||||
API_FORMATS.CLAUDE,
|
||||
API_FORMATS.CLAUDE_CLI,
|
||||
API_FORMATS.GEMINI,
|
||||
API_FORMATS.GEMINI_CLI,
|
||||
API_FORMATS.GEMINI_VIDEO,
|
||||
]
|
||||
|
||||
// 工具函数:按标准顺序排序 API 格式数组
|
||||
@@ -135,21 +162,21 @@ export function isAllowedModelsList(value: AllowedModels): value is string[] {
|
||||
export interface EndpointAPIKey {
|
||||
id: string
|
||||
provider_id: string
|
||||
api_formats: string[] // 支持的 API 格式列表
|
||||
api_formats: string[] // 支持的 endpoint signature 列表(如 "openai:chat")
|
||||
api_key_masked: string
|
||||
api_key_plain?: string | null
|
||||
auth_type: 'api_key' | 'vertex_ai' // 认证类型(必返回)
|
||||
name: string // 密钥名称(必填,用于识别)
|
||||
rate_multipliers?: Record<string, number> | null // 按 API 格式的成本倍率,如 {"CLAUDE_CLI": 1.0, "OPENAI_CLI": 0.8}
|
||||
rate_multipliers?: Record<string, number> | null // 按 endpoint signature 的成本倍率
|
||||
internal_priority: number // Key 内部优先级
|
||||
global_priority_by_format?: Record<string, number> | null // 按 API 格式的全局优先级
|
||||
global_priority_by_format?: Record<string, number> | null // 按 endpoint signature 的全局优先级
|
||||
rpm_limit?: number | null // RPM 速率限制 (1-10000),null 表示自适应模式
|
||||
allowed_models?: AllowedModels // 允许使用的模型列表(null=不限制)
|
||||
capabilities?: Record<string, boolean> | null // 能力标签配置(如 cache_1h, context_1m)
|
||||
// 缓存与熔断配置
|
||||
cache_ttl_minutes: number // 缓存 TTL(分钟),0=禁用
|
||||
max_probe_interval_minutes: number // 熔断探测间隔(分钟)
|
||||
// 按格式的健康度数据
|
||||
// 按 endpoint signature 的健康度数据
|
||||
health_by_format?: Record<string, FormatHealthData>
|
||||
circuit_breaker_by_format?: Record<string, FormatCircuitBreakerData>
|
||||
// 聚合字段(从 health_by_format 计算,用于列表显示)
|
||||
|
||||
@@ -485,7 +485,7 @@ function handleDialogUpdate(value: boolean) {
|
||||
|
||||
// 主 Tab 状态
|
||||
const activeMainTab = ref<'provider' | 'key'>('provider')
|
||||
const activeFormatTab = ref<string>('CLAUDE')
|
||||
const activeFormatTab = ref<string>('claude:chat')
|
||||
|
||||
// 提供商排序状态
|
||||
const sortedProviders = ref<ProviderWithEndpointsSummary[]>([])
|
||||
|
||||
@@ -525,6 +525,7 @@ import Tabs from '@/components/ui/tabs.vue'
|
||||
import TabsContent from '@/components/ui/tabs-content.vue'
|
||||
import { Copy, Check, Maximize2, Minimize2, Columns2, RefreshCw, X, Monitor, Server, MessageSquareText, Code2 } from 'lucide-vue-next'
|
||||
import { dashboardApi, type RequestDetail } from '@/api/dashboard'
|
||||
import { API_FORMAT_LABELS } from '@/api/endpoints/types'
|
||||
import { log } from '@/utils/logger'
|
||||
|
||||
// 子组件
|
||||
@@ -818,15 +819,13 @@ function formatDateTime(dateStr: string | null | undefined): string {
|
||||
|
||||
function formatApiFormat(format: string | null | undefined): string {
|
||||
if (!format) return '-'
|
||||
const formatMap: Record<string, string> = {
|
||||
'CLAUDE': 'Claude',
|
||||
'CLAUDE_CLI': 'Claude CLI',
|
||||
'OPENAI': 'OpenAI',
|
||||
'OPENAI_CLI': 'OpenAI CLI',
|
||||
'GEMINI': 'Gemini',
|
||||
'GEMINI_CLI': 'Gemini CLI',
|
||||
}
|
||||
return formatMap[format.toUpperCase()] || format
|
||||
const raw = (format || '').trim()
|
||||
return (
|
||||
API_FORMAT_LABELS[raw] ||
|
||||
API_FORMAT_LABELS[raw.toLowerCase()] ||
|
||||
API_FORMAT_LABELS[raw.toUpperCase()] ||
|
||||
raw
|
||||
)
|
||||
}
|
||||
|
||||
function formatNumber(num: number): string {
|
||||
|
||||
@@ -543,6 +543,7 @@ import { RefreshCcw, Search } from 'lucide-vue-next'
|
||||
import { formatTokens, formatCurrency } from '@/utils/format'
|
||||
import { formatDateTime } from '../composables'
|
||||
import { useRowClick } from '@/composables/useRowClick'
|
||||
import { API_FORMAT_LABELS } from '@/api/endpoints/types'
|
||||
import type { UsageRecord } from '../types'
|
||||
|
||||
export interface UserOption {
|
||||
@@ -689,15 +690,13 @@ onUnmounted(() => {
|
||||
|
||||
// 格式化 API 格式显示名称
|
||||
function formatApiFormat(format: string): string {
|
||||
const formatMap: Record<string, string> = {
|
||||
'CLAUDE': 'Claude',
|
||||
'CLAUDE_CLI': 'Claude CLI',
|
||||
'OPENAI': 'OpenAI',
|
||||
'OPENAI_CLI': 'OpenAI CLI',
|
||||
'GEMINI': 'Gemini',
|
||||
'GEMINI_CLI': 'Gemini CLI',
|
||||
}
|
||||
return formatMap[format.toUpperCase()] || format
|
||||
const raw = (format || '').trim()
|
||||
return (
|
||||
API_FORMAT_LABELS[raw] ||
|
||||
API_FORMAT_LABELS[raw.toLowerCase()] ||
|
||||
API_FORMAT_LABELS[raw.toUpperCase()] ||
|
||||
raw
|
||||
)
|
||||
}
|
||||
|
||||
// 判断是否应该显示格式转换信息
|
||||
@@ -711,7 +710,7 @@ function shouldShowFormatConversion(record: UsageRecord): boolean {
|
||||
return true
|
||||
}
|
||||
// 同族格式差异(精确字符串比较,不区分大小写)
|
||||
return record.api_format.toUpperCase() !== record.endpoint_api_format.toUpperCase()
|
||||
return record.api_format.trim().toLowerCase() !== record.endpoint_api_format.trim().toLowerCase()
|
||||
}
|
||||
|
||||
// 获取 API 格式的 tooltip(包含转换信息)
|
||||
|
||||
@@ -465,10 +465,10 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
|
||||
active_models: 1,
|
||||
avg_health_score: 0.825,
|
||||
unhealthy_endpoints: 0,
|
||||
api_formats: ['CLAUDE', 'CLAUDE_CLI'],
|
||||
api_formats: ['claude:chat', 'claude:cli'],
|
||||
endpoint_health_details: [
|
||||
{ api_format: 'CLAUDE', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'CLAUDE_CLI', health_score: 0.65, is_active: true, active_keys: 1 }
|
||||
{ api_format: 'claude:chat', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'claude:cli', health_score: 0.65, is_active: true, active_keys: 1 }
|
||||
],
|
||||
created_at: '2024-12-07T22:58:15.044538+08:00',
|
||||
updated_at: new Date().toISOString()
|
||||
@@ -490,10 +490,10 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
|
||||
active_models: 5,
|
||||
avg_health_score: 1.0,
|
||||
unhealthy_endpoints: 0,
|
||||
api_formats: ['CLAUDE_CLI', 'OPENAI_CLI'],
|
||||
api_formats: ['claude:cli', 'openai:cli'],
|
||||
endpoint_health_details: [
|
||||
{ api_format: 'CLAUDE_CLI', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'OPENAI_CLI', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
{ api_format: 'claude:cli', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'openai:cli', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
],
|
||||
created_at: '2024-12-07T22:56:46.361092+08:00',
|
||||
updated_at: new Date().toISOString()
|
||||
@@ -515,12 +515,12 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
|
||||
active_models: 7,
|
||||
avg_health_score: 1.0,
|
||||
unhealthy_endpoints: 0,
|
||||
api_formats: ['CLAUDE_CLI', 'GEMINI', 'GEMINI_CLI', 'OPENAI_CLI'],
|
||||
api_formats: ['claude:cli', 'gemini:chat', 'gemini:cli', 'openai:cli'],
|
||||
endpoint_health_details: [
|
||||
{ api_format: 'CLAUDE_CLI', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'GEMINI', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'GEMINI_CLI', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'OPENAI_CLI', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
{ api_format: 'claude:cli', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'gemini:chat', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'gemini:cli', health_score: 1.0, is_active: true, active_keys: 1 },
|
||||
{ api_format: 'openai:cli', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
],
|
||||
created_at: '2024-12-07T15:16:55.807595+08:00',
|
||||
updated_at: new Date().toISOString()
|
||||
@@ -542,14 +542,14 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
|
||||
active_models: 8,
|
||||
avg_health_score: 0.863,
|
||||
unhealthy_endpoints: 1,
|
||||
api_formats: ['CLAUDE', 'CLAUDE_CLI', 'GEMINI', 'GEMINI_CLI', 'OPENAI', 'OPENAI_CLI'],
|
||||
api_formats: ['claude:chat', 'claude:cli', 'gemini:chat', 'gemini:cli', 'openai:chat', 'openai:cli'],
|
||||
endpoint_health_details: [
|
||||
{ api_format: 'CLAUDE', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'CLAUDE_CLI', health_score: 0.48, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'GEMINI', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'GEMINI_CLI', health_score: 0.85, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'OPENAI', health_score: 0.85, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'OPENAI_CLI', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
{ api_format: 'claude:chat', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'claude:cli', health_score: 0.48, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'gemini:chat', health_score: 1.0, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'gemini:cli', health_score: 0.85, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'openai:chat', health_score: 0.85, is_active: true, active_keys: 2 },
|
||||
{ api_format: 'openai:cli', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
],
|
||||
created_at: '2024-12-07T22:56:09.712806+08:00',
|
||||
updated_at: new Date().toISOString()
|
||||
@@ -593,9 +593,9 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
|
||||
active_models: 1,
|
||||
avg_health_score: 1.0,
|
||||
unhealthy_endpoints: 0,
|
||||
api_formats: ['GEMINI'],
|
||||
api_formats: ['gemini:chat'],
|
||||
endpoint_health_details: [
|
||||
{ api_format: 'GEMINI', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
{ api_format: 'gemini:chat', health_score: 1.0, is_active: true, active_keys: 1 }
|
||||
],
|
||||
created_at: '2024-12-07T23:00:42.559105+08:00',
|
||||
updated_at: new Date().toISOString()
|
||||
@@ -865,11 +865,13 @@ export const MOCK_SYSTEM_CONFIGS = [
|
||||
|
||||
export const MOCK_API_FORMATS = {
|
||||
formats: [
|
||||
{ value: 'claude', label: 'Claude API', default_path: '/v1/messages', aliases: [] },
|
||||
{ value: 'claude_cli', label: 'Claude CLI', default_path: '/v1/messages', aliases: [] },
|
||||
{ value: 'openai', label: 'OpenAI API', default_path: '/v1/chat/completions', aliases: [] },
|
||||
{ value: 'openai_cli', label: 'OpenAI Responses API', default_path: '/v1/responses', aliases: [] },
|
||||
{ value: 'gemini', label: 'Gemini API', default_path: '/v1beta/models', aliases: [] },
|
||||
{ value: 'gemini_cli', label: 'Gemini CLI', default_path: '/v1beta/models', aliases: [] }
|
||||
{ value: 'claude:chat', label: 'Claude', default_path: '/v1/messages', aliases: [] },
|
||||
{ value: 'claude:cli', label: 'Claude CLI', default_path: '/v1/messages', aliases: [] },
|
||||
{ value: 'openai:chat', label: 'OpenAI', default_path: '/v1/chat/completions', aliases: [] },
|
||||
{ value: 'openai:cli', label: 'OpenAI CLI', default_path: '/responses', aliases: [] },
|
||||
{ value: 'openai:video', label: 'OpenAI Video', default_path: '/v1/videos', aliases: [] },
|
||||
{ value: 'gemini:chat', label: 'Gemini', default_path: '/v1beta/models/{model}:{action}', aliases: [] },
|
||||
{ value: 'gemini:cli', label: 'Gemini CLI', default_path: '/v1beta/models/{model}:{action}', aliases: [] },
|
||||
{ value: 'gemini:video', label: 'Gemini Video', default_path: '/v1beta/models/{model}:predictLongRunning', aliases: [] }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
generated_at: new Date().toISOString(),
|
||||
formats: [
|
||||
{
|
||||
api_format: 'CLAUDE',
|
||||
api_format: 'claude:chat',
|
||||
api_path: '/v1/messages',
|
||||
total_attempts: 2580,
|
||||
success_count: 2540,
|
||||
@@ -160,7 +160,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
events: generateHealthEvents(80, 0.984, 0.012, 0.004, 900, 500)
|
||||
},
|
||||
{
|
||||
api_format: 'CLAUDE_CLI',
|
||||
api_format: 'claude:cli',
|
||||
api_path: '/v1/messages',
|
||||
total_attempts: 1890,
|
||||
success_count: 1780,
|
||||
@@ -174,7 +174,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
events: generateHealthEvents(120, 0.942, 0.045, 0.013, 1200, 800)
|
||||
},
|
||||
{
|
||||
api_format: 'GEMINI',
|
||||
api_format: 'gemini:chat',
|
||||
api_path: '/v1beta/models',
|
||||
total_attempts: 890,
|
||||
success_count: 890,
|
||||
@@ -188,7 +188,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
events: generateHealthEvents(45, 1.0, 0, 0, 400, 200)
|
||||
},
|
||||
{
|
||||
api_format: 'GEMINI_CLI',
|
||||
api_format: 'gemini:cli',
|
||||
api_path: '/v1beta/models',
|
||||
total_attempts: 456,
|
||||
success_count: 450,
|
||||
@@ -202,7 +202,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
events: generateHealthEvents(25, 0.987, 0.009, 0.004, 500, 300)
|
||||
},
|
||||
{
|
||||
api_format: 'OPENAI',
|
||||
api_format: 'openai:chat',
|
||||
api_path: '/v1/chat/completions',
|
||||
total_attempts: 1560,
|
||||
success_count: 1520,
|
||||
@@ -216,7 +216,7 @@ const MOCK_ENDPOINT_STATUS = {
|
||||
events: generateHealthEvents(60, 0.974, 0.022, 0.004, 700, 400)
|
||||
},
|
||||
{
|
||||
api_format: 'OPENAI_CLI',
|
||||
api_format: 'openai:cli',
|
||||
api_path: '/responses',
|
||||
total_attempts: 2340,
|
||||
success_count: 2200,
|
||||
@@ -324,7 +324,7 @@ function generateMockUsageRecords(count: number = 100) {
|
||||
{ id: 'demo-user-uuid-0004', username: 'Bob Zhang', email: 'bob@demo.aether.ai' }
|
||||
]
|
||||
|
||||
const apiFormats = ['CLAUDE', 'CLAUDE_CLI', 'OPENAI', 'OPENAI_CLI', 'GEMINI', 'GEMINI_CLI']
|
||||
const apiFormats = ['claude:chat', 'claude:cli', 'openai:chat', 'openai:cli', 'gemini:chat', 'gemini:cli']
|
||||
const statusOptions: Array<'completed' | 'failed' | 'streaming'> = ['completed', 'completed', 'completed', 'completed', 'failed', 'streaming']
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
@@ -335,11 +335,11 @@ function generateMockUsageRecords(count: number = 100) {
|
||||
// 根据模型类型选择 API 格式
|
||||
let apiFormat = apiFormats[0]
|
||||
if (model.provider === 'anthropic') {
|
||||
apiFormat = Math.random() > 0.3 ? 'CLAUDE_CLI' : 'CLAUDE'
|
||||
apiFormat = Math.random() > 0.3 ? 'claude:cli' : 'claude:chat'
|
||||
} else if (model.provider === 'openai') {
|
||||
apiFormat = Math.random() > 0.3 ? 'OPENAI_CLI' : 'OPENAI'
|
||||
apiFormat = Math.random() > 0.3 ? 'openai:cli' : 'openai:chat'
|
||||
} else {
|
||||
apiFormat = Math.random() > 0.3 ? 'GEMINI_CLI' : 'GEMINI'
|
||||
apiFormat = Math.random() > 0.3 ? 'gemini:cli' : 'gemini:chat'
|
||||
}
|
||||
|
||||
const inputTokens = 500 + Math.floor(Math.random() * 10000)
|
||||
@@ -386,7 +386,7 @@ function generateMockUsageRecords(count: number = 100) {
|
||||
cost,
|
||||
actual_cost: actualCost,
|
||||
response_time_ms: responseTime,
|
||||
is_stream: apiFormat.includes('CLI'),
|
||||
is_stream: apiFormat.includes(':cli'),
|
||||
status_code: status === 'failed' ? [500, 502, 429, 400][Math.floor(Math.random() * 4)] : 200,
|
||||
error_message: status === 'failed' ? ['Rate limit exceeded', 'Internal server error', 'Model overloaded'][Math.floor(Math.random() * 3)] : undefined,
|
||||
status,
|
||||
@@ -418,16 +418,16 @@ const MOCK_ALIASES = [
|
||||
|
||||
// Mock Endpoint Keys
|
||||
const MOCK_ENDPOINT_KEYS = [
|
||||
{ id: 'ekey-001', provider_id: 'provider-001', api_formats: ['CLAUDE'], api_key_masked: 'sk-ant...abc1', name: 'Primary Key', rate_multiplier: 1.0, internal_priority: 1, health_score: 0.98, consecutive_failures: 0, request_count: 5000, success_count: 4950, error_count: 50, success_rate: 0.99, avg_response_time_ms: 1200, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ekey-002', provider_id: 'provider-001', api_formats: ['CLAUDE'], api_key_masked: 'sk-ant...def2', name: 'Backup Key', rate_multiplier: 1.0, internal_priority: 2, health_score: 0.95, consecutive_failures: 1, request_count: 2000, success_count: 1950, error_count: 50, success_rate: 0.975, avg_response_time_ms: 1350, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-02-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ekey-003', provider_id: 'provider-002', api_formats: ['OPENAI'], api_key_masked: 'sk-oai...ghi3', name: 'OpenAI Main', rate_multiplier: 1.0, internal_priority: 1, health_score: 0.97, consecutive_failures: 0, request_count: 3500, success_count: 3450, error_count: 50, success_rate: 0.986, avg_response_time_ms: 900, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-01-15T00:00:00Z', updated_at: new Date().toISOString() }
|
||||
{ id: 'ekey-001', provider_id: 'provider-001', api_formats: ['claude:chat'], api_key_masked: 'sk-ant...abc1', name: 'Primary Key', rate_multiplier: 1.0, internal_priority: 1, health_score: 0.98, consecutive_failures: 0, request_count: 5000, success_count: 4950, error_count: 50, success_rate: 0.99, avg_response_time_ms: 1200, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ekey-002', provider_id: 'provider-001', api_formats: ['claude:chat'], api_key_masked: 'sk-ant...def2', name: 'Backup Key', rate_multiplier: 1.0, internal_priority: 2, health_score: 0.95, consecutive_failures: 1, request_count: 2000, success_count: 1950, error_count: 50, success_rate: 0.975, avg_response_time_ms: 1350, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-02-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ekey-003', provider_id: 'provider-002', api_formats: ['openai:chat'], api_key_masked: 'sk-oai...ghi3', name: 'OpenAI Main', rate_multiplier: 1.0, internal_priority: 1, health_score: 0.97, consecutive_failures: 0, request_count: 3500, success_count: 3450, error_count: 50, success_rate: 0.986, avg_response_time_ms: 900, cache_ttl_minutes: 5, max_probe_interval_minutes: 32, is_active: true, created_at: '2024-01-15T00:00:00Z', updated_at: new Date().toISOString() }
|
||||
]
|
||||
|
||||
// Mock Endpoints
|
||||
const MOCK_ENDPOINTS = [
|
||||
{ id: 'ep-001', provider_id: 'provider-001', provider_name: 'anthropic', api_format: 'CLAUDE', base_url: 'https://api.anthropic.com', max_retries: 2, is_active: true, total_keys: 2, active_keys: 2, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ep-002', provider_id: 'provider-002', provider_name: 'openai', api_format: 'OPENAI', base_url: 'https://api.openai.com', max_retries: 2, is_active: true, total_keys: 1, active_keys: 1, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ep-003', provider_id: 'provider-003', provider_name: 'google', api_format: 'GEMINI', base_url: 'https://generativelanguage.googleapis.com', max_retries: 2, is_active: true, total_keys: 1, active_keys: 1, created_at: '2024-01-15T00:00:00Z', updated_at: new Date().toISOString() }
|
||||
{ id: 'ep-001', provider_id: 'provider-001', provider_name: 'anthropic', api_format: 'claude:chat', base_url: 'https://api.anthropic.com', max_retries: 2, is_active: true, total_keys: 2, active_keys: 2, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ep-002', provider_id: 'provider-002', provider_name: 'openai', api_format: 'openai:chat', base_url: 'https://api.openai.com', max_retries: 2, is_active: true, total_keys: 1, active_keys: 1, created_at: '2024-01-01T00:00:00Z', updated_at: new Date().toISOString() },
|
||||
{ id: 'ep-003', provider_id: 'provider-003', provider_name: 'google', api_format: 'gemini:chat', base_url: 'https://generativelanguage.googleapis.com', max_retries: 2, is_active: true, total_keys: 1, active_keys: 1, created_at: '2024-01-15T00:00:00Z', updated_at: new Date().toISOString() }
|
||||
]
|
||||
|
||||
// Mock 能力定义
|
||||
@@ -1218,8 +1218,8 @@ function generateMockEndpointsForProvider(providerId: string) {
|
||||
provider_id: providerId,
|
||||
provider_name: provider.name,
|
||||
api_format: format,
|
||||
base_url: format.includes('CLAUDE') ? 'https://api.anthropic.com' :
|
||||
format.includes('OPENAI') ? 'https://api.openai.com' :
|
||||
base_url: format.includes('claude') ? 'https://api.anthropic.com' :
|
||||
format.includes('openai') ? 'https://api.openai.com' :
|
||||
'https://generativelanguage.googleapis.com',
|
||||
max_retries: 2,
|
||||
is_active: healthDetail?.is_active ?? true,
|
||||
@@ -1266,9 +1266,9 @@ function generateMockModelsForProvider(providerId: string) {
|
||||
if (!provider) return []
|
||||
|
||||
// 基于 provider 的 api_formats 选择合适的模型
|
||||
const hasClaude = provider.api_formats.some(f => f.includes('CLAUDE'))
|
||||
const hasOpenAI = provider.api_formats.some(f => f.includes('OPENAI'))
|
||||
const hasGemini = provider.api_formats.some(f => f.includes('GEMINI'))
|
||||
const hasClaude = provider.api_formats.some(f => f.includes('claude'))
|
||||
const hasOpenAI = provider.api_formats.some(f => f.includes('openai'))
|
||||
const hasGemini = provider.api_formats.some(f => f.includes('gemini'))
|
||||
|
||||
const models: any[] = []
|
||||
const now = new Date().toISOString()
|
||||
|
||||
@@ -989,7 +989,7 @@ function formatWebsiteDisplay(url: string): string {
|
||||
// 端点排序
|
||||
function sortEndpoints(endpoints: any[]) {
|
||||
return [...endpoints].sort((a, b) => {
|
||||
const order = ['CLAUDE', 'OPENAI', 'CLAUDE_COMPATIBLE', 'OPENAI_COMPATIBLE', 'GEMINI', 'GEMINI_COMPATIBLE']
|
||||
const order = ['claude:chat', 'claude:cli', 'openai:chat', 'openai:cli', 'gemini:chat', 'gemini:cli', 'openai:video', 'gemini:video']
|
||||
return order.indexOf(a.api_format) - order.indexOf(b.api_format)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user