Skip to content

Provider-executed web search results are dropped on OpenAI and Gemini (forwarded on Anthropic) #1328

Description

@AlexDemzz

Summary

A provider-executed web search reaches the consumer as a tool call on Anthropic only. On OpenAI and Gemini the adapters drop the search entirely, so an app cannot show what the model searched for, nor the pages it read.

@tanstack/ai-anthropic 0.18.5 does forward it (src/adapters/text.ts): a server tool yields TOOL_CALL_START with

metadata: {
  providerExecuted: true,
  anthropic: { serverToolType, resultBlockType, result }, // result = the raw web_search_result blocks: url, title, page_age
}

then TOOL_CALL_END with the parsed input. That is everything a UI needs. The other two adapters have the same data on the wire and never emit it.

OpenAI (@tanstack/ai-openai 0.22.5, @tanstack/openai-base 0.10.10)

OpenAIBaseResponsesTextAdapter.processStreamChunks maps function_call items only (src/adapters/responses-text.ts, the response.output_item.added / response.output_item.done branches check item.type === 'reasoning' and item.type === 'function_call'). Two event shapes fall through with no handler:

  • response.output_item.{added,done} where item.type === 'web_search_call' — the item carries action.queries and, when the request asks for include: ['web_search_call.action.sources'], action.sources: Array<{ type: 'url'; url: string }>.
  • response.output_text.annotation.added where the annotation is a url_citation{ url, title, start_index, end_index }. grep -n annotation src/adapters/responses-text.ts returns nothing.

So the search is invisible, and the citations (including the character positions of each citation in the answer) are lost.

A subclass can recover it today, but only by overriding processStreamChunks and re-declaring two module-private types (StreamedFunctionCallMetadata, LegacyReasoningDeltaEvent) by shape, since neither is exported.

Gemini (@tanstack/ai-gemini 0.29.1)

google_search is offered as a tool, but src/adapters/text.ts never reads candidates[].groundingMetadata (groundingChunks, groundingSupports, webSearchQueries) — zero occurrences in the file. Its processStreamChunks is private, so unlike OpenAI there is no subclass workaround at all.

Request

Forward provider-executed search results as tool-call metadata on OpenAI and Gemini, the way the Anthropic adapter already does. A provider-neutral key (for example metadata.sources: Array<{ url, title?, pageAge? }> alongside providerExecuted: true) would let a consumer render all three the same way; the raw provider blocks could stay beside it under the provider's own key.

On OpenAI that means emitting the web_search_call item as a TOOL_CALL_START/TOOL_CALL_END pair and joining the url_citation annotations (which carry the titles, and the positions) onto the sources by URL. Adding web_search_call.action.sources to include when a web search tool is present would make the sources available without the caller having to know about the flag.

Closest existing issue I found: #495 (user-executed OpenAI provider tools being dropped) — related in spirit, different path.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions