mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-30 00:21:34 +00:00
fix(billing): resolve Gemini 3.7/3.8 Flash thinking-tier prices
This commit is contained in:
@@ -194,6 +194,42 @@ func TestGatewayServiceRecordUsage_PreservesRequestedAndUpstreamModels(t *testin
|
||||
require.Equal(t, mappedModel, *usageRepo.lastLog.UpstreamModel)
|
||||
}
|
||||
|
||||
func TestGatewayServiceRecordUsage_GeminiFlashThinkingTierUsesCatalogPrice(t *testing.T) {
|
||||
for _, baseModel := range []string{"gemini-3.7-flash", "gemini-3.8-flash"} {
|
||||
t.Run(baseModel, func(t *testing.T) {
|
||||
usageRepo := &openAIRecordUsageLogRepoStub{inserted: true}
|
||||
userRepo := &openAIRecordUsageUserRepoStub{}
|
||||
svc := newGatewayRecordUsageServiceForTest(usageRepo, userRepo, &openAIRecordUsageSubRepoStub{})
|
||||
svc.billingService = NewBillingService(svc.cfg, &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
baseModel: {InputCostPerToken: 0.75e-6, OutputCostPerToken: 3.75e-6, CacheReadInputTokenCost: 0.075e-6},
|
||||
}})
|
||||
svc.resolver = NewModelPricingResolver(nil, svc.billingService)
|
||||
group := &Group{ID: 27, Platform: PlatformGemini, RateMultiplier: 0.15}
|
||||
model := baseModel + "-medium"
|
||||
|
||||
err := svc.RecordUsage(context.Background(), &RecordUsageInput{
|
||||
Result: &ForwardResult{
|
||||
RequestID: "gemini_thinking_tier",
|
||||
Model: model,
|
||||
UpstreamModel: model,
|
||||
Usage: ClaudeUsage{InputTokens: 8498, OutputTokens: 469, CacheReadInputTokens: 159248},
|
||||
Duration: time.Second,
|
||||
},
|
||||
APIKey: &APIKey{ID: 501, GroupID: &group.ID, Group: group},
|
||||
User: &User{ID: 601},
|
||||
Account: &Account{ID: 701, Platform: PlatformGemini, Type: AccountTypeAPIKey},
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, usageRepo.lastLog)
|
||||
require.Equal(t, model, usageRepo.lastLog.Model)
|
||||
require.InDelta(t, 0.02007585, usageRepo.lastLog.TotalCost, 1e-12)
|
||||
require.InDelta(t, 0.0030113775, usageRepo.lastLog.ActualCost, 1e-12)
|
||||
require.InDelta(t, 0.0030113775, userRepo.lastAmount, 1e-12)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayServiceRecordUsage_PreservesChannelMappedUpstreamModel(t *testing.T) {
|
||||
usageRepo := &openAIRecordUsageLogRepoStub{inserted: true}
|
||||
svc := newGatewayRecordUsageServiceForTest(usageRepo, &openAIRecordUsageUserRepoStub{}, &openAIRecordUsageSubRepoStub{})
|
||||
|
||||
@@ -1190,7 +1190,7 @@ func (s *PricingService) buildModelLookupCandidates(modelLower string) []string
|
||||
normalized := normalizeModelNameForPricing(modelLower)
|
||||
|
||||
// A tier-specific entry should take precedence when the pricing catalog gains
|
||||
// one later. Today Antigravity's Gemini 3.6 Flash tiers share the base rate,
|
||||
// one later. Antigravity's Gemini Flash thinking tiers share the base rate,
|
||||
// so the normalized base remains the fallback after the exact aliases.
|
||||
candidates := rawCandidates
|
||||
if normalizeGeminiThinkingTierAlias(lastSegment(modelLower)) != lastSegment(modelLower) {
|
||||
@@ -1249,15 +1249,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 corresponding base model's price card.
|
||||
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.7-flash", "gemini-3.8-flash"} {
|
||||
for _, tier := range []string{"-high", "-low", "-medium", "-tiered"} {
|
||||
if model == baseModel+tier {
|
||||
return baseModel
|
||||
}
|
||||
}
|
||||
}
|
||||
return model
|
||||
|
||||
@@ -477,38 +477,37 @@ func TestGetModelPricing_OpenAICompactAliasUsesStaticFallback(t *testing.T) {
|
||||
require.InDelta(t, 1.5e-5, got.OutputCostPerToken, 1e-12)
|
||||
}
|
||||
|
||||
func TestPricingService_Gemini36FlashThinkingTiersUseBasePricing(t *testing.T) {
|
||||
func TestPricingService_GeminiFlashThinkingTiersUseBasePricing(t *testing.T) {
|
||||
basePricing := &LiteLLMModelPricing{
|
||||
InputCostPerToken: 1.5e-6,
|
||||
OutputCostPerToken: 7.5e-6,
|
||||
CacheReadInputTokenCost: 0.15e-6,
|
||||
}
|
||||
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
"gemini-3.6-flash": basePricing,
|
||||
}}
|
||||
|
||||
for _, model := range []string{
|
||||
"gemini-3.6-flash",
|
||||
"gemini-3.6-flash-high",
|
||||
"gemini-3.6-flash-low",
|
||||
"gemini-3.6-flash-medium",
|
||||
"gemini-3.6-flash-tiered",
|
||||
} {
|
||||
t.Run(model, func(t *testing.T) {
|
||||
require.Same(t, basePricing, svc.GetModelPricing(model))
|
||||
})
|
||||
for _, baseModel := range []string{"gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash"} {
|
||||
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{baseModel: basePricing}}
|
||||
for _, tier := range []string{"", "-high", "-low", "-medium", "-tiered"} {
|
||||
model := baseModel + tier
|
||||
t.Run(model, func(t *testing.T) {
|
||||
require.Same(t, basePricing, svc.GetModelPricing(model))
|
||||
require.Same(t, basePricing, svc.GetIdentifiedModelPricing("models/"+model))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPricingService_Gemini36FlashTierSpecificPricingTakesPrecedence(t *testing.T) {
|
||||
func TestPricingService_GeminiFlashTierSpecificPricingTakesPrecedence(t *testing.T) {
|
||||
basePricing := &LiteLLMModelPricing{InputCostPerToken: 1.5e-6}
|
||||
tierPricing := &LiteLLMModelPricing{InputCostPerToken: 2e-6}
|
||||
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
"gemini-3.6-flash": basePricing,
|
||||
"gemini-3.6-flash-low": tierPricing,
|
||||
}}
|
||||
for _, baseModel := range []string{"gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash"} {
|
||||
t.Run(baseModel, func(t *testing.T) {
|
||||
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
baseModel: basePricing,
|
||||
baseModel + "-low": tierPricing,
|
||||
}}
|
||||
|
||||
require.Same(t, tierPricing, svc.GetModelPricing("models/gemini-3.6-flash-low"))
|
||||
require.Same(t, tierPricing, svc.GetModelPricing("models/"+baseModel+"-low"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBillingService_Gemini36FlashThinkingTierFallbacksAreBillable(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user