From d24963423d5cd5601ecd3fc4ace0fcc8dc400973 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Wed, 23 Sep 2026 12:08:15 +0200 Subject: [PATCH] ui : drop the provider merge checkbox and window the manager rows Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash --- .../models/ModelsManager/ModelsManager.svelte | 5 +- .../ModelsManagerModelsTable.svelte | 109 +++++++++++++----- .../app/models/ModelsManager/utils.ts | 21 +--- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte index b7f40f8502..e9baa7bd0a 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManager.svelte @@ -50,9 +50,7 @@ // one entry per repo, so a model with several quants is a single table row; // loaded models lead the table, then favorites, then one block per provider, // and an entry lands in the first group that claims it - /** Off keeps the same repo from two providers apart; on merges them. */ - let groupProviders = $state(false); - let entries = $derived(groupModelQuants(visible, groupProviders)); + let entries = $derived(groupModelQuants(visible)); let groups = $derived.by(() => { // only llama-compat servers report a load state const isLlamaCompat = (entry: ModelQuantGroup) => @@ -217,7 +215,6 @@
(selectedId = option.id)} diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte index abc41f16b3..168e32c218 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte @@ -30,19 +30,17 @@ } from '$lib/components/app'; import { DialogConfirmDownload } from '$lib/components/app/dialogs'; import { Badge } from '$lib/components/ui/badge'; - import { Checkbox } from '$lib/components/ui/checkbox'; import { Input } from '$lib/components/ui/input'; - import { Label } from '$lib/components/ui/label'; + import { MODEL_ROW_WINDOW } from '$lib/constants'; import { ModelCapability, ModelDownloadConfirmAction, ServerModelStatus } from '$lib/enums'; import { modelsStore } from '$lib/stores'; import type { ModelOption } from '$lib/types/models'; import { getBackend } from '$lib/utils/api-base'; import { getBackendCapabilities } from '$lib/utils/backend'; - import { SvelteSet } from 'svelte/reactivity'; + import { SvelteMap, SvelteSet } from 'svelte/reactivity'; interface Props { filter?: string; - groupProviders?: boolean; groups: ModelsTableGroup[]; isFavorite: (option: ModelOption) => boolean; onSelect: (option: ModelOption) => void; @@ -53,7 +51,6 @@ let { filter = $bindable(''), - groupProviders = $bindable(false), groups, isFavorite, onSelect, @@ -84,6 +81,60 @@ deleteOpen = true; } + /** Rows mounted per section, grown by the show more row. */ + const sectionLimits = new SvelteMap(); + + function sectionLimit(key: string): number { + return sectionLimits.get(key) ?? MODEL_ROW_WINDOW; + } + + function growSection(key: string): void { + sectionLimits.set(key, sectionLimit(key) + MODEL_ROW_WINDOW); + } + + /** Cut a section down to the mounted window, counting models rather than groups. */ + function windowSection(group: (typeof sections)[number]): { + families: ModelFamilyGroup[]; + hidden: number; + items: ModelQuantGroup[]; + unit: string; + } { + const limit = sectionLimit(group.key); + + if (group.flat) { + const items: ModelQuantGroup[] = []; + + let shown = 0; + + for (const entry of group.items) { + if (shown >= limit) break; + + items.push(entry); + shown += entry.quants.length; + } + + return { families: [], hidden: group.items.length - items.length, items, unit: 'models' }; + } + + const families: ModelFamilyGroup[] = []; + + let shown = 0; + + for (const family of group.families) { + if (shown >= limit) break; + + families.push(family); + shown += family.entries.reduce((sum, entry) => sum + entry.quants.length, 0); + } + + return { + families, + hidden: group.families.length - families.length, + items: [], + unit: 'families' + }; + } + function toggleFamily(key: string): void { if (collapsedFamilies.has(key)) { collapsedFamilies.delete(key); @@ -234,11 +285,9 @@ {#snippet repoRow(entry: ModelQuantGroup, indent = 0)} {@const isExpanded = !collapsedQuants.has(entry.key)} {@const groupLabel = - entry.kind === 'providers' - ? `${entry.quants.length} providers` - : entry.kind === 'variants' - ? `${entry.quants.length} variants` - : `${entry.quants.length} quants available`} + entry.kind === 'variants' + ? `${entry.quants.length} variants` + : `${entry.quants.length} quants available`} {@const anyLoaded = entry.quants.some(isLoadedOption)}
@@ -292,7 +341,7 @@
{/snippet} -{#snippet quantRow(option: ModelOption, indent = 0, showProvider = false)} +{#snippet quantRow(option: ModelOption, indent = 0)} {@const favorite = isFavorite(option)} {@const canLoad = getBackendCapabilities(getBackend(option.backendId)).loadUnload} {@const isLoaded = isLoadedOption(option)} @@ -313,9 +362,7 @@ tabindex="0" > - - {showProvider ? (getBackend(option.backendId)?.name ?? quant) : quant} - + {quant} {option.model} @@ -346,7 +393,7 @@ {#if !collapsedQuants.has(entry.key)} {#each entry.quants as quant (quant.id)} - {@render quantRow(quant, indent + 24, entry.kind === 'providers')} + {@render quantRow(quant, indent + 24)} {/each} {/if} {:else} @@ -444,14 +491,6 @@
- - - - - - {summary}
@@ -484,6 +523,8 @@ {/if} {/snippet} + {@const windowed = windowSection(group)} + - {#if group.flat} - {#each group.items as entry (entry.key)} - {@render entryTree(entry, 16)} - {/each} - {/if} + {#each windowed.items as entry (entry.key)} + {@render entryTree(entry, 16)} + {/each} - {#each group.families as family (family.key)} + {#each windowed.families as family (family.key)} {@render familyRow(family)} {#if !collapsedFamilies.has(family.key)} @@ -507,6 +546,18 @@ {/each} {/if} {/each} + + {#if windowed.hidden > 0} +
+ +
+ {/if}
{/if} {/each} diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts b/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts index cf4becf7c3..06568067fb 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts +++ b/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts @@ -41,7 +41,7 @@ export type ModelOverrideMap = Record; export type { ModelLoadProgress }; /** What a group folds, which decides its label. */ -export type ModelGroupKind = 'providers' | 'quants' | 'variants'; +export type ModelGroupKind = 'quants' | 'variants'; /** One repo of the table, with the rows it ships as. */ export interface ModelQuantGroup { @@ -181,14 +181,14 @@ export function modelRepoKey(model: string): string { } /** Fold the quants of one repo into a single entry, so the table shows one row per model. */ -export function groupModelQuants(models: ModelOption[], mergeProviders = false): ModelQuantGroup[] { +export function groupModelQuants(models: ModelOption[]): ModelQuantGroup[] { const groups = new SvelteMap(); for (const option of models) { const repo = modelRepoKey(option.model); // groups stay within one backend, so the same repo served by two providers // is not read as two quants of one model - const key = mergeProviders ? repo : `${option.backendId ?? ''}::${repo}`; + const key = `${option.backendId ?? ''}::${repo}`; const group = groups.get(key); if (group) { @@ -204,13 +204,8 @@ export function groupModelQuants(models: ModelOption[], mergeProviders = false): const kind = groupKind(group.quants); const modelIds = group.quants.map((option) => option.model); - // the very same id twice is not a quant set; keep those rows apart, unless - // the group is there to show the providers that serve it - if ( - kind !== 'providers' && - modelIds.length > 1 && - modelIds.every((model) => model === modelIds[0]) - ) { + // the very same id twice is not a quant set; keep those rows apart + if (modelIds.length > 1 && modelIds.every((model) => model === modelIds[0])) { return group.quants.map((option) => ({ ...group, base: option, @@ -224,12 +219,8 @@ export function groupModelQuants(models: ModelOption[], mergeProviders = false): }); } -/** What a group folds, which decides its label: quants, variants or providers. */ +/** Quants share a quantization suffix; anything else is a variant. */ function groupKind(quants: ModelOption[]): ModelGroupKind { - const backends = new Set(quants.map((option) => option.backendId ?? '')); - - if (backends.size > 1) return 'providers'; - const isQuant = quants.every( (option) => (option.parsedId ?? ModelsService.parseModelId(option.model)).quantization );