feat(gallery): add nanbeige4.1-3b meta entry with hardware-resolved variants

Adds the first real meta entry to the gallery index. It resolves to the
Q8_0 build on hosts with at least 6GiB of VRAM and to the Q4_K_M build
everywhere else, installing either payload under the stable name
nanbeige4.1-3b.

The entry carries a url equal to its final candidate's url. LocalAI
releases that predate candidates support parse the index non-strictly
and drop the key silently, so without that url they would list the entry
and install nothing. A regression spec parses the index the way those
releases do and asserts every meta entry stays installable for them.

Also teaches core/schema/gallery-model.schema.json about candidates. The
schema sets additionalProperties: false at the top level, so an author
following CONTRIBUTING.md and adding the yaml-language-server comment
would otherwise get a validation error on this entry.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-07-18 11:41:16 +00:00
parent 98a0e3d7fa
commit d0d441bb43
3 changed files with 102 additions and 0 deletions

View File

@@ -368,3 +368,40 @@ var _ = Describe("InstallModelFromGallery with meta entries", func() {
Expect(record.URLs).To(ConsistOf("https://example.invalid/qwen3")) 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, &current)).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")
})
})

View File

@@ -56,6 +56,42 @@
"additionalProperties": false "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": { "prompt_templates": {
"type": "array", "type": "array",
"description": "Prompt templates written as .tmpl files", "description": "Prompt templates written as .tmpl files",

View File

@@ -4234,6 +4234,35 @@
- filename: llama-cpp/models/Qwen_Qwen3-Next-80B-A3B-Thinking-Q4_K_M.gguf - filename: llama-cpp/models/Qwen_Qwen3-Next-80B-A3B-Thinking-Q4_K_M.gguf
sha256: 83481c75cc6c0837ba9afa52b59b4cd3f85f55dd7aa6c60e27230ff329c81367 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 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 - name: nanbeige4.1-3b-q8
url: github:mudler/LocalAI/gallery/nanbeige4.1.yaml@master url: github:mudler/LocalAI/gallery/nanbeige4.1.yaml@master
urls: urls: