feat: 适配 claude-opus-4-8

This commit is contained in:
shaw 2026-05-29 09:56:48 +08:00
parent 37044b83eb
commit 514ac5c6a1
13 changed files with 99 additions and 2 deletions

View File

@ -72,6 +72,7 @@ const (
// 与前端 useModelWhitelist.ts 中的 antigravityDefaultMappings 保持一致
var DefaultAntigravityModelMapping = map[string]string{
// Claude 白名单
"claude-opus-4-8": "claude-opus-4-8", // 官方模型
"claude-opus-4-7": "claude-opus-4-7", // 官方模型
"claude-opus-4-6-thinking": "claude-opus-4-6-thinking", // 官方模型
"claude-opus-4-6": "claude-opus-4-6-thinking", // 简称映射
@ -122,6 +123,7 @@ var DefaultAntigravityModelMapping = map[string]string{
// aws_region 自动调整为匹配的区域前缀(如 eu.、apac.、jp. 等)
var DefaultBedrockModelMapping = map[string]string{
// Claude Opus
"claude-opus-4-8": "us.anthropic.claude-opus-4-8-v1",
"claude-opus-4-7": "us.anthropic.claude-opus-4-7-v1",
"claude-opus-4-6-thinking": "us.anthropic.claude-opus-4-6-v1",
"claude-opus-4-6": "us.anthropic.claude-opus-4-6-v1",

View File

@ -24,3 +24,27 @@ func TestDefaultAntigravityModelMapping_ImageCompatibilityAliases(t *testing.T)
}
}
}
func TestDefaultAntigravityModelMapping_ContainsOpus48(t *testing.T) {
t.Parallel()
got, ok := DefaultAntigravityModelMapping["claude-opus-4-8"]
if !ok {
t.Fatal("expected mapping for claude-opus-4-8 to exist")
}
if got != "claude-opus-4-8" {
t.Fatalf("unexpected claude-opus-4-8 mapping: got %q", got)
}
}
func TestDefaultBedrockModelMapping_ContainsOpus48(t *testing.T) {
t.Parallel()
got, ok := DefaultBedrockModelMapping["claude-opus-4-8"]
if !ok {
t.Fatal("expected Bedrock mapping for claude-opus-4-8 to exist")
}
if got != "us.anthropic.claude-opus-4-8-v1" {
t.Fatalf("unexpected Bedrock claude-opus-4-8 mapping: got %q", got)
}
}

View File

@ -155,6 +155,7 @@ var claudeModels = []modelDef{
{ID: "claude-opus-4-6", DisplayName: "Claude Opus 4.6", CreatedAt: "2026-02-05T00:00:00Z"},
{ID: "claude-opus-4-6-thinking", DisplayName: "Claude Opus 4.6 Thinking", CreatedAt: "2026-02-05T00:00:00Z"},
{ID: "claude-opus-4-7", DisplayName: "Claude Opus 4.7", CreatedAt: "2026-04-17T00:00:00Z"},
{ID: "claude-opus-4-8", DisplayName: "Claude Opus 4.8", CreatedAt: "2026-05-29T00:00:00Z"},
{ID: "claude-sonnet-4-6", DisplayName: "Claude Sonnet 4.6", CreatedAt: "2026-02-17T00:00:00Z"},
}

View File

@ -12,6 +12,7 @@ func TestDefaultModels_ContainsNewAndLegacyImageModels(t *testing.T) {
}
requiredIDs := []string{
"claude-opus-4-8",
"claude-opus-4-6-thinking",
"gemini-2.5-flash-image",
"gemini-2.5-flash-image-preview",

View File

@ -204,6 +204,8 @@ type modelInfo struct {
// 只有在此映射表中的模型才会注入身份提示词
// 注意:模型映射逻辑在网关层完成;这里仅用于按模型前缀判断是否注入身份提示词。
var modelInfoMap = map[string]modelInfo{
"claude-opus-4-8": {DisplayName: "Claude Opus 4.8", CanonicalID: "claude-opus-4-8"},
"claude-opus-4-7": {DisplayName: "Claude Opus 4.7", CanonicalID: "claude-opus-4-7"},
"claude-opus-4-5": {DisplayName: "Claude Opus 4.5", CanonicalID: "claude-opus-4-5-20250929"},
"claude-opus-4-6": {DisplayName: "Claude Opus 4.6", CanonicalID: "claude-opus-4-6"},
"claude-sonnet-4-6": {DisplayName: "Claude Sonnet 4.6", CanonicalID: "claude-sonnet-4-6"},
@ -587,7 +589,8 @@ func maxOutputTokensLimit(model string) int {
func isAntigravityOpusHighTierModel(model string) bool {
lower := strings.ToLower(model)
return strings.HasPrefix(lower, "claude-opus-4-6") ||
strings.HasPrefix(lower, "claude-opus-4-7")
strings.HasPrefix(lower, "claude-opus-4-7") ||
strings.HasPrefix(lower, "claude-opus-4-8")
}
func buildGenerationConfig(req *ClaudeRequest) *GeminiGenerationConfig {

View File

@ -134,6 +134,12 @@ var DefaultModels = []Model{
DisplayName: "Claude Opus 4.7",
CreatedAt: "2026-04-17T00:00:00Z",
},
{
ID: "claude-opus-4-8",
Type: "model",
DisplayName: "Claude Opus 4.8",
CreatedAt: "2026-05-29T00:00:00Z",
},
{
ID: "claude-sonnet-4-6",
Type: "model",

View File

@ -88,6 +88,18 @@ func TestAntigravityGatewayService_GetMappedModel(t *testing.T) {
accountMapping: nil,
expected: "claude-sonnet-4-5",
},
{
name: "默认映射透传 - claude-opus-4-8",
requestedModel: "claude-opus-4-8",
accountMapping: nil,
expected: "claude-opus-4-8",
},
{
name: "默认映射透传 - claude-opus-4-7",
requestedModel: "claude-opus-4-7",
accountMapping: nil,
expected: "claude-opus-4-7",
},
{
name: "默认映射透传 - claude-opus-4-6-thinking",
requestedModel: "claude-opus-4-6-thinking",
@ -210,6 +222,7 @@ func TestAntigravityGatewayService_IsModelSupported(t *testing.T) {
{"直接支持 - gemini-3-flash", "gemini-3-flash", true},
// 可映射(有明确前缀映射)
{"可映射 - claude-opus-4-8", "claude-opus-4-8", true},
{"可映射 - claude-opus-4-6", "claude-opus-4-6", true},
// 前缀透传claude 和 gemini 前缀)

View File

@ -174,6 +174,7 @@ func TestIsBedrockClaude45OrNewer(t *testing.T) {
expect bool
}{
{"us.anthropic.claude-opus-4-6-v1", true},
{"us.anthropic.claude-opus-4-8-v1", true},
{"us.anthropic.claude-sonnet-4-6", true},
{"us.anthropic.claude-sonnet-4-5-20250929-v1:0", true},
{"us.anthropic.claude-opus-4-5-20251101-v1:0", true},
@ -511,6 +512,20 @@ func TestResolveBedrockModelID(t *testing.T) {
assert.Equal(t, "au.anthropic.claude-opus-4-6-v1", modelID)
})
t.Run("default opus 4.8 mapping uses regional Bedrock model id", func(t *testing.T) {
account := &Account{
Platform: PlatformAnthropic,
Type: AccountTypeBedrock,
Credentials: map[string]any{
"aws_region": "eu-west-1",
},
}
modelID, ok := ResolveBedrockModelID(account, "claude-opus-4-8")
require.True(t, ok)
assert.Equal(t, "eu.anthropic.claude-opus-4-8-v1", modelID)
})
t.Run("force global rewrites anthropic regional model id", func(t *testing.T) {
account := &Account{
Platform: PlatformAnthropic,
@ -714,6 +729,7 @@ func TestIsBedrockOpus47OrNewer(t *testing.T) {
modelID string
expect bool
}{
{"us.anthropic.claude-opus-4-8-v1", true},
{"us.anthropic.claude-opus-4-7-v1", true},
{"us.anthropic.claude-opus-4-6-v1", false},
{"us.anthropic.claude-opus-4-5-20251101-v1:0", false},
@ -886,10 +902,12 @@ func TestIsBedrockOpus47OrNewer_EdgeCases(t *testing.T) {
modelID string
expect bool
}{
{"anthropic.claude-opus-4-8-v1", true},
{"anthropic.claude-opus-4-7-v1", true},
{"us.anthropic.claude-opus-4-7-20270101-v1:0", true},
{"", false},
// Forward() passes parsed.Model (standard names), not Bedrock IDs
{"claude-opus-4-8", true},
{"claude-opus-4-7", true},
{"claude-opus-4-6", false},
{"claude-sonnet-4-7", false},

View File

@ -0,0 +1,16 @@
-- 为已持久化的 Antigravity model_mapping 添加 claude-opus-4-8。
--
-- 未持久化 model_mapping 的账号会直接使用 DefaultAntigravityModelMapping
-- 因此这里只需要回填已有映射对象。
UPDATE accounts
SET credentials = jsonb_set(
credentials,
'{model_mapping,claude-opus-4-8}',
'"claude-opus-4-8"'::jsonb,
true
)
WHERE platform = 'antigravity'
AND deleted_at IS NULL
AND jsonb_typeof(credentials->'model_mapping') = 'object'
AND credentials->'model_mapping'->>'claude-opus-4-8' IS NULL;

View File

@ -222,6 +222,8 @@ const formatScopeName = (scope: string): string => {
// Claude
'claude-opus-4-6': 'COpus46',
'claude-opus-4-6-thinking': 'COpus46T',
'claude-opus-4-7': 'COpus47',
'claude-opus-4-8': 'COpus48',
'claude-sonnet-4-6': 'CSon46',
'claude-sonnet-4-5': 'CSon45',
'claude-sonnet-4-5-thinking': 'CSon45T',

View File

@ -664,6 +664,7 @@ const antigravityClaudeUsageFromAPI = computed(() =>
getAntigravityUsageFromAPI([
'claude-sonnet-4-5', 'claude-opus-4-5-thinking',
'claude-sonnet-4-6', 'claude-opus-4-6', 'claude-opus-4-6-thinking',
'claude-opus-4-7', 'claude-opus-4-8',
])
)

View File

@ -35,6 +35,11 @@ describe('useModelWhitelist', () => {
expect(models).toContain('gemini-3-pro-image')
})
it('Claude 模型列表包含 Opus 4.8', () => {
expect(getModelsByPlatform('claude')).toContain('claude-opus-4-8')
expect(getModelsByPlatform('antigravity')).toContain('claude-opus-4-8')
})
it('gemini 模型列表包含原生生图模型', () => {
const models = getModelsByPlatform('gemini')

View File

@ -29,6 +29,7 @@ export const claudeModels = [
'claude-opus-4-5-20251101',
'claude-opus-4-6',
'claude-opus-4-7',
'claude-opus-4-8',
'claude-sonnet-4-6'
]
@ -53,6 +54,7 @@ const antigravityModels = [
'claude-opus-4-6',
'claude-opus-4-6-thinking',
'claude-opus-4-7',
'claude-opus-4-8',
'claude-opus-4-5-thinking',
'claude-sonnet-4-6',
'claude-sonnet-4-5',
@ -238,6 +240,7 @@ const anthropicPresetMappings = [
{ label: 'Opus 4.5', from: 'claude-opus-4-5-20251101', to: 'claude-opus-4-5-20251101', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'claude-opus-4-6', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'claude-opus-4-7', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ label: 'Opus 4.8', from: 'claude-opus-4-8', to: 'claude-opus-4-8', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ label: 'Haiku 3.5', from: 'claude-3-5-haiku-20241022', to: 'claude-3-5-haiku-20241022', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' },
{ label: 'Haiku 4.5', from: 'claude-haiku-4-5-20251001', to: 'claude-haiku-4-5-20251001', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' },
{ label: 'Opus->Sonnet', from: 'claude-opus-4-6', to: 'claude-sonnet-4-5-20250929', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }
@ -297,13 +300,15 @@ const antigravityPresetMappings = [
{ 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: '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' },
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'claude-opus-4-7', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' }
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'claude-opus-4-7', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
{ label: 'Opus 4.8', from: 'claude-opus-4-8', to: 'claude-opus-4-8', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' }
]
// Bedrock 预设映射(与后端 DefaultBedrockModelMapping 保持一致)
const bedrockPresetMappings = [
{ label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'us.anthropic.claude-opus-4-6-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'us.anthropic.claude-opus-4-7-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
{ label: 'Opus 4.8', from: 'claude-opus-4-8', to: 'us.anthropic.claude-opus-4-8-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
{ label: 'Sonnet 4.6', from: 'claude-sonnet-4-6', to: 'us.anthropic.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: 'Opus 4.5', from: 'claude-opus-4-5-thinking', to: 'us.anthropic.claude-opus-4-5-20251101-v1:0', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
{ label: 'Sonnet 4.5', from: 'claude-sonnet-4-5', to: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' },