ui : drop the providers settings section and the model grouping setting

Assisted-by: pi:llama.cpp/GLM-5.3-Flash
This commit is contained in:
Aleksander Grygier
2026-09-28 12:10:13 +02:00
parent 0662d5d726
commit d1b242b222
5 changed files with 63 additions and 196 deletions
@@ -14,14 +14,7 @@
saveOverrides
} from './utils';
import { LOCAL_BACKEND_ID } from '$lib/constants';
import { ModelGroupingMode } from '$lib/enums/settings.enums';
import {
backendsStore,
conversationsStore,
modelsStore,
settingsStore,
uiStore
} from '$lib/stores';
import { backendsStore, conversationsStore, modelsStore, uiStore } from '$lib/stores';
import type { ModelOption } from '$lib/types/models';
import { getBackend } from '$lib/utils/api-base';
import { getBackendCapabilities } from '$lib/utils/backend';
@@ -142,79 +135,34 @@
});
}
// by capability: one block whose models can load and unload, one that is chat only
if (settingsStore.config.modelGrouping === ModelGroupingMode.COMPAT) {
const local = (entry: ModelQuantGroup) => entry.base.backendId === LOCAL_BACKEND_ID;
const loadable: ModelQuantGroup[] = [];
const chatOnly: ModelQuantGroup[] = [];
const localItems = byBackend.get(LOCAL_BACKEND_ID);
for (const entry of entries) {
if (claimed.has(entry.key) || hiddenKeys.has(entry.key)) continue;
if (localItems?.length) {
ordered.push({
backendId: LOCAL_BACKEND_ID,
isLocal: true,
items: localItems,
key: LOCAL_BACKEND_ID,
kind: 'local',
label: 'Local models'
});
}
const canLoad = getBackendCapabilities(getBackend(entry.base.backendId)).loadUnload;
for (const backend of backendsStore.enabled) {
if (backend.id === LOCAL_BACKEND_ID) continue;
(canLoad ? loadable : chatOnly).push(entry);
}
const items = byBackend.get(backend.id);
// the bundled server leads the block it belongs to
const leading = (list: ModelQuantGroup[]) =>
[...list].sort((a, b) => Number(local(b)) - Number(local(a)));
if (loadable.length) {
if (items?.length) {
ordered.push({
backendId: null,
backendId: backend.id,
isLocal: false,
items: leading(loadable),
key: 'llama-compat',
kind: 'compat',
label: 'Llama-compat'
items,
key: backend.id,
kind: 'provider',
label: backend.name
});
}
if (chatOnly.length) {
ordered.push({
backendId: null,
isLocal: false,
// one repo, one row per provider that serves it
items: groupModelQuants(
chatOnly.flatMap((entry) => entry.quants),
true
),
key: 'oai-compat',
kind: 'compat',
label: 'OAI-compat'
});
}
} else {
const localItems = byBackend.get(LOCAL_BACKEND_ID);
if (localItems?.length) {
ordered.push({
backendId: LOCAL_BACKEND_ID,
isLocal: true,
items: localItems,
key: LOCAL_BACKEND_ID,
kind: 'local',
label: 'Local models'
});
}
for (const backend of backendsStore.enabled) {
if (backend.id === LOCAL_BACKEND_ID) continue;
const items = byBackend.get(backend.id);
if (items?.length) {
ordered.push({
backendId: backend.id,
isLocal: false,
items,
key: backend.id,
kind: 'provider',
label: backend.name
});
}
}
}
if (hidden.length) {
@@ -1,6 +1,6 @@
<script lang="ts">
import ModelsSelectorDownloadItem from './ModelsSelectorDownloadItem.svelte';
import { Boxes, Heart, Power } from '@lucide/svelte';
import { Heart, Power } from '@lucide/svelte';
import { GroupedList, ModelAvatar, ModelsSelectorOption } from '$lib/components/app';
import { ModelsSection } from '$lib/components/app';
import { DialogConfirmDownload } from '$lib/components/app/dialogs';
@@ -12,10 +12,7 @@
} from '$lib/components/app/navigation/utils';
import { LOCAL_BACKEND_ID, MODEL_ROW_WINDOW } from '$lib/constants';
import { ModelDownloadConfirmAction } from '$lib/enums';
import { ModelGroupingMode } from '$lib/enums/settings.enums';
import { modelsStore, settingsStore } from '$lib/stores';
import { getBackend } from '$lib/utils/api-base';
import { getBackendCapabilities } from '$lib/utils/backend';
import { groupModelFamilies, type ModelFamilyGroup } from '$lib/utils/model-families';
interface Props {
@@ -89,29 +86,6 @@
let headerClass = $derived(`${sectionHeaderClass} sticky z-10 bg-popover`);
const headerStyle = 'top: var(--dropdown-sticky-height, 0px)';
/** By capability: one block whose models can load and unload, one that is chat only. */
let compatSections = $derived.by(() => {
const localItems = localGroups.available.flatMap((group) => group.items);
const canLoad = (backendId: string) => getBackendCapabilities(getBackend(backendId)).loadUnload;
const loadable = groups.providers.filter((provider) => canLoad(provider.backendId));
const chatOnly = groups.providers.filter((provider) => !canLoad(provider.backendId));
return [
{
icon: 'logo' as const,
items: [...localItems, ...loadable.flatMap((provider) => provider.items)],
key: 'llama-compat',
label: 'Llama-compat'
},
{
icon: 'boxes' as const,
items: chatOnly.flatMap((provider) => provider.items),
key: 'oai-compat',
label: 'OAI-compat'
}
].filter((section) => section.items.length > 0);
});
/** In-flight / paused downloads, tracked by the status feed. */
let getDownloadEntries = $derived(modelsStore.status.getDownloadEntries());
@@ -243,74 +217,49 @@
{/if}
{/snippet}
{#if settingsStore.config.modelGrouping === ModelGroupingMode.COMPAT}
{#snippet compatSection(
label: string,
items: ModelItem[],
iconKind: 'boxes' | 'logo',
key: string
)}
<ModelsSection {label} persistKey={key} revealChevronOnHover sticky>
{#snippet icon()}
{#if iconKind === 'logo'}
<Logo class="shrink-0" style="--size: 0.875rem" />
{:else}
<Boxes class="h-3.5 w-3.5 shrink-0" />
{/if}
{/snippet}
{#if groups.loaded.length > 0 || groups.available.length > 0}
<ModelsSection label="Local models" persistKey={LOCAL_BACKEND_ID} revealChevronOnHover sticky>
{#snippet icon()}
<Logo class="shrink-0" style="--size: 0.875rem" />
{/snippet}
{@render listRows(items, key)}
</ModelsSection>
{/snippet}
{#each compatSections as section (section.key)}
{@render compatSection(section.label, section.items, section.icon, section.key)}
{/each}
{:else}
{#if groups.loaded.length > 0 || groups.available.length > 0}
<ModelsSection label="Local models" persistKey={LOCAL_BACKEND_ID} revealChevronOnHover sticky>
{#snippet icon()}
<Logo class="shrink-0" style="--size: 0.875rem" />
{/snippet}
{@render localRows()}
</ModelsSection>
{/if}
<!-- One section per remote provider. -->
{#each groups.providers as provider (provider.backendId)}
<ModelsSection
backendId={provider.backendId}
error={Boolean(provider.error)}
label={provider.name}
loading={provider.loading}
onBack={onProviderBack}
persistKey={provider.backendId}
revealChevronOnHover
sticky
>
{#if provider.items.length > 0}
{@render listRows(provider.items, provider.backendId)}
{#if onProviderOpen && provider.matched > provider.items.length}
<!-- same box as a model row, it opens the provider's full list -->
<button
class="flex w-full cursor-pointer items-center gap-2 rounded-sm p-2 text-left text-sm text-muted-foreground transition hover:bg-accent hover:text-foreground focus:outline-none"
onclick={() => onProviderOpen(provider.backendId)}
type="button"
>
+ {provider.matched - provider.items.length} more
</button>
{/if}
{:else if provider.catalog === 0}
<p class="px-4 pb-2 text-xs text-muted-foreground">
{provider.error ?? (provider.loading ? 'Loading models...' : 'No models')}
</p>
{/if}
</ModelsSection>
{/each}
{@render localRows()}
</ModelsSection>
{/if}
<!-- One section per remote provider. -->
{#each groups.providers as provider (provider.backendId)}
<ModelsSection
backendId={provider.backendId}
error={Boolean(provider.error)}
label={provider.name}
loading={provider.loading}
onBack={onProviderBack}
persistKey={provider.backendId}
revealChevronOnHover
sticky
>
{#if provider.items.length > 0}
{@render listRows(provider.items, provider.backendId)}
{#if onProviderOpen && provider.matched > provider.items.length}
<!-- same box as a model row, it opens the provider's full list -->
<button
class="flex w-full cursor-pointer items-center gap-2 rounded-sm p-2 text-left text-sm text-muted-foreground transition hover:bg-accent hover:text-foreground focus:outline-none"
onclick={() => onProviderOpen(provider.backendId)}
type="button"
>
+ {provider.matched - provider.items.length} more
</button>
{/if}
{:else if provider.catalog === 0}
<p class="px-4 pb-2 text-xs text-muted-foreground">
{provider.error ?? (provider.loading ? 'Loading models...' : 'No models')}
</p>
{/if}
</ModelsSection>
{/each}
<DialogConfirmDownload
action={ModelDownloadConfirmAction.CANCEL}
onClose={() => (cancelOpen = false)}
@@ -42,7 +42,6 @@ export const SETTINGS_KEYS = {
MENTION_SEARCH_MAX_DEPTH: 'mentionSearchMaxDepth',
MIN_P: 'min_p',
// Display
MODEL_GROUPING: 'modelGrouping',
PASTE_LONG_TEXT_TO_FILE_LEN: 'pasteLongTextToFileLen',
PDF_AS_IMAGE: 'pdfAsImage',
// Performance
@@ -4,21 +4,18 @@ import { SETTINGS_KEYS } from './settings-keys.constants';
import { TITLE_GENERATION } from './title-generation.constants';
import { FILE_GLOB_SEARCH_PICKERS } from './working-directory.constants';
import {
Boxes,
Code,
Database,
Funnel,
Layers,
ListRestart,
Monitor,
Moon,
PencilRuler,
Server,
SlidersVertical,
Sun
} from '@lucide/svelte';
import { SyncableParameterType } from '$lib/enums';
import { ModelGroupingMode, SettingsFieldType } from '$lib/enums/settings.enums';
import { SettingsFieldType } from '$lib/enums/settings.enums';
import { ColorMode } from '$lib/enums/ui.enums';
import type {
SettingsConfigValue,
@@ -199,13 +196,6 @@ export const SETTINGS_REGISTRY: SettingsSectionEntry[] = [
slug: SETTINGS_SECTION_SLUGS.GENERAL,
title: SETTINGS_SECTION_TITLES.GENERAL
},
// Providers
{
icon: Server,
settings: [],
slug: SETTINGS_SECTION_SLUGS.BACKENDS,
title: SETTINGS_SECTION_TITLES.BACKENDS
},
// Display
{
icon: Monitor,
@@ -323,17 +313,6 @@ export const SETTINGS_REGISTRY: SettingsSectionEntry[] = [
label: 'Show full path in mentions',
type: SettingsFieldType.CHECKBOX
},
{
defaultValue: ModelGroupingMode.PROVIDER,
help: 'How the models manager groups its sections. One section per provider keeps every backend separate. By capability merges them into a llama-compat block, whose models can load and unload, and an OAI-compat block, which is chat only.',
key: SETTINGS_KEYS.MODEL_GROUPING,
label: 'Model grouping',
options: [
{ icon: Layers, label: 'One section per provider', value: ModelGroupingMode.PROVIDER },
{ icon: Boxes, label: 'By capability', value: ModelGroupingMode.COMPAT }
],
type: SettingsFieldType.SELECT
},
{
defaultValue: true,
help: 'Group models into families (e.g. "Qwen") in the model picker and the models manager. Turn it off to list every model on its own.',
-8
View File
@@ -18,14 +18,6 @@ export enum SyncableParameterType {
/**
* Settings field type - defines the input type for settings fields
*/
/** How the models manager builds its sections below Loaded and Favorites. */
export enum ModelGroupingMode {
/** One llama-compat block and one OAI-compat block, families inside each. */
COMPAT = 'compat',
/** One section per provider, families inside each. */
PROVIDER = 'provider'
}
export enum SettingsFieldType {
CHECKBOX = 'checkbox',
INPUT = 'input',