test(gallery): lint meta model entry invariants in index.yaml

Adds Ginkgo specs that parse the shipped gallery/index.yaml and enforce
the invariants that keep meta entries safe: a legacy url fallback equal
to the final candidate's url, references only to existing non-meta
entries, a min_vram floor on every candidate but the last-resort one,
a capability drawn only from the vocabulary the system can report, and
descending VRAM floors within a capability group.

The capability check is the only compensating control for a typo there.
Candidate matching is a case-sensitive exact comparison against
SystemState.DetectedCapability(), so an unknown value never matches and
falls through silently instead of erroring. The vocabulary therefore
mirrors the raw return set of getSystemCapabilities(), which notably
excludes "cpu": that is a fallback key inside Capability(capMap) on the
meta backend path, never a reported capability. A CPU-only host reports
"default".

These pass vacuously until the pilot meta entry lands; the guard is
intentionally in place before the thing it guards.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto committed 2026-07-18 10:58:34 +00:00
1 parent 4835c4373f
commit 8ed8cf60e9
1 file changed
+130
+130
View File
@@ -0,0 +1,130 @@
package gallery_test
import (
"os"
"path/filepath"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v3"
"github.com/mudler/LocalAI/core/gallery"
)
// knownCapabilities mirrors the values SystemState.DetectedCapability() can
// actually report, which is what the candidate resolver compares against with
// a case-sensitive exact match. A capability outside this set can never match,
// so a typo would silently make a candidate unreachable on every host.
//
// Note "cpu" is deliberately absent: it exists only as a fallback key inside
// SystemState.Capability(capMap) for meta backends, and is never a value
// getSystemCapabilities() returns. A CPU-only host reports "default".
var knownCapabilities = map[string]bool{
"default": true, "metal": true, "darwin-x86": true,
"nvidia": true, "nvidia-cuda-12": true, "nvidia-cuda-13": true,
"nvidia-l4t": true, "nvidia-l4t-cuda-12": true, "nvidia-l4t-cuda-13": true,
"intel": true, "amd": true, "vulkan": true,
}
var _ = Describe("gallery/index.yaml meta entry invariants", func() {
var entries []gallery.GalleryModel
var byName map[string]gallery.GalleryModel
BeforeEach(func() {
data, err := os.ReadFile(filepath.Join("..", "..", "gallery", "index.yaml"))
Expect(err).ToNot(HaveOccurred())
Expect(yaml.Unmarshal(data, &entries)).To(Succeed())
byName = map[string]gallery.GalleryModel{}
for _, e := range entries {
byName[e.Name] = e
}
})
It("gives every meta entry a legacy url and no inline payload", func() {
for _, e := range entries {
if !e.IsMeta() {
continue
}
// Released LocalAI versions ignore the candidates key. Without a
// url they would list the entry and install an empty model.
Expect(e.URL).ToNot(BeEmpty(), "meta entry %q needs a url fallback for older clients", e.Name)
Expect(e.ConfigFile).To(BeEmpty(), "meta entry %q must not carry an inline config_file", e.Name)
Expect(e.AdditionalFiles).To(BeEmpty(), "meta entry %q must not carry files", e.Name)
last := e.Candidates[len(e.Candidates)-1]
fallback, ok := byName[last.Model]
Expect(ok).To(BeTrue(), "meta entry %q final candidate %q not found", e.Name, last.Model)
Expect(e.URL).To(Equal(fallback.URL),
"meta entry %q url must equal its final candidate %q url so old and new clients agree", e.Name, last.Model)
}
})
It("references only existing, non-meta entries", func() {
for _, e := range entries {
if !e.IsMeta() {
continue
}
for _, c := range e.Candidates {
target, ok := byName[c.Model]
Expect(ok).To(BeTrue(), "meta entry %q references unknown model %q", e.Name, c.Model)
Expect(target.IsMeta()).To(BeFalse(), "meta entry %q references meta entry %q; nesting is not allowed", e.Name, c.Model)
}
}
})
It("constrains every candidate except an unconstrained last resort", func() {
for _, e := range entries {
if !e.IsMeta() {
continue
}
for i, c := range e.Candidates {
_, declared, err := c.EffectiveMinVRAM()
Expect(err).ToNot(HaveOccurred(), "meta entry %q candidate %q has a bad min_vram", e.Name, c.Model)
if i == len(e.Candidates)-1 {
Expect(declared).To(BeFalse(), "meta entry %q final candidate %q must be an unconstrained last resort", e.Name, c.Model)
Expect(c.Capability).To(BeEmpty(), "meta entry %q final candidate %q must not require a capability", e.Name, c.Model)
continue
}
Expect(declared).To(BeTrue(), "meta entry %q candidate %q needs a min_vram; the nightly job should have inferred one", e.Name, c.Model)
}
}
})
It("uses only capabilities the system can report", func() {
for _, e := range entries {
if !e.IsMeta() {
continue
}
for _, c := range e.Candidates {
if c.Capability == "" {
continue
}
Expect(knownCapabilities).To(HaveKey(c.Capability),
"meta entry %q candidate %q uses unknown capability %q", e.Name, c.Model, c.Capability)
}
}
})
It("orders candidates by descending VRAM within a capability group", func() {
for _, e := range entries {
if !e.IsMeta() {
continue
}
previous := map[string]uint64{}
for _, c := range e.Candidates {
floor, declared, err := c.EffectiveMinVRAM()
Expect(err).ToNot(HaveOccurred())
if !declared {
continue
}
if prior, seen := previous[c.Capability]; seen {
Expect(floor).To(BeNumerically("<=", prior),
"meta entry %q candidate %q raises the VRAM floor after a lower one in the same capability group, so it can never be reached", e.Name, c.Model)
}
previous[c.Capability] = floor
}
}
})
})