diff --git a/backend/internal/domain/constants.go b/backend/internal/domain/constants.go index 4e69ca0252..86113f24ed 100644 --- a/backend/internal/domain/constants.go +++ b/backend/internal/domain/constants.go @@ -75,15 +75,15 @@ var DefaultAntigravityModelMapping = map[string]string{ "claude-opus-4-6-thinking": "claude-opus-4-6-thinking", // 官方模型 "claude-opus-4-6": "claude-opus-4-6-thinking", // 简称映射 "claude-opus-4-5-thinking": "claude-opus-4-6-thinking", // 迁移旧模型 - "claude-sonnet-4-6": "claude-sonnet-4-6", - "claude-sonnet-4-5": "claude-sonnet-4-5", - "claude-sonnet-4-5-thinking": "claude-sonnet-4-5-thinking", + "claude-sonnet-4-6": "claude-opus-4-6-thinking", // Sonnet → Opus + "claude-sonnet-4-5": "claude-opus-4-6-thinking", // Sonnet → Opus + "claude-sonnet-4-5-thinking": "claude-opus-4-6-thinking", // Sonnet → Opus // Claude 详细版本 ID 映射 "claude-opus-4-5-20251101": "claude-opus-4-6-thinking", // 迁移旧模型 - "claude-sonnet-4-5-20250929": "claude-sonnet-4-5", - // Claude Haiku → Sonnet(无 Haiku 支持) - "claude-haiku-4-5": "claude-sonnet-4-6", - "claude-haiku-4-5-20251001": "claude-sonnet-4-6", + "claude-sonnet-4-5-20250929": "claude-opus-4-6-thinking", // Sonnet → Opus + // Claude Haiku → Opus(无 Haiku/Sonnet 支持) + "claude-haiku-4-5": "claude-opus-4-6-thinking", + "claude-haiku-4-5-20251001": "claude-opus-4-6-thinking", // Gemini 2.5 白名单 "gemini-2.5-flash": "gemini-2.5-flash", "gemini-2.5-flash-image": "gemini-2.5-flash-image", diff --git a/backend/internal/service/antigravity_credits_overages_test.go b/backend/internal/service/antigravity_credits_overages_test.go index 7a5224daf3..642e3dd826 100644 --- a/backend/internal/service/antigravity_credits_overages_test.go +++ b/backend/internal/service/antigravity_credits_overages_test.go @@ -239,7 +239,7 @@ func TestAntigravityRetryLoop_ModelRateLimited_InjectsCredits(t *testing.T) { Extra: map[string]any{ "allow_overages": true, modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limited_at": time.Now().UTC().Format(time.RFC3339), "rate_limit_reset_at": time.Now().Add(30 * time.Minute).UTC().Format(time.RFC3339), }, @@ -254,9 +254,9 @@ func TestAntigravityRetryLoop_ModelRateLimited_InjectsCredits(t *testing.T) { account: account, accessToken: "token", action: "generateContent", - body: []byte(`{"model":"claude-sonnet-4-5","request":{}}`), + body: []byte(`{"model":"claude-opus-4-6-thinking","request":{}}`), httpUpstream: upstream, - requestedModel: "claude-sonnet-4-5", + requestedModel: "claude-opus-4-6-thinking", handleError: func(ctx context.Context, prefix string, account *Account, statusCode int, headers http.Header, body []byte, requestedModel string, groupID int64, sessionHash string, isStickySession bool) *handleModelRateLimitResult { return nil }, @@ -290,7 +290,7 @@ func TestAntigravityRetryLoop_CreditsExhausted_DoesNotInject(t *testing.T) { Extra: map[string]any{ "allow_overages": true, modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limited_at": time.Now().UTC().Format(time.RFC3339), "rate_limit_reset_at": time.Now().Add(30 * time.Minute).UTC().Format(time.RFC3339), }, @@ -309,8 +309,8 @@ func TestAntigravityRetryLoop_CreditsExhausted_DoesNotInject(t *testing.T) { account: account, accessToken: "token", action: "generateContent", - body: []byte(`{"model":"claude-sonnet-4-5","request":{}}`), - requestedModel: "claude-sonnet-4-5", + body: []byte(`{"model":"claude-opus-4-6-thinking","request":{}}`), + requestedModel: "claude-opus-4-6-thinking", handleError: func(ctx context.Context, prefix string, account *Account, statusCode int, headers http.Header, body []byte, requestedModel string, groupID int64, sessionHash string, isStickySession bool) *handleModelRateLimitResult { return nil }, @@ -355,7 +355,7 @@ func TestAntigravityRetryLoop_CreditErrorMarksExhausted(t *testing.T) { Extra: map[string]any{ "allow_overages": true, modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limited_at": time.Now().UTC().Format(time.RFC3339), "rate_limit_reset_at": time.Now().Add(30 * time.Minute).UTC().Format(time.RFC3339), }, @@ -370,10 +370,10 @@ func TestAntigravityRetryLoop_CreditErrorMarksExhausted(t *testing.T) { account: account, accessToken: "token", action: "generateContent", - body: []byte(`{"model":"claude-sonnet-4-5","request":{}}`), + body: []byte(`{"model":"claude-opus-4-6-thinking","request":{}}`), httpUpstream: upstream, accountRepo: repo, - requestedModel: "claude-sonnet-4-5", + requestedModel: "claude-opus-4-6-thinking", handleError: func(ctx context.Context, prefix string, account *Account, statusCode int, headers http.Header, body []byte, requestedModel string, groupID int64, sessionHash string, isStickySession bool) *handleModelRateLimitResult { return nil }, diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go index 5393ca91d9..b48f028264 100644 --- a/backend/internal/service/antigravity_gateway_service.go +++ b/backend/internal/service/antigravity_gateway_service.go @@ -977,14 +977,8 @@ func (s *AntigravityGatewayService) getMappedModel(account *Account, requestedMo } // applyThinkingModelSuffix 根据 thinking 配置调整模型名 -// 当映射结果是 claude-sonnet-4-5 且请求开启了 thinking 时,改为 claude-sonnet-4-5-thinking +// 所有 Sonnet 已统一映射到 Opus,此函数保留为扩展点 func applyThinkingModelSuffix(mappedModel string, thinkingEnabled bool) string { - if !thinkingEnabled { - return mappedModel - } - if mappedModel == "claude-sonnet-4-5" { - return "claude-sonnet-4-5-thinking" - } return mappedModel } diff --git a/backend/internal/service/antigravity_model_mapping_test.go b/backend/internal/service/antigravity_model_mapping_test.go index 1dbe987073..53e150619d 100644 --- a/backend/internal/service/antigravity_model_mapping_test.go +++ b/backend/internal/service/antigravity_model_mapping_test.go @@ -57,36 +57,36 @@ func TestAntigravityGatewayService_GetMappedModel(t *testing.T) { expected: "claude-opus-4-6-thinking", }, { - name: "默认映射 - claude-haiku-4-5 → claude-sonnet-4-6", + name: "默认映射 - claude-haiku-4-5 → claude-opus-4-6-thinking", requestedModel: "claude-haiku-4-5", accountMapping: nil, - expected: "claude-sonnet-4-6", + expected: "claude-opus-4-6-thinking", }, { - name: "默认映射 - claude-haiku-4-5-20251001 → claude-sonnet-4-6", + name: "默认映射 - claude-haiku-4-5-20251001 → claude-opus-4-6-thinking", requestedModel: "claude-haiku-4-5-20251001", accountMapping: nil, - expected: "claude-sonnet-4-6", + expected: "claude-opus-4-6-thinking", }, { - name: "默认映射 - claude-sonnet-4-5-20250929 → claude-sonnet-4-5", + name: "默认映射 - claude-sonnet-4-5-20250929 → claude-opus-4-6-thinking", requestedModel: "claude-sonnet-4-5-20250929", accountMapping: nil, - expected: "claude-sonnet-4-5", + expected: "claude-opus-4-6-thinking", }, - // 3. 默认映射中的透传(映射到自己) + // 3. 默认映射 - Sonnet → Opus { - name: "默认映射透传 - claude-sonnet-4-6", + name: "默认映射 - claude-sonnet-4-6 → claude-opus-4-6-thinking", requestedModel: "claude-sonnet-4-6", accountMapping: nil, - expected: "claude-sonnet-4-6", + expected: "claude-opus-4-6-thinking", }, { - name: "默认映射透传 - claude-sonnet-4-5", + name: "默认映射 - claude-sonnet-4-5 → claude-opus-4-6-thinking", requestedModel: "claude-sonnet-4-5", accountMapping: nil, - expected: "claude-sonnet-4-5", + expected: "claude-opus-4-6-thinking", }, { name: "默认映射透传 - claude-opus-4-6-thinking", @@ -95,10 +95,10 @@ func TestAntigravityGatewayService_GetMappedModel(t *testing.T) { expected: "claude-opus-4-6-thinking", }, { - name: "默认映射透传 - claude-sonnet-4-5-thinking", + name: "默认映射 - claude-sonnet-4-5-thinking → claude-opus-4-6-thinking", requestedModel: "claude-sonnet-4-5-thinking", accountMapping: nil, - expected: "claude-sonnet-4-5-thinking", + expected: "claude-opus-4-6-thinking", }, { name: "默认映射透传 - gemini-2.5-flash", diff --git a/backend/internal/service/antigravity_rate_limit_test.go b/backend/internal/service/antigravity_rate_limit_test.go index df1ce9b90b..2eb30a5fbf 100644 --- a/backend/internal/service/antigravity_rate_limit_test.go +++ b/backend/internal/service/antigravity_rate_limit_test.go @@ -202,10 +202,11 @@ func TestHandleUpstreamError_429_NonModelRateLimit(t *testing.T) { result := svc.handleUpstreamError(context.Background(), "[test]", account, http.StatusTooManyRequests, http.Header{}, body, "claude-sonnet-4-5", 0, "", false) // handleModelRateLimit 不会处理(因为没有 RATE_LIMIT_EXCEEDED), - // 但 429 兜底逻辑会使用 requestedModel 设置模型级限流 + // 但 429 兜底逻辑会使用 requestedModel 经过映射后的最终模型 key 设置模型级限流 + // claude-sonnet-4-5 在默认映射中映射到 claude-opus-4-6-thinking require.Nil(t, result) require.Len(t, repo.modelRateLimitCalls, 1) - require.Equal(t, "claude-sonnet-4-5", repo.modelRateLimitCalls[0].modelKey) + require.Equal(t, "claude-opus-4-6-thinking", repo.modelRateLimitCalls[0].modelKey) } // TestHandleUpstreamError_429_NonModelRateLimit_UsesMappedModelKey 测试 429 非模型限流场景 @@ -822,6 +823,7 @@ func TestSetModelRateLimitByModelName_NotConvertToScope(t *testing.T) { func TestAntigravityRetryLoop_PreCheck_SwitchesWhenRateLimited(t *testing.T) { upstream := &recordingOKUpstream{} + // 限流 key 使用映射后的最终模型名:claude-sonnet-4-5 → claude-opus-4-6-thinking account := &Account{ ID: 1, Name: "acc-1", @@ -831,7 +833,7 @@ func TestAntigravityRetryLoop_PreCheck_SwitchesWhenRateLimited(t *testing.T) { Concurrency: 1, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": time.Now().Add(2 * time.Second).Format(time.RFC3339), }, }, @@ -865,6 +867,7 @@ func TestAntigravityRetryLoop_PreCheck_SwitchesWhenRateLimited(t *testing.T) { func TestAntigravityRetryLoop_PreCheck_SwitchesWhenRemainingLong(t *testing.T) { upstream := &recordingOKUpstream{} + // 限流 key 使用映射后的最终模型名:claude-sonnet-4-5 → claude-opus-4-6-thinking account := &Account{ ID: 2, Name: "acc-2", @@ -874,7 +877,7 @@ func TestAntigravityRetryLoop_PreCheck_SwitchesWhenRemainingLong(t *testing.T) { Concurrency: 1, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": time.Now().Add(11 * time.Second).Format(time.RFC3339), }, }, diff --git a/backend/internal/service/antigravity_single_account_retry_test.go b/backend/internal/service/antigravity_single_account_retry_test.go index 675e9c0cbf..d36bc12bf3 100644 --- a/backend/internal/service/antigravity_single_account_retry_test.go +++ b/backend/internal/service/antigravity_single_account_retry_test.go @@ -678,6 +678,7 @@ func TestHandleSingleAccountRetryInPlace_NetworkError_ContinuesRetry(t *testing. // 预检查中,如果有 SingleAccountRetry 标记,即使账号已限流也跳过直接发请求 func TestAntigravityRetryLoop_PreCheck_SingleAccountRetry_SkipsRateLimit(t *testing.T) { // 创建一个已设模型限流的账号 + // 限流 key 使用映射后的最终模型名:claude-sonnet-4-5 → claude-opus-4-6-thinking upstream := &recordingOKUpstream{} account := &Account{ ID: 20, @@ -689,7 +690,7 @@ func TestAntigravityRetryLoop_PreCheck_SingleAccountRetry_SkipsRateLimit(t *test Concurrency: 1, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": time.Now().Add(30 * time.Second).Format(time.RFC3339), }, }, @@ -723,6 +724,7 @@ func TestAntigravityRetryLoop_PreCheck_SingleAccountRetry_SkipsRateLimit(t *test // 对照组:无 SingleAccountRetry + 已限流 → 预检查返回 switchError func TestAntigravityRetryLoop_PreCheck_NoSingleAccountRetry_SwitchesOnRateLimit(t *testing.T) { upstream := &recordingOKUpstream{} + // 限流 key 使用映射后的最终模型名:claude-sonnet-4-5 → claude-opus-4-6-thinking account := &Account{ ID: 21, Name: "acc-rate-limited-multi", @@ -733,7 +735,7 @@ func TestAntigravityRetryLoop_PreCheck_NoSingleAccountRetry_SwitchesOnRateLimit( Concurrency: 1, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": time.Now().Add(30 * time.Second).Format(time.RFC3339), }, }, diff --git a/backend/internal/service/antigravity_thinking_test.go b/backend/internal/service/antigravity_thinking_test.go index b3952ee4a8..5a95a3a18f 100644 --- a/backend/internal/service/antigravity_thinking_test.go +++ b/backend/internal/service/antigravity_thinking_test.go @@ -13,37 +13,15 @@ func TestApplyThinkingModelSuffix(t *testing.T) { thinkingEnabled bool expected string }{ - // Thinking 未开启:保持原样 + // 函数现在直接返回原值(所有 Sonnet 已在映射层统一到 Opus) { - name: "thinking disabled - claude-sonnet-4-5 unchanged", - mappedModel: "claude-sonnet-4-5", - thinkingEnabled: false, - expected: "claude-sonnet-4-5", - }, - { - name: "thinking disabled - other model unchanged", + name: "thinking disabled - model unchanged", mappedModel: "claude-opus-4-6-thinking", thinkingEnabled: false, expected: "claude-opus-4-6-thinking", }, - - // Thinking 开启 + claude-sonnet-4-5:自动添加后缀 { - name: "thinking enabled - claude-sonnet-4-5 becomes thinking version", - mappedModel: "claude-sonnet-4-5", - thinkingEnabled: true, - expected: "claude-sonnet-4-5-thinking", - }, - - // Thinking 开启 + 其他模型:保持原样 - { - name: "thinking enabled - claude-sonnet-4-5-thinking unchanged", - mappedModel: "claude-sonnet-4-5-thinking", - thinkingEnabled: true, - expected: "claude-sonnet-4-5-thinking", - }, - { - name: "thinking enabled - claude-opus-4-6-thinking unchanged", + name: "thinking enabled - model unchanged", mappedModel: "claude-opus-4-6-thinking", thinkingEnabled: true, expected: "claude-opus-4-6-thinking", diff --git a/backend/internal/service/gateway_service_antigravity_whitelist_test.go b/backend/internal/service/gateway_service_antigravity_whitelist_test.go index c078be3264..40708b3f3e 100644 --- a/backend/internal/service/gateway_service_antigravity_whitelist_test.go +++ b/backend/internal/service/gateway_service_antigravity_whitelist_test.go @@ -103,7 +103,7 @@ func TestGatewayService_isModelSupportedByAccountWithContext_ThinkingMode(t *tes expected: false, }, // 场景 3: 配置 claude-sonnet-4-5(非 thinking),请求 claude-sonnet-4-5 + thinking=true - // 最终模型名 = claude-sonnet-4-5-thinking,不在 mapping 中,应该不匹配 + // applyThinkingModelSuffix 不再做转换,所以 finalModel == mapped,映射直接通过 { name: "thinking_enabled_no_match_non_thinking_mapping", modelMapping: map[string]any{ @@ -111,6 +111,16 @@ func TestGatewayService_isModelSupportedByAccountWithContext_ThinkingMode(t *tes }, requestedModel: "claude-sonnet-4-5", thinkingEnabled: true, + expected: true, // applyThinkingModelSuffix 不做转换,映射通过 + }, + // 场景 3b: 请求一个不在映射中的模型,thinking=true 时也不应支持 + { + name: "thinking_enabled_unmapped_model_returns_false", + modelMapping: map[string]any{ + "claude-sonnet-4-5": "claude-sonnet-4-5", + }, + requestedModel: "claude-unknown-model", + thinkingEnabled: true, expected: false, }, // 场景 4: 配置两种模型,请求 claude-sonnet-4-5 + thinking=true,应该匹配 thinking 版本 diff --git a/backend/internal/service/model_rate_limit_test.go b/backend/internal/service/model_rate_limit_test.go index b79b9688ac..da4831f452 100644 --- a/backend/internal/service/model_rate_limit_test.go +++ b/backend/internal/service/model_rate_limit_test.go @@ -181,11 +181,13 @@ func TestIsModelRateLimited_Antigravity_ThinkingAffectsModelKey(t *testing.T) { now := time.Now() future := now.Add(10 * time.Minute).Format(time.RFC3339) + // applyThinkingModelSuffix 不再做转换,claude-sonnet-4-5 在默认映射中映射到 claude-opus-4-6-thinking + // 所以限流 key 应为 claude-opus-4-6-thinking account := &Account{ Platform: PlatformAntigravity, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5-thinking": map[string]any{ + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": future, }, }, @@ -343,7 +345,8 @@ func TestGetRateLimitRemainingTime(t *testing.T) { Platform: PlatformAntigravity, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + // claude-sonnet-4-5 在默认映射中映射到 claude-opus-4-6-thinking + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": future15m, }, }, @@ -359,7 +362,8 @@ func TestGetRateLimitRemainingTime(t *testing.T) { Platform: PlatformAntigravity, Extra: map[string]any{ modelRateLimitsKey: map[string]any{ - "claude-sonnet-4-5": map[string]any{ + // claude-sonnet-4-5 在默认映射中映射到 claude-opus-4-6-thinking + "claude-opus-4-6-thinking": map[string]any{ "rate_limit_reset_at": future5m, }, }, diff --git a/backend/migrations/077_migrate_sonnet_to_opus46_thinking.sql b/backend/migrations/077_migrate_sonnet_to_opus46_thinking.sql new file mode 100644 index 0000000000..82eae70378 --- /dev/null +++ b/backend/migrations/077_migrate_sonnet_to_opus46_thinking.sql @@ -0,0 +1,51 @@ +-- Migrate all Sonnet models target to Opus 4.6-thinking +-- +-- Background: +-- We no longer use Sonnet models on Antigravity platform. +-- All Sonnet targets (claude-sonnet-4-5, claude-sonnet-4-5-thinking, claude-sonnet-4-6) +-- are replaced with claude-opus-4-6-thinking. +-- +-- Strategy: +-- Directly overwrite the entire model_mapping with updated mappings +-- This ensures consistency with DefaultAntigravityModelMapping in constants.go + +UPDATE accounts +SET credentials = jsonb_set( + credentials, + '{model_mapping}', + '{ + "claude-opus-4-6-thinking": "claude-opus-4-6-thinking", + "claude-opus-4-6": "claude-opus-4-6-thinking", + "claude-opus-4-5-thinking": "claude-opus-4-6-thinking", + "claude-opus-4-5-20251101": "claude-opus-4-6-thinking", + "claude-sonnet-4-6": "claude-opus-4-6-thinking", + "claude-sonnet-4-5": "claude-opus-4-6-thinking", + "claude-sonnet-4-5-thinking": "claude-opus-4-6-thinking", + "claude-sonnet-4-5-20250929": "claude-opus-4-6-thinking", + "claude-haiku-4-5": "claude-opus-4-6-thinking", + "claude-haiku-4-5-20251001": "claude-opus-4-6-thinking", + "gemini-2.5-flash": "gemini-2.5-flash", + "gemini-2.5-flash-image": "gemini-2.5-flash-image", + "gemini-2.5-flash-image-preview": "gemini-2.5-flash-image", + "gemini-2.5-flash-lite": "gemini-2.5-flash-lite", + "gemini-2.5-flash-thinking": "gemini-2.5-flash-thinking", + "gemini-2.5-pro": "gemini-2.5-pro", + "gemini-3-flash": "gemini-3-flash", + "gemini-3-pro-high": "gemini-3-pro-high", + "gemini-3-pro-low": "gemini-3-pro-low", + "gemini-3-flash-preview": "gemini-3-flash", + "gemini-3-pro-preview": "gemini-3-pro-high", + "gemini-3.1-pro-high": "gemini-3.1-pro-high", + "gemini-3.1-pro-low": "gemini-3.1-pro-low", + "gemini-3.1-pro-preview": "gemini-3.1-pro-high", + "gemini-3.1-flash-image": "gemini-3.1-flash-image", + "gemini-3.1-flash-image-preview": "gemini-3.1-flash-image", + "gemini-3-pro-image": "gemini-3.1-flash-image", + "gemini-3-pro-image-preview": "gemini-3.1-flash-image", + "gpt-oss-120b-medium": "gpt-oss-120b-medium", + "tab_flash_lite_preview": "tab_flash_lite_preview" + }'::jsonb +) +WHERE platform = 'antigravity' + AND deleted_at IS NULL + AND credentials->'model_mapping' IS NOT NULL; diff --git a/frontend/src/composables/useModelWhitelist.ts b/frontend/src/composables/useModelWhitelist.ts index 0ff288bb79..3fabedbf05 100644 --- a/frontend/src/composables/useModelWhitelist.ts +++ b/frontend/src/composables/useModelWhitelist.ts @@ -301,14 +301,11 @@ const geminiPresetMappings = [ // Antigravity 预设映射(支持通配符) const antigravityPresetMappings = [ - // Claude 通配符映射 - { label: 'Claude→Sonnet', from: 'claude-*', to: 'claude-sonnet-4-5', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' }, - { label: 'Sonnet→Sonnet', from: 'claude-sonnet-*', to: 'claude-sonnet-4-5', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' }, + // Claude 通配符映射(所有 Claude 统一指向 Opus) + { label: 'Claude→Opus', from: 'claude-*', to: 'claude-opus-4-6-thinking', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' }, + { label: 'Sonnet→Opus', from: 'claude-sonnet-*', to: 'claude-opus-4-6-thinking', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' }, { label: 'Opus→Opus', from: 'claude-opus-*', to: 'claude-opus-4-6-thinking', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' }, - { label: 'Haiku→Sonnet', from: 'claude-haiku-*', to: 'claude-sonnet-4-5', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' }, - { label: 'Sonnet4→4.6', from: 'claude-sonnet-4-20250514', to: 'claude-sonnet-4-6', color: 'bg-sky-100 text-sky-700 hover:bg-sky-200 dark:bg-sky-900/30 dark:text-sky-400' }, - { label: 'Sonnet4.5→4.6', from: 'claude-sonnet-4-5-20250929', to: 'claude-sonnet-4-6', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }, - { label: 'Sonnet3.5→4.6', from: 'claude-3-5-sonnet-20241022', to: 'claude-sonnet-4-6', color: 'bg-teal-100 text-teal-700 hover:bg-teal-200 dark:bg-teal-900/30 dark:text-teal-400' }, + { label: 'Haiku→Opus', from: 'claude-haiku-*', to: 'claude-opus-4-6-thinking', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' }, { label: 'Opus4.5→4.6', from: 'claude-opus-4-5-20251101', to: 'claude-opus-4-6-thinking', color: 'bg-violet-100 text-violet-700 hover:bg-violet-200 dark:bg-violet-900/30 dark:text-violet-400' }, // Gemini 3→3.1 映射 { label: '3-Pro-Preview→3.1-Pro-High', from: 'gemini-3-pro-preview', to: 'gemini-3.1-pro-high', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }, @@ -325,8 +322,8 @@ const antigravityPresetMappings = [ { label: '3-Flash透传', from: 'gemini-3-flash', to: 'gemini-3-flash', color: 'bg-lime-100 text-lime-700 hover:bg-lime-200 dark:bg-lime-900/30 dark:text-lime-400' }, { label: '2.5-Flash-Lite透传', from: 'gemini-2.5-flash-lite', to: 'gemini-2.5-flash-lite', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' }, // 精确映射 - { label: 'Sonnet 4.6', from: 'claude-sonnet-4-6', to: 'claude-sonnet-4-6', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }, - { label: 'Sonnet 4.5', from: 'claude-sonnet-4-5', to: 'claude-sonnet-4-5', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }, + { label: 'Sonnet 4.6→Opus', from: 'claude-sonnet-4-6', to: 'claude-opus-4-6-thinking', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }, + { label: 'Sonnet 4.5→Opus', from: 'claude-sonnet-4-5', to: 'claude-opus-4-6-thinking', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }, { label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'claude-opus-4-6-thinking', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' }, { label: 'Opus 4.6-thinking', from: 'claude-opus-4-6-thinking', to: 'claude-opus-4-6-thinking', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' } ]