Skip to content

fix(dashboard): let the web-search route model be cleared again - #14674

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
MikeTuev:fix/websearch-route-clearable
Sep 24, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
MikeTuev:fix/websearch-route-clearable

Conversation

@MikeTuev

Copy link
Copy Markdown
Contributor

Problem

settings.webSearchRouteModel (layer 2 of #4481) reroutes every request carrying a native web_search tool to the configured model — open-sse/services/webSearchRouting.ts::resolveWebSearchRouteOverride. Its own help text says "Leave blank to disable".

But the dashboard field renders ModelSelectField without allowEmpty:

// src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx
<ModelSelectField
  value={String(settings.webSearchRouteModel ?? "")}
  onChange={(v) => updateSetting({ webSearchRouteModel: v })}
  placeholder={t("webSearchRoutePlaceholder")}
  disabled={loading}
  ariaLabel={t("webSearchRouteTitle")}
/>

ModelSelectField forwards placeholderDisabled={!allowEmpty} to <Select>, so the blank option is rendered disabled. Once any model is selected, the operator can never go back to blank from the UI — the override stays pinned forever.

Observed live on a Claude Code + Anthropic-provider setup: web search silently stopped working because every search request was being rerouted away from the configured provider.

{"tag":"WEBSEARCH-ROUTE","msg":"web_search tool → model override: claude-opus-5-5 → cx/gpt-6-astra"}

The only recovery was editing the settings row in SQLite by hand.

Fix

Pass allowEmpty (blank option becomes selectable) and allowCustomInput (free-text field, so an unlisted/self-hosted target can be typed and blanked) — the same combination the Modality Bridge Vision/Audio pickers already use with this component.

No API change needed: settingsSchemas.ts already types it as z.string().max(200).optional(), and the hidePaidModels guard in src/app/api/settings/route.ts only runs if (typeof body.webSearchRouteModel === "string" && body.webSearchRouteModel.trim() !== "").

Validation (TDD)

New tests/unit/ui/web-search-route-model-clearable.test.tsx (vitest + jsdom, 3 cases):

  1. with allowEmpty the picker renders a non-disabled blank option and selecting it emits onChange("");
  2. without allowEmpty the blank option is disabled — pins the old behavior so the regression is unambiguous;
  3. source guard: RoutingTab must keep passing allowEmpty to the webSearchRouteModel picker.
  • Before the fix: case 3 fails (expected false to be true).
  • After the fix: 3/3 pass.

`settings.webSearchRouteModel` (layer 2 of diegosouzapw#4481) reroutes EVERY request carrying
a native web_search tool to the configured model
(`webSearchRouting.ts::resolveWebSearchRouteOverride`), and its own help text says
"leave blank to disable". The dashboard field, however, rendered
`ModelSelectField` without `allowEmpty`, so the underlying `<Select>` marked its
placeholder `disabled` (`placeholderDisabled={!allowEmpty}`). Once any model had
been selected, the blank value was unreachable from the UI — the override stayed
pinned forever and every web_search request silently left the operator's
configured provider (observed live: `WEBSEARCH-ROUTE web_search tool -> model
override: claude-opus-5-5 -> cx/gpt-6-astra`). The only way out was editing the
settings row in SQLite by hand.

Pass `allowEmpty` so the blank option is selectable, plus `allowCustomInput` so an
unlisted/self-hosted target can be typed and blanked — matching how the Modality
Bridge pickers already use the same component.

The API side already accepted the empty value: `settingsSchemas.ts` types it as
`z.string().max(200).optional()` and the paid-model guard in
`src/app/api/settings/route.ts` only runs for a non-empty string.
@MikeTuev

Copy link
Copy Markdown
Contributor Author

⚠️ base-red inherited: #14547 — the two red checks are not from this PR (it touches exactly 2 files: RoutingTab.tsx + a new test).

Docs Gates (fast-path) → step Docs accuracy (fabricated-docs + i18n mirrors, strict). Reproduced on the base:

✗ In code but missing from .env.example: 1
   - DEEP_HEALTH_CHECK_ENABLED

DEEP_HEALTH_CHECK_ENABLED is read at src/app/api/monitoring/health/route.ts:46 and was introduced by 7d5292fcac — fix(health): opt-in deep check sampling completions surface (#14236) — without the matching .env.example / docs/reference/ENVIRONMENT.md entries.

API Route Typecheck → 4 new diagnostics, all in files this PR does not touch:

✗ open-sse/executors/auggie.ts TS2769 (baseline 0, live 4)
✗ open-sse/executors/auggie.ts TS18047 (baseline 0, live 4)
✗ src/app/api/v1/combos/projectCombo.ts TS2459 (baseline 0, live 2)
✗ src/app/api/v1/combos/projectCombo.ts TS2724 (baseline 0, live 1)

Both belong in their originating branches, not here.

@diegosouzapw

Copy link
Copy Markdown
Owner

Clean, well-scoped fix — traced placeholderDisabled={!allowEmpty} down to the <Select>
and confirmed the blank option really was unreachable once a model had been picked. Ran
your new web-search-route-model-clearable.test.tsx at HEAD: 3/3 pass, and it exercises
the real ModelSelectField component, not a reimplementation. Merge-ready.

@diegosouzapw
diegosouzapw merged commit d124bb7 into diegosouzapw:release/v3.8.51 Sep 24, 2026
9 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants