Skip to content

Commit 2b75760

Browse files
committed
fix: fallback to direct provider when gateway not configured
- Track gateway availability (couponCodeSet) in localStorage - Only transform to gateway format when gateway is both enabled AND available - Hide cloud icon toggle when gateway provider is not configured - Cloud icon only shows in model selector when gateway is actually active
1 parent e93a497 commit 2b75760

File tree

3 files changed

+74
-37
lines changed

3 files changed

+74
-37
lines changed

src/browser/components/ModelSelector.tsx

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ export interface ModelSelectorRef {
2828
export const ModelSelector = forwardRef<ModelSelectorRef, ModelSelectorProps>(
2929
({ value, onChange, recentModels, onComplete, defaultModel, onSetDefaultModel }, ref) => {
3030
const { open: openSettings } = useSettings();
31-
const { isEnabled: isGatewayEnabled, toggle: toggleGateway } = useGatewayModels();
31+
const {
32+
isEnabled: isGatewayEnabled,
33+
toggle: toggleGateway,
34+
gatewayAvailable,
35+
} = useGatewayModels();
3236
const [isEditing, setIsEditing] = useState(false);
3337
const [inputValue, setInputValue] = useState(value);
3438
const [error, setError] = useState<string | null>(null);
@@ -192,7 +196,7 @@ export const ModelSelector = forwardRef<ModelSelectorRef, ModelSelectorProps>(
192196
}, [highlightedIndex]);
193197

194198
if (!isEditing) {
195-
const gatewayEnabled = isGatewayEnabled(value);
199+
const gatewayEnabled = isGatewayEnabled(value) && gatewayAvailable;
196200
return (
197201
<div ref={containerRef} className="relative flex items-center gap-1">
198202
{gatewayEnabled && (
@@ -264,34 +268,36 @@ export const ModelSelector = forwardRef<ModelSelectorRef, ModelSelectorProps>(
264268
<div className="grid w-full grid-cols-[1fr_auto] items-center gap-2">
265269
<span className="min-w-0 truncate">{model}</span>
266270
<div className="flex items-center gap-0.5">
267-
{/* Gateway toggle */}
268-
<TooltipWrapper inline>
269-
<button
270-
type="button"
271-
onMouseDown={(e) => e.preventDefault()}
272-
onClick={(e) => {
273-
e.preventDefault();
274-
e.stopPropagation();
275-
toggleGateway(model);
276-
}}
277-
className={cn(
278-
"flex items-center justify-center rounded-sm border px-1 py-0.5 transition-colors duration-150",
279-
isGatewayEnabled(model)
280-
? "text-accent border-accent/40"
281-
: "text-muted-light border-border-light/40 hover:border-foreground/60 hover:text-foreground"
282-
)}
283-
aria-label={
284-
isGatewayEnabled(model) ? "Disable Mux Gateway" : "Enable Mux Gateway"
285-
}
286-
>
287-
<Cloud
288-
className={cn("h-3 w-3", isGatewayEnabled(model) && "fill-current")}
289-
/>
290-
</button>
291-
<Tooltip className="tooltip" align="center">
292-
{isGatewayEnabled(model) ? "Using Mux Gateway" : "Use Mux Gateway"}
293-
</Tooltip>
294-
</TooltipWrapper>
271+
{/* Gateway toggle - only show when gateway is configured */}
272+
{gatewayAvailable && (
273+
<TooltipWrapper inline>
274+
<button
275+
type="button"
276+
onMouseDown={(e) => e.preventDefault()}
277+
onClick={(e) => {
278+
e.preventDefault();
279+
e.stopPropagation();
280+
toggleGateway(model);
281+
}}
282+
className={cn(
283+
"flex items-center justify-center rounded-sm border px-1 py-0.5 transition-colors duration-150",
284+
isGatewayEnabled(model)
285+
? "text-accent border-accent/40"
286+
: "text-muted-light border-border-light/40 hover:border-foreground/60 hover:text-foreground"
287+
)}
288+
aria-label={
289+
isGatewayEnabled(model) ? "Disable Mux Gateway" : "Enable Mux Gateway"
290+
}
291+
>
292+
<Cloud
293+
className={cn("h-3 w-3", isGatewayEnabled(model) && "fill-current")}
294+
/>
295+
</button>
296+
<Tooltip className="tooltip" align="center">
297+
{isGatewayEnabled(model) ? "Using Mux Gateway" : "Use Mux Gateway"}
298+
</Tooltip>
299+
</TooltipWrapper>
300+
)}
295301
{/* Default model toggle */}
296302
{onSetDefaultModel && (
297303
<TooltipWrapper inline>

src/browser/components/Settings/sections/ModelsSection.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export function ModelsSection() {
2626
const [editing, setEditing] = useState<EditingState | null>(null);
2727
const [error, setError] = useState<string | null>(null);
2828
const { defaultModel, setDefaultModel } = useModelLRU();
29-
const { isEnabled: isGatewayEnabled, toggle: toggleGateway } = useGatewayModels();
29+
const {
30+
isEnabled: isGatewayEnabled,
31+
toggle: toggleGateway,
32+
gatewayAvailable,
33+
} = useGatewayModels();
3034

3135
// Check if a model already exists (for duplicate prevention)
3236
const modelExists = useCallback(
@@ -224,7 +228,7 @@ export function ModelsSection() {
224228
setEditing((prev) => (prev ? { ...prev, newModelId: value } : null))
225229
}
226230
onRemove={() => handleRemoveModel(model.provider, model.modelId)}
227-
onToggleGateway={() => toggleGateway(model.fullId)}
231+
onToggleGateway={gatewayAvailable ? () => toggleGateway(model.fullId) : undefined}
228232
/>
229233
);
230234
})}
@@ -247,7 +251,7 @@ export function ModelsSection() {
247251
isEditing={false}
248252
isGatewayEnabled={isGatewayEnabled(model.fullId)}
249253
onSetDefault={() => setDefaultModel(model.fullId)}
250-
onToggleGateway={() => toggleGateway(model.fullId)}
254+
onToggleGateway={gatewayAvailable ? () => toggleGateway(model.fullId) : undefined}
251255
/>
252256
))}
253257
</div>

src/browser/hooks/useGatewayModels.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { useCallback } from "react";
1+
import { useCallback, useEffect } from "react";
22
import { usePersistedState, readPersistedState, updatePersistedState } from "./usePersistedState";
3+
import { useProvidersConfig } from "./useProvidersConfig";
34

45
const GATEWAY_MODELS_KEY = "gateway-models";
6+
const GATEWAY_AVAILABLE_KEY = "gateway-available";
57

68
/**
79
* Check if a model is gateway-enabled (static read, no reactivity)
@@ -12,11 +14,20 @@ export function isGatewayEnabled(modelId: string): boolean {
1214
}
1315

1416
/**
15-
* Transform a model ID to gateway format if gateway is enabled for it.
17+
* Check if the gateway provider is available (has coupon code configured)
18+
*/
19+
export function isGatewayAvailable(): boolean {
20+
return readPersistedState<boolean>(GATEWAY_AVAILABLE_KEY, false);
21+
}
22+
23+
/**
24+
* Transform a model ID to gateway format if gateway is enabled AND available.
25+
* Falls back to direct provider if gateway is not configured.
1626
* Example: "anthropic:claude-opus-4-5" → "mux-gateway:anthropic/claude-opus-4-5"
1727
*/
1828
export function toGatewayModel(modelId: string): string {
19-
if (!isGatewayEnabled(modelId)) {
29+
// Only transform if user enabled gateway for this model AND gateway is configured
30+
if (!isGatewayEnabled(modelId) || !isGatewayAvailable()) {
2031
return modelId;
2132
}
2233
// Transform provider:model to mux-gateway:provider/model
@@ -47,11 +58,27 @@ export function toggleGatewayModel(modelId: string): void {
4758
/**
4859
* Hook to manage which models use the Mux Gateway.
4960
* Returns reactive state and toggle function.
61+
*
62+
* Also syncs gateway availability from provider config to localStorage
63+
* so that toGatewayModel() can check it synchronously.
5064
*/
5165
export function useGatewayModels() {
66+
const { config } = useProvidersConfig();
5267
const [gatewayModels, setGatewayModels] = usePersistedState<string[]>(GATEWAY_MODELS_KEY, [], {
5368
listener: true,
5469
});
70+
const [gatewayAvailable, setGatewayAvailable] = usePersistedState<boolean>(
71+
GATEWAY_AVAILABLE_KEY,
72+
false,
73+
{ listener: true }
74+
);
75+
76+
// Sync gateway availability from provider config
77+
useEffect(() => {
78+
if (!config) return;
79+
const available = config["mux-gateway"]?.couponCodeSet ?? false;
80+
setGatewayAvailable(available);
81+
}, [config, setGatewayAvailable]);
5582

5683
const isEnabled = useCallback(
5784
(modelId: string) => gatewayModels.includes(modelId),
@@ -70,5 +97,5 @@ export function useGatewayModels() {
7097
[setGatewayModels]
7198
);
7299

73-
return { gatewayModels, isEnabled, toggle };
100+
return { gatewayModels, isEnabled, toggle, gatewayAvailable };
74101
}

0 commit comments

Comments
 (0)