Back to Cliproxyapi

Claude Code Web Search Router (ModelRouter example)

examples/plugin/claude-web-search-router/README.md

7.2.166.7 KB
Original Source

Claude Code Web Search Router (ModelRouter example)

This plugin demonstrates ModelRouter on Claude Code built-in web_search requests (see temp/1.json in the repo root for a captured request/response).

What it detects

  • Inbound protocol claude / anthropic
  • tools[] with type web_search_20250305 or web_search_20260209
  • Optional Claude Code heuristics: system text like “web search tool use”, or user text Perform a web search for the query: …

Routes (route config)

ValueBehavior
fallback (default)Plugin executor runs antigravity → codex → xai → tavily (built-ins via host.model.*, Tavily in-plugin). On 429/503/502, tries the next backend in the same request. Backends that fail often are deprioritized on later requests (in-memory penalty; no extra config).
antigravity_google / codex_web_search / xai_web_search / tavilySame orchestration for that backend’s chain member(s): execution retry + penalty apply when multiple backends are eligible.
default_providerdefault_provider + optional default_provider_model via built-in AuthManager (not orchestrated).
Routing for fallback requires at least one runnable backend (providers in AvailableProviders where needed, resolvable antigravity model, or tavily_api_keys).

xAI web search notes (aligned with upstream docs)

  • Model: xAI documents grok-4.3 for server-side web_search. This example sets TargetModel to grok-4.3 when xai_model is empty (do not forward claude-sonnet-4-6 to xAI).
  • Request shape: Responses API input + tools[] with "type": "web_search". Optional filters.allowed_domains / filters.excluded_domains (max 5 each, mutually exclusive).
  • Claude mapping today: internal/translator/codex/claude copies Claude allowed_domainsfilters.allowed_domains. Claude blocked_domains is not mapped to excluded_domains yet.
  • Executor: xai_executor normalizes tools (drops unsupported external_web_access if present) and posts to /responses.
  • Response: Citations / server tool metadata come back through OpenAI Responses SSE and are converted toward Claude server_tool_use / web_search_tool_result where the response translator supports it.

Configuration

Plugin config lives under plugins.configs.claude-web-search-router (key must match the plugin name). Load the shared library via plugins.path.

Tries antigravity → codex → xai → tavily; configure tavily_api_keys so the last step can succeed when built-in providers are missing or unavailable.

yaml
plugins:
  path:
    - /absolute/path/to/examples/plugin/bin/claude-web-search-router-go.dylib
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: fallback
      antigravity_model: "" # empty: registry lookup, then first supports_web_search
      codex_model: "gpt-5.4-mini"
      xai_model: "grok-4.3"
      tavily_api_keys:
        - "tvly-xxxxxxxx"
        # - "tvly-yyyyyyyy" # optional: round-robin
      require_web_search_only: true

Omit route to use the same default (fallback).

Minimal fallback (Tavily as last resort only)

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: fallback
      tavily_api_keys:
        - "tvly-xxxxxxxx"
      require_web_search_only: true

Single backend (no fallback)

Antigravity only:

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: antigravity_google
      antigravity_model: "gemini-3.1-flash-lite"
      require_web_search_only: true

Codex only:

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: codex_web_search
      codex_model: "gpt-5.4-mini"
      require_web_search_only: true

xAI only:

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: xai_web_search
      xai_model: "grok-4.3"
      require_web_search_only: true

Tavily only (plugin executor):

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: tavily
      tavily_api_keys:
        - "tvly-xxxxxxxx"
      require_web_search_only: true

Built-in provider via default_provider:

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: true
      priority: 20
      route: default_provider
      default_provider: claude
      default_provider_model: ""
      require_web_search_only: true

Disable or relax detection

yaml
plugins:
  configs:
    claude-web-search-router:
      enabled: false # plugin declines; host may use default Claude path

# Or keep enabled but allow mixed tool lists:
    claude-web-search-router:
      enabled: true
      route: fallback
      require_web_search_only: false

Config field reference

FieldDescription
enabledfalseHandled: false for all web_search matches
priorityHost plugin order for ModelRouter (higher runs earlier; see main repo plugins docs)
routefallback (default), antigravity_google, codex_web_search, xai_web_search, tavily, default_provider
antigravity_modelAntigravity execution model; never the client Claude model name
codex_modelCodex model; empty → gpt-5.4-mini
xai_modelxAI model; empty → grok-4.3
default_provider / default_provider_modelUsed when route=default_provider
tavily_api_keysRequired for route=tavily or fallback last step
require_web_search_onlytrue matches Claude Code–style exclusive web_search tools

Build

bash
make -C examples/plugin bin/claude-web-search-router-go.dylib

Use .so on Linux and .dll on Windows. Point plugins.path at the built artifact.