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
df4c0adf
Unverified
Commit
df4c0adf
authored
Feb 03, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 03, 2026
Browse files
Merge pull request #463 from DuckyProject/feat/usage-records-codex-reasoning-effort
feat(usage): add reasoning effort column
parents
7cbe4afd
53ee6383
Changes
13
Hide whitespace changes
Inline
Side-by-side
backend/internal/handler/dto/mappers.go
View file @
df4c0adf
...
...
@@ -366,6 +366,7 @@ func usageLogFromServiceUser(l *service.UsageLog) UsageLog {
AccountID
:
l
.
AccountID
,
RequestID
:
l
.
RequestID
,
Model
:
l
.
Model
,
ReasoningEffort
:
l
.
ReasoningEffort
,
GroupID
:
l
.
GroupID
,
SubscriptionID
:
l
.
SubscriptionID
,
InputTokens
:
l
.
InputTokens
,
...
...
backend/internal/handler/dto/types.go
View file @
df4c0adf
...
...
@@ -222,6 +222,9 @@ type UsageLog struct {
AccountID
int64
`json:"account_id"`
RequestID
string
`json:"request_id"`
Model
string
`json:"model"`
// ReasoningEffort is the request's reasoning effort level (OpenAI Responses API).
// nil means not provided / not applicable.
ReasoningEffort
*
string
`json:"reasoning_effort,omitempty"`
GroupID
*
int64
`json:"group_id"`
SubscriptionID
*
int64
`json:"subscription_id"`
...
...
backend/internal/repository/usage_log_repo.go
View file @
df4c0adf
...
...
@@ -22,7 +22,7 @@ import (
"github.com/lib/pq"
)
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, created_at"
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size,
reasoning_effort,
created_at"
type
usageLogRepository
struct
{
client
*
dbent
.
Client
...
...
@@ -111,21 +111,22 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
duration_ms,
first_token_ms,
user_agent,
ip_address,
image_count,
image_size,
created_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7,
$8, $9, $10, $11,
$12, $13,
$14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30
)
ON CONFLICT (request_id, api_key_id) DO NOTHING
RETURNING id, created_at
`
ip_address,
image_count,
image_size,
reasoning_effort,
created_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7,
$8, $9, $10, $11,
$12, $13,
$14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31
)
ON CONFLICT (request_id, api_key_id) DO NOTHING
RETURNING id, created_at
`
groupID
:=
nullInt64
(
log
.
GroupID
)
subscriptionID
:=
nullInt64
(
log
.
SubscriptionID
)
...
...
@@ -134,6 +135,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
userAgent
:=
nullString
(
log
.
UserAgent
)
ipAddress
:=
nullString
(
log
.
IPAddress
)
imageSize
:=
nullString
(
log
.
ImageSize
)
reasoningEffort
:=
nullString
(
log
.
ReasoningEffort
)
var
requestIDArg
any
if
requestID
!=
""
{
...
...
@@ -170,6 +172,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
ipAddress
,
log
.
ImageCount
,
imageSize
,
reasoningEffort
,
createdAt
,
}
if
err
:=
scanSingleRow
(
ctx
,
sqlq
,
query
,
args
,
&
log
.
ID
,
&
log
.
CreatedAt
);
err
!=
nil
{
...
...
@@ -2090,6 +2093,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
ipAddress
sql
.
NullString
imageCount
int
imageSize
sql
.
NullString
reasoningEffort
sql
.
NullString
createdAt
time
.
Time
)
...
...
@@ -2124,6 +2128,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
&
ipAddress
,
&
imageCount
,
&
imageSize
,
&
reasoningEffort
,
&
createdAt
,
);
err
!=
nil
{
return
nil
,
err
...
...
@@ -2183,6 +2188,9 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
if
imageSize
.
Valid
{
log
.
ImageSize
=
&
imageSize
.
String
}
if
reasoningEffort
.
Valid
{
log
.
ReasoningEffort
=
&
reasoningEffort
.
String
}
return
log
,
nil
}
...
...
backend/internal/service/openai_gateway_service.go
View file @
df4c0adf
...
...
@@ -156,12 +156,15 @@ type OpenAIUsage struct {
// OpenAIForwardResult represents the result of forwarding
type
OpenAIForwardResult
struct
{
RequestID
string
Usage
OpenAIUsage
Model
string
Stream
bool
Duration
time
.
Duration
FirstTokenMs
*
int
RequestID
string
Usage
OpenAIUsage
Model
string
// ReasoningEffort is extracted from request body (reasoning.effort) or derived from model suffix.
// Stored for usage records display; nil means not provided / not applicable.
ReasoningEffort
*
string
Stream
bool
Duration
time
.
Duration
FirstTokenMs
*
int
}
// OpenAIGatewayService handles OpenAI API gateway operations
...
...
@@ -958,13 +961,16 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
}
}
reasoningEffort
:=
extractOpenAIReasoningEffort
(
reqBody
,
originalModel
)
return
&
OpenAIForwardResult
{
RequestID
:
resp
.
Header
.
Get
(
"x-request-id"
),
Usage
:
*
usage
,
Model
:
originalModel
,
Stream
:
reqStream
,
Duration
:
time
.
Since
(
startTime
),
FirstTokenMs
:
firstTokenMs
,
RequestID
:
resp
.
Header
.
Get
(
"x-request-id"
),
Usage
:
*
usage
,
Model
:
originalModel
,
ReasoningEffort
:
reasoningEffort
,
Stream
:
reqStream
,
Duration
:
time
.
Since
(
startTime
),
FirstTokenMs
:
firstTokenMs
,
},
nil
}
...
...
@@ -1728,6 +1734,7 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
AccountID
:
account
.
ID
,
RequestID
:
result
.
RequestID
,
Model
:
result
.
Model
,
ReasoningEffort
:
result
.
ReasoningEffort
,
InputTokens
:
actualInputTokens
,
OutputTokens
:
result
.
Usage
.
OutputTokens
,
CacheCreationTokens
:
result
.
Usage
.
CacheCreationInputTokens
,
...
...
@@ -1922,3 +1929,86 @@ func (s *OpenAIGatewayService) updateCodexUsageSnapshot(ctx context.Context, acc
_
=
s
.
accountRepo
.
UpdateExtra
(
updateCtx
,
accountID
,
updates
)
}()
}
func
getOpenAIReasoningEffortFromReqBody
(
reqBody
map
[
string
]
any
)
(
value
string
,
present
bool
)
{
if
reqBody
==
nil
{
return
""
,
false
}
// Primary: reasoning.effort
if
reasoning
,
ok
:=
reqBody
[
"reasoning"
]
.
(
map
[
string
]
any
);
ok
{
if
effort
,
ok
:=
reasoning
[
"effort"
]
.
(
string
);
ok
{
return
normalizeOpenAIReasoningEffort
(
effort
),
true
}
}
// Fallback: some clients may use a flat field.
if
effort
,
ok
:=
reqBody
[
"reasoning_effort"
]
.
(
string
);
ok
{
return
normalizeOpenAIReasoningEffort
(
effort
),
true
}
return
""
,
false
}
func
deriveOpenAIReasoningEffortFromModel
(
model
string
)
string
{
if
strings
.
TrimSpace
(
model
)
==
""
{
return
""
}
modelID
:=
strings
.
TrimSpace
(
model
)
if
strings
.
Contains
(
modelID
,
"/"
)
{
parts
:=
strings
.
Split
(
modelID
,
"/"
)
modelID
=
parts
[
len
(
parts
)
-
1
]
}
parts
:=
strings
.
FieldsFunc
(
strings
.
ToLower
(
modelID
),
func
(
r
rune
)
bool
{
switch
r
{
case
'-'
,
'_'
,
' '
:
return
true
default
:
return
false
}
})
if
len
(
parts
)
==
0
{
return
""
}
return
normalizeOpenAIReasoningEffort
(
parts
[
len
(
parts
)
-
1
])
}
func
extractOpenAIReasoningEffort
(
reqBody
map
[
string
]
any
,
requestedModel
string
)
*
string
{
if
value
,
present
:=
getOpenAIReasoningEffortFromReqBody
(
reqBody
);
present
{
if
value
==
""
{
return
nil
}
return
&
value
}
value
:=
deriveOpenAIReasoningEffortFromModel
(
requestedModel
)
if
value
==
""
{
return
nil
}
return
&
value
}
func
normalizeOpenAIReasoningEffort
(
raw
string
)
string
{
value
:=
strings
.
ToLower
(
strings
.
TrimSpace
(
raw
))
if
value
==
""
{
return
""
}
// Normalize separators for "x-high"/"x_high" variants.
value
=
strings
.
NewReplacer
(
"-"
,
""
,
"_"
,
""
,
" "
,
""
)
.
Replace
(
value
)
switch
value
{
case
"none"
,
"minimal"
:
return
""
case
"low"
,
"medium"
,
"high"
:
return
value
case
"xhigh"
,
"extrahigh"
:
return
"xhigh"
default
:
// Only store known effort levels for now to keep UI consistent.
return
""
}
}
backend/internal/service/usage_log.go
View file @
df4c0adf
...
...
@@ -14,6 +14,9 @@ type UsageLog struct {
AccountID
int64
RequestID
string
Model
string
// ReasoningEffort is the request's reasoning effort level (OpenAI Responses API),
// e.g. "low" / "medium" / "high" / "xhigh". Nil means not provided / not applicable.
ReasoningEffort
*
string
GroupID
*
int64
SubscriptionID
*
int64
...
...
backend/migrations/046_add_usage_log_reasoning_effort.sql
0 → 100644
View file @
df4c0adf
-- Add reasoning_effort field to usage_logs for OpenAI/Codex requests.
-- This stores the request's reasoning effort level (e.g. low/medium/high/xhigh).
ALTER
TABLE
usage_logs
ADD
COLUMN
IF
NOT
EXISTS
reasoning_effort
VARCHAR
(
20
);
frontend/src/components/admin/usage/UsageTable.vue
View file @
df4c0adf
...
...
@@ -21,6 +21,12 @@
<span
class=
"font-medium text-gray-900 dark:text-white"
>
{{
value
}}
</span>
</
template
>
<
template
#cell-reasoning_effort=
"{ row }"
>
<span
class=
"text-sm text-gray-900 dark:text-white"
>
{{
formatReasoningEffort
(
row
.
reasoning_effort
)
}}
</span>
</
template
>
<
template
#cell-group=
"{ row }"
>
<span
v-if=
"row.group"
class=
"inline-flex items-center rounded px-2 py-0.5 text-xs font-medium bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200"
>
{{
row
.
group
.
name
}}
...
...
@@ -232,14 +238,14 @@
</Teleport>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
formatDateTime
}
from
'
@/utils/format
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
type
{
AdminUsageLog
}
from
'
@/types
'
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
formatDateTime
,
formatReasoningEffort
}
from
'
@/utils/format
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
type
{
AdminUsageLog
}
from
'
@/types
'
defineProps
([
'
data
'
,
'
loading
'
])
const
{
t
}
=
useI18n
()
...
...
@@ -259,6 +265,7 @@ const cols = computed(() => [
{
key
:
'
api_key
'
,
label
:
t
(
'
usage.apiKeyFilter
'
),
sortable
:
false
},
{
key
:
'
account
'
,
label
:
t
(
'
admin.usage.account
'
),
sortable
:
false
},
{
key
:
'
model
'
,
label
:
t
(
'
usage.model
'
),
sortable
:
true
},
{
key
:
'
reasoning_effort
'
,
label
:
t
(
'
usage.reasoningEffort
'
),
sortable
:
false
},
{
key
:
'
group
'
,
label
:
t
(
'
admin.usage.group
'
),
sortable
:
false
},
{
key
:
'
stream
'
,
label
:
t
(
'
usage.type
'
),
sortable
:
false
},
{
key
:
'
tokens
'
,
label
:
t
(
'
usage.tokens
'
),
sortable
:
false
},
...
...
frontend/src/i18n/locales/en.ts
View file @
df4c0adf
...
...
@@ -502,6 +502,7 @@ export default {
exporting
:
'
Exporting...
'
,
preparingExport
:
'
Preparing export...
'
,
model
:
'
Model
'
,
reasoningEffort
:
'
Reasoning Effort
'
,
type
:
'
Type
'
,
tokens
:
'
Tokens
'
,
cost
:
'
Cost
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
df4c0adf
...
...
@@ -498,6 +498,7 @@ export default {
exporting
:
'
导出中...
'
,
preparingExport
:
'
正在准备导出...
'
,
model
:
'
模型
'
,
reasoningEffort
:
'
推理强度
'
,
type
:
'
类型
'
,
tokens
:
'
Token
'
,
cost
:
'
费用
'
,
...
...
frontend/src/types/index.ts
View file @
df4c0adf
...
...
@@ -712,6 +712,7 @@ export interface UsageLog {
account_id
:
number
|
null
request_id
:
string
model
:
string
reasoning_effort
?:
string
|
null
group_id
:
number
|
null
subscription_id
:
number
|
null
...
...
frontend/src/utils/format.ts
View file @
df4c0adf
...
...
@@ -174,6 +174,35 @@ export function parseDateTimeLocalInput(value: string): number | null {
return
Math
.
floor
(
date
.
getTime
()
/
1000
)
}
/**
* 格式化 OpenAI reasoning effort(用于使用记录展示)
* @param effort 原始 effort(如 "low" / "medium" / "high" / "xhigh")
* @returns 格式化后的字符串(Low / Medium / High / Xhigh),无值返回 "-"
*/
export
function
formatReasoningEffort
(
effort
:
string
|
null
|
undefined
):
string
{
const
raw
=
(
effort
??
''
).
toString
().
trim
()
if
(
!
raw
)
return
'
-
'
const
normalized
=
raw
.
toLowerCase
().
replace
(
/
[
-_
\s]
/g
,
''
)
switch
(
normalized
)
{
case
'
low
'
:
return
'
Low
'
case
'
medium
'
:
return
'
Medium
'
case
'
high
'
:
return
'
High
'
case
'
xhigh
'
:
case
'
extrahigh
'
:
return
'
Xhigh
'
case
'
none
'
:
case
'
minimal
'
:
return
'
-
'
default
:
// best-effort: Title-case first letter
return
raw
.
length
>
1
?
raw
[
0
].
toUpperCase
()
+
raw
.
slice
(
1
)
:
raw
.
toUpperCase
()
}
}
/**
* 格式化时间(只显示时分)
* @param date 日期字符串或 Date 对象
...
...
frontend/src/views/admin/UsageView.vue
View file @
df4c0adf
...
...
@@ -35,12 +35,13 @@
<
script
setup
lang=
"ts"
>
import
{
ref
,
reactive
,
computed
,
onMounted
,
onUnmounted
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
saveAs
}
from
'
file-saver
'
import
{
useAppStore
}
from
'
@/stores/app
'
;
import
{
adminAPI
}
from
'
@/api/admin
'
;
import
{
adminUsageAPI
}
from
'
@/api/admin/usage
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
;
import
Pagination
from
'
@/components/common/Pagination.vue
'
;
import
Select
from
'
@/components/common/Select.vue
'
import
UsageStatsCards
from
'
@/components/admin/usage/UsageStatsCards.vue
'
;
import
UsageFilters
from
'
@/components/admin/usage/UsageFilters.vue
'
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
import
{
saveAs
}
from
'
file-saver
'
import
{
useAppStore
}
from
'
@/stores/app
'
;
import
{
adminAPI
}
from
'
@/api/admin
'
;
import
{
adminUsageAPI
}
from
'
@/api/admin/usage
'
import
{
formatReasoningEffort
}
from
'
@/utils/format
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
;
import
Pagination
from
'
@/components/common/Pagination.vue
'
;
import
Select
from
'
@/components/common/Select.vue
'
import
UsageStatsCards
from
'
@/components/admin/usage/UsageStatsCards.vue
'
;
import
UsageFilters
from
'
@/components/admin/usage/UsageFilters.vue
'
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
import
ModelDistributionChart
from
'
@/components/charts/ModelDistributionChart.vue
'
;
import
TokenUsageTrend
from
'
@/components/charts/TokenUsageTrend.vue
'
import
type
{
AdminUsageLog
,
TrendDataPoint
,
ModelStat
}
from
'
@/types
'
;
import
type
{
AdminUsageStatsResponse
,
AdminUsageQueryParams
}
from
'
@/api/admin/usage
'
...
...
@@ -104,7 +105,7 @@ const exportToExcel = async () => {
const
XLSX
=
await
import
(
'
xlsx
'
)
const
headers
=
[
t
(
'
usage.time
'
),
t
(
'
admin.usage.user
'
),
t
(
'
usage.apiKeyFilter
'
),
t
(
'
admin.usage.account
'
),
t
(
'
usage.model
'
),
t
(
'
admin.usage.group
'
),
t
(
'
admin.usage.account
'
),
t
(
'
usage.model
'
),
t
(
'
usage.reasoningEffort
'
),
t
(
'
admin.usage.group
'
),
t
(
'
usage.type
'
),
t
(
'
admin.usage.inputTokens
'
),
t
(
'
admin.usage.outputTokens
'
),
t
(
'
admin.usage.cacheReadTokens
'
),
t
(
'
admin.usage.cacheCreationTokens
'
),
...
...
@@ -120,6 +121,7 @@ const exportToExcel = async () => {
log
.
api_key
?.
name
||
''
,
log
.
account
?.
name
||
''
,
log
.
model
,
formatReasoningEffort
(
log
.
reasoning_effort
),
log
.
group
?.
name
||
''
,
log
.
stream
?
t
(
'
usage.stream
'
)
:
t
(
'
usage.sync
'
),
log
.
input_tokens
,
...
...
frontend/src/views/user/UsageView.vue
View file @
df4c0adf
...
...
@@ -157,6 +157,12 @@
<span
class=
"font-medium text-gray-900 dark:text-white"
>
{{
value
}}
</span>
</
template
>
<
template
#cell-reasoning_effort=
"{ row }"
>
<span
class=
"text-sm text-gray-900 dark:text-white"
>
{{
formatReasoningEffort
(
row
.
reasoning_effort
)
}}
</span>
</
template
>
<
template
#cell-stream=
"{ row }"
>
<span
class=
"inline-flex items-center rounded px-2 py-0.5 text-xs font-medium"
...
...
@@ -438,12 +444,12 @@ import TablePageLayout from '@/components/layout/TablePageLayout.vue'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
DateRangePicker
from
'
@/components/common/DateRangePicker.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
type
{
UsageLog
,
ApiKey
,
UsageQueryParams
,
UsageStatsResponse
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
{
formatDateTime
}
from
'
@/utils/format
'
import
Select
from
'
@/components/common/Select.vue
'
import
DateRangePicker
from
'
@/components/common/DateRangePicker.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
type
{
UsageLog
,
ApiKey
,
UsageQueryParams
,
UsageStatsResponse
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
{
formatDateTime
,
formatReasoningEffort
}
from
'
@/utils/format
'
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
...
...
@@ -466,6 +472,7 @@ const usageStats = ref<UsageStatsResponse | null>(null)
const
columns
=
computed
<
Column
[]
>
(()
=>
[
{
key
:
'
api_key
'
,
label
:
t
(
'
usage.apiKeyFilter
'
),
sortable
:
false
},
{
key
:
'
model
'
,
label
:
t
(
'
usage.model
'
),
sortable
:
true
},
{
key
:
'
reasoning_effort
'
,
label
:
t
(
'
usage.reasoningEffort
'
),
sortable
:
false
},
{
key
:
'
stream
'
,
label
:
t
(
'
usage.type
'
),
sortable
:
false
},
{
key
:
'
tokens
'
,
label
:
t
(
'
usage.tokens
'
),
sortable
:
false
},
{
key
:
'
cost
'
,
label
:
t
(
'
usage.cost
'
),
sortable
:
false
},
...
...
@@ -723,6 +730,7 @@ const exportToCSV = async () => {
'
Time
'
,
'
API Key Name
'
,
'
Model
'
,
'
Reasoning Effort
'
,
'
Type
'
,
'
Input Tokens
'
,
'
Output Tokens
'
,
...
...
@@ -739,6 +747,7 @@ const exportToCSV = async () => {
log
.
created_at
,
log
.
api_key
?.
name
||
''
,
log
.
model
,
formatReasoningEffort
(
log
.
reasoning_effort
),
log
.
stream
?
'
Stream
'
:
'
Sync
'
,
log
.
input_tokens
,
log
.
output_tokens
,
...
...
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