Skip to content

[Provider compatibility] Ollama Cloud Responses passthrough skips web-search sidecar; hosted web_search is echoed as a client function_call #3761

Description

@foo1maker

Deferred; no hosted-search execution fix landed

The four-track delivery did not implement a raw Responses passthrough search/continuation bridge. Search-result insertion, mixed-tool continuity and replay semantics remain outstanding. This issue stays open.

Verified against dev 5759d9ea2f1e7281cdc01eb9628f2e0a123fb59c. Original report by @foo1maker. The attribution record was added in #3811.

Client or integration

Codex App

Provider or upstream service

Ollama Cloud (https://ollama.com/v1, key-auth openai-responses passthrough)

OpenCodex version

2.43.0 (@bitkyc08/opencodex)

Endpoint or capability

/v1/responses + hosted {type:"web_search"} / webSearchSidecar

Current behaviour

Codex App always speaks Responses to OpenCodex (POST /v1/responses) and advertises the hosted tool {type:"web_search"}.

For a key-auth provider with adapter: "openai-responses" (Ollama Cloud GLM / DeepSeek, etc.) OpenCodex takes the passthrough branch in src/server/responses/core.ts and returns before runWithWebSearch. planWebSearch / shouldResolveOpenAiWebSearchSidecar also fail closed on isPassthrough:

// src/web-search/index.ts
if (!parsed._webSearch || isPassthrough) return undefined;

The comment assumes passthrough means ChatGPT, which already executes hosted search server-side.

Ollama Cloud does not execute OpenAI hosted {type:"web_search"} on /v1/responses. The model instead emits a client function_call named web_search. Two failures follow:

  1. Stock responses-undeclared-tool-guard treats that echo as an undeclared client tool and aborts the SSE stream:
    routed provider emitted undeclared client tool "web_search"; only request-declared tools may be called
    Codex App then reconnects 5/5 and the worker/turn dies (last_agent_message: null).

  2. If the guard is relaxed, Codex App still does not execute a function named web_search (it expects the provider to run hosted search). The next hop is unsupported call: web_search. The model can continue without live results. Dashboard webSearchSidecar (e.g. gpt-5.6-luna) stays idle.

Ollama does have a working search API, but it is a separate client-executed REST endpoint, not ChatGPT hosted search:

Official OpenCodex backends (openai / anthropic / xai / gemini / exa) never call it. The sidecar loop that would intercept a synthetic web_search function tool never runs on this passthrough path.

Switching the provider to openai-chat would enter the sidecar loop, but Codex App cannot be switched off Responses, and translating GLM/DeepSeek off /v1/responses drops reasoning summaries and the native tool surface that this client requires.

Expected behaviour

On key-auth Responses passthrough to a gateway that does not execute OpenAI hosted web_search:

  1. Do not abort the stream for a function_call named web_search when the request catalog already declared hosted {type:"web_search"} (same catalog entry, different wire shape).
  2. Intercept that call in the proxy (do not relay it to Codex as an unsupported client tool).
  3. Execute search and inject results as function_call_output (and/or a hosted web_search_call item Codex understands).
  4. Prefer Ollama's own POST /api/web_search when the route is Ollama Cloud (same credential, no ChatGPT sidecar spend). Luna/Exa remain valid fallbacks for translated routes, which is the current sidecar design.

Passthrough should keep meaning “preserve the Responses wire”, not “the upstream is ChatGPT and already searched”.

Minimal redacted request or reproduction

{
  "defaultProvider": "ollama-relay",
  "webSearchSidecar": { "enabled": true },
  "providers": {
    "ollama-relay": {
      "adapter": "openai-responses",
      "baseUrl": "https://ollama.com/v1",
      "authMode": "key",
      "selectedModels": ["glm-5.3-flash"]
    },
    "openai": {
      "adapter": "openai-responses",
      "baseUrl": "https://chatgpt.com/backend-api/codex",
      "authMode": "forward"
    }
  }
}
# Codex App
model = "glm-5.3-flash"
openai_base_url = "http://127.0.0.1:10100/v1"
  1. ocx start (2.43.0 stock).
  2. In Codex App, ask a routed GLM/DeepSeek turn for current web facts (pricing, docs, anything that triggers search).
  3. Observe either the undeclared-tool abort or unsupported call: web_search with no sidecar / no POST https://ollama.com/api/web_search.

Actual response or error

stream disconnected before completion: routed provider emitted undeclared client tool "web_search"; only request-declared tools may be called

If that guard is bypassed, the Codex client hop is:

unsupported call: web_search

Upstream GLM item shape (redacted):

{
  "type": "function_call",
  "name": "web_search",
  "arguments": "{\"query\":\"site:example.com current pricing\"}"
}

OpenCodex logs show no [web-search] sidecar enter on these turns. Ollama /api/web_search is not requested.

Upstream documentation

Suggested mapping or implementation notes

Stock code already has the intercept loop for translated adapters (runWithWebSearch + buildWebSearchTool). The gap is that adapter.passthrough both:

  • skips that loop (planWebSearch(..., isPassthrough) / early return in core.ts), and
  • forwards hosted {type:"web_search"} unchanged, which Ollama echoes as a named function_call.

A minimal passthrough-aware path:

  1. In addNamelessClientCallTypes, treat hosted web_search / web_search_preview as authorizing a client function_call named web_search (same catalog, echo shape). This at least stops Mode 2 / App workers from 5/5-dying.
  2. On non-forward (authMode: "key") passthrough SSE, intercept function_call name === "web_search", run an executor, hide the synthetic call from Codex, inject the tool result into the next upstream hop.
  3. Add an ollama web-search backend that POSTs https://ollama.com/api/web_search with the existing provider key. Keep luna for openai-chat / Anthropic routes.

Do not require operators to change Codex App to Chat Completions. The App wire is Responses; Ollama Cloud also speaks /v1/responses. The missing piece is executing search for that pair.

Additional context

Searched existing issues. Closest prior art is passthrough tool-surface gaps (e.g. #1950 tool_search rewrite on Responses passthrough) and web-search sidecar bugs on translated openai-chat routes (#265, #1001). I did not find an open issue for Ollama Cloud hosted-web_search echo vs /api/web_search.

Related but separate: Ollama /v1/responses also lacks previous_response_id store (documented). statelessResponses addresses replay; it does not execute search.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

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

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportsstreamingSSE, WebSocket, terminal stream framestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions