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
7076717b
Unverified
Commit
7076717b
authored
Mar 05, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 05, 2026
Browse files
Merge pull request #772 from mt21625457/aicodex2api-main
feat(openai-ws): 合并 WS v2 透传模式与前端 ws mode
parents
33988637
c0a4fcea
Changes
25
Hide whitespace changes
Inline
Side-by-side
frontend/src/components/account/EditAccountModal.vue
View file @
7076717b
...
@@ -708,7 +708,7 @@
...
@@ -708,7 +708,7 @@
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<!--
OpenAI
WS
Mode
三态
(
off
/
shared
/
dedicated
)
-->
<!--
OpenAI
WS
Mode
三态
(
off
/
ctx_pool
/
passthrough
)
-->
<
div
<
div
v
-
if
=
"
account?.platform === 'openai' && (account?.type === 'oauth' || account?.type === 'apikey')
"
v
-
if
=
"
account?.platform === 'openai' && (account?.type === 'oauth' || account?.type === 'apikey')
"
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
...
@@ -720,7 +720,7 @@
...
@@ -720,7 +720,7 @@
{{
t
(
'
admin.accounts.openai.wsModeDesc
'
)
}}
{{
t
(
'
admin.accounts.openai.wsModeDesc
'
)
}}
<
/p
>
<
/p
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.openai.ws
ModeConcurrencyHint
'
)
}}
{{
t
(
openAIWS
ModeConcurrencyHint
Key
)
}}
<
/p
>
<
/p
>
<
/div
>
<
/div
>
<
div
class
=
"
w-52
"
>
<
div
class
=
"
w-52
"
>
...
@@ -1273,10 +1273,11 @@ import { applyInterceptWarmup } from '@/components/account/credentialsBuilder'
...
@@ -1273,10 +1273,11 @@ import { applyInterceptWarmup } from '@/components/account/credentialsBuilder'
import
{
formatDateTimeLocalInput
,
parseDateTimeLocalInput
}
from
'
@/utils/format
'
import
{
formatDateTimeLocalInput
,
parseDateTimeLocalInput
}
from
'
@/utils/format
'
import
{
createStableObjectKeyResolver
}
from
'
@/utils/stableObjectKey
'
import
{
createStableObjectKeyResolver
}
from
'
@/utils/stableObjectKey
'
import
{
import
{
OPENAI_WS_MODE_
DEDICATED
,
OPENAI_WS_MODE_
CTX_POOL
,
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_
SHARED
,
OPENAI_WS_MODE_
PASSTHROUGH
,
isOpenAIWSModeEnabled
,
isOpenAIWSModeEnabled
,
resolveOpenAIWSModeConcurrencyHintKey
,
type
OpenAIWSMode
,
type
OpenAIWSMode
,
resolveOpenAIWSModeFromExtra
resolveOpenAIWSModeFromExtra
}
from
'
@/utils/openaiWsMode
'
}
from
'
@/utils/openaiWsMode
'
...
@@ -1387,8 +1388,8 @@ const codexCLIOnlyEnabled = ref(false)
...
@@ -1387,8 +1388,8 @@ const codexCLIOnlyEnabled = ref(false)
const
anthropicPassthroughEnabled
=
ref
(
false
)
const
anthropicPassthroughEnabled
=
ref
(
false
)
const
openAIWSModeOptions
=
computed
(()
=>
[
const
openAIWSModeOptions
=
computed
(()
=>
[
{
value
:
OPENAI_WS_MODE_OFF
,
label
:
t
(
'
admin.accounts.openai.wsModeOff
'
)
}
,
{
value
:
OPENAI_WS_MODE_OFF
,
label
:
t
(
'
admin.accounts.openai.wsModeOff
'
)
}
,
{
value
:
OPENAI_WS_MODE_
SHARED
,
label
:
t
(
'
admin.accounts.openai.wsMode
Shared
'
)
}
,
{
value
:
OPENAI_WS_MODE_
CTX_POOL
,
label
:
t
(
'
admin.accounts.openai.wsMode
CtxPool
'
)
}
,
{
value
:
OPENAI_WS_MODE_
DEDICATED
,
label
:
t
(
'
admin.accounts.openai.wsMode
Dedicated
'
)
}
{
value
:
OPENAI_WS_MODE_
PASSTHROUGH
,
label
:
t
(
'
admin.accounts.openai.wsMode
Passthrough
'
)
}
])
])
const
openaiResponsesWebSocketV2Mode
=
computed
({
const
openaiResponsesWebSocketV2Mode
=
computed
({
get
:
()
=>
{
get
:
()
=>
{
...
@@ -1405,6 +1406,9 @@ const openaiResponsesWebSocketV2Mode = computed({
...
@@ -1405,6 +1406,9 @@ const openaiResponsesWebSocketV2Mode = computed({
openaiOAuthResponsesWebSocketV2Mode
.
value
=
mode
openaiOAuthResponsesWebSocketV2Mode
.
value
=
mode
}
}
}
)
}
)
const
openAIWSModeConcurrencyHintKey
=
computed
(()
=>
resolveOpenAIWSModeConcurrencyHintKey
(
openaiResponsesWebSocketV2Mode
.
value
)
)
const
isOpenAIModelRestrictionDisabled
=
computed
(()
=>
const
isOpenAIModelRestrictionDisabled
=
computed
(()
=>
props
.
account
?.
platform
===
'
openai
'
&&
openaiPassthroughEnabled
.
value
props
.
account
?.
platform
===
'
openai
'
&&
openaiPassthroughEnabled
.
value
)
)
...
@@ -2248,10 +2252,13 @@ const handleSubmit = async () => {
...
@@ -2248,10 +2252,13 @@ const handleSubmit = async () => {
const
currentExtra
=
(
props
.
account
.
extra
as
Record
<
string
,
unknown
>
)
||
{
}
const
currentExtra
=
(
props
.
account
.
extra
as
Record
<
string
,
unknown
>
)
||
{
}
const
newExtra
:
Record
<
string
,
unknown
>
=
{
...
currentExtra
}
const
newExtra
:
Record
<
string
,
unknown
>
=
{
...
currentExtra
}
const
hadCodexCLIOnlyEnabled
=
currentExtra
.
codex_cli_only
===
true
const
hadCodexCLIOnlyEnabled
=
currentExtra
.
codex_cli_only
===
true
newExtra
.
openai_oauth_responses_websockets_v2_mode
=
openaiOAuthResponsesWebSocketV2Mode
.
value
if
(
props
.
account
.
type
===
'
oauth
'
)
{
newExtra
.
openai_apikey_responses_websockets_v2_mode
=
openaiAPIKeyResponsesWebSocketV2Mode
.
value
newExtra
.
openai_oauth_responses_websockets_v2_mode
=
openaiOAuthResponsesWebSocketV2Mode
.
value
newExtra
.
openai_oauth_responses_websockets_v2_enabled
=
isOpenAIWSModeEnabled
(
openaiOAuthResponsesWebSocketV2Mode
.
value
)
newExtra
.
openai_oauth_responses_websockets_v2_enabled
=
isOpenAIWSModeEnabled
(
openaiOAuthResponsesWebSocketV2Mode
.
value
)
newExtra
.
openai_apikey_responses_websockets_v2_enabled
=
isOpenAIWSModeEnabled
(
openaiAPIKeyResponsesWebSocketV2Mode
.
value
)
}
else
if
(
props
.
account
.
type
===
'
apikey
'
)
{
newExtra
.
openai_apikey_responses_websockets_v2_mode
=
openaiAPIKeyResponsesWebSocketV2Mode
.
value
newExtra
.
openai_apikey_responses_websockets_v2_enabled
=
isOpenAIWSModeEnabled
(
openaiAPIKeyResponsesWebSocketV2Mode
.
value
)
}
delete
newExtra
.
responses_websockets_v2_enabled
delete
newExtra
.
responses_websockets_v2_enabled
delete
newExtra
.
openai_ws_enabled
delete
newExtra
.
openai_ws_enabled
if
(
openaiPassthroughEnabled
.
value
)
{
if
(
openaiPassthroughEnabled
.
value
)
{
...
...
frontend/src/i18n/locales/en.ts
View file @
7076717b
...
@@ -1846,10 +1846,13 @@ export default {
...
@@ -1846,10 +1846,13 @@ export default {
wsMode
:
'
WS mode
'
,
wsMode
:
'
WS mode
'
,
wsModeDesc
:
'
Only applies to the current OpenAI account type.
'
,
wsModeDesc
:
'
Only applies to the current OpenAI account type.
'
,
wsModeOff
:
'
Off (off)
'
,
wsModeOff
:
'
Off (off)
'
,
wsModeCtxPool
:
'
Context Pool (ctx_pool)
'
,
wsModePassthrough
:
'
Passthrough (passthrough)
'
,
wsModeShared
:
'
Shared (shared)
'
,
wsModeShared
:
'
Shared (shared)
'
,
wsModeDedicated
:
'
Dedicated (dedicated)
'
,
wsModeDedicated
:
'
Dedicated (dedicated)
'
,
wsModeConcurrencyHint
:
wsModeConcurrencyHint
:
'
When WS mode is enabled, account concurrency becomes the WS connection pool limit for this account.
'
,
'
When WS mode is enabled, account concurrency becomes the WS connection pool limit for this account.
'
,
wsModePassthroughHint
:
'
Passthrough mode does not use the WS connection pool.
'
,
oauthResponsesWebsocketsV2
:
'
OAuth WebSocket Mode
'
,
oauthResponsesWebsocketsV2
:
'
OAuth WebSocket Mode
'
,
oauthResponsesWebsocketsV2Desc
:
oauthResponsesWebsocketsV2Desc
:
'
Only applies to OpenAI OAuth. This account can use OpenAI WebSocket Mode only when enabled.
'
,
'
Only applies to OpenAI OAuth. This account can use OpenAI WebSocket Mode only when enabled.
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
7076717b
...
@@ -1994,9 +1994,12 @@ export default {
...
@@ -1994,9 +1994,12 @@ export default {
wsMode
:
'
WS mode
'
,
wsMode
:
'
WS mode
'
,
wsModeDesc
:
'
仅对当前 OpenAI 账号类型生效。
'
,
wsModeDesc
:
'
仅对当前 OpenAI 账号类型生效。
'
,
wsModeOff
:
'
关闭(off)
'
,
wsModeOff
:
'
关闭(off)
'
,
wsModeCtxPool
:
'
上下文池(ctx_pool)
'
,
wsModePassthrough
:
'
透传(passthrough)
'
,
wsModeShared
:
'
共享(shared)
'
,
wsModeShared
:
'
共享(shared)
'
,
wsModeDedicated
:
'
独享(dedicated)
'
,
wsModeDedicated
:
'
独享(dedicated)
'
,
wsModeConcurrencyHint
:
'
启用 WS mode 后,该账号并发数将作为该账号 WS 连接池上限。
'
,
wsModeConcurrencyHint
:
'
启用 WS mode 后,该账号并发数将作为该账号 WS 连接池上限。
'
,
wsModePassthroughHint
:
'
passthrough 模式不使用 WS 连接池。
'
,
oauthResponsesWebsocketsV2
:
'
OAuth WebSocket Mode
'
,
oauthResponsesWebsocketsV2
:
'
OAuth WebSocket Mode
'
,
oauthResponsesWebsocketsV2Desc
:
oauthResponsesWebsocketsV2Desc
:
'
仅对 OpenAI OAuth 生效。开启后该账号才允许使用 OpenAI WebSocket Mode 协议。
'
,
'
仅对 OpenAI OAuth 生效。开启后该账号才允许使用 OpenAI WebSocket Mode 协议。
'
,
...
...
frontend/src/utils/__tests__/openaiWsMode.spec.ts
View file @
7076717b
import
{
describe
,
expect
,
it
}
from
'
vitest
'
import
{
describe
,
expect
,
it
}
from
'
vitest
'
import
{
import
{
OPENAI_WS_MODE_
DEDICATED
,
OPENAI_WS_MODE_
CTX_POOL
,
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_
SHARED
,
OPENAI_WS_MODE_
PASSTHROUGH
,
isOpenAIWSModeEnabled
,
isOpenAIWSModeEnabled
,
normalizeOpenAIWSMode
,
normalizeOpenAIWSMode
,
openAIWSModeFromEnabled
,
openAIWSModeFromEnabled
,
resolveOpenAIWSModeConcurrencyHintKey
,
resolveOpenAIWSModeFromExtra
resolveOpenAIWSModeFromExtra
}
from
'
@/utils/openaiWsMode
'
}
from
'
@/utils/openaiWsMode
'
describe
(
'
openaiWsMode utils
'
,
()
=>
{
describe
(
'
openaiWsMode utils
'
,
()
=>
{
it
(
'
normalizes mode values
'
,
()
=>
{
it
(
'
normalizes mode values
'
,
()
=>
{
expect
(
normalizeOpenAIWSMode
(
'
off
'
)).
toBe
(
OPENAI_WS_MODE_OFF
)
expect
(
normalizeOpenAIWSMode
(
'
off
'
)).
toBe
(
OPENAI_WS_MODE_OFF
)
expect
(
normalizeOpenAIWSMode
(
'
Shared
'
)).
toBe
(
OPENAI_WS_MODE_SHARED
)
expect
(
normalizeOpenAIWSMode
(
'
ctx_pool
'
)).
toBe
(
OPENAI_WS_MODE_CTX_POOL
)
expect
(
normalizeOpenAIWSMode
(
'
DEDICATED
'
)).
toBe
(
OPENAI_WS_MODE_DEDICATED
)
expect
(
normalizeOpenAIWSMode
(
'
passthrough
'
)).
toBe
(
OPENAI_WS_MODE_PASSTHROUGH
)
expect
(
normalizeOpenAIWSMode
(
'
Shared
'
)).
toBe
(
OPENAI_WS_MODE_CTX_POOL
)
expect
(
normalizeOpenAIWSMode
(
'
DEDICATED
'
)).
toBe
(
OPENAI_WS_MODE_CTX_POOL
)
expect
(
normalizeOpenAIWSMode
(
'
invalid
'
)).
toBeNull
()
expect
(
normalizeOpenAIWSMode
(
'
invalid
'
)).
toBeNull
()
})
})
it
(
'
maps legacy enabled flag to mode
'
,
()
=>
{
it
(
'
maps legacy enabled flag to mode
'
,
()
=>
{
expect
(
openAIWSModeFromEnabled
(
true
)).
toBe
(
OPENAI_WS_MODE_
SHARED
)
expect
(
openAIWSModeFromEnabled
(
true
)).
toBe
(
OPENAI_WS_MODE_
CTX_POOL
)
expect
(
openAIWSModeFromEnabled
(
false
)).
toBe
(
OPENAI_WS_MODE_OFF
)
expect
(
openAIWSModeFromEnabled
(
false
)).
toBe
(
OPENAI_WS_MODE_OFF
)
expect
(
openAIWSModeFromEnabled
(
'
true
'
)).
toBeNull
()
expect
(
openAIWSModeFromEnabled
(
'
true
'
)).
toBeNull
()
})
})
it
(
'
resolves by mode key first, then enabled, then fallback enabled keys
'
,
()
=>
{
it
(
'
resolves by mode key first, then enabled, then fallback enabled keys
'
,
()
=>
{
const
extra
=
{
const
extra
=
{
openai_oauth_responses_websockets_v2_mode
:
'
dedicated
'
,
openai_oauth_responses_websockets_v2_mode
:
'
passthrough
'
,
openai_oauth_responses_websockets_v2_enabled
:
false
,
openai_oauth_responses_websockets_v2_enabled
:
false
,
responses_websockets_v2_enabled
:
false
responses_websockets_v2_enabled
:
false
}
}
...
@@ -34,7 +37,7 @@ describe('openaiWsMode utils', () => {
...
@@ -34,7 +37,7 @@ describe('openaiWsMode utils', () => {
enabledKey
:
'
openai_oauth_responses_websockets_v2_enabled
'
,
enabledKey
:
'
openai_oauth_responses_websockets_v2_enabled
'
,
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
]
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
]
})
})
expect
(
mode
).
toBe
(
OPENAI_WS_MODE_
DEDICATED
)
expect
(
mode
).
toBe
(
OPENAI_WS_MODE_
PASSTHROUGH
)
})
})
it
(
'
falls back to default when nothing is present
'
,
()
=>
{
it
(
'
falls back to default when nothing is present
'
,
()
=>
{
...
@@ -47,9 +50,21 @@ describe('openaiWsMode utils', () => {
...
@@ -47,9 +50,21 @@ describe('openaiWsMode utils', () => {
expect
(
mode
).
toBe
(
OPENAI_WS_MODE_OFF
)
expect
(
mode
).
toBe
(
OPENAI_WS_MODE_OFF
)
})
})
it
(
'
treats off as disabled and
shared/dedicated
as enabled
'
,
()
=>
{
it
(
'
treats off as disabled and
non-off modes
as enabled
'
,
()
=>
{
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_OFF
)).
toBe
(
false
)
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_OFF
)).
toBe
(
false
)
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_SHARED
)).
toBe
(
true
)
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_CTX_POOL
)).
toBe
(
true
)
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_DEDICATED
)).
toBe
(
true
)
expect
(
isOpenAIWSModeEnabled
(
OPENAI_WS_MODE_PASSTHROUGH
)).
toBe
(
true
)
})
it
(
'
resolves concurrency hint key by mode
'
,
()
=>
{
expect
(
resolveOpenAIWSModeConcurrencyHintKey
(
OPENAI_WS_MODE_OFF
)).
toBe
(
'
admin.accounts.openai.wsModeConcurrencyHint
'
)
expect
(
resolveOpenAIWSModeConcurrencyHintKey
(
OPENAI_WS_MODE_CTX_POOL
)).
toBe
(
'
admin.accounts.openai.wsModeConcurrencyHint
'
)
expect
(
resolveOpenAIWSModeConcurrencyHintKey
(
OPENAI_WS_MODE_PASSTHROUGH
)).
toBe
(
'
admin.accounts.openai.wsModePassthroughHint
'
)
})
})
})
})
frontend/src/utils/openaiWsMode.ts
View file @
7076717b
export
const
OPENAI_WS_MODE_OFF
=
'
off
'
export
const
OPENAI_WS_MODE_OFF
=
'
off
'
export
const
OPENAI_WS_MODE_
SHARED
=
'
shared
'
export
const
OPENAI_WS_MODE_
CTX_POOL
=
'
ctx_pool
'
export
const
OPENAI_WS_MODE_
DEDICATED
=
'
dedicated
'
export
const
OPENAI_WS_MODE_
PASSTHROUGH
=
'
passthrough
'
export
type
OpenAIWSMode
=
export
type
OpenAIWSMode
=
|
typeof
OPENAI_WS_MODE_OFF
|
typeof
OPENAI_WS_MODE_OFF
|
typeof
OPENAI_WS_MODE_
SHARED
|
typeof
OPENAI_WS_MODE_
CTX_POOL
|
typeof
OPENAI_WS_MODE_
DEDICATED
|
typeof
OPENAI_WS_MODE_
PASSTHROUGH
const
OPENAI_WS_MODES
=
new
Set
<
OpenAIWSMode
>
([
const
OPENAI_WS_MODES
=
new
Set
<
OpenAIWSMode
>
([
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_OFF
,
OPENAI_WS_MODE_
SHARED
,
OPENAI_WS_MODE_
CTX_POOL
,
OPENAI_WS_MODE_
DEDICATED
OPENAI_WS_MODE_
PASSTHROUGH
])
])
export
interface
ResolveOpenAIWSModeOptions
{
export
interface
ResolveOpenAIWSModeOptions
{
...
@@ -23,6 +23,9 @@ export interface ResolveOpenAIWSModeOptions {
...
@@ -23,6 +23,9 @@ export interface ResolveOpenAIWSModeOptions {
export
const
normalizeOpenAIWSMode
=
(
mode
:
unknown
):
OpenAIWSMode
|
null
=>
{
export
const
normalizeOpenAIWSMode
=
(
mode
:
unknown
):
OpenAIWSMode
|
null
=>
{
if
(
typeof
mode
!==
'
string
'
)
return
null
if
(
typeof
mode
!==
'
string
'
)
return
null
const
normalized
=
mode
.
trim
().
toLowerCase
()
const
normalized
=
mode
.
trim
().
toLowerCase
()
if
(
normalized
===
'
shared
'
||
normalized
===
'
dedicated
'
)
{
return
OPENAI_WS_MODE_CTX_POOL
}
if
(
OPENAI_WS_MODES
.
has
(
normalized
as
OpenAIWSMode
))
{
if
(
OPENAI_WS_MODES
.
has
(
normalized
as
OpenAIWSMode
))
{
return
normalized
as
OpenAIWSMode
return
normalized
as
OpenAIWSMode
}
}
...
@@ -31,13 +34,22 @@ export const normalizeOpenAIWSMode = (mode: unknown): OpenAIWSMode | null => {
...
@@ -31,13 +34,22 @@ export const normalizeOpenAIWSMode = (mode: unknown): OpenAIWSMode | null => {
export
const
openAIWSModeFromEnabled
=
(
enabled
:
unknown
):
OpenAIWSMode
|
null
=>
{
export
const
openAIWSModeFromEnabled
=
(
enabled
:
unknown
):
OpenAIWSMode
|
null
=>
{
if
(
typeof
enabled
!==
'
boolean
'
)
return
null
if
(
typeof
enabled
!==
'
boolean
'
)
return
null
return
enabled
?
OPENAI_WS_MODE_
SHARED
:
OPENAI_WS_MODE_OFF
return
enabled
?
OPENAI_WS_MODE_
CTX_POOL
:
OPENAI_WS_MODE_OFF
}
}
export
const
isOpenAIWSModeEnabled
=
(
mode
:
OpenAIWSMode
):
boolean
=>
{
export
const
isOpenAIWSModeEnabled
=
(
mode
:
OpenAIWSMode
):
boolean
=>
{
return
mode
!==
OPENAI_WS_MODE_OFF
return
mode
!==
OPENAI_WS_MODE_OFF
}
}
export
const
resolveOpenAIWSModeConcurrencyHintKey
=
(
mode
:
OpenAIWSMode
):
'
admin.accounts.openai.wsModeConcurrencyHint
'
|
'
admin.accounts.openai.wsModePassthroughHint
'
=>
{
if
(
mode
===
OPENAI_WS_MODE_PASSTHROUGH
)
{
return
'
admin.accounts.openai.wsModePassthroughHint
'
}
return
'
admin.accounts.openai.wsModeConcurrencyHint
'
}
export
const
resolveOpenAIWSModeFromExtra
=
(
export
const
resolveOpenAIWSModeFromExtra
=
(
extra
:
Record
<
string
,
unknown
>
|
null
|
undefined
,
extra
:
Record
<
string
,
unknown
>
|
null
|
undefined
,
options
:
ResolveOpenAIWSModeOptions
options
:
ResolveOpenAIWSModeOptions
...
...
Prev
1
2
Next
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