in codex-cli/src/components/model-overlay.tsx [27:165]
export default function ModelOverlay({
currentModel,
providers = {},
currentProvider = "openai",
hasLastResponse,
onSelect,
onSelectProvider,
onExit,
}: Props): JSX.Element {
const [items, setItems] = useState<Array<{ label: string; value: string }>>(
[],
);
const [providerItems, _setProviderItems] = useState<
Array<{ label: string; value: string }>
>(Object.values(providers).map((p) => ({ label: p.name, value: p.name })));
const [mode, setMode] = useState<"model" | "provider">("model");
const [isLoading, setIsLoading] = useState<boolean>(true);
// This effect will run when the provider changes to update the model list
useEffect(() => {
setIsLoading(true);
(async () => {
try {
const models = await getAvailableModels(currentProvider);
// Convert the models to the format needed by TypeaheadOverlay
setItems(
models.map((m) => ({
label: m,
value: m,
})),
);
} catch (error) {
// Silently handle errors - remove console.error
// console.error("Error loading models:", error);
} finally {
setIsLoading(false);
}
})();
}, [currentProvider]);
// ---------------------------------------------------------------------------
// If the conversation already contains a response we cannot change the model
// anymore because the backend requires a consistent model across the entire
// run. In that scenario we replace the regular typeahead picker with a
// simple message instructing the user to start a new chat. The only
// available action is to dismiss the overlay (Esc or Enter).
// ---------------------------------------------------------------------------
// Register input handling for switching between model and provider selection
useInput((_input, key) => {
if (hasLastResponse && (key.escape || key.return)) {
onExit();
} else if (!hasLastResponse) {
if (key.tab) {
setMode(mode === "model" ? "provider" : "model");
}
}
});
if (hasLastResponse) {
return (
<Box
flexDirection="column"
borderStyle="round"
borderColor="gray"
width={80}
>
<Box paddingX={1}>
<Text bold color="red">
Unable to switch model
</Text>
</Box>
<Box paddingX={1}>
<Text>
You can only pick a model before the assistant sends its first
response. To use a different model please start a new chat.
</Text>
</Box>
<Box paddingX={1}>
<Text dimColor>press esc or enter to close</Text>
</Box>
</Box>
);
}
if (mode === "provider") {
return (
<TypeaheadOverlay
title="Select provider"
description={
<Box flexDirection="column">
<Text>
Current provider:{" "}
<Text color="greenBright">{currentProvider}</Text>
</Text>
<Text dimColor>press tab to switch to model selection</Text>
</Box>
}
initialItems={providerItems}
currentValue={currentProvider}
onSelect={(provider) => {
if (onSelectProvider) {
onSelectProvider(provider);
// Immediately switch to model selection so user can pick a model for the new provider
setMode("model");
}
}}
onExit={onExit}
/>
);
}
return (
<TypeaheadOverlay
title="Select model"
description={
<Box flexDirection="column">
<Text>
Current model: <Text color="greenBright">{currentModel}</Text>
</Text>
<Text>
Current provider: <Text color="greenBright">{currentProvider}</Text>
</Text>
{isLoading && <Text color="yellow">Loading models...</Text>}
<Text dimColor>press tab to switch to provider selection</Text>
</Box>
}
initialItems={items}
currentValue={currentModel}
onSelect={(selectedModel) =>
onSelect(
items?.map((m) => m.value),
selectedModel,
)
}
onExit={onExit}
/>
);
}