From 234d44f637ce34854e4d368f98ea4f4e64c4bc89 Mon Sep 17 00:00:00 2001 From: Michael Ludlow Date: Mon, 5 Jan 2026 02:08:22 -0500 Subject: [PATCH] fix(settings): allow toggle deselection and improve embedding model name matching (#661) * fix(settings): allow toggle deselection and improve embedding model name matching Fixes #650 - Can't select embedding model Changes: - Add toggle behavior: clicking selected model now deselects it - Improve model name matching to handle Ollama quantization variants (e.g., qwen3-embedding:8b-q4_K_M now matches qwen3-embedding:8b) - Added installedVersionNames set to match base:version ignoring suffixes This fixes two issues: 1. Users couldn't unselect a model once selected (no toggle) 2. Users couldn't select models when Ollama returned names with quantization suffixes that didn't match the recommended models list * refactor: remove redundant :latest check per Gemini review The installedBaseNames set already handles the case where a model without a tag (e.g., 'embeddinggemma') matches an installed model with :latest suffix. This simplifies the matching logic. --------- Co-authored-by: Alex <63423455+AlexMadera@users.noreply.github.com> --- .../onboarding/OllamaModelSelector.tsx | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/apps/frontend/src/renderer/components/onboarding/OllamaModelSelector.tsx b/apps/frontend/src/renderer/components/onboarding/OllamaModelSelector.tsx index 97257ec9..e6ed737e 100644 --- a/apps/frontend/src/renderer/components/onboarding/OllamaModelSelector.tsx +++ b/apps/frontend/src/renderer/components/onboarding/OllamaModelSelector.tsx @@ -165,29 +165,40 @@ export function OllamaModelSelector({ if (abortSignal?.aborted) return; if (result?.success && result?.data?.embedding_models) { - // Build a set of installed model names (both full name and normalized) + // Build a set of installed model names (full, base, and version-matched) const installedFullNames = new Set(); const installedBaseNames = new Set(); + const installedVersionNames = new Set(); result.data.embedding_models.forEach((m: { name: string }) => { const name = m.name; installedFullNames.add(name); - // Only normalize :latest suffix, not version tags like :4b, :8b, :0.6b + + // Normalize :latest suffix if (name.endsWith(':latest')) { installedBaseNames.add(name.replace(':latest', '')); } else if (!name.includes(':')) { installedBaseNames.add(name); } + + // Handle quantization variants (e.g., qwen3-embedding:8b-q4_K_M) + // Extract base:version without quantization suffix + const quantMatch = name.match(/^([^:]+:[^-]+)/); + if (quantMatch) { + installedVersionNames.add(quantMatch[1]); + } }); // Update models with installation status setModels( RECOMMENDED_MODELS.map(model => { - // Check exact match first, then base name (for :latest normalization) + // Check multiple matching strategies: + // 1. Exact match (e.g., "qwen3-embedding:8b" === "qwen3-embedding:8b") + // 2. Base name match for :latest normalization (handles "embeddinggemma" matching "embeddinggemma:latest") + // 3. Version match ignoring quantization suffix (e.g., "qwen3-embedding:8b" matches "qwen3-embedding:8b-q4_K_M") const isInstalled = installedFullNames.has(model.name) || installedBaseNames.has(model.name) || - // Also check if model without tag is installed (e.g., "embeddinggemma" matches "embeddinggemma") - (model.name.includes(':') ? false : installedFullNames.has(model.name + ':latest')); + installedVersionNames.has(model.name); return { ...model, installed: isInstalled, @@ -281,15 +292,22 @@ export function OllamaModelSelector({ }; /** - * Handles model selection by calling the parent callback. + * Handles model selection with toggle behavior. + * Clicking an already-selected model will deselect it. * Only allows selection of installed models and when component is not disabled. * - * @param {OllamaModel} model - The model to select + * @param {OllamaModel} model - The model to select or deselect * @returns {void} */ const handleSelect = (model: OllamaModel) => { if (!model.installed || disabled) return; - onModelSelect(model.name, model.dim); + + // Toggle behavior: if already selected, deselect by passing empty values + if (selectedModel === model.name) { + onModelSelect('', 0); + } else { + onModelSelect(model.name, model.dim); + } }; if (isLoading) {