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
ef5c8e68
Unverified
Commit
ef5c8e68
authored
Mar 26, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 26, 2026
Browse files
Merge pull request #1231 from LvyuanW/bulk-openai-passthrough-worktree
Support bulk editing for OpenAI passthrough
parents
d571f300
bb399e56
Changes
2
Show whitespace changes
Inline
Side-by-side
frontend/src/components/account/BulkEditAccountModal.vue
View file @
ef5c8e68
...
...
@@ -31,6 +31,57 @@
<
/p
>
<
/div
>
<!--
OpenAI
passthrough
-->
<
div
v
-
if
=
"
allOpenAIPassthroughCapable
"
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
>
<
div
class
=
"
mb-3 flex items-center justify-between
"
>
<
div
class
=
"
flex-1 pr-4
"
>
<
label
id
=
"
bulk-edit-openai-passthrough-label
"
class
=
"
input-label mb-0
"
for
=
"
bulk-edit-openai-passthrough-enabled
"
>
{{
t
(
'
admin.accounts.openai.oauthPassthrough
'
)
}}
<
/label
>
<
p
class
=
"
mt-1 text-xs text-gray-500 dark:text-gray-400
"
>
{{
t
(
'
admin.accounts.openai.oauthPassthroughDesc
'
)
}}
<
/p
>
<
/div
>
<
input
v
-
model
=
"
enableOpenAIPassthrough
"
id
=
"
bulk-edit-openai-passthrough-enabled
"
type
=
"
checkbox
"
aria
-
controls
=
"
bulk-edit-openai-passthrough-body
"
class
=
"
rounded border-gray-300 text-primary-600 focus:ring-primary-500
"
/>
<
/div
>
<
div
id
=
"
bulk-edit-openai-passthrough-body
"
:
class
=
"
!enableOpenAIPassthrough && 'pointer-events-none opacity-50'
"
role
=
"
group
"
aria
-
labelledby
=
"
bulk-edit-openai-passthrough-label
"
>
<
button
id
=
"
bulk-edit-openai-passthrough-toggle
"
type
=
"
button
"
:
class
=
"
[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
openaiPassthroughEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]
"
@
click
=
"
openaiPassthroughEnabled = !openaiPassthroughEnabled
"
>
<
span
:
class
=
"
[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
openaiPassthroughEnabled ? 'translate-x-5' : 'translate-x-0'
]
"
/>
<
/button
>
<
/div
>
<
/div
>
<!--
Base
URL
(
API
Key
only
)
-->
<
div
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600
"
>
<
div
class
=
"
mb-3 flex items-center justify-between
"
>
...
...
@@ -89,6 +140,16 @@
role
=
"
group
"
aria
-
labelledby
=
"
bulk-edit-model-restriction-label
"
>
<
div
v
-
if
=
"
isOpenAIModelRestrictionDisabled
"
class
=
"
rounded-lg bg-amber-50 p-3 dark:bg-amber-900/20
"
>
<
p
class
=
"
text-xs text-amber-700 dark:text-amber-400
"
>
{{
t
(
'
admin.accounts.openai.modelRestrictionDisabledByPassthrough
'
)
}}
<
/p
>
<
/div
>
<
template
v
-
else
>
<!--
Mode
Toggle
-->
<
div
class
=
"
mb-4 flex gap-2
"
>
<
button
...
...
@@ -281,6 +342,7 @@
<
/button
>
<
/div
>
<
/div
>
<
/template
>
<
/div
>
<
/div
>
...
...
@@ -865,7 +927,6 @@ import {
resolveOpenAIWSModeConcurrencyHintKey
}
from
'
@/utils/openaiWsMode
'
import
type
{
OpenAIWSMode
}
from
'
@/utils/openaiWsMode
'
interface
Props
{
show
:
boolean
accountIds
:
number
[]
...
...
@@ -887,6 +948,15 @@ const appStore = useAppStore()
// Platform awareness
const
isMixedPlatform
=
computed
(()
=>
props
.
selectedPlatforms
.
length
>
1
)
const
allOpenAIPassthroughCapable
=
computed
(()
=>
{
return
(
props
.
selectedPlatforms
.
length
===
1
&&
props
.
selectedPlatforms
[
0
]
===
'
openai
'
&&
props
.
selectedTypes
.
length
>
0
&&
props
.
selectedTypes
.
every
(
t
=>
t
===
'
oauth
'
||
t
===
'
apikey
'
)
)
}
)
const
allOpenAIOAuth
=
computed
(()
=>
{
return
(
props
.
selectedPlatforms
.
length
===
1
&&
...
...
@@ -939,6 +1009,7 @@ const enablePriority = ref(false)
const
enableRateMultiplier
=
ref
(
false
)
const
enableStatus
=
ref
(
false
)
const
enableGroups
=
ref
(
false
)
const
enableOpenAIPassthrough
=
ref
(
false
)
const
enableOpenAIWSMode
=
ref
(
false
)
const
enableRpmLimit
=
ref
(
false
)
...
...
@@ -961,6 +1032,7 @@ const priority = ref(1)
const
rateMultiplier
=
ref
(
1
)
const
status
=
ref
<
'
active
'
|
'
inactive
'
>
(
'
active
'
)
const
groupIds
=
ref
<
number
[]
>
([])
const
openaiPassthroughEnabled
=
ref
(
false
)
const
openaiOAuthResponsesWebSocketV2Mode
=
ref
<
OpenAIWSMode
>
(
OPENAI_WS_MODE_OFF
)
const
rpmLimitEnabled
=
ref
(
false
)
const
bulkBaseRpm
=
ref
<
number
|
null
>
(
null
)
...
...
@@ -988,6 +1060,13 @@ const statusOptions = computed(() => [
{
value
:
'
active
'
,
label
:
t
(
'
common.active
'
)
}
,
{
value
:
'
inactive
'
,
label
:
t
(
'
common.inactive
'
)
}
])
const
isOpenAIModelRestrictionDisabled
=
computed
(
()
=>
allOpenAIPassthroughCapable
.
value
&&
enableOpenAIPassthrough
.
value
&&
openaiPassthroughEnabled
.
value
)
const
openAIWSModeOptions
=
computed
(()
=>
[
{
value
:
OPENAI_WS_MODE_OFF
,
label
:
t
(
'
admin.accounts.openai.wsModeOff
'
)
}
,
{
value
:
OPENAI_WS_MODE_PASSTHROUGH
,
label
:
t
(
'
admin.accounts.openai.wsModePassthrough
'
)
}
...
...
@@ -1123,7 +1202,15 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
}
}
if
(
enableModelRestriction
.
value
)
{
if
(
enableOpenAIPassthrough
.
value
)
{
const
extra
=
ensureExtra
()
extra
.
openai_passthrough
=
openaiPassthroughEnabled
.
value
if
(
!
openaiPassthroughEnabled
.
value
)
{
extra
.
openai_oauth_passthrough
=
false
}
}
if
(
enableModelRestriction
.
value
&&
!
isOpenAIModelRestrictionDisabled
.
value
)
{
// 统一使用 model_mapping 字段
if
(
modelRestrictionMode
.
value
===
'
whitelist
'
)
{
// 白名单模式:将模型转换为 model_mapping 格式(key=value)
...
...
@@ -1243,6 +1330,7 @@ const handleSubmit = async () => {
const
hasAnyFieldEnabled
=
enableBaseUrl
.
value
||
enableOpenAIPassthrough
.
value
||
enableModelRestriction
.
value
||
enableCustomErrorCodes
.
value
||
enableInterceptWarmup
.
value
||
...
...
@@ -1345,11 +1433,13 @@ watch(
enableRateMultiplier
.
value
=
false
enableStatus
.
value
=
false
enableGroups
.
value
=
false
enableOpenAIPassthrough
.
value
=
false
enableOpenAIWSMode
.
value
=
false
enableRpmLimit
.
value
=
false
// Reset all values
baseUrl
.
value
=
''
openaiPassthroughEnabled
.
value
=
false
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
[]
modelMappings
.
value
=
[]
...
...
frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts
View file @
ef5c8e68
...
...
@@ -130,6 +130,25 @@ describe('BulkEditAccountModal', () => {
})
})
it
(
'
OpenAI 账号批量编辑可开启自动透传
'
,
async
()
=>
{
const
wrapper
=
mountModal
({
selectedPlatforms
:
[
'
openai
'
],
selectedTypes
:
[
'
oauth
'
]
})
await
wrapper
.
get
(
'
#bulk-edit-openai-passthrough-enabled
'
).
setValue
(
true
)
await
wrapper
.
get
(
'
#bulk-edit-openai-passthrough-toggle
'
).
trigger
(
'
click
'
)
await
wrapper
.
get
(
'
#bulk-edit-account-form
'
).
trigger
(
'
submit.prevent
'
)
await
flushPromises
()
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledTimes
(
1
)
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledWith
([
1
,
2
],
{
extra
:
{
openai_passthrough
:
true
}
})
})
it
(
'
OpenAI OAuth 批量编辑应提交 OAuth 专属 WS mode 字段
'
,
async
()
=>
{
const
wrapper
=
mountModal
({
selectedPlatforms
:
[
'
openai
'
],
...
...
@@ -158,4 +177,44 @@ describe('BulkEditAccountModal', () => {
expect
(
wrapper
.
find
(
'
#bulk-edit-openai-ws-mode-enabled
'
).
exists
()).
toBe
(
false
)
})
it
(
'
OpenAI 账号批量编辑可关闭自动透传
'
,
async
()
=>
{
const
wrapper
=
mountModal
({
selectedPlatforms
:
[
'
openai
'
],
selectedTypes
:
[
'
apikey
'
]
})
await
wrapper
.
get
(
'
#bulk-edit-openai-passthrough-enabled
'
).
setValue
(
true
)
await
wrapper
.
get
(
'
#bulk-edit-account-form
'
).
trigger
(
'
submit.prevent
'
)
await
flushPromises
()
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledTimes
(
1
)
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledWith
([
1
,
2
],
{
extra
:
{
openai_passthrough
:
false
,
openai_oauth_passthrough
:
false
}
})
})
it
(
'
开启 OpenAI 自动透传时不再同时提交模型限制
'
,
async
()
=>
{
const
wrapper
=
mountModal
({
selectedPlatforms
:
[
'
openai
'
],
selectedTypes
:
[
'
oauth
'
]
})
await
wrapper
.
get
(
'
#bulk-edit-openai-passthrough-enabled
'
).
setValue
(
true
)
await
wrapper
.
get
(
'
#bulk-edit-openai-passthrough-toggle
'
).
trigger
(
'
click
'
)
await
wrapper
.
get
(
'
#bulk-edit-model-restriction-enabled
'
).
setValue
(
true
)
await
wrapper
.
get
(
'
#bulk-edit-account-form
'
).
trigger
(
'
submit.prevent
'
)
await
flushPromises
()
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledTimes
(
1
)
expect
(
adminAPI
.
accounts
.
bulkUpdate
).
toHaveBeenCalledWith
([
1
,
2
],
{
extra
:
{
openai_passthrough
:
true
}
})
expect
(
wrapper
.
text
()).
toContain
(
'
admin.accounts.openai.modelRestrictionDisabledByPassthrough
'
)
})
})
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