Repository navigation
[Provider compatibility] Responses passthrough is missing the chat-line tool_search rewrite and restore #1950
Description
Activity
- addedprovider-compatibilityProvider compatibility reportsProvider compatibility reportsproviderProvider adapters, OpenAI-compat presets, upstream API quirksProvider adapters, OpenAI-compat presets, upstream API quirksstreamingSSE, WebSocket, terminal stream framesSSE, WebSocket, terminal stream frames
on Aug 18, 2026 github-actions commented
on Aug 18, 2026 on Aug 18, 2026 – with GitHub ActionsContributorMore actionsIssue reopened
The report now contains the information required by the automated check. Thanks for updating it.
Confirmed against current
dev. The non-canonical Responses passthrough path promotes tools from an existingtool_search_output, but it does not expose an incoming private{type:"tool_search"}declaration as a callable function and does not restore an upstreamfunction_call[name=tool_search]totool_search_call. Canonical OpenAI forward must remain native and untouched. This is a real passthrough translation gap; I will keep the implementation scoped to the non-forward request/JSON/SSE rewrite boundaries with regression coverage.Implemented in #2040 against the latest
devhead.The fix is intentionally limited to non-canonical Responses passthrough providers:
- lowers a declared private
tool_searchentry to a public function tool on the upstream wire; - restores matching JSON and SSE calls to client-side
tool_search_callitems; - preserves canonical OpenAI forward traffic unchanged;
- restores only names that were actually converted and still authorized by the caller's
tool_choice; - bounds temporary SSE argument buffering and leaves an ordinary same-named function untouched.
Focused exact-head verification is green: 154 tests, typecheck, privacy scan, and the 385-page docs build. The PR is awaiting independent maintainer review and exact-head CI; I have not self-approved or merged it.
- lowers a declared private
리뷰 · 우선순위 54 / 80
서드파티
/v1/responses패스스루에서 Codex 전용tool_search가 번역되지 않는다. 게이트웨이는{type:"tool_search"}를 함수로 못 보고, 모델이function_call[name=tool_search]를 내도 클라이언트가 기대하는tool_search_call로 안 돌아온다. zen/deepseek-v4-flash에서 재현됐다. 기본 ChatGPT forward가 아니라 라우티드 Responses 호환이라 54다. 채팅 라인에는 이미 양쪽 번역이 있다.요청 쪽은
src/responses/parser.ts가{type:"tool_search"}를name: "tool_search"함수로 노출한다. 주석도 브리지가tool_search_call로 중계한다고 한다.createResponsesPassthroughAdapter()의buildRequest는 그 변환을 안 한다.promoteClientLoadedTools()는 이미 끝난tool_search_output의 도구를 올릴 뿐, 검색 도구 자체를 함수로 바꾸지 않는다.stripSparkCompatibility()는 Spark에서tool_search를 드롭한다. 일반 패스스루 경로가 아니다.응답 쪽은 패스스루
parseStream이 업스트림을 그대로 흘린다. 예외는 라우티드 compaction뿐이다. 채팅/collaboration은src/bridge.ts와src/server/responses/collaboration.ts가function_call[name=tool_search]를tool_search_call로 복원한다. 패스스루는 그 복원을 안 타서 Codex 클라이언트가 발견을 시작 못 한다.네임스페이스는 이 이슈가 아니다. 리포터도 zen-native
{name, namespace:"codex_app"}는 유지된다고 했다.rewriteRoutedCustomToolsForUpstream()는 custom 도구용이고 tool_search 재작성이 아니다. ChatGPTauthMode=forward에는 이 번역을 넣으면 안 된다. 네이티브는type: "tool_search"를 이해한다.해결방안
authMode !== "forward"인 패스스루buildRequest에서{type:"tool_search"}를 parser.ts와 같은 함수 스키마(query/limit)로 바꾼다. 기존 함수를 복제해 이름을 새로 짓지 말고, parser의 변환을 재사용할 수 있는지 먼저 본다. 응답은 패스스루 스트림에서function_call+name=tool_search를tool_search_call로 바꾸고, 채팅 라인 복원과 같은 필드를 유지한다. forward/Spark 드롭 경로는 그대로 둔다. 회귀는 zen에{type:"tool_search"}를 보내 모델이 함수를 보고, 나온function_call이 클라이언트에tool_search_call로 보이는 것이다.이 댓글은 grok-bot이 작성했습니다
- added a commit that references this issue
on Aug 20, 2026 Fixed on
devby #2145, merged as 83d5ffa.Verified present on the branch rather than assumed:
git log origin/devshows the merge commit above. These PRs targetdevrather thanmain, so GitHub does not auto-close the linked issue — this closure is manual.It ships in the next release cut from
dev. Reopen if you still see it after updating.- added a commit that references this issue
on Sep 17, 2026
Client or integration
Codex App
Provider or upstream service
Any third-party
/v1/responsesgateway reached through Responses passthrough. Reproduced on opencode-go / zen (deepseek-v4-flash); the same gap applies to other API-key Responses endpoints (for example DeepSeek official/responses).OpenCodex version
2.24.2 (
@bitkyc08/opencodex@2.24.2, published 2026-08-17)Endpoint or capability
/v1/responsespassthrough — Codex privatetool_search(deferred tool discovery)On the Responses passthrough line (
createResponsesPassthroughAdapterinsrc/adapters/openai-responses.ts), OpenCodex forwards the Codex body to third-party gateways without the two translations the chat line already performs for Codex's privatetool_searchtool. Routed models therefore cannot start deferred-tool discovery, and afunction_call[name=tool_search]that does appear is returned as-is — the Codex client never sees atool_search_call.This is a passthrough-line gap, not a provider-specific bug. The chat translation path already has both halves.
Current behaviour
1. Request —
{type:"tool_search"}is not exposed as a callable function.Passthrough serializes the incoming tools array without rewriting the private type. Third-party gateways do not implement
type: "tool_search", so the model never sees a callabletool_searchtool and cannot start discovery.Stock 2.24.2, passthrough to zen /
deepseek-v4-flash, tools include{type:"tool_search"}plus a deferredcodex_appnamespace tool. The model states thattool_searchis not in its tool list and emits 0tool_search_callitems (live sample, translated):(
tool_search_call_count: 0)2. Response —
function_call[name=tool_search]is not restored totool_search_call.The chat / collaboration path already restores this shape (
src/bridge.ts615–620 / 1024–1031 / 1590–1595, collector insrc/server/responses/collaboration.ts111–128). Passthrough does not run that restore: it returns the upstream item unchanged.Observed passthrough output (with the request-side rewrite applied locally so the model can emit the call; stock 2.24.2 never reaches this step):
The Codex client executes discovery from
tool_search_call, not from a genericfunction_call.Not a defect: namespace. A zen-native namespaced call already arrives as
{name:"set_thread_title", namespace:"codex_app"}. Passthrough does not strip that field. This issue is only abouttool_searchrewrite + restore.Expected behaviour
On the non-forward Responses line, match the chat line:
{type:"tool_search"}to the routed model as a callable function (same schema assrc/responses/parser.ts199–214).function_call[name=tool_search]to the privatetool_search_callshape the client executes.Canonical OpenAI forward should keep the native
type: "tool_search"unchanged.Minimal redacted request or reproduction
Install
@bitkyc08/opencodex@2.24.2.Point a provider at a third-party
/v1/responsesgateway via per-modelmodelAdapters(API key omitted):{
"providers": {
"opencode-go": {
"adapter": "openai-chat",
"modelAdapters": {
"deepseek-v4-flash": "openai-responses"
}
}
}
}
In a Codex App session, ask the model to discover a deferred session tool via
tool_search(for example acodex_appthread-title tool that isdefer_loading: true).Observe: the model reports that
tool_searchis not in its tool list and does not emittool_search_call.The same tools array sent directly to the gateway (no proxy) is not the comparison that matters here: the gateway never advertised
type: "tool_search"as a function. The missing step is the proxy translation the chat line already does.Actual response or error
Stock 2.24.2 passthrough (redacted):
When a
tool_searchfunction call does appear (same local request-side rewrite; stock 2.24.2 never reaches this step), passthrough returns:not
tool_search_call.Upstream documentation
type: "tool_search"in,tool_search_callout). It is not a public OpenAI Responses hosted tool. No public specification exists for that private type.src/responses/parser.ts199–214 exposestool_searchas a function;src/bridge.tsrelays the call back astool_search_call.function,web_search, and similar) and ignore or reject unknown private types. DeepSeek Responses tools table: https://api-docs.deepseek.com/guides/responses_apiExpected behaviour is the Codex client contract plus the translation OpenCodex already ships on the chat line — not a claim that third-party gateways should implement
type: "tool_search"natively.Suggested mapping or implementation notes
The chat line already has both halves. Passthrough is missing the same pair:
promoteClientLoadedTools/rewriteRoutedCustomToolsForUpstreamcalls increateResponsesPassthroughAdapter, rewrite{type:"tool_search"}to{type:"function", name:"tool_search", ...}using the schema insrc/responses/parser.ts199–214. Reuse the current gate; do not add a new flag. Leave canonical OpenAI forward on the native type.function_call[name=tool_search]totool_search_callon the JSON and SSE rewrite chains — the same place routed custom-tool restore already hangs. The translation-line restore insrc/bridge.ts/src/server/responses/collaboration.tsdoes not run on passthrough today.promoteClientLoadedTools(#1602) only merges tools already returned by a priortool_search_output. It does not convert the private type, so it cannot start discovery.Feasibility (local experiment, not a proposed patch). On an unmodified 2.24.2 checkout plus a minimal patch at those two sites, a live third-party gateway closed the loop: the model issued
tool_search_call, discovered the deferred tool, and called the namespaced tool successfully.web_searchand ordinaryfunctioncalls did not regress.Additional context and attachments
Related (checked; this is not a duplicate of any of them):
promoteClientLoadedTools: promotes tools the client has already loaded. Half of the discovery story; notool_searchto function conversion.tool_search. Maintainer confirmation that the chat / routed path exposestype: "tool_search"as a function and converts the call back totool_search_call. That is exactly the pair passthrough does not do. Documentation to use "tool_search"? #1872 is the docs thread; this issue is the passthrough implementation gap.custom). The workaround then was to switch the provider toopenai-chat. Here the private type istool_search, and the chat line already has the translation.Checks