599599 </div >
600600 </div >
601601
602+ <!-- OpenAI OAuth WS mode -->
603+ <div v-if =" allOpenAIOAuth" class =" border-t border-gray-200 pt-4 dark:border-dark-600" >
604+ <div class =" mb-3 flex items-center justify-between" >
605+ <label
606+ id =" bulk-edit-openai-ws-mode-label"
607+ class =" input-label mb-0"
608+ for =" bulk-edit-openai-ws-mode-enabled"
609+ >
610+ {{ t('admin.accounts.openai.wsMode') }}
611+ </label >
612+ <input
613+ v-model =" enableOpenAIWSMode"
614+ id =" bulk-edit-openai-ws-mode-enabled"
615+ type =" checkbox"
616+ aria-controls =" bulk-edit-openai-ws-mode"
617+ class =" rounded border-gray-300 text-primary-600 focus:ring-primary-500"
618+ />
619+ </div >
620+ <div
621+ id =" bulk-edit-openai-ws-mode"
622+ :class =" !enableOpenAIWSMode && 'pointer-events-none opacity-50'"
623+ >
624+ <p class =" mb-3 text-xs text-gray-500 dark:text-gray-400" >
625+ {{ t('admin.accounts.openai.wsModeDesc') }}
626+ </p >
627+ <p class =" mb-3 text-xs text-gray-500 dark:text-gray-400" >
628+ {{ t(openAIWSModeConcurrencyHintKey) }}
629+ </p >
630+ <Select
631+ v-model =" openaiOAuthResponsesWebSocketV2Mode "
632+ data-testid="bulk-edit-openai-ws-mode-select"
633+ :options =" openAIWSModeOptions "
634+ aria-labelledby="bulk-edit-openai-ws-mode-label"
635+ />
636+ </div >
637+ </div >
638+
602639 <!-- RPM Limit (仅全部为 Anthropic OAuth/SetupToken 时显示) -->
603640 <div v-if =" allAnthropicOAuthOrSetupToken" class =" border-t border-gray-200 pt-4 dark:border-dark-600" >
604641 <div class =" mb-3 flex items-center justify-between" >
@@ -821,6 +858,13 @@ import {
821858 buildModelMappingObject as buildModelMappingPayload ,
822859 getPresetMappingsByPlatform
823860} from ' @/composables/useModelWhitelist'
861+ import {
862+ OPENAI_WS_MODE_OFF ,
863+ OPENAI_WS_MODE_PASSTHROUGH ,
864+ isOpenAIWSModeEnabled ,
865+ resolveOpenAIWSModeConcurrencyHintKey
866+ } from ' @/utils/openaiWsMode'
867+ import type { OpenAIWSMode } from ' @/utils/openaiWsMode'
824868
825869interface Props {
826870 show: boolean
@@ -843,6 +887,15 @@ const appStore = useAppStore()
843887// Platform awareness
844888const isMixedPlatform = computed (() => props .selectedPlatforms .length > 1 )
845889
890+ const allOpenAIOAuth = computed (() => {
891+ return (
892+ props .selectedPlatforms .length === 1 &&
893+ props .selectedPlatforms [0 ] === ' openai' &&
894+ props .selectedTypes .length > 0 &&
895+ props .selectedTypes .every (t => t === ' oauth' )
896+ )
897+ })
898+
846899// 是否全部为 Anthropic OAuth/SetupToken(RPM 配置仅在此条件下显示)
847900const allAnthropicOAuthOrSetupToken = computed (() => {
848901 return (
@@ -886,6 +939,7 @@ const enablePriority = ref(false)
886939const enableRateMultiplier = ref (false )
887940const enableStatus = ref (false )
888941const enableGroups = ref (false )
942+ const enableOpenAIWSMode = ref (false )
889943const enableRpmLimit = ref (false )
890944
891945// State - field values
@@ -907,6 +961,7 @@ const priority = ref(1)
907961const rateMultiplier = ref (1 )
908962const status = ref <' active' | ' inactive' >(' active' )
909963const groupIds = ref <number []>([])
964+ const openaiOAuthResponsesWebSocketV2Mode = ref <OpenAIWSMode >(OPENAI_WS_MODE_OFF )
910965const rpmLimitEnabled = ref (false )
911966const bulkBaseRpm = ref <number | null >(null )
912967const bulkRpmStrategy = ref <' tiered' | ' sticky_exempt' >(' tiered' )
@@ -933,6 +988,13 @@ const statusOptions = computed(() => [
933988 { value: ' active' , label: t (' common.active' ) },
934989 { value: ' inactive' , label: t (' common.inactive' ) }
935990])
991+ const openAIWSModeOptions = computed (() => [
992+ { value: OPENAI_WS_MODE_OFF , label: t (' admin.accounts.openai.wsModeOff' ) },
993+ { value: OPENAI_WS_MODE_PASSTHROUGH , label: t (' admin.accounts.openai.wsModePassthrough' ) }
994+ ])
995+ const openAIWSModeConcurrencyHintKey = computed (() =>
996+ resolveOpenAIWSModeConcurrencyHintKey (openaiOAuthResponsesWebSocketV2Mode .value )
997+ )
936998
937999// Model mapping helpers
9381000const addModelMapping = () => {
@@ -1015,6 +1077,12 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
10151077 const updates: Record <string , unknown > = {}
10161078 const credentials: Record <string , unknown > = {}
10171079 let credentialsChanged = false
1080+ const ensureExtra = (): Record <string , unknown > => {
1081+ if (! updates .extra ) {
1082+ updates .extra = {}
1083+ }
1084+ return updates .extra as Record <string , unknown >
1085+ }
10181086
10191087 if (enableProxy .value ) {
10201088 // 后端期望 proxy_id: 0 表示清除代理,而不是 null
@@ -1089,9 +1157,17 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
10891157 updates .credentials = credentials
10901158 }
10911159
1160+ if (enableOpenAIWSMode .value ) {
1161+ const extra = ensureExtra ()
1162+ extra .openai_oauth_responses_websockets_v2_mode = openaiOAuthResponsesWebSocketV2Mode .value
1163+ extra .openai_oauth_responses_websockets_v2_enabled = isOpenAIWSModeEnabled (
1164+ openaiOAuthResponsesWebSocketV2Mode .value
1165+ )
1166+ }
1167+
10921168 // RPM limit settings (写入 extra 字段)
10931169 if (enableRpmLimit .value ) {
1094- const extra: Record < string , unknown > = {}
1170+ const extra = ensureExtra ()
10951171 if (rpmLimitEnabled .value && bulkBaseRpm .value != null && bulkBaseRpm .value > 0 ) {
10961172 extra .base_rpm = bulkBaseRpm .value
10971173 extra .rpm_strategy = bulkRpmStrategy .value
@@ -1111,8 +1187,7 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
11111187
11121188 // UMQ mode(独立于 RPM 保存)
11131189 if (userMsgQueueMode .value !== null ) {
1114- if (! updates .extra ) updates .extra = {}
1115- const umqExtra = updates .extra as Record <string , unknown >
1190+ const umqExtra = ensureExtra ()
11161191 umqExtra .user_msg_queue_mode = userMsgQueueMode .value // '' = 清除账号级覆盖
11171192 umqExtra .user_msg_queue_enabled = false // 清理旧字段(JSONB merge)
11181193 }
@@ -1178,6 +1253,7 @@ const handleSubmit = async () => {
11781253 enableRateMultiplier .value ||
11791254 enableStatus .value ||
11801255 enableGroups .value ||
1256+ enableOpenAIWSMode .value ||
11811257 enableRpmLimit .value ||
11821258 userMsgQueueMode .value !== null
11831259
@@ -1269,6 +1345,7 @@ watch(
12691345 enableRateMultiplier .value = false
12701346 enableStatus .value = false
12711347 enableGroups .value = false
1348+ enableOpenAIWSMode .value = false
12721349 enableRpmLimit .value = false
12731350
12741351 // Reset all values
@@ -1286,6 +1363,7 @@ watch(
12861363 rateMultiplier .value = 1
12871364 status .value = ' active'
12881365 groupIds .value = []
1366+ openaiOAuthResponsesWebSocketV2Mode .value = OPENAI_WS_MODE_OFF
12891367 rpmLimitEnabled .value = false
12901368 bulkBaseRpm .value = null
12911369 bulkRpmStrategy .value = ' tiered'
0 commit comments