Repository navigation
feat(sse): route web_search requests to a configured model (#4481) - #4509
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request implements layer 2 web-search routing, which overrides the target model to a configured web-search-capable model when a native web-search tool is detected. The reviewer identified a high-severity security risk where this override occurs after API key policy enforcement, allowing clients to bypass restrictions and route requests to expensive models. They provided a code suggestion to re-evaluate the API key policy against the overridden model.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (hasNativeWebSearchTool(body)) { | ||
| const wsSettings = await getCachedSettings().catch(() => ({}) as Record<string, unknown>); | ||
| const wsRoute = resolveWebSearchRouteOverride(resolvedModelStr, body, wsSettings); | ||
| if (wsRoute.wasRouted) { | ||
| log.info( | ||
| "WEBSEARCH-ROUTE", | ||
| `web_search tool → model override: ${resolvedModelStr} → ${wsRoute.model}` | ||
| ); | ||
| resolvedModelStr = wsRoute.model; | ||
| body = { ...body, model: wsRoute.model }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Security/Policy Bypass Risk
The web-search routing override occurs after the API key policy enforcement (enforceApiKeyPolicy at line 300). If a client requests a cheap/allowed model but includes a native web-search tool, the request will bypass the policy check and be routed to the configured webSearchRouteModel (which could be an expensive or restricted model like Claude 3.5 Sonnet).
To prevent this policy bypass, we should re-evaluate the API key policy against the new target model if a route override occurs.
| if (hasNativeWebSearchTool(body)) { | |
| const wsSettings = await getCachedSettings().catch(() => ({}) as Record<string, unknown>); | |
| const wsRoute = resolveWebSearchRouteOverride(resolvedModelStr, body, wsSettings); | |
| if (wsRoute.wasRouted) { | |
| log.info( | |
| "WEBSEARCH-ROUTE", | |
| `web_search tool → model override: ${resolvedModelStr} → ${wsRoute.model}` | |
| ); | |
| resolvedModelStr = wsRoute.model; | |
| body = { ...body, model: wsRoute.model }; | |
| } | |
| } | |
| if (hasNativeWebSearchTool(body)) { | |
| const wsSettings = await getCachedSettings().catch(() => ({}) as Record<string, unknown>); | |
| const wsRoute = resolveWebSearchRouteOverride(resolvedModelStr, body, wsSettings); | |
| if (wsRoute.wasRouted) { | |
| const policyOverride = await enforceApiKeyPolicy(request, wsRoute.model); | |
| if (policyOverride.rejection) { | |
| log.warn( | |
| "POLICY", | |
| "API key policy rejected routed web-search model: " + wsRoute.model + " (key=" + (apiKeyInfo?.id || "unknown") + ")" | |
| ); | |
| return policyOverride.rejection; | |
| } | |
| log.info( | |
| "WEBSEARCH-ROUTE", | |
| "web_search tool → model override: " + resolvedModelStr + " → " + wsRoute.model | |
| ); | |
| resolvedModelStr = wsRoute.model; | |
| body = { ...body, model: wsRoute.model }; | |
| } | |
| } |
4125430 to
3d4eb39
Compare
…apw#4509, diegosouzapw#4481) Routes requests carrying a web_search tool to a configured webSearchRouteModel (diegosouzapw#4481 layer-2). Integrated into release/v3.8.33.
Summary
Layer 2 of #4481 (CCR-style
Router.webSearch). Layer 1 (#4490) stopped MiniMax from 400ing onweb_search_20250305by falling back to OmniRoute's/v1/search. This adds the alternative an operator can opt into: route the whole request to a model that natively runs web search, the way claude-code-router'sRouter.webSearchdoes, while every non-search request stays on the default model.Behavior
New optional setting
webSearchRouteModel(a model string —provider,model/provider/model/ alias / combo name). When a request carries a native web_search server tool (web_search,web_search_preview, or Anthropic's versionedweb_search_20250305) andwebSearchRouteModelis set, the request is routed to that model instead of the resolved default. Unset/empty = disabled (no behavior change).web_search_20250305native via the existing Claude→Claude passthrough).web_searchdoes not trigger routing (only the native server tool does).Implementation
open-sse/services/webSearchRouting.ts(hasNativeWebSearchTool,resolveWebSearchRouteOverride) — no DB, unit-testable. Detection uses aweb_searchprefix match so the raw client toolweb_search_20250305is caught at the entrypoint (the exact-set check inwebSearchFallback.tsonly sees it post-normalization).src/sse/handlers/chat.ts, mirroring the adjacent T05 task-aware-routing override. Settings are read viagetCachedSettings()only when a web-search tool is present (cheap on every other request).webSearchRouteModel: z.string().max(200).optional()registered insettingsSchemas.ts.Tests (TDD)
tests/unit/web-search-tool-routing-4481.test.ts— 13 cases (RED→GREEN): detection of plain/preview/versioned/future tool types, function-tool exclusion, malformed input; route/no-route across all gates (tool present, config set, blank, same-model no-op, trim, non-string); + source-guards asserting chat.ts wires the router and the Zod schema registers the key.Validation
typecheck:core ✅ · typecheck:noimplicit:core ✅ · lint 0 err / 0 new warn ✅ · check:cycles ✅ · new tests 13/13 ✅ · layer-1 regression
web-search-fallback-format15/15 ✅ (this composes with, doesn't replace, the #4490 fallback) · response-model-echo 5/5.check:file-size: chat.ts rebaselined 1491→1513 with justification. Note: 4 unrelated files (core.ts,usage/providerLimits.ts,constants/providers.ts,services/usage.ts) are flagged by check:file-size but are pre-existing drift on the base (origin/release/v3.8.32) from concurrent merges — untouched here (delta-0), to be reconciled separately.Follow-ups (not in this PR)
webSearchRouteModelin the Routing settings tab (the setting is already usable via the settings API).byToolmap if other server tools need it (web_search is the only one with this problem today).