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
94e26dee
Unverified
Commit
94e26dee
authored
Mar 20, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 20, 2026
Browse files
Merge pull request #1172 from alfadb/fix/openai-messages-effort-max-to-xhigh
fix(apicompat): 修正 Anthropic→OpenAI 推理级别映射
parents
94bba415
8afa8c10
Changes
2
Show whitespace changes
Inline
Side-by-side
backend/internal/pkg/apicompat/anthropic_responses_test.go
View file @
94e26dee
...
@@ -632,8 +632,8 @@ func TestAnthropicToResponses_ThinkingEnabled(t *testing.T) {
...
@@ -632,8 +632,8 @@ func TestAnthropicToResponses_ThinkingEnabled(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
// thinking.type is ignored for effort; default
x
high applies.
// thinking.type is ignored for effort; default high applies.
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
assert
.
Contains
(
t
,
resp
.
Include
,
"reasoning.encrypted_content"
)
assert
.
Contains
(
t
,
resp
.
Include
,
"reasoning.encrypted_content"
)
assert
.
NotContains
(
t
,
resp
.
Include
,
"reasoning.summary"
)
assert
.
NotContains
(
t
,
resp
.
Include
,
"reasoning.summary"
)
...
@@ -650,8 +650,8 @@ func TestAnthropicToResponses_ThinkingAdaptive(t *testing.T) {
...
@@ -650,8 +650,8 @@ func TestAnthropicToResponses_ThinkingAdaptive(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
// thinking.type is ignored for effort; default
x
high applies.
// thinking.type is ignored for effort; default high applies.
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
assert
.
NotContains
(
t
,
resp
.
Include
,
"reasoning.summary"
)
assert
.
NotContains
(
t
,
resp
.
Include
,
"reasoning.summary"
)
}
}
...
@@ -666,9 +666,9 @@ func TestAnthropicToResponses_ThinkingDisabled(t *testing.T) {
...
@@ -666,9 +666,9 @@ func TestAnthropicToResponses_ThinkingDisabled(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// Default effort applies (high →
x
high) even when thinking is disabled.
// Default effort applies (high → high) even when thinking is disabled.
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
}
}
func
TestAnthropicToResponses_NoThinking
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_NoThinking
(
t
*
testing
.
T
)
{
...
@@ -680,9 +680,9 @@ func TestAnthropicToResponses_NoThinking(t *testing.T) {
...
@@ -680,9 +680,9 @@ func TestAnthropicToResponses_NoThinking(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// Default effort applies (high →
x
high) when no thinking/output_config is set.
// Default effort applies (high → high) when no thinking/output_config is set.
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
...
@@ -690,7 +690,7 @@ func TestAnthropicToResponses_NoThinking(t *testing.T) {
...
@@ -690,7 +690,7 @@ func TestAnthropicToResponses_NoThinking(t *testing.T) {
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
func
TestAnthropicToResponses_OutputConfigOverridesDefault
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_OutputConfigOverridesDefault
(
t
*
testing
.
T
)
{
// Default is
x
high, but output_config.effort="low" overrides. low→low after mapping.
// Default is high, but output_config.effort="low" overrides. low→low after mapping.
req
:=
&
AnthropicRequest
{
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
MaxTokens
:
1024
,
...
@@ -708,7 +708,7 @@ func TestAnthropicToResponses_OutputConfigOverridesDefault(t *testing.T) {
...
@@ -708,7 +708,7 @@ func TestAnthropicToResponses_OutputConfigOverridesDefault(t *testing.T) {
func
TestAnthropicToResponses_OutputConfigWithoutThinking
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_OutputConfigWithoutThinking
(
t
*
testing
.
T
)
{
// No thinking field, but output_config.effort="medium" → creates reasoning.
// No thinking field, but output_config.effort="medium" → creates reasoning.
// medium→
high
after mapping.
// medium→
medium
after
1:1
mapping.
req
:=
&
AnthropicRequest
{
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
MaxTokens
:
1024
,
...
@@ -719,12 +719,12 @@ func TestAnthropicToResponses_OutputConfigWithoutThinking(t *testing.T) {
...
@@ -719,12 +719,12 @@ func TestAnthropicToResponses_OutputConfigWithoutThinking(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"
high
"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"
medium
"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
}
}
func
TestAnthropicToResponses_OutputConfigHigh
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_OutputConfigHigh
(
t
*
testing
.
T
)
{
// output_config.effort="high" → mapped to "
x
high".
// output_config.effort="high" → mapped to "high"
(1:1, both sides' default)
.
req
:=
&
AnthropicRequest
{
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
MaxTokens
:
1024
,
...
@@ -732,6 +732,22 @@ func TestAnthropicToResponses_OutputConfigHigh(t *testing.T) {
...
@@ -732,6 +732,22 @@ func TestAnthropicToResponses_OutputConfigHigh(t *testing.T) {
OutputConfig
:
&
AnthropicOutputConfig
{
Effort
:
"high"
},
OutputConfig
:
&
AnthropicOutputConfig
{
Effort
:
"high"
},
}
}
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"auto"
,
resp
.
Reasoning
.
Summary
)
}
func
TestAnthropicToResponses_OutputConfigMax
(
t
*
testing
.
T
)
{
// output_config.effort="max" → mapped to OpenAI's highest supported level "xhigh".
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
Messages
:
[]
AnthropicMessage
{{
Role
:
"user"
,
Content
:
json
.
RawMessage
(
`"Hello"`
)}},
OutputConfig
:
&
AnthropicOutputConfig
{
Effort
:
"max"
},
}
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
...
@@ -740,7 +756,7 @@ func TestAnthropicToResponses_OutputConfigHigh(t *testing.T) {
...
@@ -740,7 +756,7 @@ func TestAnthropicToResponses_OutputConfigHigh(t *testing.T) {
}
}
func
TestAnthropicToResponses_NoOutputConfig
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_NoOutputConfig
(
t
*
testing
.
T
)
{
// No output_config → default
x
high regardless of thinking.type.
// No output_config → default high regardless of thinking.type.
req
:=
&
AnthropicRequest
{
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
MaxTokens
:
1024
,
...
@@ -751,11 +767,11 @@ func TestAnthropicToResponses_NoOutputConfig(t *testing.T) {
...
@@ -751,11 +767,11 @@ func TestAnthropicToResponses_NoOutputConfig(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
}
}
func
TestAnthropicToResponses_OutputConfigWithoutEffort
(
t
*
testing
.
T
)
{
func
TestAnthropicToResponses_OutputConfigWithoutEffort
(
t
*
testing
.
T
)
{
// output_config present but effort empty (e.g. only format set) → default
x
high.
// output_config present but effort empty (e.g. only format set) → default high.
req
:=
&
AnthropicRequest
{
req
:=
&
AnthropicRequest
{
Model
:
"gpt-5.2"
,
Model
:
"gpt-5.2"
,
MaxTokens
:
1024
,
MaxTokens
:
1024
,
...
@@ -766,7 +782,7 @@ func TestAnthropicToResponses_OutputConfigWithoutEffort(t *testing.T) {
...
@@ -766,7 +782,7 @@ func TestAnthropicToResponses_OutputConfigWithoutEffort(t *testing.T) {
resp
,
err
:=
AnthropicToResponses
(
req
)
resp
,
err
:=
AnthropicToResponses
(
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
require
.
NotNil
(
t
,
resp
.
Reasoning
)
assert
.
Equal
(
t
,
"
x
high"
,
resp
.
Reasoning
.
Effort
)
assert
.
Equal
(
t
,
"high"
,
resp
.
Reasoning
.
Effort
)
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
...
...
backend/internal/pkg/apicompat/anthropic_to_responses.go
View file @
94e26dee
...
@@ -46,9 +46,10 @@ func AnthropicToResponses(req *AnthropicRequest) (*ResponsesRequest, error) {
...
@@ -46,9 +46,10 @@ func AnthropicToResponses(req *AnthropicRequest) (*ResponsesRequest, error) {
}
}
// Determine reasoning effort: only output_config.effort controls the
// Determine reasoning effort: only output_config.effort controls the
// level; thinking.type is ignored. Default is xhigh when unset.
// level; thinking.type is ignored. Default is high when unset (both
// Anthropic levels map to OpenAI: low→low, medium→high, high→xhigh.
// Anthropic and OpenAI default to high).
effort
:=
"high"
// default → maps to xhigh
// Anthropic levels map 1:1 to OpenAI: low→low, medium→medium, high→high, max→xhigh.
effort
:=
"high"
// default → both sides' default
if
req
.
OutputConfig
!=
nil
&&
req
.
OutputConfig
.
Effort
!=
""
{
if
req
.
OutputConfig
!=
nil
&&
req
.
OutputConfig
.
Effort
!=
""
{
effort
=
req
.
OutputConfig
.
Effort
effort
=
req
.
OutputConfig
.
Effort
}
}
...
@@ -380,18 +381,19 @@ func extractAnthropicTextFromBlocks(blocks []AnthropicContentBlock) string {
...
@@ -380,18 +381,19 @@ func extractAnthropicTextFromBlocks(blocks []AnthropicContentBlock) string {
// mapAnthropicEffortToResponses converts Anthropic reasoning effort levels to
// mapAnthropicEffortToResponses converts Anthropic reasoning effort levels to
// OpenAI Responses API effort levels.
// OpenAI Responses API effort levels.
//
//
// Both APIs default to "high". The mapping is 1:1 for shared levels;
// only Anthropic's "max" (Opus 4.6 exclusive) maps to OpenAI's "xhigh"
// (GPT-5.2+ exclusive) as both represent the highest reasoning tier.
//
// low → low
// low → low
// medium → high
// medium → medium
// high → xhigh
// high → high
// max → xhigh
func
mapAnthropicEffortToResponses
(
effort
string
)
string
{
func
mapAnthropicEffortToResponses
(
effort
string
)
string
{
switch
effort
{
if
effort
==
"max"
{
case
"medium"
:
return
"high"
case
"high"
:
return
"xhigh"
return
"xhigh"
default
:
return
effort
// "low" and any unknown values pass through unchanged
}
}
return
effort
// low→low, medium→medium, high→high, unknown→passthrough
}
}
// convertAnthropicToolsToResponses maps Anthropic tool definitions to
// convertAnthropicToolsToResponses maps Anthropic tool definitions to
...
...
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