mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-04 02:37:27 +02:00
ui : parse imatrix and standalone draft sidecar filenames
extractQuantMeta now recognizes a -draft tail after the sidecar token (Model-MTP-draft.gguf), a bare sidecar token filename (imatrix.gguf) and suffix sidecars whose head carries no quant (Model-imatrix.gguf). imatrix is an aux sidecar: it stays in the download chips with a badge, is never a serve-command option, and mmproj alone drives the Vision capability. Assisted-by: pi:zai-org/GLM-5.3
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import ModelsDiscoverModelDetailsHeader from './ModelsDiscoverModelDetailsHeader.svelte';
|
||||
import ModelsDiscoverModelDetailsReadme from './ModelsDiscoverModelDetailsReadme.svelte';
|
||||
import ModelsDiscoverModelDetailsSkeleton from './ModelsDiscoverModelDetailsSkeleton.svelte';
|
||||
import { isAuxSidecar } from '$lib/constants';
|
||||
import { ModelAuxSidecar } from '$lib/enums';
|
||||
import { HuggingFaceService } from '$lib/services';
|
||||
import type { HfModelDetailInfo, HfModelSibling } from '$lib/types/huggingface';
|
||||
import { detectThinkingSupport, detectToolUseSupport } from '$lib/utils';
|
||||
@@ -37,11 +37,7 @@
|
||||
// Capabilities derived from HF metadata. Vision comes from an mmproj sidecar
|
||||
// or a multimodal pipeline tag; tool use / reasoning from the chat template.
|
||||
let hasMmproj = $derived(
|
||||
files.some((f) => {
|
||||
const sidecar = HuggingFaceService.extractQuantMeta(f.path)?.sidecar;
|
||||
|
||||
return sidecar !== null && sidecar !== undefined && isAuxSidecar(sidecar);
|
||||
})
|
||||
files.some((f) => HuggingFaceService.extractQuantMeta(f.path)?.sidecar === ModelAuxSidecar.MMPROJ)
|
||||
);
|
||||
let hasVision = $derived(hasMmproj || details?.pipeline_tag === 'image-text-to-text');
|
||||
let hasTools = $derived(detectToolUseSupport(gguf?.chat_template ?? ''));
|
||||
@@ -57,8 +53,9 @@
|
||||
for (const file of files) {
|
||||
const meta = HuggingFaceService.extractQuantMeta(file.path);
|
||||
|
||||
// mmproj sidecars are already conveyed by the Vision capability badge.
|
||||
if (meta?.sidecar && isAuxSidecar(meta.sidecar)) continue;
|
||||
// mmproj sidecars are already conveyed by the Vision capability badge;
|
||||
// imatrix ships as a normal chip with its own badge.
|
||||
if (meta?.sidecar === ModelAuxSidecar.MMPROJ) continue;
|
||||
|
||||
const depth = meta?.quant ? HuggingFaceService.getBitDepth(meta.quant) : null;
|
||||
const bucket = depth ?? 99;
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@
|
||||
import { Check, Download, Loader2, Pause, Play, RotateCw, X } from '@lucide/svelte';
|
||||
import DialogConfirmation from '$lib/components/app/dialogs/DialogConfirmation.svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { isAuxSidecar } from '$lib/constants';
|
||||
import { HuggingFaceService } from '$lib/services';
|
||||
import { modelsStore } from '$lib/stores';
|
||||
import type { HfModelSibling } from '$lib/types';
|
||||
@@ -65,7 +64,7 @@
|
||||
onclick={() => (confirmDeleteOpen = true)}
|
||||
type="button"
|
||||
>
|
||||
{#if meta?.sidecar && !isAuxSidecar(meta.sidecar)}
|
||||
{#if meta?.sidecar}
|
||||
<span
|
||||
class="rounded-md bg-primary px-1 py-0.5 text-[10px] font-semibold tracking-wide text-primary-foreground uppercase"
|
||||
>
|
||||
@@ -131,7 +130,7 @@
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{#if meta?.sidecar && !isAuxSidecar(meta.sidecar)}
|
||||
{#if meta?.sidecar}
|
||||
<span
|
||||
class="rounded-md bg-primary px-1 py-0.5 text-[10px] font-semibold tracking-wide text-primary-foreground uppercase"
|
||||
>
|
||||
@@ -224,7 +223,7 @@
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if meta?.sidecar && !isAuxSidecar(meta.sidecar)}
|
||||
{#if meta?.sidecar}
|
||||
<span
|
||||
class="rounded-md bg-primary px-1 py-0.5 text-[10px] font-semibold tracking-wide text-primary-foreground uppercase"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isAuxSidecar, type ModelSidecar } from '$lib/constants';
|
||||
import { isAuxSidecar, isDraftSidecar, type ModelSidecar } from '$lib/constants';
|
||||
import { ModelAuxSidecar, ModelDraftSidecar } from '$lib/enums';
|
||||
import { HuggingFaceService } from '$lib/services';
|
||||
import type { HfModelSibling } from '$lib/types/huggingface';
|
||||
@@ -43,9 +43,13 @@ export function classify(path: string): 'main' | 'draft' | 'aux' {
|
||||
|
||||
/** Display label of a file: its quant, else the file name without the extension. */
|
||||
export function labelFor(path: string): string {
|
||||
const quant = HuggingFaceService.extractQuantMeta(path)?.quant;
|
||||
const meta = HuggingFaceService.extractQuantMeta(path);
|
||||
|
||||
if (quant) return quant;
|
||||
if (meta?.quant) return meta.quant;
|
||||
|
||||
// Quantless sidecar files: the chip badge already carries the sidecar type,
|
||||
// so the label only marks draft files; aux sidecars badge alone.
|
||||
if (meta?.sidecar) return isDraftSidecar(meta.sidecar) ? 'draft' : '';
|
||||
|
||||
const basename = path.split('/').pop() ?? path;
|
||||
|
||||
@@ -65,9 +69,10 @@ export function quantBitDepth(quant: string | null): number {
|
||||
return bit ? Number(bit) : 99;
|
||||
}
|
||||
|
||||
// LLAMA-APP-REUSE: --spec-type value for each draft sidecar
|
||||
// LLAMA-APP-REUSE: --spec-type value for each draft sidecar; aux sidecars stay empty
|
||||
export const SPEC_TYPE: Record<ModelSidecar, string> = {
|
||||
[ModelAuxSidecar.MMPROJ]: '',
|
||||
[ModelAuxSidecar.IMATRIX]: '',
|
||||
[ModelDraftSidecar.DFLASH]: 'draft-dflash',
|
||||
[ModelDraftSidecar.DSPARK]: 'draft-dspark',
|
||||
[ModelDraftSidecar.EAGLE3]: 'eagle3',
|
||||
|
||||
@@ -75,10 +75,12 @@ export const MODEL_ID = {
|
||||
/**
|
||||
* Trailing `-<type>` suffix marking a GGUF with an embedded draft in the
|
||||
* same weight file (MTP) or a sidecar download entry, e.g.
|
||||
* `Hy3-IQ1_M-mtp.gguf`, `Q4_K_M-dspark`. The captured prefix is the
|
||||
* candidate model id; the caller decides whether it looks quantized.
|
||||
* `Hy3-IQ1_M-mtp.gguf`, `Q4_K_M-dspark`. An optional `-draft` tail covers
|
||||
* standalone sidecar files, e.g. `Model-MTP-draft.gguf`. The captured
|
||||
* prefix is the candidate model id; the caller decides whether it looks
|
||||
* quantized.
|
||||
*/
|
||||
SIDECAR_SUFFIX_REGEX: new RegExp(`^(.*)-(${SIDECAR_TOKEN_ALTERNATION})$`, 'i'),
|
||||
SIDECAR_SUFFIX_REGEX: new RegExp(`^(.*)-(${SIDECAR_TOKEN_ALTERNATION})(-draft)?$`, 'i'),
|
||||
|
||||
/** Matches a trailing weight file extension, e.g. `model.gguf` -> `model`. */
|
||||
WEIGHT_EXTENSION_REGEX: /\.(gguf|ggml)$/i
|
||||
|
||||
@@ -32,5 +32,7 @@ export enum ModelDraftSidecar {
|
||||
*/
|
||||
export enum ModelAuxSidecar {
|
||||
/** Multimodal projector: unlocks vision and/or audio input modalities. */
|
||||
MMPROJ = 'mmproj'
|
||||
MMPROJ = 'mmproj',
|
||||
/** Importance-matrix data used to build imatrix quants; not loaded at serve time. */
|
||||
IMATRIX = 'imatrix'
|
||||
}
|
||||
|
||||
@@ -168,8 +168,9 @@ export class HuggingFaceService {
|
||||
* Extract the GGUF quantization token (e.g. `Q4_K_M`) and any sidecar type
|
||||
* (`mtp`, `dflash`, `mmproj`, ...) from a `.gguf` filename. The sidecar token
|
||||
* shows up either as a sidecar prefix (`mtp-<name>.gguf`, `dflash-<name>.gguf`,
|
||||
* `mmproj-<name>.gguf`) or as the `-mtp` suffix when the draft model is
|
||||
* embedded in the same GGUF weight file.
|
||||
* `mmproj-<name>.gguf`), as a `-mtp` suffix, or as the whole filename
|
||||
* (`imatrix.gguf`); a `-draft` tail marks a standalone sidecar file
|
||||
* (`Model-MTP-draft.gguf`).
|
||||
*
|
||||
* `sidecarForm` records which side of the filename the sidecar token sat
|
||||
* on so callers can render badges differently (e.g. prefix on the left of
|
||||
@@ -197,6 +198,14 @@ export class HuggingFaceService {
|
||||
let sidecar: ModelSidecar | null = null;
|
||||
let sidecarForm: SidecarForm | null = null;
|
||||
|
||||
// A file named just the sidecar token (`imatrix.gguf`) is the sidecar
|
||||
// itself: no name or quant segments to parse.
|
||||
const bareSidecar = sidecarFromFileToken(source.toLowerCase());
|
||||
|
||||
if (bareSidecar) {
|
||||
return { quant: null, shared: false, sidecar: bareSidecar, sidecarForm: SidecarForm.PREFIX };
|
||||
}
|
||||
|
||||
const prefixMatch = source.match(MODEL_ID.SIDECAR_PREFIX_REGEX);
|
||||
|
||||
if (prefixMatch) {
|
||||
@@ -207,14 +216,12 @@ export class HuggingFaceService {
|
||||
const suffixMatch = source.match(MODEL_ID.SIDECAR_SUFFIX_REGEX);
|
||||
|
||||
if (suffixMatch) {
|
||||
const candidate = suffixMatch[1];
|
||||
const headSeg = candidate.split(MODEL_ID.SEGMENT_SEPARATOR).pop();
|
||||
|
||||
if (headSeg && MODEL_ID.QUANTIZATION_SEGMENT_REGEX.test(headSeg)) {
|
||||
sidecar = sidecarFromFileToken(suffixMatch[2].toLowerCase());
|
||||
sidecarForm = SidecarForm.SUFFIX;
|
||||
source = candidate;
|
||||
}
|
||||
// Take the suffix sidecar even when the head carries no quant:
|
||||
// embedded drafts end in one (`Hy3-IQ1_M-mtp`), standalone sidecar
|
||||
// files do not (`Model-MTP-draft`, `Model-imatrix`).
|
||||
sidecar = sidecarFromFileToken(suffixMatch[2].toLowerCase());
|
||||
sidecarForm = SidecarForm.SUFFIX;
|
||||
source = suffixMatch[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user