Skip to content

fix(responses): execute hosted web_search and accept web_search_call replay - #45

Merged
jacek4yang merged 1 commit into
mainfrom
fix/responses-web-search
Sep 13, 2026
Merged

jacek4yang merged 1 commit into
mainfrom
fix/responses-web-search

Conversation

@jacek4yang

Copy link
Copy Markdown
Owner

Fixes two Grok Build session breakers in the Responses frontend (/v1/responses). Closes #43.

Bug 1: hosted web_search declaration was dropped

Grok Build (backend search enabled, the default) declares {"type":"web_search"}; the proxy dropped it, so the model never had a search tool and Grok Build's server-side search was dead at this gateway.

Fix: the declaration converts to the same upstream web_search function the Anthropic frontend offers (websearch::upstream_function). When the model calls it, the gateway executes Cline /search/websearch with the active credential (search_web, same client/route/never-rotates-pool rules as the Anthropic server-tool loop) and streams:

  • response.web_search_call.in_progress per call;
  • response.output_item.done carrying the full web_search_call item (id, status, action.search with query + source URLs) — also recorded for terminal reconstruction, so Grok Build can replay it next turn;
  • then a continuation generation runs with the grounding appended to the upstream conversation (assistant tool_calls + role=tool results). Pure server-tool rounds continue the same downstream stream; mixed rounds (server + client tools) end the stream after executing the searches so the client can run its own tools.

A per-request budget (from the declaration's max_uses, clamped to MAX_WEB_SEARCH_USES_PER_REQUEST, default 5) bounds searches; exceeded calls surface as max_uses_exceeded failed results, never silently truncated. Continuations reuse send_chat, so effective-429 failover and no-replay-after-commit apply per round; a failed continuation is terminal (semantic output already streamed).

Bug 2: replayed web_search_call input items hard-failed the session

Grok Build replays response.completed.output items as the next turn's input. A web_search_call item hit the unsupported item type 400 and permanently broke the session (every later request failed).

Fix: web_search_call items rebuild the assistant(tool_calls)/role=tool message pair from the item's own action.search.query and action.sources URLs — nothing invented (a failed call replays as its failure; its sources list is empty). The item's own id is reused, so the tool_calls ↔ tool_call_id chain stays valid.

Unchanged

  • Other hosted backend tools (x_search, code_interpreter, MCP, …) remain dropped, never forwarded, never faked.
  • Reasoning exposure gating, GLM policy, shadow-store epochs, prefix telemetry: untouched paths.
  • No prompt/query/result content in logs — search counts only (web_searches in the request summary).

Tests

New coverage: hosted web_search conversion (+ max_uses clamping), web_search_call replay rebuild (unit + integration), hosted-tool drop for non-web_search types, and a full streaming server-round integration test (model calls web_search → gateway executes → continuation generation runs with the grounding → client sees web_search_call items and the grounded answer, never a function_call for the server tool). Existing tool/replay/429 tests unchanged and green.

Gates: fmt OK, check OK, clippy -D warnings OK, 309 tests debug OK, 309 tests release OK.

Docs

README (web_search support in the Grok Build section) and docs/DEVELOPMENT_STATE.md updated.

@jacek4yang
jacek4yang merged commit 9f5a0ec into main Sep 13, 2026
1 check passed
@jacek4yang
jacek4yang deleted the fix/responses-web-search branch September 13, 2026 14:45
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.

fix(responses): web_search breaks Grok Build sessions (400 on replay, tool never executable)

2 participants