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
a9398d21
Unverified
Commit
a9398d21
authored
Feb 03, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 03, 2026
Browse files
Merge pull request #475 from touwaeriol/fix/openai-oauth-instructions-v2
fix(openai): 统一 OAuth instructions 处理逻辑,修复 Codex CLI 400 错误
parents
df1c2383
9a48b2e9
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/service/openai_codex_transform.go
View file @
a9398d21
...
...
@@ -72,7 +72,7 @@ type opencodeCacheMetadata struct {
LastChecked
int64
`json:"lastChecked"`
}
func
applyCodexOAuthTransform
(
reqBody
map
[
string
]
any
)
codexTransformResult
{
func
applyCodexOAuthTransform
(
reqBody
map
[
string
]
any
,
isCodexCLI
bool
)
codexTransformResult
{
result
:=
codexTransformResult
{}
// 工具续链需求会影响存储策略与 input 过滤逻辑。
needsToolContinuation
:=
NeedsToolContinuation
(
reqBody
)
...
...
@@ -118,22 +118,9 @@ func applyCodexOAuthTransform(reqBody map[string]any) codexTransformResult {
result
.
PromptCacheKey
=
strings
.
TrimSpace
(
v
)
}
instructions
:=
strings
.
TrimSpace
(
getOpenCodeCodexHeader
())
existingInstructions
,
_
:=
reqBody
[
"instructions"
]
.
(
string
)
existingInstructions
=
strings
.
TrimSpace
(
existingInstructions
)
if
instructions
!=
""
{
if
existingInstructions
!=
instructions
{
reqBody
[
"instructions"
]
=
instructions
result
.
Modified
=
true
}
}
else
if
existingInstructions
==
""
{
// 未获取到 opencode 指令时,回退使用 Codex CLI 指令。
codexInstructions
:=
strings
.
TrimSpace
(
getCodexCLIInstructions
())
if
codexInstructions
!=
""
{
reqBody
[
"instructions"
]
=
codexInstructions
result
.
Modified
=
true
}
// instructions 处理逻辑:根据是否是 Codex CLI 分别调用不同方法
if
applyInstructions
(
reqBody
,
isCodexCLI
)
{
result
.
Modified
=
true
}
// 续链场景保留 item_reference 与 id,避免 call_id 上下文丢失。
...
...
@@ -276,6 +263,72 @@ func GetCodexCLIInstructions() string {
return
getCodexCLIInstructions
()
}
// applyInstructions 处理 instructions 字段
// isCodexCLI=true: 仅补充缺失的 instructions(使用 opencode 指令)
// isCodexCLI=false: 优先使用 opencode 指令覆盖
func
applyInstructions
(
reqBody
map
[
string
]
any
,
isCodexCLI
bool
)
bool
{
if
isCodexCLI
{
return
applyCodexCLIInstructions
(
reqBody
)
}
return
applyOpenCodeInstructions
(
reqBody
)
}
// applyCodexCLIInstructions 为 Codex CLI 请求补充缺失的 instructions
// 仅在 instructions 为空时添加 opencode 指令
func
applyCodexCLIInstructions
(
reqBody
map
[
string
]
any
)
bool
{
if
!
isInstructionsEmpty
(
reqBody
)
{
return
false
// 已有有效 instructions,不修改
}
instructions
:=
strings
.
TrimSpace
(
getOpenCodeCodexHeader
())
if
instructions
!=
""
{
reqBody
[
"instructions"
]
=
instructions
return
true
}
return
false
}
// applyOpenCodeInstructions 为非 Codex CLI 请求应用 opencode 指令
// 优先使用 opencode 指令覆盖
func
applyOpenCodeInstructions
(
reqBody
map
[
string
]
any
)
bool
{
instructions
:=
strings
.
TrimSpace
(
getOpenCodeCodexHeader
())
existingInstructions
,
_
:=
reqBody
[
"instructions"
]
.
(
string
)
existingInstructions
=
strings
.
TrimSpace
(
existingInstructions
)
if
instructions
!=
""
{
if
existingInstructions
!=
instructions
{
reqBody
[
"instructions"
]
=
instructions
return
true
}
}
else
if
existingInstructions
==
""
{
codexInstructions
:=
strings
.
TrimSpace
(
getCodexCLIInstructions
())
if
codexInstructions
!=
""
{
reqBody
[
"instructions"
]
=
codexInstructions
return
true
}
}
return
false
}
// isInstructionsEmpty 检查 instructions 字段是否为空
// 处理以下情况:字段不存在、nil、空字符串、纯空白字符串
func
isInstructionsEmpty
(
reqBody
map
[
string
]
any
)
bool
{
val
,
exists
:=
reqBody
[
"instructions"
]
if
!
exists
{
return
true
}
if
val
==
nil
{
return
true
}
str
,
ok
:=
val
.
(
string
)
if
!
ok
{
return
true
}
return
strings
.
TrimSpace
(
str
)
==
""
}
// ReplaceWithCodexInstructions 将请求 instructions 替换为内置 Codex 指令(必要时)。
func
ReplaceWithCodexInstructions
(
reqBody
map
[
string
]
any
)
bool
{
codexInstructions
:=
strings
.
TrimSpace
(
getCodexCLIInstructions
())
...
...
backend/internal/service/openai_codex_transform_test.go
View file @
a9398d21
...
...
@@ -23,7 +23,7 @@ func TestApplyCodexOAuthTransform_ToolContinuationPreservesInput(t *testing.T) {
"tool_choice"
:
"auto"
,
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
// 未显式设置 store=true,默认为 false。
store
,
ok
:=
reqBody
[
"store"
]
.
(
bool
)
...
...
@@ -59,7 +59,7 @@ func TestApplyCodexOAuthTransform_ExplicitStoreFalsePreserved(t *testing.T) {
"tool_choice"
:
"auto"
,
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
store
,
ok
:=
reqBody
[
"store"
]
.
(
bool
)
require
.
True
(
t
,
ok
)
...
...
@@ -79,7 +79,7 @@ func TestApplyCodexOAuthTransform_ExplicitStoreTrueForcedFalse(t *testing.T) {
"tool_choice"
:
"auto"
,
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
store
,
ok
:=
reqBody
[
"store"
]
.
(
bool
)
require
.
True
(
t
,
ok
)
...
...
@@ -97,7 +97,7 @@ func TestApplyCodexOAuthTransform_NonContinuationDefaultsStoreFalseAndStripsIDs(
},
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
store
,
ok
:=
reqBody
[
"store"
]
.
(
bool
)
require
.
True
(
t
,
ok
)
...
...
@@ -148,7 +148,7 @@ func TestApplyCodexOAuthTransform_NormalizeCodexTools_PreservesResponsesFunction
},
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
tools
,
ok
:=
reqBody
[
"tools"
]
.
([]
any
)
require
.
True
(
t
,
ok
)
...
...
@@ -169,7 +169,7 @@ func TestApplyCodexOAuthTransform_EmptyInput(t *testing.T) {
"input"
:
[]
any
{},
}
applyCodexOAuthTransform
(
reqBody
)
applyCodexOAuthTransform
(
reqBody
,
false
)
input
,
ok
:=
reqBody
[
"input"
]
.
([]
any
)
require
.
True
(
t
,
ok
)
...
...
@@ -196,3 +196,77 @@ func setupCodexCache(t *testing.T) {
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
os
.
WriteFile
(
filepath
.
Join
(
cacheDir
,
"opencode-codex-header-meta.json"
),
data
,
0
o644
))
}
func
TestApplyCodexOAuthTransform_CodexCLI_PreservesExistingInstructions
(
t
*
testing
.
T
)
{
// Codex CLI 场景:已有 instructions 时不修改
setupCodexCache
(
t
)
reqBody
:=
map
[
string
]
any
{
"model"
:
"gpt-5.1"
,
"instructions"
:
"existing instructions"
,
}
result
:=
applyCodexOAuthTransform
(
reqBody
,
true
)
// isCodexCLI=true
instructions
,
ok
:=
reqBody
[
"instructions"
]
.
(
string
)
require
.
True
(
t
,
ok
)
require
.
Equal
(
t
,
"existing instructions"
,
instructions
)
// Modified 仍可能为 true(因为其他字段被修改),但 instructions 应保持不变
_
=
result
}
func
TestApplyCodexOAuthTransform_CodexCLI_SuppliesDefaultWhenEmpty
(
t
*
testing
.
T
)
{
// Codex CLI 场景:无 instructions 时补充默认值
setupCodexCache
(
t
)
reqBody
:=
map
[
string
]
any
{
"model"
:
"gpt-5.1"
,
// 没有 instructions 字段
}
result
:=
applyCodexOAuthTransform
(
reqBody
,
true
)
// isCodexCLI=true
instructions
,
ok
:=
reqBody
[
"instructions"
]
.
(
string
)
require
.
True
(
t
,
ok
)
require
.
NotEmpty
(
t
,
instructions
)
require
.
True
(
t
,
result
.
Modified
)
}
func
TestApplyCodexOAuthTransform_NonCodexCLI_OverridesInstructions
(
t
*
testing
.
T
)
{
// 非 Codex CLI 场景:使用 opencode 指令覆盖
setupCodexCache
(
t
)
reqBody
:=
map
[
string
]
any
{
"model"
:
"gpt-5.1"
,
"instructions"
:
"old instructions"
,
}
result
:=
applyCodexOAuthTransform
(
reqBody
,
false
)
// isCodexCLI=false
instructions
,
ok
:=
reqBody
[
"instructions"
]
.
(
string
)
require
.
True
(
t
,
ok
)
require
.
NotEqual
(
t
,
"old instructions"
,
instructions
)
require
.
True
(
t
,
result
.
Modified
)
}
func
TestIsInstructionsEmpty
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
reqBody
map
[
string
]
any
expected
bool
}{
{
"missing field"
,
map
[
string
]
any
{},
true
},
{
"nil value"
,
map
[
string
]
any
{
"instructions"
:
nil
},
true
},
{
"empty string"
,
map
[
string
]
any
{
"instructions"
:
""
},
true
},
{
"whitespace only"
,
map
[
string
]
any
{
"instructions"
:
" "
},
true
},
{
"non-string"
,
map
[
string
]
any
{
"instructions"
:
123
},
true
},
{
"valid string"
,
map
[
string
]
any
{
"instructions"
:
"hello"
},
false
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
result
:=
isInstructionsEmpty
(
tt
.
reqBody
)
require
.
Equal
(
t
,
tt
.
expected
,
result
)
})
}
}
backend/internal/service/openai_gateway_service.go
View file @
a9398d21
...
...
@@ -796,8 +796,8 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
}
}
if
account
.
Type
==
AccountTypeOAuth
&&
!
isCodexCLI
{
codexResult
:=
applyCodexOAuthTransform
(
reqBody
)
if
account
.
Type
==
AccountTypeOAuth
{
codexResult
:=
applyCodexOAuthTransform
(
reqBody
,
isCodexCLI
)
if
codexResult
.
Modified
{
bodyModified
=
true
}
...
...
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