Skip to content

fix(responses): scope tool-field stripping per destination - #2267

Closed
olddonkey wants to merge 1 commit into
lidge-jun:devfrom
olddonkey:fix/web-search-field-scope
Closed

fix(responses): scope tool-field stripping per destination#2267
olddonkey wants to merge 1 commit into
lidge-jun:devfrom
olddonkey:fix/web-search-field-scope

Conversation

@olddonkey

@olddonkey olddonkey commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Fixes a test that fails on untouched dev @ 6c928aace: tests/responses-routed-web-search-fields.test.ts > "official OpenAI API-key traffic retains OpenAI web_search fields" (3 pass / 1 fail).

external_web_access was stripped from traffic bound for the official OpenAI API, which supports it.

Cause — two layers, one of them too broad

  1. stripOpenAiOnlyWebSearchFields is gated correctly: it runs only when provider.supportsOpenAiWebSearchToolFields === false, which xAI declares in the registry (src/providers/registry.ts:1011).
  2. CANONICAL_ONLY_TOOL_FIELDS applied its whole table under one outer gate, !isCanonicalOpenAiForwardProvider. An official OpenAI API provider is not the canonical ChatGPT surface, so the table stripped the field there as well.

The two fields in that table do not have the same scope:

field understood by must be stripped for
external_web_access canonical ChatGPT Codex and the official OpenAI API everything else (xAI: Argument not supported)
defer_loading canonical ChatGPT Codex only everything else, including the official OpenAI API

One gate cannot express both, so the narrower field dragged the wider one down with it.

The obvious fix is wrong

Deleting the external_web_access row looks right — xAI already declares the capability flag, so the capability layer covers the case the row was added for. Verified it is not: that turns the failing test green and breaks three that correctly require the field stripped for routed providers.

OpenAI Responses passthrough sanitization > drops ChatGPT's external_web_access hint but keeps routed web search
OpenAI Responses passthrough sanitization > drops Codex-private tool fields from routed declarations
xAI OAuth Responses streaming opt-in > lowers Codex namespaces for xAI and restores routed calls on the client stream

Approach

Each row now carries its own isSupportedDestination predicate, and the call moves outside the namespace-lowering gate so OpenAI API-key traffic is covered too — where only the canonical-only row applies.

The table stays declarative: adding a field remains a data change rather than a new traversal. That property is why the table exists — before it, each Codex-private bit arrived as its own bespoke strip, and the traversals disagreed about which containers they covered.

A comment names the capability flag as the mechanism that owns the routed web-search case, so a later reader does not collapse the two layers back together.

Tests

bun run test: 14016 pass, 10 skip, 1 fail across 886 files.

The single failure is tests/key-login-live-update.test.ts > "notify after key login pushes the merged row and keeps modelCosts on live and disk" — pre-existing and unrelated, and it fails identically on untouched dev.

New coverage: an official OpenAI API-key provider keeps external_web_access and loses defer_loading; a canonical ChatGPT forward provider keeps both. The three tests quoted above are unchanged and still pass.

(Plain bun test with no arguments hangs on this tree with high CPU and no progress — use bun run test.)

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes

    • Improved compatibility for web-search requests across supported OpenAI destinations.
    • Preserved external_web_access where supported while removing unsupported deferred-loading settings.
    • Ensured request fields are consistently sanitized for API-key and routed connections.
  • Tests

    • Added coverage verifying destination-specific handling of web-search fields.

`tests/responses-routed-web-search-fields.test.ts` > "official OpenAI API-key
traffic retains OpenAI web_search fields" fails on untouched `dev`:
`external_web_access` is stripped from traffic bound for the official OpenAI
API, which supports it.

Two layers strip that field. The capability layer is gated correctly — it runs
only when `supportsOpenAiWebSearchToolFields === false`, which xAI declares in
the registry. `CANONICAL_ONLY_TOOL_FIELDS` applied its whole table under one
outer gate, `!isCanonicalOpenAiForwardProvider`, and an official OpenAI API
provider is not the canonical ChatGPT surface, so the table stripped it there
too.

The two fields in that table do not have the same scope:

- `external_web_access` is understood by both OpenAI-operated surfaces — the
  canonical ChatGPT Codex backend and the official OpenAI API. Routed
  third-party gateways reject it (xAI: `Argument not supported`).
- `defer_loading` is private to the canonical surface; the official OpenAI API
  rejects it, so it must keep being stripped there.

Deleting the `external_web_access` row is the obvious fix and it is wrong —
verified: it turns the failing test green and breaks three that correctly
require the field stripped for routed providers, including the xAI namespace
lowering case.

Give each row its own `isSupportedDestination` predicate instead, and move the
call outside the namespace-lowering gate so OpenAI API-key traffic is also
covered, where only the canonical-only row applies. The table stays
declarative: adding a field remains a data change, which is the property that
made it worth having after each private bit previously arrived as its own
bespoke traversal.

A comment names the capability flag as the mechanism owning the routed
web-search case, so the two layers are not later collapsed back together.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the bug Something isn't working label Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5421a75c-9021-4319-b5c5-c4fda035a0ac

📥 Commits

Reviewing files that changed from the base of the PR and between 6aecc8f and 657232d.

📒 Files selected for processing (2)
  • src/adapters/openai-responses.ts
  • tests/responses-routed-web-search-fields.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Responses tool-field routing

Layer / File(s) Summary
Destination-aware field sanitization
src/adapters/openai-responses.ts
CANONICAL_ONLY_TOOL_FIELDS now checks destination support for each field. Sanitization runs for every noncanonical destination after namespace promotion.
Routing behavior coverage
tests/responses-routed-web-search-fields.test.ts
Tests verify that OpenAI API-key routing preserves external_web_access and removes defer_loading, while ChatGPT forward routing preserves both fields.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 65723

This is a narrowly scoped destination-specific field-sanitization change with targeted test coverage, and no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. 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 title clearly and concisely describes the main change: scoping Responses tool-field stripping per destination.
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.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/web-search-field-scope
🧪 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.

@github-actions

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This PR stays in draft until every box above is ticked.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 40 / 80

본문이 가리키는 실패는 tests/responses-routed-web-search-fields.test.ts "official OpenAI API-key traffic retains OpenAI web_search fields"임. 작성 기준 dev6c928aace. 그 위에 #2262가 이미 머지됨 (2b988b39d). 지금 HEAD 3ffffc802 CANONICAL_ONLY_TOOL_FIELDS (src/adapters/openai-responses.ts:169-180)가 external_web_accesscapabilityGated: true로 둠. stripCanonicalOnlyToolFields (:182)가 플래그 아니면 그 줄을 스킵함. 공식 OpenAI API-key는 supportsOpenAiWebSearchToolFields === false가 아니라서 필드를 남김. 그 테스트가 가리키던 구멍은 dev에 없음.

이 PR은 테이블을 isSupportedDestination으로 갈아낌. external_web_accessisOpenAiOperatedResponsesDestination. defer_loadingisCanonicalOpenAiForwardProvider. 호출을 네임스페이스 게이트 밖으로 뺌. 의도는 ㅇㅇ 두 필드 스코프가 다름. 근데 지금 dev에서 official OpenAI는 이미 !isCanonicalOpenAiForwardProvider 안으로 들어감 (:1722-1730). defer_loading은 capabilityGated가 아니라서 공식 API에서도 이미 뜯김. 캐논 ChatGPT는 바깥 게이트가 통째로 스킵해서 둘 다 남음. 행동 차이는 unclassified 게이트웨이임.

#2262 OWNERSHIP 주석이 unclassified는 필드를 받는다고 박음. 캐퍼빌리티가 명시로 거부할 때만 테이블이 external_web_access를 뜯음. stripOpenAiOnlyWebSearchFields (:1506-1517)도 같은 축. 이 PR은 OpenAI-operated가 아니면 뜯음. unclassified를 fail-closed로 뒤집음. xAI는 레지스트리가 supportsOpenAiWebSearchToolFields === false라 지금 dev에서도 뜯김. 핫 버그 픽스 아님. 정책 뒤집기임.

#2188 L1–L9 사이드카는 이미 dev. 이 테이블은 hosted web_search의 Codex private 비트임. 사이드카 실행기 아님. #2190 x_search는 xAI OAuth 페이지 호스트 툴임. 여기 넣지 말 것. #2264가 같은 openai-responses.ts를 1줄 만짐(커스텀 툴 게이트). 필드 테이블이랑 접지 말 것. types.ts/config.ts 안 건드림. 스플릿 안 씹힘. 근데 #2262 테이블 셰이프랑 충돌함. 리베이스하면 capabilityGated를 지우고 destination predicate로 덮음.

draft 체크리스트 0/4. 2.28 블로커 아님. 프리뷰 배포 아님. 사용자 체감 구멍은 #2262가 이미 닫음. 중복에 가까움. 리베이스 지옥 만들 가치 없음.

해결방안: 리베이스하지 말고 닫아라. official OpenAI keep은 #2262가 소유자임. unclassified keep 정책을 뒤집지 말 것. destination predicate가 정말 필요하면 #2262 위에 새 PR로 defer_loading 테스트만 추가하고 capabilityGated 줄은 유지. #2264에 접지 말 것.

이 댓글은 grok-bot이 작성했습니다

@Ingwannu

Copy link
Copy Markdown
Owner

Closing as superseded by #2262, which is already on current dev as 2b988b3. The current implementation capability-gates external_web_access, preserves it for official OpenAI API-key traffic, strips it for xAI when supportsOpenAiWebSearchToolFields is false, and still removes defer_loading from noncanonical destinations.

I verified the exact current dev head after #2265: the focused web-search field suite passes 4/4 and typecheck passes. This branch now conflicts because it reimplements the same boundary with a different table shape, so merging it would add duplicate churn rather than a missing fix. If the two extra destination-scoping assertions are still desired, please recut those tests only on current dev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants