mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-13 15:54:34 +00:00
fix: settle Responses cached token usage (#6892)
This commit is contained in:
@@ -113,6 +113,29 @@ func usageFromOpenAIBillingUsage(billingUsage *dto.BillingUsage) *dto.Usage {
|
|||||||
if usage.TotalTokens == 0 {
|
if usage.TotalTokens == 0 {
|
||||||
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
|
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
|
||||||
}
|
}
|
||||||
|
if inputDetails := usage.InputTokensDetails; inputDetails != nil {
|
||||||
|
if usage.PromptTokensDetails.CachedTokens == 0 && inputDetails.CachedTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.CachedTokens = inputDetails.CachedTokens
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.CachedCreationTokens == 0 && inputDetails.CachedCreationTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.CachedCreationTokens = inputDetails.CachedCreationTokens
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.CacheWriteTokens == 0 && inputDetails.CacheWriteTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.CacheWriteTokens = inputDetails.CacheWriteTokens
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.TextTokens == 0 && inputDetails.TextTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.TextTokens = inputDetails.TextTokens
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.ImageTokens == 0 && inputDetails.ImageTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.ImageTokens = inputDetails.ImageTokens
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.AudioTokens == 0 && inputDetails.AudioTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.AudioTokens = inputDetails.AudioTokens
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if usage.PromptTokensDetails.CachedTokens == 0 && usage.PromptCacheHitTokens > 0 {
|
||||||
|
usage.PromptTokensDetails.CachedTokens = usage.PromptCacheHitTokens
|
||||||
|
}
|
||||||
usage.UsageSemantic = dto.BillingUsageSemanticOpenAI
|
usage.UsageSemantic = dto.BillingUsageSemanticOpenAI
|
||||||
usage.UsageSource = billingUsage.Source
|
usage.UsageSource = billingUsage.Source
|
||||||
usage.BillingUsage = dto.CloneBillingUsage(billingUsage)
|
usage.BillingUsage = dto.CloneBillingUsage(billingUsage)
|
||||||
|
|||||||
@@ -283,6 +283,92 @@ func TestCalculateTextQuotaSummaryUsesOpenAIBillingUsageBeforeTopLevelUsage(t *t
|
|||||||
require.Equal(t, 98, summary.Quota)
|
require.Equal(t, 98, summary.Quota)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCalculateTextQuotaSummaryUsesOpenAIResponsesInputTokenDetails(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||||
|
relayInfo := &relaycommon.RelayInfo{
|
||||||
|
RelayFormat: types.RelayFormatOpenAI,
|
||||||
|
OriginModelName: "gpt-4o",
|
||||||
|
PriceData: hosttypes.PriceData{
|
||||||
|
ModelRatio: 1,
|
||||||
|
CompletionRatio: 2,
|
||||||
|
CacheRatio: 0.25,
|
||||||
|
GroupRatioInfo: hosttypes.GroupRatioInfo{GroupRatio: 1},
|
||||||
|
},
|
||||||
|
StartTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
responsesUsage := &dto.Usage{
|
||||||
|
InputTokens: 100,
|
||||||
|
OutputTokens: 10,
|
||||||
|
TotalTokens: 110,
|
||||||
|
InputTokensDetails: &dto.InputTokenDetails{
|
||||||
|
CachedTokens: 40,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
convertedUsage := &dto.Usage{
|
||||||
|
PromptTokens: 100,
|
||||||
|
CompletionTokens: 10,
|
||||||
|
TotalTokens: 110,
|
||||||
|
PromptTokensDetails: dto.InputTokenDetails{
|
||||||
|
CachedTokens: 40,
|
||||||
|
},
|
||||||
|
BillingUsage: dto.NewOpenAIResponsesBillingUsage(responsesUsage),
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveUsage := effectiveBillingUsage(convertedUsage)
|
||||||
|
require.Equal(t, 40, effectiveUsage.PromptTokensDetails.CachedTokens)
|
||||||
|
require.Zero(t, convertedUsage.BillingUsage.OpenAIUsage.PromptTokensDetails.CachedTokens)
|
||||||
|
|
||||||
|
summary := calculateTextQuotaSummary(ctx, relayInfo, effectiveUsage)
|
||||||
|
require.Equal(t, 40, summary.CacheTokens)
|
||||||
|
// 60 uncached input + 40*0.25 cached input + 10*2 output = 90.
|
||||||
|
require.Equal(t, 90, summary.Quota)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageFromOpenAIBillingUsageNormalizesCacheDetailsWithoutOverwritingCanonicalValues(t *testing.T) {
|
||||||
|
responsesUsage := &dto.Usage{
|
||||||
|
InputTokens: 100,
|
||||||
|
OutputTokens: 10,
|
||||||
|
PromptCacheHitTokens: 55,
|
||||||
|
PromptTokensDetails: dto.InputTokenDetails{
|
||||||
|
CachedTokens: 8,
|
||||||
|
TextTokens: 12,
|
||||||
|
},
|
||||||
|
InputTokensDetails: &dto.InputTokenDetails{
|
||||||
|
CachedTokens: 40,
|
||||||
|
CachedCreationTokens: 5,
|
||||||
|
CacheWriteTokens: 6,
|
||||||
|
TextTokens: 60,
|
||||||
|
ImageTokens: 7,
|
||||||
|
AudioTokens: 9,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
billingUsage := dto.NewOpenAIResponsesBillingUsage(responsesUsage)
|
||||||
|
usage := effectiveBillingUsage(&dto.Usage{BillingUsage: billingUsage})
|
||||||
|
|
||||||
|
require.Equal(t, 8, usage.PromptTokensDetails.CachedTokens)
|
||||||
|
require.Equal(t, 5, usage.PromptTokensDetails.CachedCreationTokens)
|
||||||
|
require.Equal(t, 6, usage.PromptTokensDetails.CacheWriteTokens)
|
||||||
|
require.Equal(t, 12, usage.PromptTokensDetails.TextTokens)
|
||||||
|
require.Equal(t, 7, usage.PromptTokensDetails.ImageTokens)
|
||||||
|
require.Equal(t, 9, usage.PromptTokensDetails.AudioTokens)
|
||||||
|
require.Zero(t, billingUsage.OpenAIUsage.PromptTokensDetails.CachedCreationTokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageFromOpenAIBillingUsageFallsBackToPromptCacheHitTokens(t *testing.T) {
|
||||||
|
usage := effectiveBillingUsage(&dto.Usage{
|
||||||
|
BillingUsage: dto.NewOpenAIChatBillingUsage(&dto.Usage{
|
||||||
|
PromptTokens: 100,
|
||||||
|
CompletionTokens: 10,
|
||||||
|
PromptCacheHitTokens: 35,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, 35, usage.PromptTokensDetails.CachedTokens)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUsageBillingPathForLog(t *testing.T) {
|
func TestUsageBillingPathForLog(t *testing.T) {
|
||||||
require.Equal(t, usageBillingPathAnthropic, usageBillingPathForLog(true, &dto.Usage{
|
require.Equal(t, usageBillingPathAnthropic, usageBillingPathForLog(true, &dto.Usage{
|
||||||
BillingUsage: dto.NewClaudeMessagesBillingUsage(&dto.ClaudeUsage{InputTokens: 1}),
|
BillingUsage: dto.NewClaudeMessagesBillingUsage(&dto.ClaudeUsage{InputTokens: 1}),
|
||||||
|
|||||||
Reference in New Issue
Block a user