mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-17 20:31:47 +02:00
ui : keep models of every enabled backend selectable
Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
@@ -91,6 +91,7 @@
|
||||
|
||||
for (const item of ms.groupedFilteredOptions.favorites) order.push(item.option.id);
|
||||
for (const item of ms.groupedFilteredOptions.loaded) order.push(item.option.id);
|
||||
for (const item of ms.groupedFilteredOptions.external) order.push(item.option.id);
|
||||
for (const group of ms.groupedFilteredOptions.available) {
|
||||
for (const item of group.items) order.push(item.option.id);
|
||||
}
|
||||
@@ -342,7 +343,6 @@
|
||||
<ModelsSelectorList
|
||||
activeId={ms.activeId}
|
||||
{currentModel}
|
||||
flat={!ms.isLocalBackend}
|
||||
groups={ms.groupedFilteredOptions}
|
||||
onInfoClick={ms.handleInfoClick}
|
||||
onSelect={ms.handleSelect}
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
groups: GroupedModelOptions;
|
||||
currentModel: string | null;
|
||||
activeId: string | null;
|
||||
/** Render one flat list without section headers (external backends). */
|
||||
flat?: boolean;
|
||||
sectionHeaderClass?: string;
|
||||
onSelect: (modelId: string) => void;
|
||||
onInfoClick: (modelName: string) => void;
|
||||
@@ -21,7 +19,6 @@
|
||||
let {
|
||||
activeId,
|
||||
currentModel,
|
||||
flat = false,
|
||||
groups,
|
||||
onInfoClick,
|
||||
onSelect,
|
||||
@@ -64,44 +61,44 @@
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
{#if flat}
|
||||
{#each [...groups.favorites, ...groups.available.flatMap((group) => group.items)] as item (item.option.id)}
|
||||
{#if groups.favorites.length > 0}
|
||||
<p class={sectionHeaderClass}>Favorite models</p>
|
||||
|
||||
{#each groups.favorites as item (`fav-${item.option.id}`)}
|
||||
{@render render(item, true)}
|
||||
{/each}
|
||||
{:else}
|
||||
{#if groups.favorites.length > 0}
|
||||
<p class={sectionHeaderClass}>Favorite models</p>
|
||||
{/if}
|
||||
|
||||
{#each groups.favorites as item (`fav-${item.option.id}`)}
|
||||
{#if getDownloadEntries.length > 0}
|
||||
<p class={sectionHeaderClass}>Download in progress</p>
|
||||
|
||||
{#each getDownloadEntries as entry (entry.repoWithTag)}
|
||||
<ModelsSelectorDownloadItem {entry} onRequestCancel={requestCancel} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if groups.loaded.length > 0}
|
||||
<p class={sectionHeaderClass}>Loaded models</p>
|
||||
|
||||
{#each groups.loaded as item (`loaded-${item.option.id}`)}
|
||||
{@render render(item, false)}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if groups.available.length > 0}
|
||||
<h2 class={sectionHeaderClass}>Downloaded models</h2>
|
||||
|
||||
{#each groups.available as group (group.orgName)}
|
||||
{#each group.items as item (item.option.id)}
|
||||
{@render render(item, true)}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if getDownloadEntries.length > 0}
|
||||
<p class={sectionHeaderClass}>Download in progress</p>
|
||||
|
||||
{#each getDownloadEntries as entry (entry.repoWithTag)}
|
||||
<ModelsSelectorDownloadItem {entry} onRequestCancel={requestCancel} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if groups.loaded.length > 0}
|
||||
<p class={sectionHeaderClass}>Loaded models</p>
|
||||
|
||||
{#each groups.loaded as item (`loaded-${item.option.id}`)}
|
||||
{@render render(item, false)}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if groups.available.length > 0}
|
||||
<h2 class={sectionHeaderClass}>Downloaded models</h2>
|
||||
|
||||
{#each groups.available as group (group.orgName)}
|
||||
{#each group.items as item (item.option.id)}
|
||||
{@render render(item, true)}
|
||||
{/each}
|
||||
{/each}
|
||||
{/if}
|
||||
{#if groups.external.length > 0}
|
||||
{#each groups.external as item (`ext-${item.option.id}`)}
|
||||
{@render render(item, true)}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<DialogConfirmDownload
|
||||
|
||||
@@ -188,7 +188,6 @@
|
||||
<ModelsSelectorList
|
||||
activeId={ms.activeId}
|
||||
{currentModel}
|
||||
flat={!ms.isLocalBackend}
|
||||
groups={ms.groupedFilteredOptions}
|
||||
onInfoClick={ms.handleInfoClick}
|
||||
onSelect={ms.handleSelect}
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface GroupedModelOptions {
|
||||
loaded: ModelItem[];
|
||||
favorites: ModelItem[];
|
||||
available: OrgGroup[];
|
||||
/** Models served by other backends; rendered flat, without categories. */
|
||||
external: ModelItem[];
|
||||
}
|
||||
|
||||
function matchesModality(option: ModelOption, term: string): boolean {
|
||||
@@ -53,7 +55,8 @@ export function filterModelOptions(options: ModelOption[], searchTerm: string):
|
||||
export function groupModelOptions(
|
||||
filteredOptions: ModelOption[],
|
||||
favoriteIds: Set<string>,
|
||||
isModelLoaded: (model: string) => boolean
|
||||
isModelLoaded: (model: string) => boolean,
|
||||
isLocalModel: (option: ModelOption) => boolean = () => true
|
||||
): GroupedModelOptions {
|
||||
// Loaded models
|
||||
const loaded: ModelItem[] = [];
|
||||
@@ -79,6 +82,8 @@ export function groupModelOptions(
|
||||
|
||||
// Available models grouped by org (excluding loaded and favorites)
|
||||
const available: OrgGroup[] = [];
|
||||
// models from other backends have no load state, so they stay flat
|
||||
const external: ModelItem[] = [];
|
||||
const orgGroups = new SvelteMap<string, ModelItem[]>();
|
||||
|
||||
for (let i = 0; i < filteredOptions.length; i++) {
|
||||
@@ -86,6 +91,12 @@ export function groupModelOptions(
|
||||
|
||||
if (loadedModelIds.has(option.model) || favoriteIds.has(option.model)) continue;
|
||||
|
||||
if (!isLocalModel(option)) {
|
||||
external.push({ flatIndex: i, option });
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = option.parsedId?.orgName ?? '';
|
||||
|
||||
if (!orgGroups.has(key)) orgGroups.set(key, []);
|
||||
@@ -97,5 +108,5 @@ export function groupModelOptions(
|
||||
available.push({ items, orgName: orgName || null });
|
||||
}
|
||||
|
||||
return { available, favorites, loaded };
|
||||
return { available, external, favorites, loaded };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { filterModelOptions, groupModelOptions } from '$lib/components/app/navigation/utils';
|
||||
import { CHAT_INPUT_FOCUS_SELECTOR } from '$lib/constants';
|
||||
import { CHAT_INPUT_FOCUS_SELECTOR, LOCAL_BACKEND_ID } from '$lib/constants';
|
||||
import { backendsModelsStore, backendsStore, modelsStore, serverStore } from '$lib/stores';
|
||||
import type { Backend } from '$lib/types';
|
||||
import type { ModelOption } from '$lib/types/models';
|
||||
@@ -21,7 +21,6 @@ export interface UseModelsSelectorReturn {
|
||||
readonly updating: boolean;
|
||||
readonly activeId: string | null;
|
||||
readonly activeBackendId: string;
|
||||
readonly isLocalBackend: boolean;
|
||||
readonly backends: Backend[];
|
||||
readonly isMultiModel: boolean;
|
||||
readonly isRouter: boolean;
|
||||
@@ -53,12 +52,10 @@ export interface UseModelsSelectorReturn {
|
||||
*/
|
||||
export function useModelsSelector(opts: UseModelsSelectorOptions): UseModelsSelectorReturn {
|
||||
const activeBackendId = $derived(backendsStore.active.id);
|
||||
const isLocalBackend = $derived(backendsStore.active.protocol === 'llama.cpp');
|
||||
// the backend switcher tabs scope the list to one backend's models
|
||||
// every enabled backend's models are selectable; the switcher only picks
|
||||
// the backend new requests target
|
||||
const options = $derived(
|
||||
modelsStore.models.filter((option) => {
|
||||
if (option.backendId !== activeBackendId) return false;
|
||||
|
||||
const modelProps = modelsStore.props.getModelProps(option.model);
|
||||
|
||||
return modelProps?.ui !== false;
|
||||
@@ -95,8 +92,11 @@ export function useModelsSelector(opts: UseModelsSelectorOptions): UseModelsSele
|
||||
|
||||
const filteredOptions = $derived(filterModelOptions(options, searchTerm));
|
||||
const groupedFilteredOptions = $derived(
|
||||
groupModelOptions(filteredOptions, modelsStore.favoriteModelIds, (m) =>
|
||||
modelsStore.isModelLoaded(m)
|
||||
groupModelOptions(
|
||||
filteredOptions,
|
||||
modelsStore.favoriteModelIds,
|
||||
(m) => modelsStore.isModelLoaded(m),
|
||||
(option) => option.backendId === LOCAL_BACKEND_ID
|
||||
)
|
||||
);
|
||||
|
||||
@@ -277,10 +277,6 @@ export function useModelsSelector(opts: UseModelsSelectorOptions): UseModelsSele
|
||||
return isLoadingModel;
|
||||
},
|
||||
|
||||
get isLocalBackend() {
|
||||
return isLocalBackend;
|
||||
},
|
||||
|
||||
get isMultiModel() {
|
||||
return isMultiModel;
|
||||
},
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
orgName: 'intel'
|
||||
}
|
||||
],
|
||||
external: [],
|
||||
favorites: favoriteModels,
|
||||
loaded: loadedModels
|
||||
};
|
||||
@@ -138,6 +139,7 @@
|
||||
currentModel={null}
|
||||
groups={{
|
||||
available: [],
|
||||
external: [],
|
||||
favorites: [],
|
||||
loaded: [loadedModels[0]]
|
||||
}}
|
||||
@@ -154,6 +156,7 @@
|
||||
currentModel={null}
|
||||
groups={{
|
||||
available: [],
|
||||
external: [],
|
||||
favorites: favoriteModels,
|
||||
loaded: []
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user