fix(codex): 添加codex CLI instructions fallback机制
## 问题 - 使用OpenAI API key时,opencode客户端可能因instructions不兼容而报错 - 依赖外部GitHub获取instructions,网络故障时会失败 ## 解决方案 1. 将codex CLI标准instructions嵌入到项目中 2. 实现自动fallback机制: - 优先使用opencode GitHub的instructions - 失败时自动fallback到本地codex CLI instructions 3. 添加辅助函数用于错误检测和手动替换 ## 改动 - 新增: internal/service/prompts/codex_cli_instructions.md - 从codex项目复制的标准instructions - 使用go:embed嵌入到二进制文件 - 修改: internal/service/openai_codex_transform.go - 添加embed支持 - 增强getOpenCodeCodexHeader()的fallback逻辑 - 新增GetCodexCLIInstructions()公开函数 - 新增ReplaceWithCodexInstructions()用于手动替换 - 新增IsInstructionError()用于错误检测 ## 优势 - 零停机:GitHub不可用时仍能正常工作 - 离线可用:不依赖外部网络 - 兼容性:使用标准codex CLI instructions - 部署简单:instructions嵌入到二进制文件
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -16,6 +17,9 @@ const (
|
||||
codexCacheTTL = 15 * time.Minute
|
||||
)
|
||||
|
||||
//go:embed prompts/codex_cli_instructions.md
|
||||
var codexCLIInstructions string
|
||||
|
||||
var codexModelMap = map[string]string{
|
||||
"gpt-5.1-codex": "gpt-5.1-codex",
|
||||
"gpt-5.1-codex-low": "gpt-5.1-codex",
|
||||
@@ -119,6 +123,13 @@ func applyCodexOAuthTransform(reqBody map[string]any) codexTransformResult {
|
||||
reqBody["instructions"] = instructions
|
||||
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 {
|
||||
@@ -235,13 +246,69 @@ func getOpenCodeCachedPrompt(url, cacheFileName, metaFileName string) 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 {
|
||||
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 {
|
||||
filtered := make([]any, 0, len(input))
|
||||
for _, item := range input {
|
||||
|
||||
Reference in New Issue
Block a user