fix(apicompat): Responses↔Anthropic 转换补齐 cache_creation_input_tokens

Fixes #3935
This commit is contained in:
li
2026-07-10 20:40:45 +08:00
parent deff3123de
commit 0d28f7f90d
3 changed files with 77 additions and 10 deletions
@@ -102,9 +102,10 @@ func AnthropicToResponsesResponse(resp *AnthropicResponse) *ResponsesResponse {
resp.Usage.CacheReadInputTokens +
resp.Usage.CacheCreationInputTokens
out.Usage = &ResponsesUsage{
InputTokens: totalInputTokens,
OutputTokens: resp.Usage.OutputTokens,
TotalTokens: totalInputTokens + resp.Usage.OutputTokens,
InputTokens: totalInputTokens,
OutputTokens: resp.Usage.OutputTokens,
TotalTokens: totalInputTokens + resp.Usage.OutputTokens,
CacheCreationInputTokens: resp.Usage.CacheCreationInputTokens,
}
if resp.Usage.CacheReadInputTokens > 0 {
out.Usage.InputTokensDetails = &ResponsesInputTokensDetails{
@@ -497,9 +498,10 @@ func makeResponsesCompletedEvent(
// back to match OpenAI Responses semantics where input_tokens is the total.
totalInputTokens := state.InputTokens + state.CacheReadInputTokens + state.CacheCreationInputTokens
usage := &ResponsesUsage{
InputTokens: totalInputTokens,
OutputTokens: state.OutputTokens,
TotalTokens: totalInputTokens + state.OutputTokens,
InputTokens: totalInputTokens,
OutputTokens: state.OutputTokens,
TotalTokens: totalInputTokens + state.OutputTokens,
CacheCreationInputTokens: state.CacheCreationInputTokens,
}
if state.CacheReadInputTokens > 0 {
usage.InputTokensDetails = &ResponsesInputTokensDetails{
@@ -0,0 +1,64 @@
package apicompat
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAnthropicUsageFromResponsesUsage_CacheCreation(t *testing.T) {
usage := &ResponsesUsage{
InputTokens: 20,
OutputTokens: 5,
CacheCreationInputTokens: 6,
InputTokensDetails: &ResponsesInputTokensDetails{
CachedTokens: 4,
},
}
got := anthropicUsageFromResponsesUsage(usage)
assert.Equal(t, 10, got.InputTokens, "input = total(20) - cache_read(4) - cache_creation(6)")
assert.Equal(t, 5, got.OutputTokens)
assert.Equal(t, 4, got.CacheReadInputTokens)
assert.Equal(t, 6, got.CacheCreationInputTokens, "cache creation must be preserved")
}
func TestAnthropicUsageFromResponsesUsage_NoCacheCreation(t *testing.T) {
usage := &ResponsesUsage{
InputTokens: 10,
OutputTokens: 5,
InputTokensDetails: &ResponsesInputTokensDetails{
CachedTokens: 3,
},
}
got := anthropicUsageFromResponsesUsage(usage)
assert.Equal(t, 7, got.InputTokens)
assert.Equal(t, 3, got.CacheReadInputTokens)
assert.Equal(t, 0, got.CacheCreationInputTokens)
}
func TestAnthropicToResponsesResponse_CacheCreation(t *testing.T) {
resp := AnthropicResponse{
ID: "msg_test",
Type: "message",
Role: "assistant",
Model: "claude-opus-4-6",
Usage: AnthropicUsage{
InputTokens: 10,
OutputTokens: 5,
CacheReadInputTokens: 4,
CacheCreationInputTokens: 6,
},
StopReason: "end_turn",
}
out := AnthropicToResponsesResponse(&resp)
require.NotNil(t, out.Usage)
assert.Equal(t, 20, out.Usage.InputTokens, "total = input(10) + cache_read(4) + cache_creation(6)")
assert.Equal(t, 6, out.Usage.CacheCreationInputTokens, "cache creation must round-trip")
}
@@ -100,15 +100,16 @@ func anthropicUsageFromResponsesUsage(usage *ResponsesUsage) AnthropicUsage {
cachedTokens = usage.InputTokensDetails.CachedTokens
}
inputTokens := usage.InputTokens - cachedTokens
inputTokens := usage.InputTokens - cachedTokens - usage.CacheCreationInputTokens
if inputTokens < 0 {
inputTokens = 0
}
return AnthropicUsage{
InputTokens: inputTokens,
OutputTokens: usage.OutputTokens,
CacheReadInputTokens: cachedTokens,
InputTokens: inputTokens,
OutputTokens: usage.OutputTokens,
CacheReadInputTokens: cachedTokens,
CacheCreationInputTokens: usage.CacheCreationInputTokens,
}
}