Skip to content

feat: 提升 Codex custom tool 事件兼容#286

Merged
james-6-23 merged 1 commit into
james-6-23:mainfrom
huangye123:worktree-codex-mimicry-include-custom-delta
Jun 20, 2026
Merged

feat: 提升 Codex custom tool 事件兼容#286
james-6-23 merged 1 commit into
james-6-23:mainfrom
huangye123:worktree-codex-mimicry-include-custom-delta

Conversation

@huangye123

@huangye123 huangye123 commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

背景

本次变更参考 Wei-Shaw/sub2api#3065 中 Codex 模拟保真度的低风险部分,聚焦两个已确认范围:

  1. 在不改既有可工作请求头的前提下,补齐 Responses 请求中 include 的合并逻辑。
  2. 补齐上游 custom_tool_call / response.custom_tool_call_input.delta 事件在 Chat Completions 与 Anthropic 兼容路径中的处理。

现有实现已经具备较完整的 Codex UA / Originator / Session / WebSocket 头部策略,因此本 PR 不调整请求头相关逻辑,避免破坏已有可工作指纹组合。

修改前

include 行为

PrepareResponsesBody 在请求没有 include 时会默认注入:

["reasoning.encrypted_content"]

但如果客户端已经传入了其他 include,即使请求包含 reasoning,也不会补充 reasoning.encrypted_content。例如:

{
  "reasoning": {"effort": "high"},
  "include": ["file_search_call.results"]
}

旧行为会原样保留 include,导致 reasoning 加密内容项缺失。

custom tool call 事件

旧逻辑主要识别:

  • function_call
  • response.function_call_arguments.delta

对于新版上游可能返回的:

  • custom_tool_call
  • response.custom_tool_call_input.delta

部分路径无法按工具调用处理,可能出现工具参数增量无法映射、非流式工具调用提取缺失,或旧无状态转换 fallback 将 custom tool input delta 当普通文本输出的问题。

修改后

include 合并

新增 Codex include 合并辅助逻辑:

  • include 时保持原默认行为,注入 reasoning.encrypted_content
  • reasoning 且已有 include 时,如缺少 reasoning.encrypted_content 则追加。
  • 保留既有 include 项。
  • 已存在时不重复追加。
  • 非数组 include 保守不覆盖,避免破坏客户端自定义异常输入。

custom tool call 兼容

补齐以下兼容逻辑:

  • Chat Completions 有状态流转换支持 custom_tool_call
  • 支持 response.custom_tool_call_input.delta / done
  • 同时映射 item.idcall_id 到同一 OpenAI tool call index,兼容上游 delta 使用不同 ID 字段回传。
  • 非流式 completed output 提取支持 custom_tool_call,并从 input 字段读取参数。
  • Anthropic 流式转换支持 custom_tool_callresponse.custom_tool_call_input.delta
  • Anthropic 非流式 completed fallback 支持 custom_tool_call
  • Chat / Anthropic handler 的 delta 字符统计纳入 custom tool input delta。
  • 旧无状态 TranslateStreamChunk 显式忽略 response.custom_tool_call_input.delta/done,避免被 fallback 当作普通文本输出;同时保留旧 function_call_arguments.delta 行为,减少回归风险。

前后对比分析

兼容性

  • 不修改 Codex HTTP / WebSocket 请求头生成逻辑。
  • 不修改 User-AgentOriginatorOpenAI-BetaSession_idConversation_idAuthorization 等既有请求头策略。
  • include 逻辑为加法合并,保留既有项且不重复。
  • custom_tool_call 被映射到 OpenAI/Anthropic 现有工具调用结构,对旧 function_call 行为保持兼容。

风险控制

  • 对异常非数组 include 不强行覆盖,避免破坏未知客户端输入。
  • custom tool delta 同时支持 item_idcall_id,降低上游事件字段差异导致的索引丢失风险。
  • 对无状态转换路径只显式忽略 custom tool input delta,不改变旧 function call delta fallback 行为。
  • 新增定向测试覆盖 include 合并、去重、custom tool delta、非流式 tool call 提取与 Anthropic 流式转换。

行为变化

  • 当请求包含 reasoning 且已有 include 数组但缺少 reasoning.encrypted_content 时,会自动追加该项。
  • 上游返回 custom_tool_call / response.custom_tool_call_input.delta 时,将按工具调用参数增量处理,而不是丢失或误当普通文本。

测试情况

已执行以下测试:

go test ./proxy -run "TestStreamTranslator_CustomToolCallInputDelta|TestStreamTranslator_FunctionCall|TestPrepareResponsesBody_(MergesReasoningInclude|DoesNotDuplicateReasoningInclude)" -count=1

结果:

ok   github.com/codex2api/proxy
go test ./proxy ./proxy/wsrelay -count=1

结果:

ok   github.com/codex2api/proxy
ok   github.com/codex2api/proxy/wsrelay
go test ./admin ./api ./auth ./cache ./config ./database ./internal/imageproc ./internal/imagestore ./internal/signedasset ./proxy ./proxy/wsrelay ./security ./security/promptfilter -count=1

结果:

ok   github.com/codex2api/admin
ok   github.com/codex2api/api
ok   github.com/codex2api/auth
ok   github.com/codex2api/cache
ok   github.com/codex2api/config
ok   github.com/codex2api/database
ok   github.com/codex2api/internal/imageproc
ok   github.com/codex2api/internal/imagestore
ok   github.com/codex2api/internal/signedasset
ok   github.com/codex2api/proxy
ok   github.com/codex2api/proxy/wsrelay
ok   github.com/codex2api/security
ok   github.com/codex2api/security/promptfilter

另外执行:

git diff --check

结果:无空白错误。Windows 环境下仅出现 Git 换行提示 LF will be replaced by CRLF the next time Git touches it

未通过 / 未执行说明

执行 go test ./... 时,根包因当前 worktree 缺少前端构建产物失败:

main.go:29:12: pattern frontend/dist/*: no matching files found

该失败与本 PR 的 Go 代码修改无关;已用除根包外的全部 Go 子包测试进行覆盖验证。

修改总结

本 PR 在不改既有可工作请求头的前提下,补齐了 Codex Responses 请求体 include 合并和 custom tool call SSE 事件兼容能力,提升 Codex 模拟保真度与新版上游事件格式兼容性,并通过定向测试与相关 Go 子包测试验证。

Summary by CodeRabbit

  • New Features

    • Extended tool call support to include custom tool calls alongside function calls. Tool inputs are now properly handled and translated in both streaming and non-streaming response modes.
  • Tests

    • Added test coverage for custom tool call input deltas, streaming behavior, and token estimation.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbb4c831-ddad-4554-93dc-8ffd6be7cf46

📥 Commits

Reviewing files that changed from the base of the PR and between fabab0d and 588a192.

📒 Files selected for processing (6)
  • proxy/anthropic.go
  • proxy/anthropic_test.go
  • proxy/handler.go
  • proxy/handler_anthropic.go
  • proxy/translator.go
  • proxy/translator_test.go

📝 Walkthrough

Walkthrough

Extends Codex-to-OpenAI and Codex-to-Anthropic translators to recognize custom_tool_call items and response.custom_tool_call_input.delta/done events, mapping them into standard tool call output with call_id-to-id fallback and input-field argument sourcing. Also refactors reasoning.encrypted_content include injection from unconditional to conditional helper functions.

Changes

custom_tool_call Translation Support

Layer / File(s) Summary
Helper predicates and StreamTranslator/ExtractToolCalls core
proxy/translator.go
Introduces isCodexToolCallItem and isCodexToolInputDeltaEvent predicates. Updates TranslateStreamChunk to skip custom_tool_call_input events, extends TranslateParsed to announce and stream custom_tool_call items with call_id/id fallback, and updates ExtractToolCallsFromOutput to extract custom_tool_call items using the input field.
Anthropic streaming and non-streaming translator
proxy/anthropic.go
Routes response.custom_tool_call_input.delta into the existing tool-input buffer, extends handleOutputItemAdded to create tool_use blocks for custom_tool_call, and extends buildAnthropicResponseFromCompleted to build tool_use blocks with input-field sourcing.
Token estimation char counting in handler paths
proxy/handler.go, proxy/handler_anthropic.go
Replaces hardcoded response.function_call_arguments.delta string comparisons with isCodexToolInputDeltaEvent in deltaCharCount accumulation across stream and non-stream paths in both handlers.
Tests for custom_tool_call translation
proxy/anthropic_test.go, proxy/translator_test.go
Adds TestAnthropicStreamTranslator_CustomToolCallInputDelta, TestStreamTranslator_CustomToolCallInputDelta, and extends the ExtractToolCallsFromOutput fixture to include a custom_tool_call item (expected count: 2 → 3).

Reasoning Include Handling Refactor

Layer / File(s) Summary
Include helpers, wiring, and tests
proxy/translator.go, proxy/translator_test.go
Adds codexReasoningEncryptedContentInclude constant and ensureDefaultCodexInclude/ensureCodexReasoningInclude helpers; replaces unconditional include initialization; adds tests for include merging and deduplication.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • james-6-23/codex2api#235: Modifies the same prepareResponsesBodyWithOptions path in proxy/translator.go around reasoning.encrypted_content sanitization and inclusion logic, intersecting directly with the include refactor in this PR.

Poem

🐇 A custom tool call came knocking one day,
The translator said, "I know the way!"
With call_id fallback and input in hand,
I map every chunk across OpenAI land.
No delta goes lost, no include unseen —
The fluffiest proxy there's ever been! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title is in Chinese and translates to 'feat: Enhance Codex custom tool event compatibility'. It accurately reflects the main changes: adding support for custom_tool_call events and improving include parameter merge logic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@james-6-23 james-6-23 merged commit b0b9088 into james-6-23:main Jun 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants