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
89933867
authored
Apr 24, 2026
by
Calcium-Ion
Committed by
GitHub
Apr 24, 2026
Browse files
Options
Browse Files
Download
Plain Diff
feat: support DeepSeek V4 reasoning suffix handling (#4428)
parents
3a2138ba
435d7ae0
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
113 additions
and
19 deletions
+113
-19
relay/channel/deepseek/adaptor.go
+76
-1
relay/channel/deepseek/constants.go
+2
-0
relay/channel/openai/adaptor.go
+3
-17
setting/reasoning/suffix.go
+32
-1
No files found.
relay/channel/deepseek/adaptor.go
View file @
89933867
...
@@ -7,12 +7,14 @@ import (
...
@@ -7,12 +7,14 @@ import (
"net/http"
"net/http"
"strings"
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/relay/channel"
"github.com/QuantumNous/new-api/relay/channel"
"github.com/QuantumNous/new-api/relay/channel/claude"
"github.com/QuantumNous/new-api/relay/channel/claude"
"github.com/QuantumNous/new-api/relay/channel/openai"
"github.com/QuantumNous/new-api/relay/channel/openai"
relaycommon
"github.com/QuantumNous/new-api/relay/common"
relaycommon
"github.com/QuantumNous/new-api/relay/common"
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/QuantumNous/new-api/types"
"github.com/QuantumNous/new-api/types"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
)
...
@@ -27,7 +29,18 @@ func (a *Adaptor) ConvertGeminiRequest(*gin.Context, *relaycommon.RelayInfo, *dt
...
@@ -27,7 +29,18 @@ func (a *Adaptor) ConvertGeminiRequest(*gin.Context, *relaycommon.RelayInfo, *dt
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
req
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
req
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
adaptor
:=
claude
.
Adaptor
{}
adaptor
:=
claude
.
Adaptor
{}
return
adaptor
.
ConvertClaudeRequest
(
c
,
info
,
req
)
convertedRequest
,
err
:=
adaptor
.
ConvertClaudeRequest
(
c
,
info
,
req
)
if
err
!=
nil
{
return
nil
,
err
}
claudeRequest
,
ok
:=
convertedRequest
.
(
*
dto
.
ClaudeRequest
)
if
!
ok
{
return
convertedRequest
,
nil
}
if
err
:=
applyDeepSeekV4ClaudeThinkingSuffix
(
info
,
claudeRequest
);
err
!=
nil
{
return
nil
,
err
}
return
claudeRequest
,
nil
}
}
func
(
a
*
Adaptor
)
ConvertAudioRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
AudioRequest
)
(
io
.
Reader
,
error
)
{
func
(
a
*
Adaptor
)
ConvertAudioRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
AudioRequest
)
(
io
.
Reader
,
error
)
{
...
@@ -71,9 +84,71 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -71,9 +84,71 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
if
request
==
nil
{
if
request
==
nil
{
return
nil
,
errors
.
New
(
"request is nil"
)
return
nil
,
errors
.
New
(
"request is nil"
)
}
}
if
err
:=
applyDeepSeekV4OpenAIThinkingSuffix
(
info
,
request
);
err
!=
nil
{
return
nil
,
err
}
return
request
,
nil
return
request
,
nil
}
}
func
applyDeepSeekV4OpenAIThinkingSuffix
(
info
*
relaycommon
.
RelayInfo
,
request
*
dto
.
GeneralOpenAIRequest
)
error
{
modelName
:=
request
.
Model
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
modelName
=
info
.
UpstreamModelName
}
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
if
!
ok
{
return
nil
}
thinking
,
err
:=
common
.
Marshal
(
map
[
string
]
string
{
"type"
:
thinkingType
,
})
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error marshalling thinking: %w"
,
err
)
}
request
.
Model
=
baseModel
request
.
THINKING
=
thinking
request
.
ReasoningEffort
=
effort
if
info
!=
nil
{
if
info
.
ChannelMeta
!=
nil
{
info
.
UpstreamModelName
=
baseModel
}
info
.
ReasoningEffort
=
effort
}
return
nil
}
func
applyDeepSeekV4ClaudeThinkingSuffix
(
info
*
relaycommon
.
RelayInfo
,
request
*
dto
.
ClaudeRequest
)
error
{
modelName
:=
request
.
Model
if
info
!=
nil
&&
info
.
ChannelMeta
!=
nil
&&
info
.
UpstreamModelName
!=
""
{
modelName
=
info
.
UpstreamModelName
}
baseModel
,
thinkingType
,
effort
,
ok
:=
reasoning
.
ParseDeepSeekV4ThinkingSuffix
(
modelName
)
if
!
ok
{
return
nil
}
request
.
Model
=
baseModel
request
.
Thinking
=
&
dto
.
Thinking
{
Type
:
thinkingType
}
if
effort
==
""
{
request
.
OutputConfig
=
nil
}
else
{
outputConfig
,
err
:=
common
.
Marshal
(
map
[
string
]
string
{
"effort"
:
effort
,
})
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error marshalling output_config: %w"
,
err
)
}
request
.
OutputConfig
=
outputConfig
}
if
info
!=
nil
{
if
info
.
ChannelMeta
!=
nil
{
info
.
UpstreamModelName
=
baseModel
}
info
.
ReasoningEffort
=
effort
}
return
nil
}
func
(
a
*
Adaptor
)
ConvertRerankRequest
(
c
*
gin
.
Context
,
relayMode
int
,
request
dto
.
RerankRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertRerankRequest
(
c
*
gin
.
Context
,
relayMode
int
,
request
dto
.
RerankRequest
)
(
any
,
error
)
{
return
nil
,
nil
return
nil
,
nil
}
}
...
...
relay/channel/deepseek/constants.go
View file @
89933867
...
@@ -2,6 +2,8 @@ package deepseek
...
@@ -2,6 +2,8 @@ package deepseek
var
ModelList
=
[]
string
{
var
ModelList
=
[]
string
{
"deepseek-chat"
,
"deepseek-reasoner"
,
"deepseek-chat"
,
"deepseek-reasoner"
,
"deepseek-v4-flash"
,
"deepseek-v4-flash-none"
,
"deepseek-v4-flash-max"
,
"deepseek-v4-pro"
,
"deepseek-v4-pro-none"
,
"deepseek-v4-pro-max"
,
}
}
var
ChannelName
=
"deepseek"
var
ChannelName
=
"deepseek"
relay/channel/openai/adaptor.go
View file @
89933867
...
@@ -28,6 +28,7 @@ import (
...
@@ -28,6 +28,7 @@ import (
relayconstant
"github.com/QuantumNous/new-api/relay/constant"
relayconstant
"github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/reasoning"
"github.com/QuantumNous/new-api/types"
"github.com/QuantumNous/new-api/types"
"github.com/samber/lo"
"github.com/samber/lo"
...
@@ -39,21 +40,6 @@ type Adaptor struct {
...
@@ -39,21 +40,6 @@ type Adaptor struct {
ResponseFormat
string
ResponseFormat
string
}
}
// parseReasoningEffortFromModelSuffix 从模型名称中解析推理级别
// support OAI models: o1-mini/o3-mini/o4-mini/o1/o3 etc...
// minimal effort only available in gpt-5
func
parseReasoningEffortFromModelSuffix
(
model
string
)
(
string
,
string
)
{
effortSuffixes
:=
[]
string
{
"-high"
,
"-minimal"
,
"-low"
,
"-medium"
,
"-none"
,
"-xhigh"
}
for
_
,
suffix
:=
range
effortSuffixes
{
if
strings
.
HasSuffix
(
model
,
suffix
)
{
effort
:=
strings
.
TrimPrefix
(
suffix
,
"-"
)
originModel
:=
strings
.
TrimSuffix
(
model
,
suffix
)
return
effort
,
originModel
}
}
return
""
,
model
}
func
(
a
*
Adaptor
)
ConvertGeminiRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
*
dto
.
GeminiChatRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertGeminiRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
*
dto
.
GeminiChatRequest
)
(
any
,
error
)
{
// 使用 service.GeminiToOpenAIRequest 转换请求格式
// 使用 service.GeminiToOpenAIRequest 转换请求格式
openaiRequest
,
err
:=
service
.
GeminiToOpenAIRequest
(
request
,
info
)
openaiRequest
,
err
:=
service
.
GeminiToOpenAIRequest
(
request
,
info
)
...
@@ -342,7 +328,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -342,7 +328,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
}
}
// 转换模型推理力度后缀
// 转换模型推理力度后缀
effort
,
originModel
:=
parse
ReasoningEffortFromModelSuffix
(
info
.
UpstreamModelName
)
effort
,
originModel
:=
reasoning
.
ParseOpenAI
ReasoningEffortFromModelSuffix
(
info
.
UpstreamModelName
)
if
effort
!=
""
{
if
effort
!=
""
{
request
.
ReasoningEffort
=
effort
request
.
ReasoningEffort
=
effort
info
.
UpstreamModelName
=
originModel
info
.
UpstreamModelName
=
originModel
...
@@ -587,7 +573,7 @@ func detectImageMimeType(filename string) string {
...
@@ -587,7 +573,7 @@ func detectImageMimeType(filename string) string {
func
(
a
*
Adaptor
)
ConvertOpenAIResponsesRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
OpenAIResponsesRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertOpenAIResponsesRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
OpenAIResponsesRequest
)
(
any
,
error
)
{
// 转换模型推理力度后缀
// 转换模型推理力度后缀
effort
,
originModel
:=
parse
ReasoningEffortFromModelSuffix
(
request
.
Model
)
effort
,
originModel
:=
reasoning
.
ParseOpenAI
ReasoningEffortFromModelSuffix
(
request
.
Model
)
if
effort
!=
""
{
if
effort
!=
""
{
if
request
.
Reasoning
==
nil
{
if
request
.
Reasoning
==
nil
{
request
.
Reasoning
=
&
dto
.
Reasoning
{
request
.
Reasoning
=
&
dto
.
Reasoning
{
...
...
setting/reasoning/suffix.go
View file @
89933867
...
@@ -8,9 +8,17 @@ import (
...
@@ -8,9 +8,17 @@ import (
var
EffortSuffixes
=
[]
string
{
"-max"
,
"-xhigh"
,
"-high"
,
"-medium"
,
"-low"
,
"-minimal"
}
var
EffortSuffixes
=
[]
string
{
"-max"
,
"-xhigh"
,
"-high"
,
"-medium"
,
"-low"
,
"-minimal"
}
var
OpenAIEffortSuffixes
=
[]
string
{
"-high"
,
"-minimal"
,
"-low"
,
"-medium"
,
"-none"
,
"-xhigh"
}
var
DeepSeekV4EffortSuffixes
=
[]
string
{
"-none"
,
"-max"
}
// TrimEffortSuffix -> modelName level(low) exists
// TrimEffortSuffix -> modelName level(low) exists
func
TrimEffortSuffix
(
modelName
string
)
(
string
,
string
,
bool
)
{
func
TrimEffortSuffix
(
modelName
string
)
(
string
,
string
,
bool
)
{
suffix
,
found
:=
lo
.
Find
(
EffortSuffixes
,
func
(
s
string
)
bool
{
return
TrimEffortSuffixWithSuffixes
(
modelName
,
EffortSuffixes
)
}
func
TrimEffortSuffixWithSuffixes
(
modelName
string
,
suffixes
[]
string
)
(
string
,
string
,
bool
)
{
suffix
,
found
:=
lo
.
Find
(
suffixes
,
func
(
s
string
)
bool
{
return
strings
.
HasSuffix
(
modelName
,
s
)
return
strings
.
HasSuffix
(
modelName
,
s
)
})
})
if
!
found
{
if
!
found
{
...
@@ -18,3 +26,26 @@ func TrimEffortSuffix(modelName string) (string, string, bool) {
...
@@ -18,3 +26,26 @@ func TrimEffortSuffix(modelName string) (string, string, bool) {
}
}
return
strings
.
TrimSuffix
(
modelName
,
suffix
),
strings
.
TrimPrefix
(
suffix
,
"-"
),
true
return
strings
.
TrimSuffix
(
modelName
,
suffix
),
strings
.
TrimPrefix
(
suffix
,
"-"
),
true
}
}
func
ParseOpenAIReasoningEffortFromModelSuffix
(
modelName
string
)
(
string
,
string
)
{
baseModel
,
effort
,
ok
:=
TrimEffortSuffixWithSuffixes
(
modelName
,
OpenAIEffortSuffixes
)
if
!
ok
{
return
""
,
modelName
}
return
effort
,
baseModel
}
func
ParseDeepSeekV4ThinkingSuffix
(
modelName
string
)
(
baseModel
string
,
thinkingType
string
,
effort
string
,
ok
bool
)
{
baseModel
,
suffix
,
ok
:=
TrimEffortSuffixWithSuffixes
(
modelName
,
DeepSeekV4EffortSuffixes
)
if
!
ok
||
!
strings
.
HasPrefix
(
baseModel
,
"deepseek-v4-"
)
{
return
modelName
,
""
,
""
,
false
}
switch
suffix
{
case
"none"
:
return
baseModel
,
"disabled"
,
""
,
true
case
"max"
:
return
baseModel
,
"enabled"
,
"max"
,
true
default
:
return
modelName
,
""
,
""
,
false
}
}
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