Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down
6 changes: 4 additions & 2 deletions src/components/chat/MessageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions src/hooks/useProviderModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -21,6 +23,7 @@ export function useProviderModels(
): UseProviderModelsReturn {
const [providerGroups, setProviderGroups] = useState<ProviderModelGroup[]>([]);
const [defaultProviderId, setDefaultProviderId] = useState<string>('');
const [loaded, setLoaded] = useState(false);

const fetchProviderModels = useCallback(() => {
fetch('/api/providers/models')
Expand All @@ -46,6 +49,9 @@ export function useProviderModels(
models: DEFAULT_MODEL_OPTIONS,
}]);
setDefaultProviderId('');
})
.finally(() => {
setLoaded(true);
});
}, []);

Expand Down Expand Up @@ -75,5 +81,6 @@ export function useProviderModels(
currentProviderIdValue,
modelOptions,
currentModelOption,
loaded,
};
}