diff --git a/src/components/chat/ChatView.tsx b/src/components/chat/ChatView.tsx index 4870077b..58284848 100644 --- a/src/components/chat/ChatView.tsx +++ b/src/components/chat/ChatView.tsx @@ -122,6 +122,9 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal const handleProviderModelChange = useCallback((newProviderId: string, model: string) => { setCurrentProviderId(newProviderId); setCurrentModel(model); + // Persist to localStorage so new chats inherit the latest selection + localStorage.setItem('codepilot:last-model', model); + localStorage.setItem('codepilot:last-provider-id', newProviderId); fetch(`/api/chat/sessions/${sessionId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, diff --git a/src/components/chat/MessageInput.tsx b/src/components/chat/MessageInput.tsx index d54d1776..90b0f053 100644 --- a/src/components/chat/MessageInput.tsx +++ b/src/components/chat/MessageInput.tsx @@ -81,17 +81,19 @@ export function MessageInput({ // --- Extracted hooks --- const popover = usePopoverState(modelName); - const { providerGroups, currentProviderIdValue, modelOptions, currentModelOption } = useProviderModels(providerId, modelName); + const { providerGroups, currentProviderIdValue, modelOptions, currentModelOption, loaded: modelsLoaded } = useProviderModels(providerId, modelName); // Auto-correct model when it doesn't exist in the current provider's model list. // This prevents sending an unsupported model name (e.g. 'opus' to MiniMax which only has 'sonnet'). + // Gate on `modelsLoaded` to avoid premature correction before the provider models API responds. useEffect(() => { + if (!modelsLoaded) return; if (modelName && modelOptions.length > 0 && !modelOptions.some(m => m.value === modelName)) { const fallback = modelOptions[0].value; onModelChange?.(fallback); onProviderModelChange?.(currentProviderIdValue, fallback); } - }, [modelName, modelOptions, currentProviderIdValue, onModelChange, onProviderModelChange]); + }, [modelsLoaded, modelName, modelOptions, currentProviderIdValue, onModelChange, onProviderModelChange]); const { badge, setBadge, cliBadge, setCliBadge, removeBadge, removeCliBadge, hasBadge } = useCommandBadge(textareaRef); diff --git a/src/hooks/useProviderModels.ts b/src/hooks/useProviderModels.ts index 5c1baddb..aef8e85c 100644 --- a/src/hooks/useProviderModels.ts +++ b/src/hooks/useProviderModels.ts @@ -13,6 +13,8 @@ export interface UseProviderModelsReturn { currentProviderIdValue: string; modelOptions: typeof DEFAULT_MODEL_OPTIONS; currentModelOption: (typeof DEFAULT_MODEL_OPTIONS)[number]; + /** True once the initial fetch has completed (success or error). */ + loaded: boolean; } export function useProviderModels( @@ -21,6 +23,7 @@ export function useProviderModels( ): UseProviderModelsReturn { const [providerGroups, setProviderGroups] = useState([]); const [defaultProviderId, setDefaultProviderId] = useState(''); + const [loaded, setLoaded] = useState(false); const fetchProviderModels = useCallback(() => { fetch('/api/providers/models') @@ -46,6 +49,9 @@ export function useProviderModels( models: DEFAULT_MODEL_OPTIONS, }]); setDefaultProviderId(''); + }) + .finally(() => { + setLoaded(true); }); }, []); @@ -75,5 +81,6 @@ export function useProviderModels( currentProviderIdValue, modelOptions, currentModelOption, + loaded, }; }