Skip to content

OpenAI: opt-in server-side web search (web_search_options)#146

Merged
FMXExpress merged 3 commits into
mainfrom
claude/openai-server-websearch
Jun 5, 2026
Merged

OpenAI: opt-in server-side web search (web_search_options)#146
FMXExpress merged 3 commits into
mainfrom
claude/openai-server-websearch

Conversation

@FMXExpress

Copy link
Copy Markdown
Owner

Summary

Mirrors the Anthropic server-tool plumbing landed in #145 on the OpenAI side. When the flag is on, the OpenAI provider emits "web_search_options": {} as a top-level Chat Completions request field. OpenAI's search-capable model (currently gpt-5-search-api, and the deprecated gpt-4o-search-preview / gpt-4o-mini-search-preview shutting down 2026-07-23) then runs the query on OpenAI's side and stitches results into the response.

Opt-in via config.json:

"openai_server_tools": {
  "web_search": true
}

Off by default. Operators picking a search-capable model in Cfg.DefaultModel (or per-call) get the server-side path; on every other OpenAI model — and on third-party OpenAI-compatible endpoints (Groq, OpenRouter, Together, vLLM, Ollama) — the field is silently ignored, so leaving the flag off costs nothing.

Implementation notes

  • Top-level field, not a tool entry. Unlike Anthropic where the server tool goes in tools[] (and collides with user function tools of the same name), OpenAI's web_search_options is a top-level request field. A user-defined web_search function tool coexists in tools[] without conflict — both stay in the request, OpenAI picks whichever path makes sense.
  • No web_fetch equivalent. Chat Completions only exposes web_search_options; URL fetching lives on the Responses API.
  • No max_uses / search_context_size / user_location. Chat Completions accepts an optional user_location refinement but the other Responses-API knobs aren't supported there. Empty-object opt-in is the documented minimal form; richer config can land later if anyone needs it.
  • Constructor surface. Backwards-compat TOpenAIProvider.Create(APIKey, APIBase, DefaultModel) keeps every existing call site working (used by embedders / sample apps); the catalog-aware overload (used by the factory) gained the new ServerTools record.

Ref: https://developers.openai.com/api/docs/guides/tools-web-search

Test plan

  • make test-openai-server-tools — three wire-shape cases (off, on, coexistence with a user-defined web_search function tool)
  • make test-anthropic-server-tools — Anthropic suite unchanged
  • make smoke — every top-level CLI command still launches
  • Full FPC build clean — no new warnings on touched units
  • Live exercise: set OPENAI_API_KEY, flip openai_server_tools.web_search: true in config.json, set the model to gpt-5-search-api, and run pasclaw agent "what's the latest CVE on CVE.org?" — confirm response text references current results

Not in this PR (follow-ups)

  • pasclaw onboard doesn't yet prompt for the toggle — edit config.json directly.
  • user_location refinement: easy to add (sub-object inside web_search_options); skipped until someone asks.

https://claude.ai/code/session_01TBcLtmpj7dqA5tyFbGnQon


Generated by Claude Code

Mirrors the Anthropic server-tool plumbing on the OpenAI side. When
WebSearch is on, the OpenAI provider emits "web_search_options": {}
as a top-level Chat Completions request field. Only OpenAI's
search-capable models honour the field — currently gpt-5-search-api
(and the deprecated gpt-4o-search-preview / gpt-4o-mini-search-preview,
shutdown 2026-07-23). The operator picks the model via Cfg.DefaultModel
or per-call; we don't auto-rewrite it.

Opt-in via config.json:

  "openai_server_tools": {
    "web_search": true
  }

Off by default. Third-party OpenAI-compatible endpoints (Groq,
OpenRouter, Together, vLLM, Ollama) silently ignore web_search_options
— flipping the flag while pointed at one of them is harmless but
pointless.

Unlike Anthropic's tools-array entry, web_search_options is a
top-level parameter, not a tool entry, so it does NOT collide with a
user-defined web_search function tool. Both can be active in the same
request without dropping anything.

Optional refinements supported by the Chat Completions surface
(user_location, etc.) and Responses-API-only knobs (search_context_size,
domain filters) are not exposed yet — operators who need them can drop
to the Responses API directly.

Ref: https://developers.openai.com/api/docs/guides/tools-web-search

Test plan:
  - make test-openai-server-tools — three wire-shape cases (off, on,
    coexistence with a user-defined function tool of the same name)
  - make test-anthropic-server-tools — Anthropic suite unchanged
  - make smoke — every top-level CLI command still launches
  - Full FPC build clean — no new warnings on touched units

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23270b5d5a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +125 to +127
OAIServerTools.WebSearch := Cfg.OpenAIServerTools.WebSearch;
Provider := TOpenAIProvider.Create(APIKey, Base, Model, Kind, Spec.Auth,
OAIServerTools);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate OpenAI-only search options to OpenAI providers

When openai_server_tools.web_search is enabled, this copies the flag into every pfOpenAI provider constructed by the factory, including OpenAI-compatible providers such as Groq, OpenRouter, Ollama, vLLM, and LiteLLM from BuildCatalog. In configs with an OpenAI primary and an OpenAI-compatible fallback (or when the default provider is one of those), the fallback request will also emit the OpenAI-specific web_search_options field added in BuildOAIRequest, which those providers do not implement as OpenAI server-side web search and may reject or mishandle. Please gate this to Kind = 'openai' or make the toggle provider-specific.

Useful? React with 👍 / 👎.

claude added 2 commits June 5, 2026 21:14
Flip openai_server_tools.web_search default from False to True. The
field is silently ignored on every non-search-capable model
(gpt-4o, gpt-4o-mini, ...) and on third-party OpenAI-compatible
endpoints (Groq, OpenRouter, vLLM, Ollama), so leaving it on costs
nothing there. Operators who pick gpt-5-search-api as their model
get server-side search for free with no extra config step.

ToJSON now always emits the openai_server_tools section so that an
operator setting web_search: false in config.json round-trips. The
previous "emit only when True" path was fine while the default was
False, but with default True an explicit false would silently
revert on the next load — the section would be absent on save and
the default would win on the next FromJSON.

Test rename: TestNoServerTools → TestServerToolsExplicitlyOff. The
"by default" wording in the assertion message was misleading after
the flip; the test still exercises the off-sentinel (NoOpenAIServerTools)
because BuildOAIRequest is pure and doesn't know about TConfig defaults.

Smoke check confirms a fresh config.json now carries:
  "openai_server_tools": { "web_search": true }
Codex P2: the factory was copying Cfg.OpenAIServerTools.WebSearch
into every pfOpenAI-family provider — including Groq, OpenRouter,
Ollama, vLLM, LiteLLM, DeepSeek, Mistral, Together, and the
generic "openai-compat" entry. Those backends speak the OpenAI
wire shape but don't implement server-side web search, and some
reject unknown top-level fields outright. With the default flipped
to True in the previous commit, every operator with an
OpenAI-compatible fallback (or one of those as their primary)
would suddenly start sending web_search_options to backends that
don't know what to do with it.

Fix: gate the flag on the operator's RAW config Kind (or Name when
Kind is empty), matching only the catalog's genuine 'openai' entry.
NOT on NormalizeProviderKind(Kind), because that helper collapses
'openai-compat' to 'openai' for spec lookup — correct for inheriting
OpenAI's Bearer auth shape, but wrong for "send an OpenAI-only
request field," since openai-compat configs intentionally point at
non-OpenAI backends (self-hosted proxies, vLLM, etc.).

IsGenuineOpenAI is exposed in PasClaw.Providers.Factory's interface
so the gating boundary has direct test coverage. New test cases:
  - kind=openai / OpenAI / "  openai  " → True
  - kind empty + name=openai → True
  - kind=openai-compat → False (the trap that prompted this fix)
  - kind=groq/openrouter/ollama/vllm/litellm/deepseek/mistral/together → False
  - kind=groq + name=openai → False (explicit Kind wins)
@FMXExpress FMXExpress merged commit 5d598ae into main Jun 5, 2026
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.

2 participants