Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
b17704d6
Commit
b17704d6
authored
Apr 26, 2026
by
deqiying
Browse files
fix(anthropic): 修正缓存 token 的 Anthropic 用量语义
parent
9d1751ec
Changes
2
Show whitespace changes
Inline
Side-by-side
backend/internal/pkg/apicompat/anthropic_responses_test.go
View file @
b17704d6
...
...
@@ -181,6 +181,55 @@ func TestResponsesToAnthropic_TextOnly(t *testing.T) {
assert
.
Equal
(
t
,
5
,
anth
.
Usage
.
OutputTokens
)
}
func
TestResponsesToAnthropic_CachedTokensUseAnthropicInputSemantics
(
t
*
testing
.
T
)
{
resp
:=
&
ResponsesResponse
{
ID
:
"resp_cached"
,
Model
:
"gpt-5.2"
,
Status
:
"completed"
,
Output
:
[]
ResponsesOutput
{
{
Type
:
"message"
,
Content
:
[]
ResponsesContentPart
{
{
Type
:
"output_text"
,
Text
:
"Cached response"
},
},
},
},
Usage
:
&
ResponsesUsage
{
InputTokens
:
54006
,
OutputTokens
:
123
,
TotalTokens
:
54129
,
InputTokensDetails
:
&
ResponsesInputTokensDetails
{
CachedTokens
:
50688
,
},
},
}
anth
:=
ResponsesToAnthropic
(
resp
,
"claude-sonnet-4-5-20250929"
)
assert
.
Equal
(
t
,
3318
,
anth
.
Usage
.
InputTokens
)
assert
.
Equal
(
t
,
50688
,
anth
.
Usage
.
CacheReadInputTokens
)
assert
.
Equal
(
t
,
123
,
anth
.
Usage
.
OutputTokens
)
}
func
TestResponsesToAnthropic_CachedTokensClampInputTokens
(
t
*
testing
.
T
)
{
resp
:=
&
ResponsesResponse
{
ID
:
"resp_cached_clamp"
,
Model
:
"gpt-5.2"
,
Status
:
"completed"
,
Usage
:
&
ResponsesUsage
{
InputTokens
:
100
,
OutputTokens
:
5
,
InputTokensDetails
:
&
ResponsesInputTokensDetails
{
CachedTokens
:
150
,
},
},
}
anth
:=
ResponsesToAnthropic
(
resp
,
"claude-sonnet-4-5-20250929"
)
assert
.
Equal
(
t
,
0
,
anth
.
Usage
.
InputTokens
)
assert
.
Equal
(
t
,
150
,
anth
.
Usage
.
CacheReadInputTokens
)
assert
.
Equal
(
t
,
5
,
anth
.
Usage
.
OutputTokens
)
}
func
TestResponsesToAnthropic_ToolUse
(
t
*
testing
.
T
)
{
resp
:=
&
ResponsesResponse
{
ID
:
"resp_456"
,
...
...
@@ -343,6 +392,36 @@ func TestStreamingTextOnly(t *testing.T) {
assert
.
Equal
(
t
,
"message_stop"
,
events
[
1
]
.
Type
)
}
func
TestStreamingCachedTokensUseAnthropicInputSemantics
(
t
*
testing
.
T
)
{
state
:=
NewResponsesEventToAnthropicState
()
ResponsesEventToAnthropicEvents
(
&
ResponsesStreamEvent
{
Type
:
"response.created"
,
Response
:
&
ResponsesResponse
{
ID
:
"resp_cached_stream"
,
Model
:
"gpt-5.2"
},
},
state
)
events
:=
ResponsesEventToAnthropicEvents
(
&
ResponsesStreamEvent
{
Type
:
"response.completed"
,
Response
:
&
ResponsesResponse
{
Status
:
"completed"
,
Usage
:
&
ResponsesUsage
{
InputTokens
:
54006
,
OutputTokens
:
123
,
TotalTokens
:
54129
,
InputTokensDetails
:
&
ResponsesInputTokensDetails
{
CachedTokens
:
50688
,
},
},
},
},
state
)
require
.
Len
(
t
,
events
,
2
)
assert
.
Equal
(
t
,
"message_delta"
,
events
[
0
]
.
Type
)
assert
.
Equal
(
t
,
3318
,
events
[
0
]
.
Usage
.
InputTokens
)
assert
.
Equal
(
t
,
50688
,
events
[
0
]
.
Usage
.
CacheReadInputTokens
)
assert
.
Equal
(
t
,
123
,
events
[
0
]
.
Usage
.
OutputTokens
)
assert
.
Equal
(
t
,
"message_stop"
,
events
[
1
]
.
Type
)
}
func
TestStreamingToolCall
(
t
*
testing
.
T
)
{
state
:=
NewResponsesEventToAnthropicState
()
...
...
backend/internal/pkg/apicompat/responses_to_anthropic.go
View file @
b17704d6
...
...
@@ -84,16 +84,32 @@ func ResponsesToAnthropic(resp *ResponsesResponse, model string) *AnthropicRespo
out
.
StopReason
=
responsesStatusToAnthropicStopReason
(
resp
.
Status
,
resp
.
IncompleteDetails
,
blocks
)
if
resp
.
Usage
!=
nil
{
out
.
Usage
=
AnthropicUsage
{
InputTokens
:
resp
.
Usage
.
InputTokens
,
OutputTokens
:
resp
.
Usage
.
OutputTokens
,
out
.
Usage
=
anthropicUsageFromResponsesUsage
(
resp
.
Usage
)
}
if
resp
.
Usage
.
InputTokensDetails
!=
nil
{
out
.
Usage
.
CacheReadInputTokens
=
resp
.
Usage
.
InputTokensDetails
.
CachedTokens
return
out
}
func
anthropicUsageFromResponsesUsage
(
usage
*
ResponsesUsage
)
AnthropicUsage
{
if
usage
==
nil
{
return
AnthropicUsage
{}
}
cachedTokens
:=
0
if
usage
.
InputTokensDetails
!=
nil
{
cachedTokens
=
usage
.
InputTokensDetails
.
CachedTokens
}
inputTokens
:=
usage
.
InputTokens
-
cachedTokens
if
inputTokens
<
0
{
inputTokens
=
0
}
return
out
return
AnthropicUsage
{
InputTokens
:
inputTokens
,
OutputTokens
:
usage
.
OutputTokens
,
CacheReadInputTokens
:
cachedTokens
,
}
}
func
responsesStatusToAnthropicStopReason
(
status
string
,
details
*
ResponsesIncompleteDetails
,
blocks
[]
AnthropicContentBlock
)
string
{
...
...
@@ -466,11 +482,10 @@ func resToAnthHandleCompleted(evt *ResponsesStreamEvent, state *ResponsesEventTo
stopReason
:=
"end_turn"
if
evt
.
Response
!=
nil
{
if
evt
.
Response
.
Usage
!=
nil
{
state
.
InputTokens
=
evt
.
Response
.
Usage
.
InputTokens
state
.
OutputTokens
=
evt
.
Response
.
Usage
.
OutputTokens
if
evt
.
Response
.
Usage
.
InputTokensDetails
!=
nil
{
state
.
CacheReadInputTokens
=
evt
.
Response
.
Usage
.
InputTokensDetails
.
CachedTokens
}
usage
:=
anthropicUsageFromResponsesUsage
(
evt
.
Response
.
Usage
)
state
.
InputTokens
=
usage
.
InputTokens
state
.
OutputTokens
=
usage
.
OutputTokens
state
.
CacheReadInputTokens
=
usage
.
CacheReadInputTokens
}
switch
evt
.
Response
.
Status
{
case
"incomplete"
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment