fix(apicompat): 补齐 Responses→Anthropic 流式路径的 cache_creation_input_tokens

This commit is contained in:
li
2026-07-10 20:49:44 +08:00
parent 0d28f7f90d
commit 83f169e4fa
2 changed files with 49 additions and 9 deletions
@@ -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",
@@ -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"},