From 531946e9967ec00116ae12908e96ed0411f24ec5 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 8 Oct 2026 08:54:06 -0700 Subject: [PATCH] server: hide duplicate and downgrade guards from list (#18874) When a legacy GGUF which requires our llama.cpp patch set is loaded, we do a lazy conversion on disk to make it llama.cpp compatible. To support downgrades, we temporarily hold both the new and old GGUFs and create a shadow v1 manifest tag to prevent the downgraded server from deleting the new blobs. Showing these in the list output is confusing to users and API consumers. The legacy copies will be removed in a future release to recover the disk space. --- server/model_list.go | 11 +++++++++-- server/routes_list_test.go | 17 ++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/server/model_list.go b/server/model_list.go index 9976b4b66..6be286281 100644 --- a/server/model_list.go +++ b/server/model_list.go @@ -18,7 +18,7 @@ import ( // listModels builds /api/tags from the manifests and the per-blob metadata // files, extracting for any blob that has none yet. Manifest lists contribute -// one row per child runner. +// one row for the locally preferred child. func listModels(ctx context.Context) ([]api.ListModelResponse, error) { manifests, err := manifest.Manifests(true) if err != nil { @@ -32,6 +32,10 @@ func listModels(ctx context.Context) ([]api.ListModelResponse, error) { return nil, err } } + // Suppress showing the downgrade guard tag + if name.EqualFold(model.ParseName(manifest.RunnerLlamaCPP + ":" + mf.Digest())) { + continue + } rows, err := describeModelRows(name, mf) if err != nil { @@ -46,7 +50,7 @@ func listModels(ctx context.Context) ([]api.ListModelResponse, error) { } // describeModelRows describes one named manifest for /api/tags. A manifest -// list describes one row per child runner, keyed by the child digest and +// list describes its selected child, keyed by the child digest and // carrying the parent's modification time; every other manifest describes // itself. func describeModelRows(name model.Name, mf *manifest.Manifest) ([]api.ListModelResponse, error) { @@ -79,6 +83,9 @@ func describeModelRows(name model.Name, mf *manifest.Manifest) ([]api.ListModelR if err != nil { return nil, err } + if !manifest.SameDigest(digest, mf.SelectedDigest()) { + continue + } resolved, ok, err := resolveLocalShowManifestChild(child) if err != nil { return nil, err diff --git a/server/routes_list_test.go b/server/routes_list_test.go index f12188e85..08f50cfb6 100644 --- a/server/routes_list_test.go +++ b/server/routes_list_test.go @@ -235,7 +235,7 @@ func writeManifestListFixture(t *testing.T, name string, children ...manifestLis return manifests } -func TestListIncludesManifestListChildrenAsSeparateRows(t *testing.T) { +func TestListShowsPreferredManifestListChild(t *testing.T) { gin.SetMode(gin.TestMode) t.Setenv("OLLAMA_MODELS", t.TempDir()) @@ -281,6 +281,10 @@ func TestListIncludesManifestListChildrenAsSeparateRows(t *testing.T) { manifestListFixtureChild{name: "test-mlx", runner: manifest.RunnerMLX, format: manifest.FormatSafetensors}, ) ggufManifest, mlxManifest := children[0], children[1] + selected, err := manifest.ParseNamedManifest(model.ParseName("test-list")) + if err != nil { + t.Fatal(err) + } parentPath, err := manifest.ResolvePathForName(model.ParseName("test-list")) if err != nil { t.Fatal(err) @@ -309,8 +313,11 @@ func TestListIncludesManifestListChildrenAsSeparateRows(t *testing.T) { listed = append(listed, resp.Models[i]) } } - if len(listed) != 2 { - t.Fatalf("test-list:latest rows = %d, want 2: %+v", len(listed), listed) + if len(listed) != 1 { + t.Fatalf("test-list:latest rows = %d, want 1: %+v", len(listed), listed) + } + if listed[0].Digest != selected.SelectedDigest() { + t.Fatalf("listed digest = %s, want selected digest %s", listed[0].Digest, selected.SelectedDigest()) } wantSizes := map[string]int64{ @@ -335,10 +342,6 @@ func TestListIncludesManifestListChildrenAsSeparateRows(t *testing.T) { if row.Details.Runner != wantRunners[row.Digest] { t.Fatalf("runner for %s = %q, want %q", row.Digest, row.Details.Runner, wantRunners[row.Digest]) } - delete(wantSizes, row.Digest) - } - if len(wantSizes) != 0 { - t.Fatalf("missing list rows for digests: %+v", wantSizes) } }