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
55796a11
Unverified
Commit
55796a11
authored
Jan 13, 2026
by
Wesley Liddick
Committed by
GitHub
Jan 13, 2026
Browse files
Merge pull request #264 from IanShaw027/fix/openai-opencode-compatibility
fix(openai): 增强 OpenCode 兼容性和模型规范化
parents
93db889a
d7fa47d7
Changes
3
Expand all
Show whitespace changes
Inline
Side-by-side
backend/internal/service/openai_codex_transform.go
View file @
55796a11
package
service
package
service
import
(
import
(
_
"embed"
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"io"
"io"
...
@@ -16,6 +17,9 @@ const (
...
@@ -16,6 +17,9 @@ const (
codexCacheTTL
=
15
*
time
.
Minute
codexCacheTTL
=
15
*
time
.
Minute
)
)
//go:embed prompts/codex_cli_instructions.md
var
codexCLIInstructions
string
var
codexModelMap
=
map
[
string
]
string
{
var
codexModelMap
=
map
[
string
]
string
{
"gpt-5.1-codex"
:
"gpt-5.1-codex"
,
"gpt-5.1-codex"
:
"gpt-5.1-codex"
,
"gpt-5.1-codex-low"
:
"gpt-5.1-codex"
,
"gpt-5.1-codex-low"
:
"gpt-5.1-codex"
,
...
@@ -119,6 +123,13 @@ func applyCodexOAuthTransform(reqBody map[string]any) codexTransformResult {
...
@@ -119,6 +123,13 @@ func applyCodexOAuthTransform(reqBody map[string]any) codexTransformResult {
reqBody
[
"instructions"
]
=
instructions
reqBody
[
"instructions"
]
=
instructions
result
.
Modified
=
true
result
.
Modified
=
true
}
}
}
else
if
existingInstructions
==
""
{
// If no opencode instructions available, try codex CLI instructions
codexInstructions
:=
strings
.
TrimSpace
(
getCodexCLIInstructions
())
if
codexInstructions
!=
""
{
reqBody
[
"instructions"
]
=
codexInstructions
result
.
Modified
=
true
}
}
}
if
input
,
ok
:=
reqBody
[
"input"
]
.
([]
any
);
ok
{
if
input
,
ok
:=
reqBody
[
"input"
]
.
([]
any
);
ok
{
...
@@ -235,13 +246,69 @@ func getOpenCodeCachedPrompt(url, cacheFileName, metaFileName string) string {
...
@@ -235,13 +246,69 @@ func getOpenCodeCachedPrompt(url, cacheFileName, metaFileName string) string {
}
}
func
getOpenCodeCodexHeader
()
string
{
func
getOpenCodeCodexHeader
()
string
{
return
getOpenCodeCachedPrompt
(
opencodeCodexHeaderURL
,
"opencode-codex-header.txt"
,
"opencode-codex-header-meta.json"
)
// Try to get from opencode repository first
opencodeInstructions
:=
getOpenCodeCachedPrompt
(
opencodeCodexHeaderURL
,
"opencode-codex-header.txt"
,
"opencode-codex-header-meta.json"
)
// If opencode instructions are available, return them
if
opencodeInstructions
!=
""
{
return
opencodeInstructions
}
// Fallback to local codex CLI instructions
return
getCodexCLIInstructions
()
}
func
getCodexCLIInstructions
()
string
{
return
codexCLIInstructions
}
}
func
GetOpenCodeInstructions
()
string
{
func
GetOpenCodeInstructions
()
string
{
return
getOpenCodeCodexHeader
()
return
getOpenCodeCodexHeader
()
}
}
func
GetCodexCLIInstructions
()
string
{
return
getCodexCLIInstructions
()
}
func
ReplaceWithCodexInstructions
(
reqBody
map
[
string
]
any
)
bool
{
codexInstructions
:=
strings
.
TrimSpace
(
getCodexCLIInstructions
())
if
codexInstructions
==
""
{
return
false
}
existingInstructions
,
_
:=
reqBody
[
"instructions"
]
.
(
string
)
if
strings
.
TrimSpace
(
existingInstructions
)
!=
codexInstructions
{
reqBody
[
"instructions"
]
=
codexInstructions
return
true
}
return
false
}
func
IsInstructionError
(
errorMessage
string
)
bool
{
if
errorMessage
==
""
{
return
false
}
lowerMsg
:=
strings
.
ToLower
(
errorMessage
)
instructionKeywords
:=
[]
string
{
"instruction"
,
"instructions"
,
"system prompt"
,
"system message"
,
"invalid prompt"
,
"prompt format"
,
}
for
_
,
keyword
:=
range
instructionKeywords
{
if
strings
.
Contains
(
lowerMsg
,
keyword
)
{
return
true
}
}
return
false
}
func
filterCodexInput
(
input
[]
any
)
[]
any
{
func
filterCodexInput
(
input
[]
any
)
[]
any
{
filtered
:=
make
([]
any
,
0
,
len
(
input
))
filtered
:=
make
([]
any
,
0
,
len
(
input
))
for
_
,
item
:=
range
input
{
for
_
,
item
:=
range
input
{
...
@@ -250,15 +317,36 @@ func filterCodexInput(input []any) []any {
...
@@ -250,15 +317,36 @@ func filterCodexInput(input []any) []any {
filtered
=
append
(
filtered
,
item
)
filtered
=
append
(
filtered
,
item
)
continue
continue
}
}
if
typ
,
ok
:=
m
[
"type"
]
.
(
string
);
ok
&&
typ
==
"item_reference"
{
typ
,
_
:=
m
[
"type"
]
.
(
string
)
if
typ
==
"item_reference"
{
filtered
=
append
(
filtered
,
m
)
continue
continue
}
}
// Strip per-item ids; keep call_id only for tool call items so outputs can match.
if
isCodexToolCallItemType
(
typ
)
{
callID
,
_
:=
m
[
"call_id"
]
.
(
string
)
if
strings
.
TrimSpace
(
callID
)
==
""
{
if
id
,
ok
:=
m
[
"id"
]
.
(
string
);
ok
&&
strings
.
TrimSpace
(
id
)
!=
""
{
m
[
"call_id"
]
=
id
}
}
}
delete
(
m
,
"id"
)
delete
(
m
,
"id"
)
if
!
isCodexToolCallItemType
(
typ
)
{
delete
(
m
,
"call_id"
)
}
filtered
=
append
(
filtered
,
m
)
filtered
=
append
(
filtered
,
m
)
}
}
return
filtered
return
filtered
}
}
func
isCodexToolCallItemType
(
typ
string
)
bool
{
if
typ
==
""
{
return
false
}
return
strings
.
HasSuffix
(
typ
,
"_call"
)
||
strings
.
HasSuffix
(
typ
,
"_call_output"
)
}
func
normalizeCodexTools
(
reqBody
map
[
string
]
any
)
bool
{
func
normalizeCodexTools
(
reqBody
map
[
string
]
any
)
bool
{
rawTools
,
ok
:=
reqBody
[
"tools"
]
rawTools
,
ok
:=
reqBody
[
"tools"
]
if
!
ok
||
rawTools
==
nil
{
if
!
ok
||
rawTools
==
nil
{
...
...
backend/internal/service/openai_gateway_service.go
View file @
55796a11
...
@@ -42,6 +42,7 @@ var openaiSSEDataRe = regexp.MustCompile(`^data:\s*`)
...
@@ -42,6 +42,7 @@ var openaiSSEDataRe = regexp.MustCompile(`^data:\s*`)
var
openaiAllowedHeaders
=
map
[
string
]
bool
{
var
openaiAllowedHeaders
=
map
[
string
]
bool
{
"accept-language"
:
true
,
"accept-language"
:
true
,
"content-type"
:
true
,
"content-type"
:
true
,
"conversation_id"
:
true
,
"user-agent"
:
true
,
"user-agent"
:
true
,
"originator"
:
true
,
"originator"
:
true
,
"session_id"
:
true
,
"session_id"
:
true
,
...
@@ -553,6 +554,27 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
...
@@ -553,6 +554,27 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
bodyModified
=
true
bodyModified
=
true
}
}
// Apply Codex model normalization for all OpenAI accounts
if
model
,
ok
:=
reqBody
[
"model"
]
.
(
string
);
ok
{
normalizedModel
:=
normalizeCodexModel
(
model
)
if
normalizedModel
!=
""
&&
normalizedModel
!=
model
{
log
.
Printf
(
"[OpenAI] Codex model normalization: %s -> %s (account: %s, type: %s, isCodexCLI: %v)"
,
model
,
normalizedModel
,
account
.
Name
,
account
.
Type
,
isCodexCLI
)
reqBody
[
"model"
]
=
normalizedModel
mappedModel
=
normalizedModel
bodyModified
=
true
}
}
// Normalize reasoning.effort parameter (minimal -> none)
if
reasoning
,
ok
:=
reqBody
[
"reasoning"
]
.
(
map
[
string
]
any
);
ok
{
if
effort
,
ok
:=
reasoning
[
"effort"
]
.
(
string
);
ok
&&
effort
==
"minimal"
{
reasoning
[
"effort"
]
=
"none"
bodyModified
=
true
log
.
Printf
(
"[OpenAI] Normalized reasoning.effort: minimal -> none (account: %s)"
,
account
.
Name
)
}
}
if
account
.
Type
==
AccountTypeOAuth
&&
!
isCodexCLI
{
if
account
.
Type
==
AccountTypeOAuth
&&
!
isCodexCLI
{
codexResult
:=
applyCodexOAuthTransform
(
reqBody
)
codexResult
:=
applyCodexOAuthTransform
(
reqBody
)
if
codexResult
.
Modified
{
if
codexResult
.
Modified
{
...
@@ -783,9 +805,6 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin.
...
@@ -783,9 +805,6 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin.
if
promptCacheKey
!=
""
{
if
promptCacheKey
!=
""
{
req
.
Header
.
Set
(
"conversation_id"
,
promptCacheKey
)
req
.
Header
.
Set
(
"conversation_id"
,
promptCacheKey
)
req
.
Header
.
Set
(
"session_id"
,
promptCacheKey
)
req
.
Header
.
Set
(
"session_id"
,
promptCacheKey
)
}
else
{
req
.
Header
.
Del
(
"conversation_id"
)
req
.
Header
.
Del
(
"session_id"
)
}
}
}
}
...
...
backend/internal/service/prompts/codex_cli_instructions.md
0 → 100644
View file @
55796a11
This diff is collapsed.
Click to expand it.
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