[Provider compatibility] Conditional hosted-search filtering for Gemini Responses mixed with client tools

Open
#8,204 2 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
api, backend

Research direction

Start with the hosted-tool filtering mechanism related to #5002, especially stripUnsupportedHostedTools and the provider configuration and management paths described in the issue. Trace how root tools, additional_tools catalogs, aliases, and selectors are handled, then check the existing hosted-tool and provider management tests. Done means conditional filtering preserves standalone search and ordinary tools, validates configuration, survives provider saves and PATCH clearing, and leaves undeclared-tool validation intact.

Written by the indexing model from the issue text.

Description

provider provider-compatibility
Client or integration

Codex App and direct HTTP client

Provider or upstream service

IkunCode OpenAI-compatible Responses endpoint, https://api.ikuncode.cc/v1/responses. Tested model IDs: gemini-4-argon and gemini-3.8-flash.

OpenCodex version

2.97.0 on macOS; source base tag v2.97.0.

Endpoint or capability

Responses hosted web_search_preview / web_search declarations combined with ordinary client function/custom tools, including namespace and client-loaded tool catalogs.

Current behaviour

The existing unsupportedHostedTools: ["web_search"] is an effective workaround for mixed-tool requests, but also removes the search-only capability that the upstream actually accepts.

Synthetic direct tests on both model IDs, 2026-11-06:

Declared tools Result
ordinary function only 200, completed
hosted search only 200, completed
hosted search + ordinary function 400, error below
mixed tools + top-level tool_config.include_server_side_tool_invocations=true same 400

A separate search-only request through the locally patched gateway actually emitted response.web_search_call.in_progress, searching, completed, a completed web_search_call item and URL citations. This is more than a declaration-only success.

These tests establish this endpoint's observed contract. They do not establish whether the mixed-tool limitation originates in IkunCode's conversion or Google's serving stack, and do not verify the underlying model identity.

Expected behaviour

Allow a provider to describe a conditional incompatibility: omit a named hosted declaration only when client function/custom tools are also present. Preserve standalone hosted search, preserve ordinary tools, reconcile selectors and client-loaded catalogs, and retain undeclared-tool validation. Do not silently change unrelated providers.

Minimal redacted request or reproduction

Use an existing key for the intended provider. All content below is synthetic.

curl https://api.ikuncode.cc/v1/responses \
  -H "Authorization: Bearer $IKUNCODE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-4-argon",
    "input": "Synthetic test. Do not search or call tools. Reply exactly OK.",
    "stream": true,
    "tools": [
      {"type": "web_search_preview"},
      {
        "type": "function",
        "name": "lookup_marker",
        "description": "Read a synthetic diagnostic marker.",
        "parameters": {
          "type": "object",
          "properties": {"marker": {"type": "string"}},
          "required": ["marker"],
          "additionalProperties": false
        }
      }
    ]
  }'

Remove either tool declaration: both isolated variants complete. Replace the model ID with gemini-3.8-flash: the same matrix reproduces. Adding the requested flag at the Responses top level did not change the observed mixed-tool 400.

Actual response or error
{
  "error": {
    "message": "Please enable tool_config.include_server_side_tool_invocations to use Built-in tools with Function calling.",
    "type": "upstream_error",
    "param": "",
    "code": 400
  }
}
Upstream documentation

Provider metadata: https://api.ikuncode.cc/api/pricing lists the tested model IDs with Gemini and OpenAI endpoint types. It does not document this mixed-tool restriction.

Google's referenced tool-combination documentation: https://ai.google.dev/gemini-api/docs/generate-content/tool-combination . The compatibility claim here is based on the concrete synthetic matrix above, not an assumption that every OpenAI-compatible Responses gateway forwards Google's tool_config fields.

Suggested mapping or implementation notes

A tested local implementation adds an opt-in provider field:

{
  "adapter": "openai-responses",
  "unsupportedHostedToolsWithFunctions": ["web_search"]
}

The existing unconditional unsupportedHostedTools remains authoritative and unchanged. The new conditional policy:

  • detects function/custom tools or populated namespaces in root and additional_tools catalogs;
  • expands hosted aliases through the existing policy helper;
  • reuses the existing filtering and selector reconciliation;
  • validates a closed hosted-tool vocabulary at config and management boundaries;
  • survives same-destination provider saves, is returned by provider GET, and supports PATCH clearing;
  • does not alter the undeclared-tool guard or grant Bash/other tools.

Core change in stripUnsupportedHostedTools:

const conditional = provider.unsupportedHostedToolsWithFunctions;
const catalogs = conditional?.length
  ? [body.tools, ...(Array.isArray(body.input) ? body.input.flatMap(item =>
    isPlainObject(item) && item.type === "additional_tools" ? [item.tools] : []) : [])]
  : [];
const hasFunctions = catalogs.some(tools => Array.isArray(tools) && tools.some(tool =>
  isPlainObject(tool) && (tool.type === "function" || tool.type === "custom"
    || (tool.type === "namespace" && Array.isArray(tool.tools) && tool.tools.length > 0))));
const declaredUnsupported = declaredUnsupportedHostedTools(hasFunctions
  ? { unsupportedHostedTools: [...(provider.unsupportedHostedTools ?? []), ...conditional!] }
  : provider);

A narrower provider capability is preferable to hard-coding IkunCode's URL. An upstream mixed-tool fix could later make even this opt-in unnecessary.

Additional context and attachments

Related mechanism: #5002. This request is specifically conditional filtering rather than a second unconditional deny list.

Local validation:

  • 38 hosted-tool declaration tests passed, including new mixed/root/loaded/alias cases.
  • 38 management carry-over and reset-policy tests passed.
  • 168 undeclared-tool/inbound/model-rename tests passed.
  • Typecheck passed for 2,305 source files.
  • Privacy scan passed; docs build passed (641 pages).
  • The broad import-graph changed-test run was interrupted after several minutes without a completed selection/result; it is not reported as passing. No full-suite result is claimed.
  • Isolated and production synthetic requests passed after the local patch: mixed tool declarations, namespace custom-tool call and result continuation, and standalone hosted search with actual search events.
  • Official OpenAI connectivity was checked separately; this change does not change provider/network routing.

A separate intermittent pre-output EOF/long wait has also been observed. This conditional-filtering patch is not claimed to fix all Gemini disconnects.

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.
Dominant language
TypeScript
Stars
16.9k
Forks
1.3k
Avg merge
19h 24m
Merged PRs (30d)
676

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from lidge-jun/opencodex

All issues in lidge-jun/opencodex

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.