mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
feat(antigravity): support Gemini 3.8 Flash models
Google released Gemini 3.8 Flash on 2026-09-02. Register the model and its Antigravity thinking tiers the same way Gemini 3.6 Flash is registered: - DefaultAntigravityModelMapping: bare id plus -high/-low/-medium/-tiered - geminiModels catalog exposed through /antigravity/v1/models - ensureAntigravityDefaultPassthroughs so accounts with a custom model_mapping still resolve the new ids - fallback pricing and the thinking-tier alias normalizer, so the tiers share the base model price card instead of failing pricing lookup Fallback rates are Google's published prices: $0.75 input / $3.75 output / $0.075 cached input per MTok. These are promotional through 2026-12-31 and double on 2027-01-01; the constant carries a note to that effect. Refs #6523
This commit is contained in:
@@ -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.8 Flash tiered models
|
||||
"gemini-3.8-flash": "gemini-3.8-flash",
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-low": "gemini-3.8-flash-low",
|
||||
"gemini-3.8-flash-medium": "gemini-3.8-flash-medium",
|
||||
"gemini-3.8-flash-tiered": "gemini-3.8-flash-tiered",
|
||||
// Gemini 3 image 兼容映射(向 3.1 image 迁移)
|
||||
"gemini-3-pro-image": "gemini-3.1-flash-image",
|
||||
"gemini-3-pro-image-preview": "gemini-3.1-flash-image",
|
||||
|
||||
@@ -89,6 +89,14 @@ func TestDefaultAntigravityModelMapping_Gemini36FlashModels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("expected %s to map to itself, got %q", model, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultBedrockModelMapping_ContainsNewClaudeModels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -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.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},
|
||||
{ID: "gemini-3.8-flash-medium", DisplayName: "Gemini 3.8 Flash Medium", CreatedAt: "2026-09-02T00:00:00Z", IsReasoning: true},
|
||||
{ID: "gemini-3.8-flash-tiered", DisplayName: "Gemini 3.8 Flash", CreatedAt: "2026-09-02T00:00:00Z", IsReasoning: true},
|
||||
{ID: "gemini-3-pro-preview", DisplayName: "Gemini 3 Pro Preview", CreatedAt: "2025-06-01T00:00:00Z", IsReasoning: true},
|
||||
{ID: "gemini-3-pro-image", DisplayName: "Gemini 3 Pro Image", CreatedAt: "2025-06-01T00:00:00Z"},
|
||||
}
|
||||
|
||||
@@ -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.8-flash",
|
||||
"gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-low",
|
||||
"gemini-3.8-flash-medium",
|
||||
"gemini-3.8-flash-tiered",
|
||||
}
|
||||
|
||||
for _, id := range requiredIDs {
|
||||
|
||||
@@ -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.8-flash",
|
||||
"gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-low",
|
||||
"gemini-3.8-flash-medium",
|
||||
"gemini-3.8-flash-tiered",
|
||||
})
|
||||
applyAntigravityGemini31ProAliases(result)
|
||||
}
|
||||
|
||||
@@ -400,6 +400,18 @@ func (s *BillingService) initFallbackPricing() {
|
||||
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
|
||||
// -high/-low/-medium/-tiered aliases are matched below so unavailable
|
||||
// remote pricing never records token-bearing requests at $0.
|
||||
s.fallbackPrices["gemini-3.8-flash"] = &ModelPricing{
|
||||
InputPricePerToken: 0.75e-6,
|
||||
OutputPricePerToken: 3.75e-6,
|
||||
CacheReadPricePerToken: 0.075e-6,
|
||||
SupportsCacheBreakdown: false,
|
||||
}
|
||||
|
||||
// OpenAI GPT-5.4(业务指定价格)
|
||||
s.fallbackPrices["gpt-5.4"] = &ModelPricing{
|
||||
InputPricePerToken: 2.5e-6, // $2.5 per MTok
|
||||
@@ -851,6 +863,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.8-flash") || strings.Contains(modelLower, "gemini-3-8-flash") {
|
||||
return s.fallbackPrices["gemini-3.8-flash"]
|
||||
}
|
||||
|
||||
// DeepSeek 系列:官方模型 V4 Pro/Flash(含 vision-exp)按各自价卡;
|
||||
// 其余 deepseek-*(含已停服的 deepseek-chat / deepseek-reasoner 与未知型号)
|
||||
|
||||
@@ -1111,15 +1111,16 @@ func normalizeModelNameForPricing(model string) string {
|
||||
return normalizeGeminiThinkingTierAlias(model)
|
||||
}
|
||||
|
||||
// normalizeGeminiThinkingTierAlias maps Antigravity's Gemini 3.6 Flash
|
||||
// normalizeGeminiThinkingTierAlias maps Antigravity's Gemini Flash
|
||||
// thinking-tier model IDs to the public base model. The tier controls reasoning
|
||||
// behavior, not the published token rate, so this keeps -high/-low/-medium and
|
||||
// -tiered requests on the same price card as gemini-3.6-flash.
|
||||
// -tiered requests on the same price card as their base model.
|
||||
func normalizeGeminiThinkingTierAlias(model string) string {
|
||||
const baseModel = "gemini-3.6-flash"
|
||||
for _, tier := range []string{"-high", "-low", "-medium", "-tiered"} {
|
||||
if model == baseModel+tier {
|
||||
return baseModel
|
||||
for _, baseModel := range []string{"gemini-3.6-flash", "gemini-3.8-flash"} {
|
||||
for _, tier := range []string{"-high", "-low", "-medium", "-tiered"} {
|
||||
if model == baseModel+tier {
|
||||
return baseModel
|
||||
}
|
||||
}
|
||||
}
|
||||
return model
|
||||
|
||||
@@ -533,6 +533,51 @@ func TestBillingService_Gemini36FlashThinkingTierFallbacksAreBillable(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestPricingService_Gemini38FlashThinkingTiersUseBasePricing(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.8-flash": basePricing,
|
||||
}}
|
||||
|
||||
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",
|
||||
} {
|
||||
t.Run(model, func(t *testing.T) {
|
||||
require.Same(t, basePricing, svc.GetModelPricing(model))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBillingService_Gemini38FlashThinkingTierFallbacksAreBillable(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.8-flash",
|
||||
"gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-low",
|
||||
"gemini-3.8-flash-medium",
|
||||
"gemini-3.8-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 TestDefaultPricingIncludesGemini36FlashRates(t *testing.T) {
|
||||
data, err := os.ReadFile(filepath.Join("..", "..", "resources", "model-pricing", "model_prices_and_context_window.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user