Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
6b659fd6
authored
Sep 05, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(relay): preserve reasoning effort without implicit remapping
parent
7c044d7c
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
126 additions
and
41 deletions
+126
-41
relay/channel/deepseek/adaptor.go
+10
-0
relay/channel/openai/adaptor.go
+30
-5
relay/channel/volcengine/adaptor.go
+1
-0
relay/channel/xai/adaptor.go
+4
-2
relay/common/relay_info.go
+4
-3
relay/helper/model_modifier.go
+2
-2
relay/helper/reasoning_suffix.go
+3
-0
relaykit/relayconvert/internal/shared/claude/reasoning.go
+12
-7
relaykit/relayconvert/internal/shared/gemini/request.go
+7
-6
relaykit/relayconvert/reasoning/intent.go
+6
-16
relaykit/relayconvert/reasoning/intent_test.go
+47
-0
No files found.
relay/channel/deepseek/adaptor.go
View file @
6b659fd6
...
@@ -15,6 +15,7 @@ import (
...
@@ -15,6 +15,7 @@ import (
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
)
...
@@ -98,6 +99,9 @@ func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *d
...
@@ -98,6 +99,9 @@ func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *d
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
modelName
=
info
.
UpstreamModelName
modelName
=
info
.
UpstreamModelName
}
}
if
model_setting
.
ShouldPreserveThinkingSuffix
(
modelName
)
||
info
!=
nil
&&
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
{
return
nil
}
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
if
!
ok
{
if
!
ok
{
return
nil
return
nil
...
@@ -125,6 +129,9 @@ func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *d
...
@@ -125,6 +129,9 @@ func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *d
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
modelName
=
info
.
UpstreamModelName
modelName
=
info
.
UpstreamModelName
}
}
if
model_setting
.
ShouldPreserveThinkingSuffix
(
modelName
)
||
info
!=
nil
&&
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
{
return
nil
}
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
if
!
ok
{
if
!
ok
{
return
nil
return
nil
...
@@ -170,6 +177,9 @@ func applyDeepSeekV4ResponsesThinkingSuffix(info *relaycommon.RelayInfo, request
...
@@ -170,6 +177,9 @@ func applyDeepSeekV4ResponsesThinkingSuffix(info *relaycommon.RelayInfo, request
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
modelName
=
info
.
UpstreamModelName
modelName
=
info
.
UpstreamModelName
}
}
if
model_setting
.
ShouldPreserveThinkingSuffix
(
modelName
)
||
info
!=
nil
&&
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
{
return
}
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
if
ok
{
if
ok
{
if
thinkingType
==
"disabled"
{
if
thinkingType
==
"disabled"
{
...
...
relay/channel/openai/adaptor.go
View file @
6b659fd6
...
@@ -249,6 +249,18 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -249,6 +249,18 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
if
info
.
ChannelType
!=
constant
.
ChannelTypeOpenAI
&&
info
.
ChannelType
!=
constant
.
ChannelTypeAzure
{
if
info
.
ChannelType
!=
constant
.
ChannelTypeOpenAI
&&
info
.
ChannelType
!=
constant
.
ChannelTypeAzure
{
request
.
StreamOptions
=
nil
request
.
StreamOptions
=
nil
}
}
// Nested reasoning is an OpenRouter-compatible input dialect and needs
// projection even without a protocol conversion hop. Native top-level
// reasoning_effort stays untouched unless a modifier or conversion applies.
// OpenRouter retains its own dialect normalization below.
preserveSuffix
:=
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
||
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
UpstreamModelName
)
upstreamEffort
,
_
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
info
.
UpstreamModelName
)
originEffort
,
_
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
info
.
OriginModelName
)
renderReasoning
:=
len
(
request
.
Reasoning
)
>
0
||
len
(
info
.
RequestConversionChain
)
>
1
||
request
.
ReasoningConversion
!=
nil
||
info
.
ReasoningState
()
!=
nil
||
!
preserveSuffix
&&
(
upstreamEffort
!=
""
||
originEffort
!=
""
)
if
info
.
ChannelType
!=
constant
.
ChannelTypeOpenRouter
&&
!
renderReasoning
{
info
.
SetReasoningEffort
(
request
.
ReasoningEffort
)
}
if
info
.
ChannelType
==
constant
.
ChannelTypeOpenRouter
{
if
info
.
ChannelType
==
constant
.
ChannelTypeOpenRouter
{
initialIntent
,
err
:=
kitreasoning
.
FromOpenAIChat
(
request
)
initialIntent
,
err
:=
kitreasoning
.
FromOpenAIChat
(
request
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -273,7 +285,6 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -273,7 +285,6 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
request
.
Usage
=
json
.
RawMessage
(
`{"include":true}`
)
request
.
Usage
=
json
.
RawMessage
(
`{"include":true}`
)
}
}
// 合并 effort 尾巴产生的意图
// 合并 effort 尾巴产生的意图
preserveSuffix
:=
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
||
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
UpstreamModelName
)
mergeEffortSuffix
:=
func
(
modelName
string
)
error
{
mergeEffortSuffix
:=
func
(
modelName
string
)
error
{
rawEffort
,
_
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
modelName
)
rawEffort
,
_
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
modelName
)
if
rawEffort
==
""
{
if
rawEffort
==
""
{
...
@@ -374,8 +385,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -374,8 +385,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
}
}
}
}
if
info
.
ChannelType
!=
constant
.
ChannelTypeOpenRouter
{
if
info
.
ChannelType
!=
constant
.
ChannelTypeOpenRouter
&&
renderReasoning
{
preserveSuffix
:=
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
||
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
UpstreamModelName
)
effort
,
baseModel
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
info
.
UpstreamModelName
)
effort
,
baseModel
:=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
info
.
UpstreamModelName
)
if
preserveSuffix
{
if
preserveSuffix
{
effort
=
""
effort
=
""
...
@@ -412,7 +422,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -412,7 +422,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
info
.
UpstreamModelName
=
baseModel
info
.
UpstreamModelName
=
baseModel
request
.
Model
=
baseModel
request
.
Model
=
baseModel
}
}
if
canonicalEffort
:=
kitreasoning
.
OpenAIEffort
(
kitreasoning
.
EffectiveEffort
(
currentIntent
)
);
canonicalEffort
!=
""
{
if
canonicalEffort
:=
kitreasoning
.
EffectiveEffort
(
currentIntent
);
canonicalEffort
!=
""
{
request
.
ReasoningEffort
=
string
(
canonicalEffort
)
request
.
ReasoningEffort
=
string
(
canonicalEffort
)
info
.
SetReasoningEffort
(
string
(
canonicalEffort
))
info
.
SetReasoningEffort
(
string
(
canonicalEffort
))
}
}
...
@@ -666,6 +676,21 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
...
@@ -666,6 +676,21 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
if
preserveSuffix
{
if
preserveSuffix
{
effort
=
""
effort
=
""
}
}
originEffort
:=
""
if
info
!=
nil
&&
!
preserveSuffix
{
originEffort
,
_
=
reasoning
.
ParseOpenAIReasoningEffortFromModelSuffix
(
info
.
OriginModelName
)
}
crossProtocol
:=
info
!=
nil
&&
len
(
info
.
RequestConversionChain
)
>
1
if
(
info
==
nil
||
info
.
ChannelType
!=
constant
.
ChannelTypeOpenRouter
)
&&
!
crossProtocol
&&
effort
==
""
&&
originEffort
==
""
&&
request
.
ReasoningConversion
==
nil
&&
info
.
ReasoningState
()
==
nil
{
if
info
!=
nil
{
rawEffort
:=
""
if
request
.
Reasoning
!=
nil
{
rawEffort
=
request
.
Reasoning
.
Effort
}
info
.
SetReasoningEffort
(
rawEffort
)
}
return
request
,
nil
}
currentIntent
,
err
:=
kitreasoning
.
FromOpenAIResponses
(
&
request
)
currentIntent
,
err
:=
kitreasoning
.
FromOpenAIResponses
(
&
request
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
kitreasoning
.
AsClientError
(
err
)
return
nil
,
kitreasoning
.
AsClientError
(
err
)
...
@@ -700,7 +725,7 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
...
@@ -700,7 +725,7 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
info
.
UpstreamModelName
=
originModel
info
.
UpstreamModelName
=
originModel
}
}
}
}
if
canonicalEffort
:=
kitreasoning
.
OpenAIEffort
(
kitreasoning
.
EffectiveEffort
(
currentIntent
)
);
canonicalEffort
!=
""
{
if
canonicalEffort
:=
kitreasoning
.
EffectiveEffort
(
currentIntent
);
canonicalEffort
!=
""
{
if
request
.
Reasoning
==
nil
{
if
request
.
Reasoning
==
nil
{
request
.
Reasoning
=
&
dto
.
Reasoning
{}
request
.
Reasoning
=
&
dto
.
Reasoning
{}
}
}
...
...
relay/channel/volcengine/adaptor.go
View file @
6b659fd6
...
@@ -308,6 +308,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -308,6 +308,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
}
}
if
!
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
&&
if
!
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
&&
!
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
UpstreamModelName
)
&&
strings
.
HasSuffix
(
info
.
UpstreamModelName
,
"-thinking"
)
&&
strings
.
HasSuffix
(
info
.
UpstreamModelName
,
"-thinking"
)
&&
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"deepseek"
)
{
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"deepseek"
)
{
info
.
UpstreamModelName
=
strings
.
TrimSuffix
(
info
.
UpstreamModelName
,
"-thinking"
)
info
.
UpstreamModelName
=
strings
.
TrimSuffix
(
info
.
UpstreamModelName
,
"-thinking"
)
...
...
relay/channel/xai/adaptor.go
View file @
6b659fd6
...
@@ -11,6 +11,7 @@ import (
...
@@ -11,6 +11,7 @@ import (
relaycommon
"github.com/QuantumNous/new-api/relay/common"
relaycommon
"github.com/QuantumNous/new-api/relay/common"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/relay/constant"
...
@@ -78,10 +79,11 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -78,10 +79,11 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
request
.
MaxCompletionTokens
=
request
.
MaxTokens
request
.
MaxCompletionTokens
=
request
.
MaxTokens
request
.
MaxTokens
=
nil
request
.
MaxTokens
=
nil
}
}
if
strings
.
HasSuffix
(
request
.
Model
,
"-high"
)
{
preserveSuffix
:=
model_setting
.
ShouldPreserveThinkingSuffix
(
info
.
OriginModelName
)
||
model_setting
.
ShouldPreserveThinkingSuffix
(
request
.
Model
)
if
!
preserveSuffix
&&
strings
.
HasSuffix
(
request
.
Model
,
"-high"
)
{
request
.
ReasoningEffort
=
"high"
request
.
ReasoningEffort
=
"high"
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-high"
)
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-high"
)
}
else
if
strings
.
HasSuffix
(
request
.
Model
,
"-low"
)
{
}
else
if
!
preserveSuffix
&&
strings
.
HasSuffix
(
request
.
Model
,
"-low"
)
{
request
.
ReasoningEffort
=
"low"
request
.
ReasoningEffort
=
"low"
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-low"
)
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-low"
)
}
}
...
...
relay/common/relay_info.go
View file @
6b659fd6
...
@@ -496,9 +496,10 @@ func reasoningEffortFromRequest(request dto.Request) string {
...
@@ -496,9 +496,10 @@ func reasoningEffortFromRequest(request dto.Request) string {
}
}
case
*
dto
.
GeminiChatRequest
:
case
*
dto
.
GeminiChatRequest
:
if
req
!=
nil
&&
req
.
GenerationConfig
.
ThinkingConfig
!=
nil
{
if
req
!=
nil
&&
req
.
GenerationConfig
.
ThinkingConfig
!=
nil
{
intent
,
err
:=
kitreasoning
.
FromGemini
(
req
)
config
:=
req
.
GenerationConfig
.
ThinkingConfig
if
err
==
nil
{
effort
=
config
.
ThinkingLevel
effort
=
string
(
kitreasoning
.
EffectiveEffort
(
intent
))
if
effort
==
""
&&
config
.
ThinkingBudget
!=
nil
{
effort
=
string
(
kitreasoning
.
EffortFromBudget
(
*
config
.
ThinkingBudget
))
}
}
}
}
}
}
...
...
relay/helper/model_modifier.go
View file @
6b659fd6
...
@@ -211,7 +211,7 @@ func applyModelControls(req dto.Request, parsed parsedModelModifiers) error {
...
@@ -211,7 +211,7 @@ func applyModelControls(req dto.Request, parsed parsedModelModifiers) error {
delete
(
reasoningConfig
,
"max_tokens"
)
delete
(
reasoningConfig
,
"max_tokens"
)
request
.
ReasoningEffort
=
""
request
.
ReasoningEffort
=
""
if
parsed
.
intent
.
Effort
!=
""
{
if
parsed
.
intent
.
Effort
!=
""
{
request
.
ReasoningEffort
=
string
(
reasoning
.
OpenAIEffort
(
parsed
.
intent
.
Effort
)
)
request
.
ReasoningEffort
=
string
(
parsed
.
intent
.
Effort
)
}
}
}
}
if
len
(
reasoningConfig
)
==
0
{
if
len
(
reasoningConfig
)
==
0
{
...
@@ -237,7 +237,7 @@ func applyModelControls(req dto.Request, parsed parsedModelModifiers) error {
...
@@ -237,7 +237,7 @@ func applyModelControls(req dto.Request, parsed parsedModelModifiers) error {
if
request
.
Reasoning
==
nil
{
if
request
.
Reasoning
==
nil
{
request
.
Reasoning
=
&
dto
.
Reasoning
{}
request
.
Reasoning
=
&
dto
.
Reasoning
{}
}
}
request
.
Reasoning
.
Effort
=
string
(
reasoning
.
OpenAIEffort
(
parsed
.
intent
.
Effort
)
)
request
.
Reasoning
.
Effort
=
string
(
parsed
.
intent
.
Effort
)
}
else
if
request
.
Reasoning
!=
nil
&&
parsed
.
intent
.
BudgetTokens
==
nil
{
}
else
if
request
.
Reasoning
!=
nil
&&
parsed
.
intent
.
BudgetTokens
==
nil
{
request
.
Reasoning
.
Effort
=
""
request
.
Reasoning
.
Effort
=
""
}
}
...
...
relay/helper/reasoning_suffix.go
View file @
6b659fd6
...
@@ -106,6 +106,9 @@ func ApplyReasoningModelSuffix(c *gin.Context, info *relaycommon.RelayInfo, outb
...
@@ -106,6 +106,9 @@ func ApplyReasoningModelSuffix(c *gin.Context, info *relaycommon.RelayInfo, outb
if
info
.
Request
!=
nil
{
if
info
.
Request
!=
nil
{
info
.
Request
.
SetModelName
(
info
.
UpstreamModelName
)
info
.
Request
.
SetModelName
(
info
.
UpstreamModelName
)
}
}
if
selected
.
hasThinking
{
info
.
SetReasoningEffort
(
string
(
reasoning
.
EffectiveEffort
(
selected
.
intent
)))
}
for
i
:=
range
diagnostics
{
for
i
:=
range
diagnostics
{
diagnostics
[
i
]
.
From
=
info
.
RelayFormat
diagnostics
[
i
]
.
From
=
info
.
RelayFormat
}
}
...
...
relaykit/relayconvert/internal/shared/claude/reasoning.go
View file @
6b659fd6
...
@@ -35,14 +35,19 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
...
@@ -35,14 +35,19 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
// accounting metadata, but do not run the capability renderer or rewrite
// accounting metadata, but do not run the capability renderer or rewrite
// provider-native controls.
// provider-native controls.
if
!
crossProtocol
&&
source
.
IsEmpty
()
&&
suffix
.
IsEmpty
()
{
if
!
crossProtocol
&&
source
.
IsEmpty
()
&&
suffix
.
IsEmpty
()
{
native
,
err
:=
reasoning
.
FromClaude
(
req
)
if
err
!=
nil
{
return
err
}
if
info
!=
nil
{
if
info
!=
nil
{
if
effort
:=
reasoning
.
EffectiveEffort
(
native
);
effort
!=
""
{
effort
:=
req
.
GetEfforts
()
info
.
SetReasoningEffort
(
string
(
effort
))
if
effort
==
""
&&
req
.
Thinking
!=
nil
{
}
switch
{
case
req
.
Thinking
.
Type
==
"disabled"
:
effort
=
string
(
reasoning
.
EffortNone
)
case
req
.
Thinking
.
BudgetTokens
!=
nil
:
effort
=
string
(
reasoning
.
EffortFromBudget
(
*
req
.
Thinking
.
BudgetTokens
))
case
req
.
Thinking
.
Type
==
"enabled"
||
req
.
Thinking
.
Type
==
"adaptive"
:
effort
=
string
(
reasoning
.
EffortHigh
)
}
}
info
.
SetReasoningEffort
(
effort
)
}
}
return
nil
return
nil
}
}
...
...
relaykit/relayconvert/internal/shared/gemini/request.go
View file @
6b659fd6
...
@@ -106,14 +106,15 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Met
...
@@ -106,14 +106,15 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Met
// modifier, read portable effort metadata without running the capability
// modifier, read portable effort metadata without running the capability
// renderer or rewriting provider-native controls.
// renderer or rewriting provider-native controls.
if
!
crossProtocol
&&
suffix
.
IsEmpty
()
{
if
!
crossProtocol
&&
suffix
.
IsEmpty
()
{
native
,
err
:=
reasoning
.
FromGemini
(
geminiRequest
)
if
err
!=
nil
{
return
err
}
if
info
!=
nil
{
if
info
!=
nil
{
if
effort
:=
reasoning
.
EffectiveEffort
(
native
);
effort
!=
""
{
effort
:=
""
info
.
SetReasoningEffort
(
string
(
effort
))
if
config
:=
geminiRequest
.
GenerationConfig
.
ThinkingConfig
;
config
!=
nil
{
effort
=
config
.
ThinkingLevel
if
effort
==
""
&&
config
.
ThinkingBudget
!=
nil
{
effort
=
string
(
reasoning
.
EffortFromBudget
(
*
config
.
ThinkingBudget
))
}
}
}
info
.
SetReasoningEffort
(
effort
)
}
}
return
nil
return
nil
}
}
...
...
relaykit/relayconvert/reasoning/intent.go
View file @
6b659fd6
...
@@ -363,8 +363,8 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
...
@@ -363,8 +363,8 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
BudgetSource
:
SourcePivot
,
BudgetSource
:
SourcePivot
,
}
}
if
req
.
ReasoningEffort
!=
""
{
if
req
.
ReasoningEffort
!=
""
{
p
rojectedEffort
:=
OpenAIEffort
(
EffectiveEffort
(
pivot
)
)
p
ivotEffort
:=
EffectiveEffort
(
pivot
)
if
Effort
(
req
.
ReasoningEffort
)
==
p
rojected
Effort
{
if
Effort
(
req
.
ReasoningEffort
)
==
p
ivot
Effort
{
intent
.
Effort
=
""
intent
.
Effort
=
""
intent
.
Mode
=
ModeUnset
intent
.
Mode
=
ModeUnset
}
}
...
@@ -384,7 +384,7 @@ func ApplyToOpenAIChat(req *dto.GeneralOpenAIRequest, intent Intent) error {
...
@@ -384,7 +384,7 @@ func ApplyToOpenAIChat(req *dto.GeneralOpenAIRequest, intent Intent) error {
return
err
return
err
}
}
if
effort
:=
OpenAIEffort
(
EffectiveEffort
(
intent
)
);
effort
!=
""
{
if
effort
:=
EffectiveEffort
(
intent
);
effort
!=
""
{
req
.
ReasoningEffort
=
string
(
effort
)
req
.
ReasoningEffort
=
string
(
effort
)
}
}
...
@@ -412,7 +412,7 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
...
@@ -412,7 +412,7 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
return
err
return
err
}
}
if
effort
:=
OpenAIEffort
(
EffectiveEffort
(
intent
)
);
effort
!=
""
{
if
effort
:=
EffectiveEffort
(
intent
);
effort
!=
""
{
summary
:=
"detailed"
summary
:=
"detailed"
if
effort
==
EffortNone
||
(
intent
.
IncludeThoughts
!=
nil
&&
!*
intent
.
IncludeThoughts
)
{
if
effort
==
EffortNone
||
(
intent
.
IncludeThoughts
!=
nil
&&
!*
intent
.
IncludeThoughts
)
{
summary
=
""
summary
=
""
...
@@ -436,16 +436,6 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
...
@@ -436,16 +436,6 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
return
nil
return
nil
}
}
// OpenAIEffort maps the canonical cross-provider vocabulary to the public
// OpenAI reasoning_effort vocabulary. Claude/OpenRouter "max" has no direct
// OpenAI equivalent and is represented by xhigh at that wire boundary.
func
OpenAIEffort
(
effort
Effort
)
Effort
{
if
effort
==
EffortMax
{
return
EffortXHigh
}
return
effort
}
func
FromOpenAIResponses
(
req
*
dto
.
OpenAIResponsesRequest
)
(
Intent
,
error
)
{
func
FromOpenAIResponses
(
req
*
dto
.
OpenAIResponsesRequest
)
(
Intent
,
error
)
{
if
req
==
nil
{
if
req
==
nil
{
return
Intent
{},
nil
return
Intent
{},
nil
...
@@ -481,8 +471,8 @@ func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, error) {
...
@@ -481,8 +471,8 @@ func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, error) {
BudgetSource
:
SourcePivot
,
BudgetSource
:
SourcePivot
,
}
}
if
req
.
Reasoning
!=
nil
&&
req
.
Reasoning
.
Effort
!=
""
{
if
req
.
Reasoning
!=
nil
&&
req
.
Reasoning
.
Effort
!=
""
{
p
rojectedEffort
:=
OpenAIEffort
(
EffectiveEffort
(
pivot
)
)
p
ivotEffort
:=
EffectiveEffort
(
pivot
)
if
Effort
(
req
.
Reasoning
.
Effort
)
==
p
rojected
Effort
{
if
Effort
(
req
.
Reasoning
.
Effort
)
==
p
ivot
Effort
{
intent
.
Effort
=
""
intent
.
Effort
=
""
intent
.
Mode
=
ModeUnset
intent
.
Mode
=
ModeUnset
}
}
...
...
relaykit/relayconvert/reasoning/intent_test.go
View file @
6b659fd6
...
@@ -3,6 +3,7 @@ package reasoning
...
@@ -3,6 +3,7 @@ package reasoning
import
(
import
(
"testing"
"testing"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -123,3 +124,49 @@ func TestIntentStateRoundTrip(t *testing.T) {
...
@@ -123,3 +124,49 @@ func TestIntentStateRoundTrip(t *testing.T) {
func
boolPtr
(
v
bool
)
*
bool
{
func
boolPtr
(
v
bool
)
*
bool
{
return
&
v
return
&
v
}
}
func
TestOpenAIPivotRetainsExactStrengthAndBudget
(
t
*
testing
.
T
)
{
budget
,
include
:=
16384
,
false
for
_
,
effort
:=
range
[]
Effort
{
EffortMax
,
EffortXHigh
}
{
t
.
Run
(
string
(
effort
),
func
(
t
*
testing
.
T
)
{
intent
:=
Intent
{
Mode
:
ModeEnabled
,
Effort
:
effort
,
BudgetTokens
:
&
budget
,
IncludeThoughts
:
&
include
}
chat
:=
&
dto
.
GeneralOpenAIRequest
{}
require
.
NoError
(
t
,
ApplyToOpenAIChat
(
chat
,
intent
))
assert
.
Equal
(
t
,
string
(
effort
),
chat
.
ReasoningEffort
)
restored
,
err
:=
FromOpenAIChat
(
chat
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
effort
,
restored
.
Effort
)
require
.
NotNil
(
t
,
restored
.
BudgetTokens
)
assert
.
Equal
(
t
,
budget
,
*
restored
.
BudgetTokens
)
require
.
NotNil
(
t
,
restored
.
IncludeThoughts
)
assert
.
False
(
t
,
*
restored
.
IncludeThoughts
)
responses
:=
&
dto
.
OpenAIResponsesRequest
{}
require
.
NoError
(
t
,
ApplyToOpenAIResponses
(
responses
,
restored
))
require
.
NotNil
(
t
,
responses
.
Reasoning
)
assert
.
Equal
(
t
,
string
(
effort
),
responses
.
Reasoning
.
Effort
)
restored
,
err
=
FromOpenAIResponses
(
responses
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
effort
,
restored
.
Effort
)
require
.
NotNil
(
t
,
restored
.
BudgetTokens
)
assert
.
Equal
(
t
,
budget
,
*
restored
.
BudgetTokens
)
require
.
NotNil
(
t
,
restored
.
IncludeThoughts
)
assert
.
False
(
t
,
*
restored
.
IncludeThoughts
)
})
}
}
func
TestOpenAIPivotDoesNotTreatMaxAndXHighAsEquivalent
(
t
*
testing
.
T
)
{
intent
:=
Intent
{
Mode
:
ModeEnabled
,
Effort
:
EffortMax
}
chat
:=
&
dto
.
GeneralOpenAIRequest
{}
require
.
NoError
(
t
,
ApplyToOpenAIChat
(
chat
,
intent
))
chat
.
ReasoningEffort
=
"xhigh"
_
,
err
:=
FromOpenAIChat
(
chat
)
require
.
ErrorIs
(
t
,
err
,
ErrEffortConflict
)
responses
:=
&
dto
.
OpenAIResponsesRequest
{}
require
.
NoError
(
t
,
ApplyToOpenAIResponses
(
responses
,
intent
))
responses
.
Reasoning
.
Effort
=
"xhigh"
_
,
err
=
FromOpenAIResponses
(
responses
)
require
.
ErrorIs
(
t
,
err
,
ErrEffortConflict
)
}
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