feat(openai): 添加Codex工具调用自动修正功能
实现了完整的Codex工具调用拦截和自动修正系统,解决OpenCode使用Codex模型时的工具调用兼容性问题。 **核心功能:** 1. **工具名称自动映射** - apply_patch/applyPatch → edit - update_plan/updatePlan → todowrite - read_plan/readPlan → todoread - search_files/searchFiles → grep - list_files/listFiles → glob - read_file/readFile → read - write_file/writeFile → write - execute_bash/executeBash/exec_bash/execBash → bash 2. **工具参数自动修正** - bash: 自动移除不支持的 workdir/work_dir 参数 - edit: 自动将 path 参数重命名为 file_path - 支持 JSON 字符串和对象两种参数格式 3. **流式响应集成** - 在 SSE 数据流中实时修正工具调用 - 支持多种 JSON 结构(tool_calls, function_call, delta, choices等) - 不影响响应性能和用户体验 4. **统计和监控** - 记录每次工具修正的详细信息 - 提供修正统计数据查询 - 便于问题排查和性能优化 **实现文件:** - `openai_tool_corrector.go`: 工具修正核心逻辑(250行) - `openai_tool_corrector_test.go`: 完整的单元测试(380+行) - `openai_gateway_service.go`: 流式响应集成 - `openai_gateway_service_tool_correction_test.go`: 集成测试 **测试覆盖:** - 工具名称映射测试(18个映射规则) - 参数修正测试(bash workdir、edit path等) - SSE数据修正测试(多种JSON结构) - 统计功能测试 - 所有测试通过 ✅ **解决的问题:** 修复了 OpenCode 使用 sub2api 中转 Codex 时,因工具名称和参数不兼容导致的工具调用失败问题。 Codex 模型有时会忽略指令文件中的工具映射说明,导致调用不存在的工具(如 apply_patch)。 现在通过流式响应拦截,自动将错误的工具调用修正为 OpenCode 兼容的格式。 **参考文档:** - OpenCode 工具规范: https://opencode.ai/docs/ - Codex Bridge 指令: backend/internal/service/prompts/codex_opencode_bridge.txt
This commit is contained in:
@@ -94,6 +94,7 @@ type OpenAIGatewayService struct {
|
||||
httpUpstream HTTPUpstream
|
||||
deferredService *DeferredService
|
||||
openAITokenProvider *OpenAITokenProvider
|
||||
toolCorrector *CodexToolCorrector
|
||||
}
|
||||
|
||||
// NewOpenAIGatewayService creates a new OpenAIGatewayService
|
||||
@@ -128,6 +129,7 @@ func NewOpenAIGatewayService(
|
||||
httpUpstream: httpUpstream,
|
||||
deferredService: deferredService,
|
||||
openAITokenProvider: openAITokenProvider,
|
||||
toolCorrector: NewCodexToolCorrector(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,6 +1108,11 @@ func (s *OpenAIGatewayService) handleStreamingResponse(ctx context.Context, resp
|
||||
line = s.replaceModelInSSELine(line, mappedModel, originalModel)
|
||||
}
|
||||
|
||||
// Correct Codex tool calls if needed (apply_patch -> edit, etc.)
|
||||
if correctedData, corrected := s.toolCorrector.CorrectToolCallsInSSEData(data); corrected {
|
||||
line = "data: " + correctedData
|
||||
}
|
||||
|
||||
// Forward line
|
||||
if _, err := fmt.Fprintf(w, "%s\n", line); err != nil {
|
||||
sendErrorEvent("write_failed")
|
||||
@@ -1193,6 +1200,20 @@ func (s *OpenAIGatewayService) replaceModelInSSELine(line, fromModel, toModel st
|
||||
return line
|
||||
}
|
||||
|
||||
// correctToolCallsInResponseBody 修正响应体中的工具调用
|
||||
func (s *OpenAIGatewayService) correctToolCallsInResponseBody(body []byte) []byte {
|
||||
if len(body) == 0 {
|
||||
return body
|
||||
}
|
||||
|
||||
bodyStr := string(body)
|
||||
corrected, changed := s.toolCorrector.CorrectToolCallsInSSEData(bodyStr)
|
||||
if changed {
|
||||
return []byte(corrected)
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *OpenAIGatewayService) parseSSEUsage(data string, usage *OpenAIUsage) {
|
||||
// Parse response.completed event for usage (OpenAI Responses format)
|
||||
var event struct {
|
||||
@@ -1296,6 +1317,8 @@ func (s *OpenAIGatewayService) handleOAuthSSEToJSON(resp *http.Response, c *gin.
|
||||
if originalModel != mappedModel {
|
||||
body = s.replaceModelInResponseBody(body, mappedModel, originalModel)
|
||||
}
|
||||
// Correct tool calls in final response
|
||||
body = s.correctToolCallsInResponseBody(body)
|
||||
} else {
|
||||
usage = s.parseSSEUsageFromBody(bodyText)
|
||||
if originalModel != mappedModel {
|
||||
|
||||
Reference in New Issue
Block a user