176 lines
6.7 KiB
Markdown
176 lines
6.7 KiB
Markdown
# 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)
|
||
|
||
| Value | Behavior |
|
||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `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` / `tavily` | Same orchestration for that backend’s chain member(s): execution retry + penalty apply when multiple backends are eligible. |
|
||
| `default_provider` | `default_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_domains` → `filters.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`.
|
||
|
||
### Recommended: fallback chain (default)
|
||
|
||
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
|
||
|
||
| Field | Description |
|
||
| ----- | ----------- |
|
||
| `enabled` | `false` → `Handled: false` for all web_search matches |
|
||
| `priority` | Host plugin order for ModelRouter (higher runs earlier; see main repo plugins docs) |
|
||
| `route` | `fallback` (default), `antigravity_google`, `codex_web_search`, `xai_web_search`, `tavily`, `default_provider` |
|
||
| `antigravity_model` | Antigravity execution model; never the client Claude model name |
|
||
| `codex_model` | Codex model; empty → `gpt-5.4-mini` |
|
||
| `xai_model` | xAI model; empty → `grok-4.3` |
|
||
| `default_provider` / `default_provider_model` | Used when `route=default_provider` |
|
||
| `tavily_api_keys` | Required for `route=tavily` or fallback last step |
|
||
| `require_web_search_only` | `true` 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.
|