From 86b320e7469866609d0e1b4d11988eda940e89c8 Mon Sep 17 00:00:00 2001 From: Worldwide <141194268+sunlight7777777@users.noreply.github.com> Date: Mon, 8 Jun 2026 02:31:22 -0700 Subject: [PATCH] feat(web): show provider count on each model-type filter tag (#15444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #15413 ### What problem does this PR solve? In **Settings → Model providers**, the *Available models* panel lets you filter providers by model type (All, LLM, Embedding, Rerank, TTS, ASR, VLM, …), but the filter tags gave no hint of how many providers fall under each type. Users had to click a tag to find out, and an empty category looked identical to a populated one. This PR adds a count to each filter tag in `AvailableModels`: - The **All** tag shows the total number of providers currently listed. - Each model-type tag shows how many providers offer that model type. - Counts respect the active search term, so the badge always matches the number of cards shown once that tag is selected. - Each provider is counted once per model type (deduplicated via a `Set`), so a provider that lists the same type more than once isn't double-counted. Counts are rendered with `tabular-nums` for stable width and dimmed via `opacity-60` so they read as secondary to the label. No API changes; the existing filter logic is untouched — this is purely an additive UI affordance. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../setting-model/components/un-add-model.tsx | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/web/src/pages/user-setting/setting-model/components/un-add-model.tsx b/web/src/pages/user-setting/setting-model/components/un-add-model.tsx index 41691b3a4..07c5da943 100644 --- a/web/src/pages/user-setting/setting-model/components/un-add-model.tsx +++ b/web/src/pages/user-setting/setting-model/components/un-add-model.tsx @@ -57,18 +57,32 @@ export const AvailableModels: FC<{ const [searchTerm, setSearchTerm] = useState(''); const [selectedTag, setSelectedTag] = useState(null); + const searchedModels = useMemo(() => { + return factoryList.filter((model) => + model.name.toLowerCase().includes(searchTerm.toLowerCase()), + ); + }, [factoryList, searchTerm]); + const filteredModels = useMemo(() => { - const models = factoryList.filter((model) => { - const matchesSearch = model.name - .toLowerCase() - .includes(searchTerm.toLowerCase()); - const matchesTag = - selectedTag === null || - model.model_types.some((type) => type === selectedTag); - return matchesSearch && matchesTag; - }); - return models; - }, [factoryList, searchTerm, selectedTag]); + if (selectedTag === null) { + return searchedModels; + } + return searchedModels.filter((model) => + model.model_types.some((type) => type === selectedTag), + ); + }, [searchedModels, selectedTag]); + + // Number of providers matching each tag, respecting the current search term so + // the badge always reflects how many cards are shown when the tag is selected. + const tagCounts = useMemo(() => { + return searchedModels.reduce>((acc, model) => { + // Count each provider once per model type, even if listed more than once. + new Set(model.model_types).forEach((type) => { + acc[type] = (acc[type] || 0) + 1; + }); + return acc; + }, {}); + }, [searchedModels]); const allTags = useMemo(() => { const tagsSet = new Set(); @@ -118,6 +132,9 @@ export const AvailableModels: FC<{ onClick={() => setSelectedTag(null)} > All + + {searchedModels.length} + {allTags.map((tag) => ( @@ -130,6 +147,9 @@ export const AvailableModels: FC<{ > {mapModelKey[tag.trim() as keyof typeof mapModelKey] || tag.trim()} + + {tagCounts[tag] ?? 0} + ))}