import type { LargeLanguageModel, ModelProvider } from "@/lib/schemas"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "@/components/ui/dropdown-menu"; import { useEffect, useState } from "react"; import { MODEL_OPTIONS } from "@/constants/models"; import { useLocalModels } from "@/hooks/useLocalModels"; import { useLocalLMSModels } from "@/hooks/useLMStudioModels"; import { ChevronDown } from "lucide-react"; import { LocalModel } from "@/ipc/ipc_types"; interface ModelPickerProps { selectedModel: LargeLanguageModel; onModelSelect: (model: LargeLanguageModel) => void; } export function ModelPicker({ selectedModel, onModelSelect, }: ModelPickerProps) { const [open, setOpen] = useState(false); // Ollama Models Hook const { models: ollamaModels, loading: ollamaLoading, error: ollamaError, loadModels: loadOllamaModels, } = useLocalModels(); // LM Studio Models Hook const { models: lmStudioModels, loading: lmStudioLoading, error: lmStudioError, loadModels: loadLMStudioModels, } = useLocalLMSModels(); // Load models when the dropdown opens useEffect(() => { if (open) { loadOllamaModels(); loadLMStudioModels(); } }, [open, loadOllamaModels, loadLMStudioModels]); // Get display name for the selected model const getModelDisplayName = () => { if (selectedModel.provider === "ollama") { return ( ollamaModels.find( (model: LocalModel) => model.modelName === selectedModel.name, )?.displayName || selectedModel.name ); } if (selectedModel.provider === "lmstudio") { return ( lmStudioModels.find( (model: LocalModel) => model.modelName === selectedModel.name, )?.displayName || selectedModel.name // Fallback to path if not found ); } // Fallback for cloud models return ( MODEL_OPTIONS[selectedModel.provider]?.find( (model) => model.name === selectedModel.name, )?.displayName || selectedModel.name ); }; const modelDisplayName = getModelDisplayName(); // Flatten the cloud model options const cloudModels = Object.entries(MODEL_OPTIONS).flatMap( ([provider, models]) => models.map((model) => ({ ...model, provider: provider as ModelProvider, })), ); // Determine availability of local models const hasOllamaModels = !ollamaLoading && !ollamaError && ollamaModels.length > 0; const hasLMStudioModels = !lmStudioLoading && !lmStudioError && lmStudioModels.length > 0; return ( {" "} {/* Increased width slightly */} Cloud Models {/* Cloud models */} {cloudModels.map((model) => ( { onModelSelect({ name: model.name, provider: model.provider, }); setOpen(false); }} >
{model.displayName} {model.provider} {model.tag && ( {model.tag} )}
{model.description}
))} {/* Local Models Parent SubMenu */}
Local models LM Studio, Ollama
{/* Ollama Models SubMenu */}
Ollama {ollamaLoading ? ( Loading... ) : ollamaError ? ( Error loading ) : !hasOllamaModels ? ( None available ) : ( {ollamaModels.length} models )}
Ollama Models {ollamaLoading && ollamaModels.length === 0 ? ( // Show loading only if no models are loaded yet
Loading models...
) : ollamaError ? (
Error loading models Is Ollama running?
) : !hasOllamaModels ? (
No local models found Ensure Ollama is running and models are pulled.
) : ( ollamaModels.map((model: LocalModel) => ( { onModelSelect({ name: model.modelName, provider: "ollama", }); setOpen(false); }} >
{model.displayName} {model.modelName}
)) )}
{/* LM Studio Models SubMenu */}
LM Studio {lmStudioLoading ? ( Loading... ) : lmStudioError ? ( Error loading ) : !hasLMStudioModels ? ( None available ) : ( {lmStudioModels.length} models )}
LM Studio Models {lmStudioLoading && lmStudioModels.length === 0 ? ( // Show loading only if no models are loaded yet
Loading models...
) : lmStudioError ? (
Error loading models {lmStudioError.message} {/* Display specific error */}
) : !hasLMStudioModels ? (
No loaded models found Ensure LM Studio is running and models are loaded.
) : ( lmStudioModels.map((model: LocalModel) => ( { onModelSelect({ name: model.modelName, provider: "lmstudio", }); setOpen(false); }} >
{/* Display the user-friendly name */} {model.displayName} {/* Show the path as secondary info */} {model.modelName}
)) )}
); }