diff --git a/tools/ui/src/lib/components/app/models/ModelSize.svelte b/tools/ui/src/lib/components/app/models/ModelSize.svelte deleted file mode 100644 index 2b76a2d767..0000000000 --- a/tools/ui/src/lib/components/app/models/ModelSize.svelte +++ /dev/null @@ -1,81 +0,0 @@ - - -{label} diff --git a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelSettings.svelte b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelSettings.svelte index 89a71be3ea..571174c387 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelSettings.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelSettings.svelte @@ -5,6 +5,7 @@ type ModelOverride, modelQuantLabel, modelSizeLabel, + resolveModelSize, SAMPLING_DEFAULTS, SPECULATIVE_OPTIONS } from './utils'; @@ -79,6 +80,26 @@ let quant = $derived(modelQuantLabel(option)); let size = $derived(modelSizeLabel(option)); let stopStrings = $derived(draft.stopStrings ?? []); + let resolvedSize = $state(null); + + // the router rarely reports a size, the repo tree does + $effect(() => { + const target = option; + + let cancelled = false; + + resolvedSize = null; + + void resolveModelSize(target) + .then((value) => { + if (!cancelled) resolvedSize = value; + }) + .catch(() => {}); + + return () => { + cancelled = true; + }; + }); let infoRows = $derived([ { label: 'Model', value: option.model }, { label: 'File Path', value: serverProps?.model_path ?? null }, @@ -92,7 +113,7 @@ label: 'Training Context', value: option.contextLength ? `${formatParameters(option.contextLength)} tokens` : null }, - { label: 'Model Size', value: size }, + { label: 'Model Size', value: resolvedSize ?? size }, { label: 'Parameters', value: option.parsedId?.params ?? null }, { isBadge: true, label: 'Quantization', value: quant }, { isBadge: true, label: 'Architecture', value: (option.meta?.architecture as string) ?? null }, 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 3421b44490..d90ca6742b 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsManager/ModelsManagerModelsTable.svelte @@ -7,7 +7,6 @@ ModelId, ModelLoadControl, ModelRowActions, - ModelSize, ModelsSection } from '$lib/components/app'; import { Input } from '$lib/components/ui/input'; @@ -36,7 +35,7 @@ }: Props = $props(); let isEmpty = $derived(groups.every((group) => group.items.length === 0)); - const rowGrid = 'grid grid-cols-[minmax(0,1fr)_5.5rem_3rem_4.5rem] items-center gap-3'; + const rowGrid = 'grid grid-cols-[minmax(0,1fr)_3rem_4.5rem] items-center gap-3'; function stateOf(option: ModelOption): ServerModelStatus | null { const model = modelsStore.routerModels.find((m) => m.id === option.model); @@ -83,8 +82,6 @@ /> - - Model - Size - State Actions 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 201cb4ea49..43da090581 100644 --- a/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts +++ b/tools/ui/src/lib/components/app/models/ModelsManager/utils.ts @@ -1,4 +1,5 @@ import { LOCAL_BACKEND_ID, MODEL_OVERRIDES_LOCALSTORAGE_KEY } from '$lib/constants'; +import { HuggingFaceService } from '$lib/services'; import type { ModelOption } from '$lib/types/models'; import { getBackend } from '$lib/utils/api-base'; import { formatFileSize, formatParameters } from '$lib/utils/formatters'; @@ -129,6 +130,34 @@ export function modelQuantLabel(option: ModelOption): string | null { return option.parsedId?.quantization ?? null; } +/** + * File size of the model's own quant. The router reports one for some backends; + * otherwise a local GGUF reads it from its repo tree, the same source the + * discovery details use. Returns null when neither knows. + */ +export async function resolveModelSize(option: ModelOption): Promise { + const reported = modelSizeLabel(option); + + if (reported) return reported; + + if (!isLocalOption(option)) return null; + + const [repo, quant] = option.model.split(':'); + + if (!repo || !quant) return null; + + const tree = await HuggingFaceService.getTree(repo); + const file = HuggingFaceService.collapseGgufShards( + HuggingFaceService.filterByExtension(tree, '.gguf') + ).find((entry) => { + const meta = HuggingFaceService.extractQuantMeta(entry.path); + + return meta?.quant === quant && !meta.sidecar; + }); + + return file?.size ? formatFileSize(file.size) : null; +} + /** Extra args the router applies when this model is loaded. */ export function loadExtraArgs(override?: ModelOverride): string[] { const load = override?.load; diff --git a/tools/ui/src/lib/components/app/models/index.ts b/tools/ui/src/lib/components/app/models/index.ts index fed5646148..074e253b62 100644 --- a/tools/ui/src/lib/components/app/models/index.ts +++ b/tools/ui/src/lib/components/app/models/index.ts @@ -51,7 +51,6 @@ export { default as ModelId } from './ModelId.svelte'; export { default as ModelAvatar } from './ModelAvatar.svelte'; export { default as ModelLoadControl } from './ModelLoadControl.svelte'; export { default as ModelRowActions } from './ModelRowActions.svelte'; -export { default as ModelSize } from './ModelSize.svelte'; export { default as ModelsSection } from './ModelsSection.svelte'; /**