feat(antigravity): support Gemini 3.7 Flash models

Google released Gemini 3.7 Flash on 2026-08-13. Register it alongside 3.8
using the same shape as the existing 3.6 entries: bare id plus the
Antigravity -high/-low/-medium/-tiered thinking tiers, the model catalog,
the custom-mapping passthroughs, fallback pricing and the tier alias
normalizer.

Pricing matches 3.8 at $0.75 input / $3.75 output / $0.075 cached input
per MTok. Both are promotional through 2026-12-31 and double on
2027-01-01; the constants note this.

Also corrects the 3.8 catalog timestamps to the documented GA dates.
This commit is contained in:
doublepi123
2026-09-04 16:04:47 +08:00
parent 27bcec7645
commit 8ed57b000c
8 changed files with 90 additions and 1 deletions
+6
View File
@@ -145,6 +145,12 @@ var DefaultAntigravityModelMapping = map[string]string{
"gemini-3.6-flash-low": "gemini-3.6-flash-low",
"gemini-3.6-flash-medium": "gemini-3.6-flash-medium",
"gemini-3.6-flash-tiered": "gemini-3.6-flash-tiered",
// Gemini 3.7 Flash tiered models
"gemini-3.7-flash": "gemini-3.7-flash",
"gemini-3.7-flash-high": "gemini-3.7-flash-high",
"gemini-3.7-flash-low": "gemini-3.7-flash-low",
"gemini-3.7-flash-medium": "gemini-3.7-flash-medium",
"gemini-3.7-flash-tiered": "gemini-3.7-flash-tiered",
// Gemini 3.8 Flash tiered models
"gemini-3.8-flash": "gemini-3.8-flash",
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
@@ -89,6 +89,14 @@ func TestDefaultAntigravityModelMapping_Gemini36FlashModels(t *testing.T) {
}
}
func TestDefaultAntigravityModelMapping_Gemini37FlashModels(t *testing.T) {
for _, model := range []string{"gemini-3.7-flash", "gemini-3.7-flash-high", "gemini-3.7-flash-low", "gemini-3.7-flash-medium", "gemini-3.7-flash-tiered"} {
if got := DefaultAntigravityModelMapping[model]; got != model {
t.Fatalf("expected %s to map to itself, got %q", model, got)
}
}
}
func TestDefaultAntigravityModelMapping_Gemini38FlashModels(t *testing.T) {
for _, model := range []string{"gemini-3.8-flash", "gemini-3.8-flash-high", "gemini-3.8-flash-low", "gemini-3.8-flash-medium", "gemini-3.8-flash-tiered"} {
if got := DefaultAntigravityModelMapping[model]; got != model {
@@ -184,6 +184,11 @@ var geminiModels = []modelDef{
{ID: "gemini-3.6-flash-low", DisplayName: "Gemini 3.6 Flash Low", CreatedAt: "2026-07-21T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.6-flash-medium", DisplayName: "Gemini 3.6 Flash Medium", CreatedAt: "2026-07-21T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.6-flash-tiered", DisplayName: "Gemini 3.6 Flash", CreatedAt: "2026-07-21T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.7-flash", DisplayName: "Gemini 3.7 Flash", CreatedAt: "2026-08-13T00:00:00Z"},
{ID: "gemini-3.7-flash-high", DisplayName: "Gemini 3.7 Flash High", CreatedAt: "2026-08-13T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.7-flash-low", DisplayName: "Gemini 3.7 Flash Low", CreatedAt: "2026-08-13T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.7-flash-medium", DisplayName: "Gemini 3.7 Flash Medium", CreatedAt: "2026-08-13T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.7-flash-tiered", DisplayName: "Gemini 3.7 Flash", CreatedAt: "2026-08-13T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.8-flash", DisplayName: "Gemini 3.8 Flash", CreatedAt: "2026-09-02T00:00:00Z"},
{ID: "gemini-3.8-flash-high", DisplayName: "Gemini 3.8 Flash High", CreatedAt: "2026-09-02T00:00:00Z", IsReasoning: true},
{ID: "gemini-3.8-flash-low", DisplayName: "Gemini 3.8 Flash Low", CreatedAt: "2026-09-02T00:00:00Z", IsReasoning: true},
@@ -26,6 +26,11 @@ func TestDefaultModels_ContainsNewAndLegacyImageModels(t *testing.T) {
"gemini-3.6-flash-low",
"gemini-3.6-flash-medium",
"gemini-3.6-flash-tiered",
"gemini-3.7-flash",
"gemini-3.7-flash-high",
"gemini-3.7-flash-low",
"gemini-3.7-flash-medium",
"gemini-3.7-flash-tiered",
"gemini-3.8-flash",
"gemini-3.8-flash-high",
"gemini-3.8-flash-low",
+5
View File
@@ -662,6 +662,11 @@ func (a *Account) resolveModelMapping(rawMapping map[string]any) map[string]stri
"gemini-3.6-flash-low",
"gemini-3.6-flash-medium",
"gemini-3.6-flash-tiered",
"gemini-3.7-flash",
"gemini-3.7-flash-high",
"gemini-3.7-flash-low",
"gemini-3.7-flash-medium",
"gemini-3.7-flash-tiered",
"gemini-3.8-flash",
"gemini-3.8-flash-high",
"gemini-3.8-flash-low",
@@ -400,6 +400,18 @@ func (s *BillingService) initFallbackPricing() {
SupportsCacheBreakdown: false,
}
// Gemini 3.7 Flash (Google AI pricing: $0.75 input / $3.75 output /
// $0.075 cached input per MTok, promotional through 2026-12-31; official
// rates double to $1.50/$7.50/$0.15 from 2027-01-01). Antigravity's
// -high/-low/-medium/-tiered aliases are matched below so unavailable
// remote pricing never records token-bearing requests at $0.
s.fallbackPrices["gemini-3.7-flash"] = &ModelPricing{
InputPricePerToken: 0.75e-6,
OutputPricePerToken: 3.75e-6,
CacheReadPricePerToken: 0.075e-6,
SupportsCacheBreakdown: false,
}
// Gemini 3.8 Flash (Google AI pricing: $0.75 input / $3.75 output /
// $0.075 cached input per MTok, promotional through 2026-12-31; official
// rates double to $1.50/$7.50/$0.15 from 2027-01-01). Antigravity's
@@ -863,6 +875,9 @@ func (s *BillingService) getFallbackPricing(model string) *ModelPricing {
if strings.Contains(modelLower, "gemini-3.6-flash") || strings.Contains(modelLower, "gemini-3-6-flash") {
return s.fallbackPrices["gemini-3.6-flash"]
}
if strings.Contains(modelLower, "gemini-3.7-flash") || strings.Contains(modelLower, "gemini-3-7-flash") {
return s.fallbackPrices["gemini-3.7-flash"]
}
if strings.Contains(modelLower, "gemini-3.8-flash") || strings.Contains(modelLower, "gemini-3-8-flash") {
return s.fallbackPrices["gemini-3.8-flash"]
}
+1 -1
View File
@@ -1116,7 +1116,7 @@ func normalizeModelNameForPricing(model string) string {
// behavior, not the published token rate, so this keeps -high/-low/-medium and
// -tiered requests on the same price card as their base model.
func normalizeGeminiThinkingTierAlias(model string) string {
for _, baseModel := range []string{"gemini-3.6-flash", "gemini-3.8-flash"} {
for _, baseModel := range []string{"gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash"} {
for _, tier := range []string{"-high", "-low", "-medium", "-tiered"} {
if model == baseModel+tier {
return baseModel
@@ -533,6 +533,51 @@ func TestBillingService_Gemini36FlashThinkingTierFallbacksAreBillable(t *testing
}
}
func TestPricingService_Gemini37FlashThinkingTiersUseBasePricing(t *testing.T) {
basePricing := &LiteLLMModelPricing{
InputCostPerToken: 0.75e-6,
OutputCostPerToken: 3.75e-6,
CacheReadInputTokenCost: 0.075e-6,
}
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
"gemini-3.7-flash": basePricing,
}}
for _, model := range []string{
"gemini-3.7-flash",
"gemini-3.7-flash-high",
"gemini-3.7-flash-low",
"gemini-3.7-flash-medium",
"gemini-3.7-flash-tiered",
} {
t.Run(model, func(t *testing.T) {
require.Same(t, basePricing, svc.GetModelPricing(model))
})
}
}
func TestBillingService_Gemini37FlashThinkingTierFallbacksAreBillable(t *testing.T) {
svc := NewBillingService(&config.Config{}, nil)
tokens := UsageTokens{InputTokens: 1_000_000, OutputTokens: 1_000_000, CacheReadTokens: 1_000_000}
for _, model := range []string{
"gemini-3.7-flash",
"gemini-3.7-flash-high",
"gemini-3.7-flash-low",
"gemini-3.7-flash-medium",
"gemini-3.7-flash-tiered",
} {
t.Run(model, func(t *testing.T) {
cost, err := svc.CalculateCost(model, tokens, 1)
require.NoError(t, err)
require.InDelta(t, 0.75, cost.InputCost, 1e-12)
require.InDelta(t, 3.75, cost.OutputCost, 1e-12)
require.InDelta(t, 0.075, cost.CacheReadCost, 1e-12)
require.InDelta(t, 4.575, cost.TotalCost, 1e-12)
})
}
}
func TestPricingService_Gemini38FlashThinkingTiersUseBasePricing(t *testing.T) {
basePricing := &LiteLLMModelPricing{
InputCostPerToken: 0.75e-6,