diff --git a/controller/channel-test.go b/controller/channel-test.go index 037b8496c88f..4e7167ead9bb 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -546,8 +546,16 @@ func settleTestQuota(info *relaycommon.RelayInfo, priceData types.PriceData, usa quota := 0 if !priceData.UsePrice { - quota = usage.PromptTokens + int(math.Round(float64(usage.CompletionTokens)*priceData.CompletionRatio)) - quota = int(math.Round(float64(quota) * priceData.ModelRatio)) + isClaudeUsageSemantic := usage.UsageSemantic == "anthropic" || + (info != nil && info.GetFinalRequestRelayFormat() == types.RelayFormatClaude) + promptTokens := float64(usage.PromptTokens) + cacheTokens := float64(usage.PromptTokensDetails.CachedTokens) + completionTokens := float64(usage.CompletionTokens) + quotaTokens := promptTokens + cacheTokens*priceData.CacheRatio + completionTokens*priceData.CompletionRatio + if !isClaudeUsageSemantic { + quotaTokens -= cacheTokens + } + quota = int(math.Round(quotaTokens * priceData.ModelRatio)) if priceData.ModelRatio != 0 && quota <= 0 { quota = 1 } diff --git a/controller/channel_test_internal_test.go b/controller/channel_test_internal_test.go index 025408010b1d..4c9f65ca556b 100644 --- a/controller/channel_test_internal_test.go +++ b/controller/channel_test_internal_test.go @@ -41,6 +41,41 @@ func TestSettleTestQuotaUsesTieredBilling(t *testing.T) { require.Equal(t, "stream", result.MatchedTier) } +func TestSettleTestQuotaAppliesCacheRatio(t *testing.T) { + quota, result := settleTestQuota(nil, types.PriceData{ + ModelRatio: 2.5, + CompletionRatio: 6, + CacheRatio: 0.1, + }, &dto.Usage{ + PromptTokens: 4440, + CompletionTokens: 13, + PromptTokensDetails: dto.InputTokenDetails{ + CachedTokens: 3840, + }, + }) + + require.Equal(t, 2655, quota) + require.Nil(t, result) +} + +func TestSettleTestQuotaKeepsClaudeSemanticCachedTokensInPromptBase(t *testing.T) { + quota, result := settleTestQuota(nil, types.PriceData{ + ModelRatio: 1, + CompletionRatio: 2, + CacheRatio: 0.1, + }, &dto.Usage{ + PromptTokens: 1000, + CompletionTokens: 200, + UsageSemantic: "anthropic", + PromptTokensDetails: dto.InputTokenDetails{ + CachedTokens: 100, + }, + }) + + require.Equal(t, 1410, quota) + require.Nil(t, result) +} + func TestBuildTestLogOtherInjectsTieredInfo(t *testing.T) { gin.SetMode(gin.TestMode) ctx, _ := gin.CreateTestContext(httptest.NewRecorder())