diff --git a/backend/internal/pkg/apicompat/responses_anthropic_cache_creation_test.go b/backend/internal/pkg/apicompat/responses_anthropic_cache_creation_test.go index a5856845c9..b8c7916d2d 100644 --- a/backend/internal/pkg/apicompat/responses_anthropic_cache_creation_test.go +++ b/backend/internal/pkg/apicompat/responses_anthropic_cache_creation_test.go @@ -41,6 +41,41 @@ func TestAnthropicUsageFromResponsesUsage_NoCacheCreation(t *testing.T) { assert.Equal(t, 0, got.CacheCreationInputTokens) } +func TestResponsesEventToAnthropicEvents_StreamingCacheCreation(t *testing.T) { + state := NewResponsesEventToAnthropicState() + state.MessageStartSent = true + + completedEvt := &ResponsesStreamEvent{ + Type: "response.completed", + Response: &ResponsesResponse{ + Status: "completed", + Usage: &ResponsesUsage{ + InputTokens: 20, + OutputTokens: 5, + CacheCreationInputTokens: 6, + InputTokensDetails: &ResponsesInputTokensDetails{ + CachedTokens: 4, + }, + }, + }, + } + + events := ResponsesEventToAnthropicEvents(completedEvt, state) + + var deltaEvt *AnthropicStreamEvent + for i := range events { + if events[i].Type == "message_delta" { + deltaEvt = &events[i] + break + } + } + require.NotNil(t, deltaEvt, "should have message_delta event") + require.NotNil(t, deltaEvt.Usage) + assert.Equal(t, 6, deltaEvt.Usage.CacheCreationInputTokens, "streaming cache_creation must be preserved") + assert.Equal(t, 10, deltaEvt.Usage.InputTokens, "input = 20 - 4(read) - 6(creation)") + assert.Equal(t, 4, deltaEvt.Usage.CacheReadInputTokens) +} + func TestAnthropicToResponsesResponse_CacheCreation(t *testing.T) { resp := AnthropicResponse{ ID: "msg_test", diff --git a/backend/internal/pkg/apicompat/responses_to_anthropic.go b/backend/internal/pkg/apicompat/responses_to_anthropic.go index 9cc6e49c75..9c3b85b2ef 100644 --- a/backend/internal/pkg/apicompat/responses_to_anthropic.go +++ b/backend/internal/pkg/apicompat/responses_to_anthropic.go @@ -182,9 +182,10 @@ type ResponsesEventToAnthropicState struct { // OutputIndexToBlockIdx maps Responses output_index → Anthropic content block index. OutputIndexToBlockIdx map[int]int - InputTokens int - OutputTokens int - CacheReadInputTokens int + InputTokens int + OutputTokens int + CacheReadInputTokens int + CacheCreationInputTokens int ResponseID string Model string @@ -259,9 +260,10 @@ func FinalizeResponsesAnthropicStream(state *ResponsesEventToAnthropicState) []A StopReason: stopReason, }, Usage: &AnthropicUsage{ - InputTokens: state.InputTokens, - OutputTokens: state.OutputTokens, - CacheReadInputTokens: state.CacheReadInputTokens, + InputTokens: state.InputTokens, + OutputTokens: state.OutputTokens, + CacheReadInputTokens: state.CacheReadInputTokens, + CacheCreationInputTokens: state.CacheCreationInputTokens, }, }, AnthropicStreamEvent{Type: "message_stop"}, @@ -579,6 +581,7 @@ func resToAnthHandleCompleted(evt *ResponsesStreamEvent, state *ResponsesEventTo state.InputTokens = usage.InputTokens state.OutputTokens = usage.OutputTokens state.CacheReadInputTokens = usage.CacheReadInputTokens + state.CacheCreationInputTokens = usage.CacheCreationInputTokens } if evt.Response != nil { if evt.Response.Usage != nil { @@ -586,6 +589,7 @@ func resToAnthHandleCompleted(evt *ResponsesStreamEvent, state *ResponsesEventTo state.InputTokens = usage.InputTokens state.OutputTokens = usage.OutputTokens state.CacheReadInputTokens = usage.CacheReadInputTokens + state.CacheCreationInputTokens = usage.CacheCreationInputTokens } switch evt.Response.Status { case "incomplete": @@ -606,9 +610,10 @@ func resToAnthHandleCompleted(evt *ResponsesStreamEvent, state *ResponsesEventTo StopReason: stopReason, }, Usage: &AnthropicUsage{ - InputTokens: state.InputTokens, - OutputTokens: state.OutputTokens, - CacheReadInputTokens: state.CacheReadInputTokens, + InputTokens: state.InputTokens, + OutputTokens: state.OutputTokens, + CacheReadInputTokens: state.CacheReadInputTokens, + CacheCreationInputTokens: state.CacheCreationInputTokens, }, }, AnthropicStreamEvent{Type: "message_stop"},