Skip to content

feat(ai): add xAI hosted X search and web search tools - #56021

Closed
vinlee19 wants to merge 1 commit into
anomalyco:devfrom
vinlee19:xai-native-search
Closed

vinlee19 wants to merge 1 commit into
anomalyco:devfrom
vinlee19:xai-native-search

Conversation

@vinlee19

@vinlee19 vinlee19 commented Oct 16, 2026 •

Copy link
Copy Markdown

Issue for this PR

Part of #54168 (this is the @opencode/ai library layer; the opencode-side opt-in is a follow-up, see below). Related: #35038.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

packages/ai already parses xAI's x_search_call / web_search_call output items into provider-executed tool events (protocols/xai-responses.ts), but there was no way to request those tools: the xAI adapter had no nativeTool, so any ToolDefinition.native failed with "xAI Responses does not support provider-native tool". The Alibaba and Meta Responses adapters already have this seam, so this follows them:

  • protocols/xai-responses.ts: nativeTool on the adapter validates native.xai against the two documented hosted search tools, { type: "x_search", allowed_x_handles?, excluded_x_handles?, from_date?, to_date?, enable_image_understanding?, enable_video_understanding? } and { type: "web_search", filters?: { allowed_domains?, excluded_domains? }, enable_image_search?, enable_image_understanding? }. Web search domain lists go under filters because that is how the xAI Responses API examples pass them; X search takes its handles at the top level. Anything else, including another provider's native tool, still fails with InvalidRequest before any network call. Compaction uses the same lowerTools, so it accepts them too.
  • providers/xai.ts: XAI.xSearch(options) and XAI.webSearch(options) factories with camelCase options, mirroring Meta.webSearch(). The tool names x_search / web_search match what the stream parser already assigns to the hosted items.
  • README: a short xAI section.

Nothing is enabled by default; the tools are only sent when a caller puts them in tools. xAI bills them per fetched source, which is why I kept this strictly opt-in and did not add an opencode config switch here. How search gets exposed at the app level is the #35038 discussion, and I'd rather land the library piece first.

One thing worth flagging for whoever has an xAI key: the X search docs say sub-tool calls (x_keyword_search, x_semantic_search, ...) show up as custom_tool_call output items. The Open Responses baseline ignores unknown item types, so if live streams use that shape instead of x_search_call, the calls would not surface as tool events. That is pre-existing behaviour on dev and out of scope here, but a recorded cassette would settle it.

How did you verify your code works?

  • New tests in test/provider/xai-responses.test.ts: lowered body.tools with and without options next to a function tool, and rejection of another provider's native tool. They failed first (XAI.xSearch is not a function), then passed.
  • bun test in packages/ai: 1818 pass, 0 fail. bun typecheck clean. oxlint and prettier clean on the changed files.
  • Request shapes checked against the xAI docs for X search and web search. I do not have an xAI API key, so there is no new recorded cassette; the response-side parsing of hosted items is unchanged.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #54168

Public API: adds XAI.xSearch / XAI.webSearch hosted tool factories and their option types to @opencode/ai (backwards compatible)

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. It follows the existing nativeTool pattern from the Meta and Alibaba Responses adapters, and XAI.xSearch() / XAI.webSearch() mirror Meta.webSearch(). I ran the xAI Responses tests, the full packages/ai suite (no failures) and the type check (clean). I also lowered both tools with and without options in a scratch test. Options you leave out are dropped from the wire body, web search domains go under filters, and wrongly typed options fail with InvalidRequest before any request is sent. I couldn't run it against the real xAI API without a key, so a recorded cassette from someone with a key would still be good to have. That would also answer your open question about custom_tool_call sub-tool items.

Public API: @opencode/ai/providers gains XAI.xSearch, XAI.webSearch, XAI.XSearchOptions and XAI.WebSearchOptions, and XAIResponses gains the XSearch / WebSearch schemas. The xAI Responses adapter now accepts native.xai tools that used to be rejected. All of this is additive and backwards compatible.

No blocking concerns.

Related: #54168

@vinlee19

Copy link
Copy Markdown
Author

@rekram1-node @nexxeln — would either of you have time to review this?

It's the library-only half of #54168: it adds the missing nativeTool seam to the xAI Responses adapter, following the Meta / Alibaba adapters (XAI.xSearch() / XAI.webSearch() mirror Meta.webSearch()). It's strictly opt-in and nothing changes unless a caller adds the tools. The bot review found no blockers.

I also verified it end-to-end on the v1 side with a SuperGrok subscription login (same api.x.ai endpoint): the model issued x_search calls that completed with cited sources.

The one open item is a recorded cassette, since I don't have an xAI API key. The repo already has xai-images / xai-video recordings, so if one of you could record xai-search during review, I'm happy to add a xai-search.recorded.test.ts scaffold so it only needs a RECORD=true run. That would also settle the custom_tool_call question in the description. I'm also open to reshaping the API surface if you'd prefer something different. Thanks!

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.

1 participant