mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-07 03:48:44 +08:00
feat(web): show provider count on each model-type filter tag (#15444)
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)
This commit is contained in:
@@ -57,18 +57,32 @@ export const AvailableModels: FC<{
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedTag, setSelectedTag] = useState<string | null>(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<Record<string, number>>((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<string>();
|
||||
@@ -118,6 +132,9 @@ export const AvailableModels: FC<{
|
||||
onClick={() => setSelectedTag(null)}
|
||||
>
|
||||
All
|
||||
<span className="ml-1 tabular-nums opacity-60">
|
||||
{searchedModels.length}
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
{allTags.map((tag) => (
|
||||
@@ -130,6 +147,9 @@ export const AvailableModels: FC<{
|
||||
>
|
||||
{mapModelKey[tag.trim() as keyof typeof mapModelKey] ||
|
||||
tag.trim()}
|
||||
<span className="ml-1 tabular-nums opacity-60">
|
||||
{tagCounts[tag] ?? 0}
|
||||
</span>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user