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
eab18a83
authored
Aug 10, 2026
by
Seefs
Committed by
GitHub
Aug 10, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: record reasoning effort consistently in usage logs (#6641)
parent
7dd1000a
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
333 additions
and
14 deletions
+333
-14
relay/channel/deepseek/adaptor.go
+3
-3
relay/channel/openai/adaptor.go
+2
-2
relay/channel/xai/adaptor.go
+1
-1
relay/claude_handler.go
+5
-0
relay/common/override.go
+51
-0
relay/common/override_test.go
+111
-0
relay/common/relay_info.go
+39
-2
relay/common/relay_info_test.go
+98
-0
web/src/features/usage-logs/components/dialogs/details-dialog.tsx
+4
-6
web/src/features/usage-logs/lib/format.ts
+19
-0
No files found.
relay/channel/deepseek/adaptor.go
View file @
eab18a83
...
@@ -115,7 +115,7 @@ func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *d
...
@@ -115,7 +115,7 @@ func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *d
if
info
.
ChannelMeta
!=
nil
{
if
info
.
ChannelMeta
!=
nil
{
info
.
UpstreamModelName
=
baseModel
info
.
UpstreamModelName
=
baseModel
}
}
info
.
ReasoningEffort
=
effort
info
.
SetReasoningEffort
(
effort
)
}
}
return
nil
return
nil
}
}
...
@@ -146,7 +146,7 @@ func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *d
...
@@ -146,7 +146,7 @@ func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *d
if
info
.
ChannelMeta
!=
nil
{
if
info
.
ChannelMeta
!=
nil
{
info
.
UpstreamModelName
=
baseModel
info
.
UpstreamModelName
=
baseModel
}
}
info
.
ReasoningEffort
=
effort
info
.
SetReasoningEffort
(
effort
)
}
}
return
nil
return
nil
}
}
...
@@ -185,7 +185,7 @@ func applyDeepSeekV4ResponsesThinkingSuffix(info *relaycommon.RelayInfo, request
...
@@ -185,7 +185,7 @@ func applyDeepSeekV4ResponsesThinkingSuffix(info *relaycommon.RelayInfo, request
}
}
}
}
if
info
!=
nil
&&
request
.
Reasoning
!=
nil
{
if
info
!=
nil
&&
request
.
Reasoning
!=
nil
{
info
.
ReasoningEffort
=
request
.
Reasoning
.
Effort
info
.
SetReasoningEffort
(
request
.
Reasoning
.
Effort
)
}
}
}
}
...
...
relay/channel/openai/adaptor.go
View file @
eab18a83
...
@@ -352,7 +352,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -352,7 +352,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
request
.
Model
=
originModel
request
.
Model
=
originModel
}
}
info
.
ReasoningEffort
=
request
.
ReasoningEffort
info
.
SetReasoningEffort
(
request
.
ReasoningEffort
)
// o系列模型developer适配(o1-mini除外)
// o系列模型developer适配(o1-mini除外)
if
!
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"o1-mini"
)
&&
!
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"o1-preview"
)
{
if
!
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"o1-mini"
)
&&
!
strings
.
HasPrefix
(
info
.
UpstreamModelName
,
"o1-preview"
)
{
...
@@ -615,7 +615,7 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
...
@@ -615,7 +615,7 @@ func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommo
request
.
Model
=
originModel
request
.
Model
=
originModel
}
}
if
info
!=
nil
&&
request
.
Reasoning
!=
nil
&&
request
.
Reasoning
.
Effort
!=
""
{
if
info
!=
nil
&&
request
.
Reasoning
!=
nil
&&
request
.
Reasoning
.
Effort
!=
""
{
info
.
ReasoningEffort
=
request
.
Reasoning
.
Effort
info
.
SetReasoningEffort
(
request
.
Reasoning
.
Effort
)
}
}
return
request
,
nil
return
request
,
nil
}
}
...
...
relay/channel/xai/adaptor.go
View file @
eab18a83
...
@@ -85,7 +85,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
...
@@ -85,7 +85,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
request
.
ReasoningEffort
=
"low"
request
.
ReasoningEffort
=
"low"
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-low"
)
request
.
Model
=
strings
.
TrimSuffix
(
request
.
Model
,
"-low"
)
}
}
info
.
ReasoningEffort
=
request
.
ReasoningEffort
info
.
SetReasoningEffort
(
request
.
ReasoningEffort
)
info
.
UpstreamModelName
=
request
.
Model
info
.
UpstreamModelName
=
request
.
Model
}
}
return
request
,
nil
return
request
,
nil
...
...
relay/claude_handler.go
View file @
eab18a83
...
@@ -106,6 +106,11 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ
...
@@ -106,6 +106,11 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ
}
}
info
.
UpstreamModelName
=
request
.
Model
info
.
UpstreamModelName
=
request
.
Model
}
}
if
!
model_setting
.
GetGlobalSettings
()
.
PassThroughRequestEnabled
&&
!
info
.
ChannelSetting
.
PassThroughBodyEnabled
{
if
effort
:=
request
.
GetEfforts
();
effort
!=
""
{
info
.
SetReasoningEffort
(
effort
)
}
}
if
info
.
ChannelSetting
.
SystemPrompt
!=
""
{
if
info
.
ChannelSetting
.
SystemPrompt
!=
""
{
if
request
.
System
==
nil
{
if
request
.
System
==
nil
{
...
...
relay/common/override.go
View file @
eab18a83
...
@@ -30,6 +30,11 @@ var paramOverrideSensitivePathPrefixes = []string{
...
@@ -30,6 +30,11 @@ var paramOverrideSensitivePathPrefixes = []string{
"model"
,
"model"
,
"original_model"
,
"original_model"
,
"upstream_model"
,
"upstream_model"
,
"reasoning"
,
"reasoning_effort"
,
"output_config"
,
"generationConfig.thinkingConfig"
,
"generation_config.thinking_config"
,
"service_tier"
,
"service_tier"
,
"inference_geo"
,
"inference_geo"
,
"speed"
,
"speed"
,
...
@@ -191,6 +196,7 @@ func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte,
...
@@ -191,6 +196,7 @@ func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte,
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
syncReasoningEffortAfterParamOverride
(
info
,
jsonData
,
result
)
syncRuntimeHeaderOverrideFromContext
(
info
,
overrideCtx
)
syncRuntimeHeaderOverrideFromContext
(
info
,
overrideCtx
)
if
info
!=
nil
{
if
info
!=
nil
{
if
recorder
!=
nil
{
if
recorder
!=
nil
{
...
@@ -202,6 +208,51 @@ func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte,
...
@@ -202,6 +208,51 @@ func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte,
return
result
,
nil
return
result
,
nil
}
}
func
syncReasoningEffortAfterParamOverride
(
info
*
RelayInfo
,
before
,
after
[]
byte
)
{
if
info
==
nil
{
return
}
_
,
existedBefore
:=
extractReasoningEffortFromJSON
(
info
.
GetFinalRequestRelayFormat
(),
before
)
effort
,
existsAfter
:=
extractReasoningEffortFromJSON
(
info
.
GetFinalRequestRelayFormat
(),
after
)
if
existsAfter
{
info
.
SetReasoningEffort
(
effort
)
return
}
if
existedBefore
{
info
.
SetReasoningEffort
(
""
)
}
}
func
extractReasoningEffortFromJSON
(
format
types
.
RelayFormat
,
data
[]
byte
)
(
string
,
bool
)
{
var
paths
[]
string
switch
format
{
case
types
.
RelayFormatOpenAI
:
paths
=
[]
string
{
"reasoning_effort"
,
"reasoning.effort"
}
case
types
.
RelayFormatOpenAIResponses
:
paths
=
[]
string
{
"reasoning.effort"
}
case
types
.
RelayFormatClaude
:
paths
=
[]
string
{
"output_config.effort"
}
case
types
.
RelayFormatGemini
:
paths
=
[]
string
{
"generationConfig.thinkingConfig.thinkingLevel"
,
"generation_config.thinking_config.thinking_level"
,
}
default
:
return
""
,
false
}
for
_
,
path
:=
range
paths
{
value
:=
gjson
.
GetBytes
(
data
,
path
)
if
!
value
.
Exists
()
{
continue
}
if
value
.
Type
!=
gjson
.
String
{
return
""
,
true
}
return
strings
.
TrimSpace
(
value
.
String
()),
true
}
return
""
,
false
}
func
shouldEnableParamOverrideAudit
(
paramOverride
map
[
string
]
interface
{})
bool
{
func
shouldEnableParamOverrideAudit
(
paramOverride
map
[
string
]
interface
{})
bool
{
if
common
.
DebugEnabled
{
if
common
.
DebugEnabled
{
return
true
return
true
...
...
relay/common/override_test.go
View file @
eab18a83
...
@@ -12,6 +12,7 @@ import (
...
@@ -12,6 +12,7 @@ import (
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/samber/lo"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -2302,3 +2303,113 @@ func assertJSONEqual(t *testing.T, want, got string) {
...
@@ -2302,3 +2303,113 @@ func assertJSONEqual(t *testing.T, want, got string) {
t
.
Fatalf
(
"json not equal
\n
want: %s
\n
got: %s"
,
want
,
got
)
t
.
Fatalf
(
"json not equal
\n
want: %s
\n
got: %s"
,
want
,
got
)
}
}
}
}
func
TestApplyParamOverrideWithRelayInfoSynchronizesReasoningEffort
(
t
*
testing
.
T
)
{
originalDebugEnabled
:=
common2
.
DebugEnabled
common2
.
DebugEnabled
=
false
t
.
Cleanup
(
func
()
{
common2
.
DebugEnabled
=
originalDebugEnabled
})
tests
:=
[]
struct
{
name
string
relayFormat
types
.
RelayFormat
initialEffort
string
input
string
operation
map
[
string
]
interface
{}
expected
string
}{
{
name
:
"Responses set"
,
relayFormat
:
types
.
RelayFormatOpenAIResponses
,
initialEffort
:
"high"
,
input
:
`{"reasoning":{"effort":"high"}}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"reasoning.effort"
,
"value"
:
"max"
},
expected
:
"max"
,
},
{
name
:
"chat delete"
,
relayFormat
:
types
.
RelayFormatOpenAI
,
initialEffort
:
"high"
,
input
:
`{"reasoning_effort":"high"}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"delete"
,
"path"
:
"reasoning_effort"
},
expected
:
""
,
},
{
name
:
"OpenRouter nested set"
,
relayFormat
:
types
.
RelayFormatOpenAI
,
initialEffort
:
"medium"
,
input
:
`{"reasoning":{"effort":"medium"}}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"reasoning.effort"
,
"value"
:
"xhigh"
},
expected
:
"xhigh"
,
},
{
name
:
"Claude output config set"
,
relayFormat
:
types
.
RelayFormatClaude
,
initialEffort
:
"high"
,
input
:
`{"output_config":{"effort":"high"}}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"output_config.effort"
,
"value"
:
"max"
},
expected
:
"max"
,
},
{
name
:
"Gemini thinking level set"
,
relayFormat
:
types
.
RelayFormatGemini
,
initialEffort
:
"medium"
,
input
:
`{"generationConfig":{"thinkingConfig":{"thinkingLevel":"medium"}}}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"generationConfig.thinkingConfig.thinkingLevel"
,
"value"
:
"high"
},
expected
:
"high"
,
},
{
name
:
"non-string value clears effort"
,
relayFormat
:
types
.
RelayFormatOpenAIResponses
,
initialEffort
:
"high"
,
input
:
`{"reasoning":{"effort":"high"}}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"reasoning.effort"
,
"value"
:
42
},
expected
:
""
,
},
{
name
:
"unrelated override preserves converter-derived effort"
,
relayFormat
:
types
.
RelayFormatClaude
,
initialEffort
:
"high"
,
input
:
`{"thinking":{"type":"adaptive"},"max_tokens":4096}`
,
operation
:
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"max_tokens"
,
"value"
:
8192
},
expected
:
"high"
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
info
:=
&
RelayInfo
{
RelayFormat
:
tt
.
relayFormat
,
ReasoningEffort
:
tt
.
initialEffort
,
ChannelMeta
:
&
ChannelMeta
{
ParamOverride
:
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
tt
.
operation
},
}},
}
_
,
err
:=
ApplyParamOverrideWithRelayInfo
([]
byte
(
tt
.
input
),
info
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
expected
,
info
.
ReasoningEffort
)
})
}
}
func
TestReasoningEffortOverrideIsAuditedWithoutDebugMode
(
t
*
testing
.
T
)
{
originalDebugEnabled
:=
common2
.
DebugEnabled
common2
.
DebugEnabled
=
false
t
.
Cleanup
(
func
()
{
common2
.
DebugEnabled
=
originalDebugEnabled
})
info
:=
&
RelayInfo
{
RelayFormat
:
types
.
RelayFormatOpenAIResponses
,
ChannelMeta
:
&
ChannelMeta
{
ParamOverride
:
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"mode"
:
"set"
,
"path"
:
"reasoning.effort"
,
"value"
:
"max"
},
},
}},
}
_
,
err
:=
ApplyParamOverrideWithRelayInfo
([]
byte
(
`{"reasoning":{"effort":"high"}}`
),
info
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
[]
string
{
"set reasoning.effort = max"
},
info
.
ParamOverrideAudit
)
}
relay/common/relay_info.go
View file @
eab18a83
...
@@ -234,6 +234,11 @@ func (info *RelayInfo) InitChannelMeta(c *gin.Context) {
...
@@ -234,6 +234,11 @@ func (info *RelayInfo) InitChannelMeta(c *gin.Context) {
// Channel identity feeds the converter options snapshot (e.g.
// Channel identity feeds the converter options snapshot (e.g.
// OpenRouterDialect); drop the cache so a cross-channel retry rebuilds it.
// OpenRouterDialect); drop the cache so a cross-channel retry rebuilds it.
info
.
convOptions
=
nil
info
.
convOptions
=
nil
if
model_setting
.
GetGlobalSettings
()
.
PassThroughRequestEnabled
||
channelMeta
.
ChannelSetting
.
PassThroughBodyEnabled
{
info
.
ReasoningEffort
=
""
}
else
{
info
.
ReasoningEffort
=
reasoningEffortFromRequest
(
info
.
Request
)
}
// reset some fields based on channel meta
// reset some fields based on channel meta
// 重置某些字段,例如模型名称等
// 重置某些字段,例如模型名称等
...
@@ -435,6 +440,36 @@ func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo {
...
@@ -435,6 +440,36 @@ func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo {
return
info
return
info
}
}
func
reasoningEffortFromRequest
(
request
dto
.
Request
)
string
{
var
effort
string
switch
req
:=
request
.
(
type
)
{
case
*
dto
.
GeneralOpenAIRequest
:
if
req
==
nil
{
return
""
}
effort
=
req
.
ReasoningEffort
if
strings
.
TrimSpace
(
effort
)
==
""
&&
len
(
req
.
Reasoning
)
>
0
{
value
:=
gjson
.
GetBytes
(
req
.
Reasoning
,
"effort"
)
if
value
.
Type
==
gjson
.
String
{
effort
=
value
.
String
()
}
}
case
*
dto
.
OpenAIResponsesRequest
:
if
req
!=
nil
&&
req
.
Reasoning
!=
nil
{
effort
=
req
.
Reasoning
.
Effort
}
case
*
dto
.
ClaudeRequest
:
if
req
!=
nil
{
effort
=
req
.
GetEfforts
()
}
case
*
dto
.
GeminiChatRequest
:
if
req
!=
nil
&&
req
.
GenerationConfig
.
ThinkingConfig
!=
nil
{
effort
=
req
.
GenerationConfig
.
ThinkingConfig
.
ThinkingLevel
}
}
return
strings
.
TrimSpace
(
effort
)
}
func
genBaseRelayInfo
(
c
*
gin
.
Context
,
request
dto
.
Request
)
*
RelayInfo
{
func
genBaseRelayInfo
(
c
*
gin
.
Context
,
request
dto
.
Request
)
*
RelayInfo
{
//channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
//channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
...
@@ -465,8 +500,10 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo {
...
@@ -465,8 +500,10 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo {
if
reqId
==
""
{
if
reqId
==
""
{
reqId
=
common
.
NewRequestId
()
reqId
=
common
.
NewRequestId
()
}
}
reasoningEffort
:=
reasoningEffortFromRequest
(
request
)
info
:=
&
RelayInfo
{
info
:=
&
RelayInfo
{
Request
:
request
,
Request
:
request
,
ReasoningEffort
:
reasoningEffort
,
RequestId
:
reqId
,
RequestId
:
reqId
,
UserId
:
common
.
GetContextKeyInt
(
c
,
constant
.
ContextKeyUserId
),
UserId
:
common
.
GetContextKeyInt
(
c
,
constant
.
ContextKeyUserId
),
...
@@ -740,7 +777,7 @@ func (info *RelayInfo) SetReasoningEffort(effort string) {
...
@@ -740,7 +777,7 @@ func (info *RelayInfo) SetReasoningEffort(effort string) {
if
info
==
nil
{
if
info
==
nil
{
return
return
}
}
info
.
ReasoningEffort
=
effort
info
.
ReasoningEffort
=
strings
.
TrimSpace
(
effort
)
}
}
func
(
info
*
RelayInfo
)
EnsureClaudeConvertInfo
()
*
convmeta
.
ClaudeConvertInfo
{
func
(
info
*
RelayInfo
)
EnsureClaudeConvertInfo
()
*
convmeta
.
ClaudeConvertInfo
{
...
...
relay/common/relay_info_test.go
View file @
eab18a83
package
common
package
common
import
(
import
(
"encoding/json"
"net/http/httptest"
"testing"
"testing"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -78,3 +82,97 @@ func TestRelayInfoMetaTypedNilReceiver(t *testing.T) {
...
@@ -78,3 +82,97 @@ func TestRelayInfoMetaTypedNilReceiver(t *testing.T) {
assert
.
NotNil
(
t
,
firstOptions
.
Gemini
.
SafetySetting
)
assert
.
NotNil
(
t
,
firstOptions
.
Gemini
.
SafetySetting
)
assert
.
NotNil
(
t
,
firstOptions
.
PreserveThinkingSuffix
)
assert
.
NotNil
(
t
,
firstOptions
.
PreserveThinkingSuffix
)
}
}
func
TestGenRelayInfoCapturesRequestReasoningEffort
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
tests
:=
[]
struct
{
name
string
path
string
relayFormat
types
.
RelayFormat
request
dto
.
Request
expected
string
}{
{
name
:
"OpenAI chat top-level effort"
,
path
:
"/v1/chat/completions"
,
relayFormat
:
types
.
RelayFormatOpenAI
,
request
:
&
dto
.
GeneralOpenAIRequest
{
Model
:
"gpt-5.6-sol"
,
ReasoningEffort
:
" high "
},
expected
:
"high"
,
},
{
name
:
"OpenRouter nested chat effort"
,
path
:
"/v1/chat/completions"
,
relayFormat
:
types
.
RelayFormatOpenAI
,
request
:
&
dto
.
GeneralOpenAIRequest
{
Model
:
"anthropic/claude"
,
Reasoning
:
json
.
RawMessage
(
`{"effort":"xhigh"}`
)},
expected
:
"xhigh"
,
},
{
name
:
"OpenAI Responses effort"
,
path
:
"/v1/responses"
,
relayFormat
:
types
.
RelayFormatOpenAIResponses
,
request
:
&
dto
.
OpenAIResponsesRequest
{
Model
:
"gpt-5.6-sol"
,
Reasoning
:
&
dto
.
Reasoning
{
Effort
:
"max"
}},
expected
:
"max"
,
},
{
name
:
"explicit none is preserved"
,
path
:
"/v1/responses"
,
relayFormat
:
types
.
RelayFormatOpenAIResponses
,
request
:
&
dto
.
OpenAIResponsesRequest
{
Model
:
"gpt-5.6-sol"
,
Reasoning
:
&
dto
.
Reasoning
{
Effort
:
"none"
}},
expected
:
"none"
,
},
{
name
:
"non-string nested effort is ignored"
,
path
:
"/v1/chat/completions"
,
relayFormat
:
types
.
RelayFormatOpenAI
,
request
:
&
dto
.
GeneralOpenAIRequest
{
Model
:
"anthropic/claude"
,
Reasoning
:
json
.
RawMessage
(
`{"effort":42}`
)},
expected
:
""
,
},
{
name
:
"Claude output config effort"
,
path
:
"/v1/messages"
,
relayFormat
:
types
.
RelayFormatClaude
,
request
:
&
dto
.
ClaudeRequest
{
Model
:
"claude-opus-4-7"
,
OutputConfig
:
json
.
RawMessage
(
`{"effort":"medium"}`
)},
expected
:
"medium"
,
},
{
name
:
"Gemini thinking level"
,
path
:
"/v1beta/models/gemini-3-pro:generateContent"
,
relayFormat
:
types
.
RelayFormatGemini
,
request
:
&
dto
.
GeminiChatRequest
{
GenerationConfig
:
dto
.
GeminiChatGenerationConfig
{
ThinkingConfig
:
&
dto
.
GeminiThinkingConfig
{
ThinkingLevel
:
"low"
},
}},
expected
:
"low"
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
ctx
,
_
:=
gin
.
CreateTestContext
(
httptest
.
NewRecorder
())
ctx
.
Request
=
httptest
.
NewRequest
(
"POST"
,
tt
.
path
,
nil
)
info
,
err
:=
GenRelayInfo
(
ctx
,
tt
.
relayFormat
,
tt
.
request
,
nil
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
expected
,
info
.
ReasoningEffort
)
})
}
}
func
TestInitChannelMetaRestoresRequestReasoningEffortForRetry
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
ctx
,
_
:=
gin
.
CreateTestContext
(
httptest
.
NewRecorder
())
ctx
.
Request
=
httptest
.
NewRequest
(
"POST"
,
"/v1/responses"
,
nil
)
request
:=
&
dto
.
OpenAIResponsesRequest
{
Model
:
"gpt-5.6-sol"
,
Reasoning
:
&
dto
.
Reasoning
{
Effort
:
"max"
},
}
info
,
err
:=
GenRelayInfo
(
ctx
,
types
.
RelayFormatOpenAIResponses
,
request
,
nil
)
require
.
NoError
(
t
,
err
)
info
.
SetReasoningEffort
(
"high"
)
info
.
InitChannelMeta
(
ctx
)
assert
.
Equal
(
t
,
"max"
,
info
.
ReasoningEffort
)
info
.
SetReasoningEffort
(
"low"
)
info
.
InitChannelMeta
(
ctx
)
assert
.
Equal
(
t
,
"max"
,
info
.
ReasoningEffort
)
}
web/src/features/usage-logs/components/dialogs/details-dialog.tsx
View file @
eab18a83
...
@@ -74,6 +74,7 @@ import {
...
@@ -74,6 +74,7 @@ import {
isViolationFeeLog
,
isViolationFeeLog
,
getFirstResponseTimeColor
,
getFirstResponseTimeColor
,
getResponseTimeColor
,
getResponseTimeColor
,
getReasoningEffortVariant
,
renderAuditContent
,
renderAuditContent
,
}
from
'../../lib/format'
}
from
'../../lib/format'
import
{
import
{
...
@@ -604,12 +605,9 @@ export function DetailsDialog(props: DetailsDialogProps) {
...
@@ -604,12 +605,9 @@ export function DetailsDialog(props: DetailsDialogProps) {
const
useChannel
=
other
?.
admin_info
?.
use_channel
const
useChannel
=
other
?.
admin_info
?.
use_channel
const
channelChain
=
const
channelChain
=
useChannel
&&
useChannel
.
length
>
0
?
useChannel
.
join
(
' → '
)
:
undefined
useChannel
&&
useChannel
.
length
>
0
?
useChannel
.
join
(
' → '
)
:
undefined
let
reasoningEffortVariant
:
StatusBadgeProps
[
'variant'
]
=
'green'
const
reasoningEffortVariant
=
getReasoningEffortVariant
(
if
(
other
?.
reasoning_effort
===
'high'
)
{
other
?.
reasoning_effort
reasoningEffortVariant
=
'orange'
)
}
else
if
(
other
?.
reasoning_effort
===
'medium'
)
{
reasoningEffortVariant
=
'yellow'
}
return
(
return
(
<
Dialog
<
Dialog
...
...
web/src/features/usage-logs/lib/format.ts
View file @
eab18a83
...
@@ -167,6 +167,25 @@ export function parseLogOther(other: string): LogOtherData | null {
...
@@ -167,6 +167,25 @@ export function parseLogOther(other: string): LogOtherData | null {
}
}
}
}
export
function
getReasoningEffortVariant
(
effort
:
string
|
undefined
):
StatusBadgeProps
[
'variant'
]
{
switch
(
effort
?.
trim
().
toLowerCase
())
{
case
'max'
:
case
'xhigh'
:
case
'high'
:
return
'orange'
case
'medium'
:
return
'yellow'
case
'low'
:
case
'minimal'
:
return
'green'
case
'none'
:
default
:
return
'grey'
}
}
/**
/**
* Get time color based on duration (in seconds)
* Get time color based on duration (in seconds)
*/
*/
...
...
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