mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-30 00:21:34 +00:00
fix(openai): make Codex fingerprint convergence opt-in and cover passthrough
Default codex_fingerprint_mode to off. v0.1.175 treated a missing key as "session", so upgrading silently rewrote installation/session/thread/turn/ window identifiers for every existing OAuth account that had never configured this field. The quota regressions in #5555, #5556 and #5582 line up with that version boundary, with A/B reports that rolling back to v0.1.173 restores quota. Convergence is now explicit opt-in (#5610). Only accounts that never set the field change behaviour; explicit off / device / session / full keep working exactly as configured. That required flipping the persistence condition in all three account modals from "!== 'session'" to "!== 'off'": the old rule deleted the key when it equalled the default, which after the flip would have silently discarded an administrator's explicit opt-in to session. Also extend convergence to the passthrough path, which previously left client identifiers untouched: - resolve the ids once in forwardOpenAIPassthrough and rewrite client_metadata on the raw bytes (gjson extract + sjson splice) because passthrough is a hot path that must not fully unmarshal multi-MB bodies; a shared core keeps the raw and map variants from drifting - both request builders apply the staged ids at the same relative position (after session isolation, before identity enforcement) so headers and body share one id set and turn_id stays consistent - stage the ids unconditionally, including nil: a failover from a converged account to an off account must not leave the previous account's ids behind
This commit is contained in:
@@ -1535,7 +1535,7 @@ const codexCLIOnlyEnabled = ref(false)
|
||||
const codexCLIOnlyAppServerEnabled = ref(false)
|
||||
type CodexFingerprintMode = 'off' | 'device' | 'session' | 'full'
|
||||
const enableCodexFingerprintMode = ref(false)
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('session')
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('off')
|
||||
const codexFingerprintModeOptions = computed(() => [
|
||||
{ value: 'off' as CodexFingerprintMode, label: t('admin.accounts.openai.codexFingerprintOff') },
|
||||
{ value: 'device' as CodexFingerprintMode, label: t('admin.accounts.openai.codexFingerprintDevice') },
|
||||
@@ -1829,7 +1829,8 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
|
||||
|
||||
if (enableCodexFingerprintMode.value) {
|
||||
const extra = ensureExtra()
|
||||
if (codexFingerprintMode.value !== 'session') {
|
||||
// off = 默认值,清键即可;device/session/full 是显式 opt-in,必须落键(#5610)。
|
||||
if (codexFingerprintMode.value !== 'off') {
|
||||
extra.codex_fingerprint_mode = codexFingerprintMode.value
|
||||
} else {
|
||||
delete extra.codex_fingerprint_mode
|
||||
@@ -2082,7 +2083,7 @@ watch(
|
||||
enableCodexCLIOnly.value = false
|
||||
enableCodexCLIOnlyAppServer.value = false
|
||||
enableCodexFingerprintMode.value = false
|
||||
codexFingerprintMode.value = 'session'
|
||||
codexFingerprintMode.value = 'off'
|
||||
enableOpenAICompactMode.value = false
|
||||
enableOpenAICompactModelMapping.value = false
|
||||
enableRpmLimit.value = false
|
||||
|
||||
@@ -3855,7 +3855,7 @@ const openaiAPIKeyResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OF
|
||||
const codexCLIOnlyEnabled = ref(false)
|
||||
const codexCLIOnlyAppServerEnabled = ref(false)
|
||||
type CodexFingerprintMode = 'off' | 'device' | 'session' | 'full'
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('session')
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('off')
|
||||
const codexFingerprintModeOptions = computed(() => [
|
||||
{ value: 'off' as CodexFingerprintMode, label: t('admin.accounts.openai.codexFingerprintOff') },
|
||||
{ value: 'device' as CodexFingerprintMode, label: t('admin.accounts.openai.codexFingerprintDevice') },
|
||||
@@ -4741,7 +4741,7 @@ const resetForm = () => {
|
||||
openaiAPIKeyResponsesWebSocketV2Mode.value = OPENAI_WS_MODE_OFF
|
||||
codexCLIOnlyEnabled.value = false
|
||||
codexCLIOnlyAppServerEnabled.value = false
|
||||
codexFingerprintMode.value = 'session'
|
||||
codexFingerprintMode.value = 'off'
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
webSearchEmulationMode.value = 'default'
|
||||
@@ -4840,7 +4840,9 @@ const buildOpenAIExtra = (base?: Record<string, unknown>): Record<string, unknow
|
||||
} else {
|
||||
delete extra.codex_cli_only_allow_app_server
|
||||
}
|
||||
if (codexFingerprintMode.value !== 'session') {
|
||||
// 收敛是显式 opt-in:off 即默认值,不落键;device/session/full 必须显式写入,
|
||||
// 否则管理员的选择会被当成默认而丢失(#5610)。
|
||||
if (codexFingerprintMode.value !== 'off') {
|
||||
extra.codex_fingerprint_mode = codexFingerprintMode.value
|
||||
} else {
|
||||
delete extra.codex_fingerprint_mode
|
||||
|
||||
@@ -2980,7 +2980,7 @@ const openaiAPIKeyResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OF
|
||||
const codexCLIOnlyEnabled = ref(false)
|
||||
const codexCLIOnlyAppServerEnabled = ref(false)
|
||||
type CodexFingerprintMode = 'off' | 'device' | 'session' | 'full'
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('session')
|
||||
const codexFingerprintMode = ref<CodexFingerprintMode>('off')
|
||||
type CodexImageToolMode = 'inherit' | 'enabled' | 'disabled' | 'block'
|
||||
const codexImageToolMode = ref<CodexImageToolMode>('inherit')
|
||||
type AnthropicAPIKeyAuthScheme = 'x_api_key' | 'authorization_bearer'
|
||||
@@ -3440,7 +3440,7 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
openaiAPIKeyResponsesWebSocketV2Mode.value = OPENAI_WS_MODE_OFF
|
||||
codexCLIOnlyEnabled.value = false
|
||||
codexCLIOnlyAppServerEnabled.value = false
|
||||
codexFingerprintMode.value = 'session'
|
||||
codexFingerprintMode.value = 'off'
|
||||
codexImageToolMode.value = 'inherit'
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
@@ -3494,9 +3494,10 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
}
|
||||
if (newAccount.type === 'oauth') {
|
||||
const fpMode = extra?.codex_fingerprint_mode as string | undefined
|
||||
// 缺省/非法值按 off 呈现,与后端 GetCodexFingerprintMode 的 opt-in 语义一致(#5610)
|
||||
codexFingerprintMode.value = (['off', 'device', 'session', 'full'].includes(fpMode || '')
|
||||
? fpMode as CodexFingerprintMode
|
||||
: 'session')
|
||||
: 'off')
|
||||
}
|
||||
const credentials = newAccount.credentials as Record<string, unknown> | undefined
|
||||
const compactMappings = credentials?.compact_model_mapping as Record<string, string> | undefined
|
||||
@@ -4843,9 +4844,10 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 指纹收敛模式:默认 session,不写入;非默认值显式写入(包括 off)
|
||||
// 指纹收敛模式:默认 off(不写入);device/session/full 是显式 opt-in,
|
||||
// 必须落键,否则管理员的选择会被后端当作"未设置"而回落到 off(#5610)。
|
||||
if (props.account.type === 'oauth') {
|
||||
if (codexFingerprintMode.value !== 'session') {
|
||||
if (codexFingerprintMode.value !== 'off') {
|
||||
newExtra.codex_fingerprint_mode = codexFingerprintMode.value
|
||||
} else {
|
||||
delete newExtra.codex_fingerprint_mode
|
||||
|
||||
@@ -574,10 +574,10 @@ export default {
|
||||
codexCLIOnlyAppServerDesc:
|
||||
"Effective only when the switch above is on. When enabled, this account also allows third-party clients that embed the Codex engine over the app-server protocol (e.g. Claude Code's codex plugin); they still pass the global engine-fingerprint gate. OR-combined with the global app-server toggle.",
|
||||
codexFingerprintMode: 'Codex fingerprint convergence',
|
||||
codexFingerprintModeDesc: 'When multiple users share the same OAuth account, converge device/session identifiers to account-level stable values to reduce upstream-visible device and session count. Off = pass through client identifiers as-is.',
|
||||
codexFingerprintOff: 'Off (passthrough)',
|
||||
codexFingerprintModeDesc: 'When multiple users share the same OAuth account, converge device/session identifiers to account-level stable values to reduce upstream-visible device and session count. Off by default (client identifiers pass through as-is); opt in explicitly when needed. Some accounts reported quota shrinkage after enabling convergence, so choose based on your own measurements.',
|
||||
codexFingerprintOff: 'Off (passthrough, default)',
|
||||
codexFingerprintDevice: 'Device only',
|
||||
codexFingerprintSession: 'Device + Session (recommended)',
|
||||
codexFingerprintSession: 'Device + Session',
|
||||
codexFingerprintFull: 'Full convergence',
|
||||
codexImageTool: 'Codex image bridge policy',
|
||||
codexImageToolDesc:
|
||||
|
||||
@@ -644,10 +644,10 @@ export default {
|
||||
codexCLIOnlyAppServer: '允许 Codex app-server 客户端',
|
||||
codexCLIOnlyAppServerDesc: '仅在上方开关开启时生效。开启后本账号额外放行内嵌 Codex 引擎、经 app-server 协议接入的第三方客户端(如 Claude Code 的 codex 插件),仍需通过全局引擎指纹门;与全局 app-server 开关取 OR(任一开即放行)。',
|
||||
codexFingerprintMode: 'Codex 指纹收敛',
|
||||
codexFingerprintModeDesc: '多人共享同一 OAuth 账号时,将各用户的设备/会话标识收敛为账号级恒定值,减少上游可见的设备数和会话数。关闭时原样透传客户端标识。',
|
||||
codexFingerprintOff: '关闭(透传)',
|
||||
codexFingerprintModeDesc: '多人共享同一 OAuth 账号时,将各用户的设备/会话标识收敛为账号级恒定值,减少上游可见的设备数和会话数。默认关闭(原样透传客户端标识),需要时再显式开启;部分账号开启收敛后出现过额度缩水,请按自己的实测结果选择。',
|
||||
codexFingerprintOff: '关闭(透传,默认)',
|
||||
codexFingerprintDevice: '仅设备',
|
||||
codexFingerprintSession: '设备+会话(推荐)',
|
||||
codexFingerprintSession: '设备+会话',
|
||||
codexFingerprintFull: '完全收敛',
|
||||
codexImageTool: 'Codex 图片桥接策略',
|
||||
codexImageToolDesc:
|
||||
|
||||
Reference in New Issue
Block a user