mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 11:12:17 +03:00
open-sse/translator/response/openai-responses.ts's isCustomTool check
unconditionally treats any tool named "apply_patch" as a Codex-style
custom tool: `toolName === "apply_patch" || state.customToolNames?.has?.(toolName)`.
This overrides a client's own explicit declaration whenever it registers
apply_patch as a plain `type:"function"` tool (with its own JSON-schema
parameters) instead of `type:"custom"`.
Live incident: OpenClaw (combo "default" -> opencode-zen/big-pickle)
declared apply_patch as `type:"function"` with `{input:string}`
parameters. The model correctly produced valid JSON matching that
schema (`{"input":"*** Begin Patch..."}`), but OmniRoute unwrapped it
into a custom_tool_call with raw-text `input` instead of the
function_call/`arguments` shape the client actually registered.
OpenClaw's own dispatcher only implements function_call handling for a
name it declared as type:"function", so it silently never recognized
the tool call at all -- no error, no execution, no follow-up request
ever carrying a result back to the model.
Traced the exact live code path (chatCore.ts -> createSSEStream
translate mode -> translator/index.ts's hub-and-spoke openai ->
openai-responses conversion) to confirm this file -- not
transformer/responsesTransformer.ts -- is what handles combo-routed
streaming for this client/provider format pair.
PR #7905 ("Restore Responses API custom tool calls") already states
this exact precedence should hold ("...while preserving explicit
function-tool precedence") but its unconditional `toolName ===
"apply_patch"` OR never actually implemented that carve-out for
apply_patch specifically -- this fixes the gap between that PR's
stated intent and its actual behavior.
Fix: state.toolSchemas (populated from body.tools by
extractToolSchemaMap(), already threaded through stream.ts's translate
state for a different purpose -- #6951's stripEmptyOptionalToolArgs)
only contains an entry for a tool name when the client's request
declared it with a `parameters` JSON schema, i.e. as type:"function".
Gate the apply_patch fallback on NOT finding it there: apply_patch
still defaults to custom (native Codex CLI convention -- the model
emits it without the client ever declaring it as a tool) unless the
client explicitly registered it as a function tool, in which case that
explicit declaration wins.
Test plan:
- TDD: two new regression tests in
tests/unit/translator-openai-responses-custom-tool-1007.test.ts --
"...with tool defined" (function_call, arguments stay raw JSON) and
"...without tool defined" (unchanged custom_tool_call fallback,
mirroring the existing #1007 coverage). The "with" test is confirmed
failing against the pre-fix code, passing after; the "without" test
passed before and after (regression guard for the existing fallback
behavior).
- Full related suite (translator-openai-responses-custom-tool-1007,
responses-handler, responses-active-stream-custom-tool,
translator-resp-openai-responses,
translator-resp-openai-responses-namespace-identity,
translator-openai-responses-image-output-8459,
responses-transformer) -- 64/64 passing, no regressions to PR #7905's
own custom-tool coverage.
- npx tsc --noEmit -- clean (pre-existing loose-typing errors in this
test file confirmed identical on a pristine upstream checkout).
- npm run lint -- clean.
⚠️ base-red inherited: #9985