diff --git a/core/gallery/meta_install_test.go b/core/gallery/meta_install_test.go index df730b3cb..ee57162a7 100644 --- a/core/gallery/meta_install_test.go +++ b/core/gallery/meta_install_test.go @@ -368,3 +368,40 @@ var _ = Describe("InstallModelFromGallery with meta entries", func() { Expect(record.URLs).To(ConsistOf("https://example.invalid/qwen3")) }) }) + +var _ = Describe("legacy client compatibility", func() { + It("exposes a url on every meta entry to clients that ignore candidates", func() { + data, err := os.ReadFile(filepath.Join("..", "..", "gallery", "index.yaml")) + Expect(err).ToNot(HaveOccurred()) + + // Parse exactly as an older LocalAI release would: non-strictly, with + // no knowledge of the candidates key. + var legacy []struct { + Name string `yaml:"name"` + URL string `yaml:"url"` + } + Expect(yaml.Unmarshal(data, &legacy)).To(Succeed()) + + var current []gallery.GalleryModel + Expect(yaml.Unmarshal(data, ¤t)).To(Succeed()) + + urlByName := map[string]string{} + for _, e := range legacy { + urlByName[e.Name] = e.URL + } + + metaCount := 0 + for _, e := range current { + if !e.IsMeta() { + continue + } + metaCount++ + // Without a url an old client lists the entry and installs an + // empty model, because it silently drops candidates. + Expect(urlByName[e.Name]).ToNot(BeEmpty(), + "meta entry %q is invisible payload-wise to older clients", e.Name) + } + Expect(metaCount).To(BeNumerically(">", 0), + "expected at least one meta entry in the gallery index") + }) +}) diff --git a/core/schema/gallery-model.schema.json b/core/schema/gallery-model.schema.json index 9ce13ac0f..d43b42e07 100644 --- a/core/schema/gallery-model.schema.json +++ b/core/schema/gallery-model.schema.json @@ -56,6 +56,42 @@ "additionalProperties": false } }, + "candidates": { + "type": "array", + "description": "Ordered variant list of a meta entry. The first candidate the host satisfies is installed, under the meta entry's own name. An entry carrying candidates must not also carry files or config_file.", + "minItems": 1, + "items": { + "type": "object", + "required": ["model"], + "properties": { + "model": { + "type": "string", + "description": "Name of a concrete (non-meta) gallery entry" + }, + "capability": { + "type": "string", + "description": "Host capability this candidate requires, matched exactly and case-sensitively (for example metal, nvidia-cuda-12). Absent matches any host." + }, + "min_vram": { + "type": "string", + "description": "Authored VRAM floor, for example 20GiB. Authoritative: inference never overwrites it." + }, + "backend": { + "type": "string", + "description": "Denormalized by the nightly job for display. Never authored by hand." + }, + "quantization": { + "type": "string", + "description": "Denormalized by the nightly job for display. Never authored by hand." + }, + "inferred_min_vram": { + "type": "string", + "description": "Denormalized by the nightly job as a fallback floor. Never authored by hand; min_vram wins." + } + }, + "additionalProperties": false + } + }, "prompt_templates": { "type": "array", "description": "Prompt templates written as .tmpl files", diff --git a/gallery/index.yaml b/gallery/index.yaml index 79e7e265a..c449afa6e 100644 --- a/gallery/index.yaml +++ b/gallery/index.yaml @@ -4234,6 +4234,35 @@ - filename: llama-cpp/models/Qwen_Qwen3-Next-80B-A3B-Thinking-Q4_K_M.gguf sha256: 83481c75cc6c0837ba9afa52b59b4cd3f85f55dd7aa6c60e27230ff329c81367 uri: https://huggingface.co/bartowski/Qwen_Qwen3-Next-80B-A3B-Thinking-GGUF/resolve/main/Qwen_Qwen3-Next-80B-A3B-Thinking-Q4_K_M.gguf +- name: nanbeige4.1-3b + description: | + Nanbeige4.1-3B is built upon Nanbeige4-3B-Base and represents an enhanced iteration of our previous reasoning model, Nanbeige4-3B-Thinking-2511, achieved through further post-training optimization with supervised fine-tuning (SFT) and reinforcement learning (RL). As a highly competitive open-source model at a small parameter scale, Nanbeige4.1-3B illustrates that compact models can simultaneously achieve robust reasoning, preference alignment, and effective agentic behaviors. + + Automatically resolves to the best variant for your hardware: the Q8_0 build where there is enough VRAM for it, the Q4_K_M build otherwise. + license: apache-2.0 + icon: https://cdn-avatars.huggingface.co/v1/production/uploads/646f0d118ff94af23bc44aab/GXHCollpMRgvYqUXQ2BQ7.png + urls: + - https://huggingface.co/Nanbeige/Nanbeige4.1-3B + tags: + - nanbeige + - 3b + - llm + - gguf + - quantized + - chat + - reasoning + - agent + - multilingual + - instruction-tuned + # url is the fallback for LocalAI releases that predate candidates support: + # they drop the candidates key silently and would otherwise install nothing. + # Lint requires it to equal the final candidate's url exactly, so old and new + # clients agree on the least demanding option. + url: github:mudler/LocalAI/gallery/nanbeige4.1.yaml@master + candidates: + - model: nanbeige4.1-3b-q8 + min_vram: 6GiB + - model: nanbeige4.1-3b-q4 - name: nanbeige4.1-3b-q8 url: github:mudler/LocalAI/gallery/nanbeige4.1.yaml@master urls: