Normalize canonical API formats

This commit is contained in:
fawney19
2026-04-29 09:25:19 +08:00
parent 02ad67fe33
commit 07a319259b
171 changed files with 2460 additions and 1962 deletions

View File

@@ -453,10 +453,10 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
active_models: 7,
avg_health_score: 0.91,
unhealthy_endpoints: 0,
api_formats: ['CLAUDE_CLI', 'GEMINI_CLI', 'OPENAI_RESPONSES'],
api_formats: ['CLAUDE_MESSAGES', 'GEMINI_GENERATE_CONTENT', 'OPENAI_RESPONSES'],
endpoint_health_details: [
{ api_format: 'CLAUDE_CLI', health_score: 0.73, is_active: true, active_keys: 1 },
{ api_format: 'GEMINI_CLI', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'CLAUDE_MESSAGES', health_score: 0.73, is_active: true, active_keys: 1 },
{ api_format: 'GEMINI_GENERATE_CONTENT', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'OPENAI_RESPONSES', health_score: 1.0, is_active: true, active_keys: 1 }
],
created_at: '2024-12-09T14:10:36.446217+08:00',
@@ -471,18 +471,17 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
billing_type: 'pay_as_you_go',
monthly_used_usd: 545.18,
is_active: true,
total_endpoints: 2,
active_endpoints: 2,
total_endpoints: 1,
active_endpoints: 1,
total_keys: 3,
active_keys: 3,
total_models: 3,
active_models: 1,
avg_health_score: 0.825,
unhealthy_endpoints: 0,
api_formats: ['claude:chat', 'claude:cli'],
api_formats: ['claude:messages'],
endpoint_health_details: [
{ 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 }
{ api_format: 'claude:messages', health_score: 1.0, is_active: true, active_keys: 2 }
],
created_at: '2024-12-07T22:58:15.044538+08:00',
updated_at: new Date().toISOString()
@@ -504,9 +503,9 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
active_models: 5,
avg_health_score: 1.0,
unhealthy_endpoints: 0,
api_formats: ['claude:cli', 'openai:responses'],
api_formats: ['claude:messages', 'openai:responses'],
endpoint_health_details: [
{ api_format: 'claude:cli', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'claude:messages', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'openai:responses', health_score: 1.0, is_active: true, active_keys: 1 }
],
created_at: '2024-12-07T22:56:46.361092+08:00',
@@ -521,19 +520,18 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
billing_type: 'pay_as_you_go',
monthly_used_usd: 268.65,
is_active: true,
total_endpoints: 4,
active_endpoints: 4,
total_endpoints: 3,
active_endpoints: 3,
total_keys: 3,
active_keys: 3,
total_models: 7,
active_models: 7,
avg_health_score: 1.0,
unhealthy_endpoints: 0,
api_formats: ['claude:cli', 'gemini:chat', 'gemini:cli', 'openai:responses'],
api_formats: ['claude:messages', 'gemini:generate_content', 'openai:responses'],
endpoint_health_details: [
{ 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: 'claude:messages', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'gemini:generate_content', health_score: 1.0, is_active: true, active_keys: 1 },
{ api_format: 'openai:responses', health_score: 1.0, is_active: true, active_keys: 1 }
],
created_at: '2024-12-07T15:16:55.807595+08:00',
@@ -548,20 +546,18 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
billing_type: 'pay_as_you_go',
monthly_used_usd: 5.29,
is_active: true,
total_endpoints: 6,
active_endpoints: 6,
total_endpoints: 4,
active_endpoints: 4,
total_keys: 11,
active_keys: 11,
total_models: 8,
active_models: 8,
avg_health_score: 0.863,
unhealthy_endpoints: 1,
api_formats: ['claude:chat', 'claude:cli', 'gemini:chat', 'gemini:cli', 'openai:chat', 'openai:responses'],
api_formats: ['claude:messages', 'gemini:generate_content', 'openai:chat', 'openai:responses'],
endpoint_health_details: [
{ 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: 'claude:messages', health_score: 1.0, is_active: true, active_keys: 2 },
{ api_format: 'gemini:generate_content', health_score: 1.0, is_active: true, active_keys: 2 },
{ api_format: 'openai:chat', health_score: 0.85, is_active: true, active_keys: 2 },
{ api_format: 'openai:responses', health_score: 1.0, is_active: true, active_keys: 1 }
],
@@ -607,9 +603,9 @@ export const MOCK_PROVIDERS: ProviderWithEndpointsSummary[] = [
active_models: 1,
avg_health_score: 1.0,
unhealthy_endpoints: 0,
api_formats: ['gemini:chat'],
api_formats: ['gemini:generate_content'],
endpoint_health_details: [
{ api_format: 'gemini:chat', health_score: 1.0, is_active: true, active_keys: 1 }
{ api_format: 'gemini:generate_content', 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()
@@ -878,15 +874,13 @@ export const MOCK_SYSTEM_CONFIGS = [
export const MOCK_API_FORMATS = {
formats: [
{ value: 'claude:chat', label: 'Claude Chat', default_path: '/v1/messages', aliases: [] },
{ value: 'claude:cli', label: 'Claude CLI', default_path: '/v1/messages', aliases: [] },
{ value: 'claude:messages', label: 'Claude Messages', default_path: '/v1/messages', aliases: [] },
{ value: 'openai:chat', label: 'OpenAI Chat', default_path: '/v1/chat/completions', aliases: [] },
{ value: 'openai:responses', label: 'OpenAI Responses', default_path: '/v1/responses', aliases: ['openai:cli'] },
{ value: 'openai:responses:compact', label: 'OpenAI Responses Compact', default_path: '/v1/responses/compact', aliases: ['openai:compact'] },
{ value: 'openai:responses', label: 'OpenAI Responses', default_path: '/v1/responses', aliases: [] },
{ value: 'openai:responses:compact', label: 'OpenAI Responses Compact', default_path: '/v1/responses/compact', aliases: [] },
{ value: 'openai:image', label: 'OpenAI Image', default_path: '/v1/images/generations', aliases: [] },
{ value: 'openai:video', label: 'OpenAI Video', default_path: '/v1/videos', aliases: [] },
{ value: 'gemini:chat', label: 'Gemini Chat', default_path: '/v1beta/models/{model}:{action}', aliases: [] },
{ value: 'gemini:cli', label: 'Gemini CLI', default_path: '/v1beta/models/{model}:{action}', aliases: [] },
{ value: 'gemini:generate_content', label: 'Gemini Generate Content', default_path: '/v1beta/models/{model}:{action}', aliases: [] },
{ value: 'gemini:video', label: 'Gemini Video', default_path: '/v1beta/models/{model}:predictLongRunning', aliases: [] }
]
}

View File

@@ -146,7 +146,7 @@ const MOCK_ENDPOINT_STATUS = {
generated_at: new Date().toISOString(),
formats: [
{
api_format: 'claude:chat',
api_format: 'claude:messages',
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:messages',
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:chat',
api_format: 'gemini:generate_content',
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:generate_content',
api_path: '/v1beta/models',
total_attempts: 456,
success_count: 450,
@@ -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:chat', 'claude:cli', 'openai:chat', 'openai:responses', 'gemini:chat', 'gemini:cli']
const apiFormats = ['claude:messages', 'openai:chat', 'openai:responses', 'gemini:generate_content']
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:chat'
apiFormat = 'claude:messages'
} else if (model.provider === 'openai') {
apiFormat = Math.random() > 0.3 ? 'openai:responses' : 'openai:chat'
} else {
apiFormat = Math.random() > 0.3 ? 'gemini:cli' : 'gemini:chat'
apiFormat = 'gemini:generate_content'
}
const inputTokens = 500 + Math.floor(Math.random() * 10000)
@@ -424,7 +424,7 @@ function getMockEndpointExtras(apiFormat: string) {
const normalizedFormat = normalizeApiFormat(apiFormat)
const extras: Record<string, unknown> = {}
if (normalizedFormat === 'claude:chat') {
if (normalizedFormat === 'claude:messages') {
extras.header_rules = [
{ action: 'set', key: 'x-app-id', value: 'demo-app' },
{ action: 'rename', from: 'x-client-id', to: 'x-client' },
@@ -442,12 +442,12 @@ function getMockEndpointExtras(apiFormat: string) {
]
extras.format_acceptance_config = {
enabled: true,
accept_formats: ['openai:chat', 'claude:chat']
accept_formats: ['openai:chat', 'claude:messages']
}
extras.config = { upstream_stream_policy: 'force_stream' }
} else if (normalizedFormat === 'openai:responses' || normalizedFormat === 'openai:cli') {
} else if (normalizedFormat === 'openai:responses') {
extras.config = { upstream_stream_policy: 'force_non_stream' }
} else if (normalizedFormat === 'gemini:chat') {
} else if (normalizedFormat === 'gemini:generate_content') {
extras.custom_path = '/v1beta/models/gemini-3-pro-preview:generateContent'
extras.body_rules = [
{ action: 'drop', path: 'metadata.debug' }
@@ -460,16 +460,16 @@ function getMockEndpointExtras(apiFormat: string) {
// Mock Endpoint Keys
const MOCK_ENDPOINT_KEYS = [
{ id: 'ekey-001', provider_id: 'provider-001', api_formats: ['claude:chat'], api_key_masked: 'sk-ant...abc1', auth_type: 'api_key', 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', auth_type: 'api_key', 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-001', provider_id: 'provider-001', api_formats: ['claude:messages'], api_key_masked: 'sk-ant...abc1', auth_type: 'api_key', 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:messages'], api_key_masked: 'sk-ant...def2', auth_type: 'api_key', 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', auth_type: 'oauth', name: 'OpenAI OAuth', oauth_email: 'oauth-demo@aether.dev', oauth_expires_at: Math.floor(Date.now() / 1000) + 6 * 3600, oauth_plan_type: 'pro', oauth_account_id: 'acct-demo-002', 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: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(), ...getMockEndpointExtras('claude:chat') },
{ id: 'ep-001', provider_id: 'provider-001', provider_name: 'anthropic', api_format: 'claude:messages', 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(), ...getMockEndpointExtras('claude:messages') },
{ 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(), ...getMockEndpointExtras('openai:chat') },
{ 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(), ...getMockEndpointExtras('gemini:chat') }
{ id: 'ep-003', provider_id: 'provider-003', provider_name: 'google', api_format: 'gemini:generate_content', 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(), ...getMockEndpointExtras('gemini:generate_content') }
]
// Mock 能力定义
@@ -3148,14 +3148,14 @@ mockHandlers['GET /api/admin/monitoring/resilience-status'] = async () => {
{
error_id: 'usage-request-2',
error_type: 'server_error',
operation: 'Anthropic:claude:chat',
operation: 'Anthropic:claude:messages',
timestamp: new Date(Date.now() - 11 * 60 * 1000).toISOString(),
context: {
request_id: 'req-live-002',
provider_id: 'provider-anthropic',
provider_name: 'Anthropic',
model: 'claude-sonnet-4-5',
api_format: 'claude:chat',
api_format: 'claude:messages',
status_code: 502,
error_message: '上游返回 502 Bad Gateway'
}
@@ -3192,7 +3192,7 @@ mockHandlers['GET /api/admin/monitoring/resilience/circuit-history'] = async ()
provider_id: 'provider-gemini',
provider_name: 'Gemini',
key_name: 'gemini-burst',
api_format: 'gemini:chat',
api_format: 'gemini:generate_content',
reason: '正在探测恢复',
recovery_seconds: 120,
timestamp: new Date(Date.now() - 22 * 60 * 1000).toISOString()