From e5761dfc5ee0af6cdb90db769eafa01716966f9b Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:53:18 +0000 Subject: [PATCH] fix: show disabled gateway button when not configured - Filter mux-gateway from provider dropdown in ModelsSection - Add shouldShowGatewayToggle() to show button for supported providers - Show disabled gateway button with tooltip when not configured - Update ModelRow and ModelSelector with new disabled state styling - Filter stale built-in models from LRU cache (fixes model list sync) --- src/browser/components/ModelSelector.tsx | 31 ++++++++++++------- .../components/Settings/sections/ModelRow.tsx | 27 +++++++++++++--- .../Settings/sections/ModelsSection.tsx | 18 +++++------ src/browser/hooks/useGatewayModels.ts | 11 ++++++- src/browser/hooks/useModelLRU.ts | 16 ++++++++-- src/common/constants/knownModels.ts | 3 ++ 6 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/browser/components/ModelSelector.tsx b/src/browser/components/ModelSelector.tsx index 1a56038d62..0d87f44530 100644 --- a/src/browser/components/ModelSelector.tsx +++ b/src/browser/components/ModelSelector.tsx @@ -286,7 +286,7 @@ export const ModelSelector = forwardRef( {model}
{/* Gateway toggle */} - {gateway.canToggleModel(model) && ( + {gateway.shouldShowGatewayToggle(model) && ( - {gateway.modelUsesGateway(model) - ? "Using Mux Gateway" - : "Use Mux Gateway"} + {!gateway.canToggleModel(model) + ? "Configure Mux Gateway in Settings → Providers" + : gateway.modelUsesGateway(model) + ? "Using Mux Gateway" + : "Use Mux Gateway"} )} diff --git a/src/browser/components/Settings/sections/ModelRow.tsx b/src/browser/components/Settings/sections/ModelRow.tsx index 23dce37183..666f03503d 100644 --- a/src/browser/components/Settings/sections/ModelRow.tsx +++ b/src/browser/components/Settings/sections/ModelRow.tsx @@ -21,6 +21,10 @@ export interface ModelRowProps { hasActiveEdit?: boolean; /** Whether gateway mode is enabled for this model */ isGatewayEnabled?: boolean; + /** Whether to show the gateway toggle button */ + showGatewayToggle?: boolean; + /** Whether the gateway toggle is disabled (show but can't click) */ + gatewayToggleDisabled?: boolean; onSetDefault: () => void; onStartEdit?: () => void; onSaveEdit?: () => void; @@ -100,25 +104,38 @@ export function ModelRow(props: ModelRowProps) { ) : ( <> {/* Gateway toggle button */} - {props.onToggleGateway && ( + {props.showGatewayToggle && ( - {props.isGatewayEnabled ? "Using Mux Gateway" : "Use Mux Gateway"} + {props.gatewayToggleDisabled + ? "Configure Mux Gateway in Providers to enable" + : props.isGatewayEnabled + ? "Using Mux Gateway" + : "Use Mux Gateway"} )} diff --git a/src/browser/components/Settings/sections/ModelsSection.tsx b/src/browser/components/Settings/sections/ModelsSection.tsx index 1fc8de4fab..a1f3b458e3 100644 --- a/src/browser/components/Settings/sections/ModelsSection.tsx +++ b/src/browser/components/Settings/sections/ModelsSection.tsx @@ -184,7 +184,7 @@ export function ModelsSection() { - {SUPPORTED_PROVIDERS.map((p) => ( + {SUPPORTED_PROVIDERS.filter((p) => p !== "mux-gateway").map((p) => ( {PROVIDER_DISPLAY_NAMES[p]} @@ -233,6 +233,8 @@ export function ModelsSection() { saving={false} hasActiveEdit={editing !== null} isGatewayEnabled={gateway.modelUsesGateway(model.fullId)} + showGatewayToggle={gateway.shouldShowGatewayToggle(model.fullId)} + gatewayToggleDisabled={!gateway.canToggleModel(model.fullId)} onSetDefault={() => setDefaultModel(model.fullId)} onStartEdit={() => handleStartEdit(model.provider, model.modelId)} onSaveEdit={handleSaveEdit} @@ -241,11 +243,7 @@ export function ModelsSection() { setEditing((prev) => (prev ? { ...prev, newModelId: value } : null)) } onRemove={() => handleRemoveModel(model.provider, model.modelId)} - onToggleGateway={ - gateway.canToggleModel(model.fullId) - ? () => gateway.toggleModelGateway(model.fullId) - : undefined - } + onToggleGateway={() => gateway.toggleModelGateway(model.fullId)} /> ); })} @@ -267,12 +265,10 @@ export function ModelsSection() { isDefault={defaultModel === model.fullId} isEditing={false} isGatewayEnabled={gateway.modelUsesGateway(model.fullId)} + showGatewayToggle={gateway.shouldShowGatewayToggle(model.fullId)} + gatewayToggleDisabled={!gateway.canToggleModel(model.fullId)} onSetDefault={() => setDefaultModel(model.fullId)} - onToggleGateway={ - gateway.canToggleModel(model.fullId) - ? () => gateway.toggleModelGateway(model.fullId) - : undefined - } + onToggleGateway={() => gateway.toggleModelGateway(model.fullId)} /> ))}
diff --git a/src/browser/hooks/useGatewayModels.ts b/src/browser/hooks/useGatewayModels.ts index 172379de6a..d79de67eda 100644 --- a/src/browser/hooks/useGatewayModels.ts +++ b/src/browser/hooks/useGatewayModels.ts @@ -128,8 +128,10 @@ export interface GatewayState { modelUsesGateway: (modelId: string) => boolean; /** Toggle gateway routing for a specific model */ toggleModelGateway: (modelId: string) => void; - /** Check if gateway toggle should be shown for a model (active + provider supported) */ + /** Check if gateway toggle should be enabled for a model (active + provider supported) */ canToggleModel: (modelId: string) => boolean; + /** Check if gateway toggle should be visible for a model (provider supported, regardless of config) */ + shouldShowGatewayToggle: (modelId: string) => boolean; /** Check if model is actively routing through gateway (for display) */ isModelRoutingThroughGateway: (modelId: string) => boolean; } @@ -195,6 +197,11 @@ export function useGateway(): GatewayState { [isActive] ); + const shouldShowGatewayToggle = useCallback( + (modelId: string) => isProviderSupported(modelId), + [] + ); + const isModelRoutingThroughGateway = useCallback( (modelId: string) => isActive && isProviderSupported(modelId) && enabledModels.includes(modelId), @@ -210,6 +217,7 @@ export function useGateway(): GatewayState { modelUsesGateway, toggleModelGateway, canToggleModel, + shouldShowGatewayToggle, isModelRoutingThroughGateway, }), [ @@ -220,6 +228,7 @@ export function useGateway(): GatewayState { modelUsesGateway, toggleModelGateway, canToggleModel, + shouldShowGatewayToggle, isModelRoutingThroughGateway, ] ); diff --git a/src/browser/hooks/useModelLRU.ts b/src/browser/hooks/useModelLRU.ts index 898e071762..ebba45add2 100644 --- a/src/browser/hooks/useModelLRU.ts +++ b/src/browser/hooks/useModelLRU.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { usePersistedState, readPersistedState, updatePersistedState } from "./usePersistedState"; -import { MODEL_ABBREVIATIONS } from "@/browser/utils/slashCommands/registry"; +import { MODEL_ABBREVIATIONS, KNOWN_MODEL_IDS } from "@/common/constants/knownModels"; import { defaultModel } from "@/common/utils/ai/models"; import { WORKSPACE_DEFAULTS } from "@/constants/workspaceDefaults"; import { useAPI } from "@/browser/contexts/API"; @@ -70,8 +70,20 @@ export function useModelLRU() { const migrated = prev.map((m) => migrateGatewayModel(m)); // Remove any remaining mux-gateway entries that couldn't be migrated const filtered = migrated.filter((m) => !isGatewayFormat(m)); + // Remove stale built-in models that are no longer in KNOWN_MODELS + // (keeps custom models like "bedrock:..." or "ollama:..." that aren't in KNOWN_MODEL_IDS) + const withoutStale = filtered.filter((m) => { + // Check if this looks like a built-in model (provider:model format with a known provider) + const knownProviders = ["anthropic", "openai", "google", "xai"]; + const provider = m.split(":")[0]; + // If it's from a known provider but not in KNOWN_MODEL_IDS, it's stale + if (knownProviders.includes(provider) && !KNOWN_MODEL_IDS.has(m)) { + return false; + } + return true; + }); // Deduplicate (migration might create duplicates) - const deduped = [...new Set(filtered)]; + const deduped = [...new Set(withoutStale)]; // Merge defaults const merged = [...deduped]; diff --git a/src/common/constants/knownModels.ts b/src/common/constants/knownModels.ts index 716109dc83..a4f7c5b02c 100644 --- a/src/common/constants/knownModels.ts +++ b/src/common/constants/knownModels.ts @@ -139,6 +139,9 @@ export const MODEL_ABBREVIATIONS: Record = Object.fromEntries( .sort(([a], [b]) => a.localeCompare(b)) ); +/** Set of all current known model IDs - used to filter stale cached models */ +export const KNOWN_MODEL_IDS: Set = new Set(Object.values(KNOWN_MODELS).map((m) => m.id)); + export const TOKENIZER_MODEL_OVERRIDES: Record = Object.fromEntries( Object.values(KNOWN_MODELS) .filter((model) => Boolean(model.tokenizerOverride))