From 3de8e0757d7599ea3cd479c0c198b5c0ece5c66b Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Mon, 15 Dec 2025 18:05:32 +0100 Subject: [PATCH] Fix model selector fuzzy search to match provider and support multi-token queries --- packages/coding-agent/CHANGELOG.md | 4 ++ .../interactive/components/model-selector.ts | 2 +- packages/coding-agent/src/utils/fuzzy.ts | 37 ++++++++++++++++--- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0a354973..bed2bad5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Model selector fuzzy search now matches against provider name (not just model ID) and supports space-separated tokens where all tokens must match + ## [0.21.0] - 2025-12-14 ### Added diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index c4b5e0cc..17afdbc7 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -115,7 +115,7 @@ export class ModelSelectorComponent extends Container { } private filterModels(query: string): void { - this.filteredModels = fuzzyFilter(this.allModels, query, ({ id }) => id); + this.filteredModels = fuzzyFilter(this.allModels, query, ({ id, provider }) => `${id} ${provider}`); this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1)); this.updateList(); } diff --git a/packages/coding-agent/src/utils/fuzzy.ts b/packages/coding-agent/src/utils/fuzzy.ts index 837c8bb2..f37c25e1 100644 --- a/packages/coding-agent/src/utils/fuzzy.ts +++ b/packages/coding-agent/src/utils/fuzzy.ts @@ -61,23 +61,48 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch { } // Filter and sort items by fuzzy match quality (best matches first) +// Supports space-separated tokens: all tokens must match, sorted by match count then score export function fuzzyFilter(items: T[], query: string, getText: (item: T) => string): T[] { if (!query.trim()) { return items; } - const results: { item: T; score: number }[] = []; + // Split query into tokens + const tokens = query + .trim() + .split(/\s+/) + .filter((t) => t.length > 0); + + if (tokens.length === 0) { + return items; + } + + const results: { item: T; totalScore: number }[] = []; for (const item of items) { const text = getText(item); - const match = fuzzyMatch(query, text); - if (match.matches) { - results.push({ item, score: match.score }); + let totalScore = 0; + let allMatch = true; + + // Check each token against the text - ALL must match + for (const token of tokens) { + const match = fuzzyMatch(token, text); + if (match.matches) { + totalScore += match.score; + } else { + allMatch = false; + break; + } + } + + // Only include if all tokens match + if (allMatch) { + results.push({ item, totalScore }); } } - // Sort ascending by score (lower = better match) - results.sort((a, b) => a.score - b.score); + // Sort by score (asc, lower is better) + results.sort((a, b) => a.totalScore - b.totalScore); return results.map((r) => r.item); }