Files
LocalAI/.github/gallery-agent/library_test.go
T
mudler-agentandEttore Di Giacinto 3b179ed142 ci(gallery): have the index bots write the model library (#12849)
* ci(gallery): have the checksum bot write the model library

The checksum bot edited gallery/index.yaml, which is a generated copy
of gallery/models/index.yaml. The model library check fails any PR
where the copy differs from the generated one, and an edit made to the
copy never reaches the source.

Point the checker at the library, read the files under .model.files as
the library keeps them, and run modelcatalog after the checker so the
PR carries both files.

Assisted-by: Claude:claude-sonnet-5-5 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* ci(gallery): have the other index bots write the model library

The gallery agent and the variant proposer still read and wrote the
generated gallery copy, so their pull requests failed the model
library check and their changes never reached the source.

The gallery agent now appends library entries to
gallery/models/index.yaml, converting its entries with the gallery
package and skipping ids the library already has. Its duplicate check
reads the same file.

The variant proposer decodes the library shape (the body under
model:) and splices variants into the model body through a library
layout in galleryedit. The anchor refusal goes: the library has no
anchors. Its tests use library entries.

Both workflows run modelcatalog after the bot and before the pull
request step, and use the Go version from go.mod.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-10-23 11:55:56 +02:00

125 lines
3.1 KiB
Go

package main
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/mudler/LocalAI/core/gallery"
)
const libraryFixture = `# header comment
- kind: model
id: existing-model
name: existing-model
version: 0.0.0-gallery.000000000000
description: |
An existing model.
urls:
- https://huggingface.co/acme/existing-GGUF
channel: gallery
schemaVersion: 1
model:
url: github:mudler/LocalAI/gallery/virtual.yaml@master
`
// agentEntry has the shape generateYAMLEntry writes.
const agentEntry = `- name: "new-model"
url: "github:mudler/LocalAI/gallery/virtual.yaml@master"
urls:
- https://huggingface.co/acme/new-model-GGUF
description: |
A new model.
license: "apache-2.0"
tags:
- llm
- gguf
overrides:
parameters:
model: new-model-Q4_K_M.gguf
files:
- filename: new-model-Q4_K_M.gguf
sha256: abc
uri: huggingface://acme/new-model-GGUF/new-model-Q4_K_M.gguf
`
func writeFixture(t *testing.T) string {
t.Helper()
p := filepath.Join(t.TempDir(), "index.yaml")
if err := os.WriteFile(p, []byte(libraryFixture), 0o644); err != nil {
t.Fatal(err)
}
return p
}
func TestAppendLibraryEntriesWritesTheLibraryShape(t *testing.T) {
p := writeFixture(t)
added, skipped, err := appendLibraryEntries(p, []string{agentEntry})
if err != nil {
t.Fatal(err)
}
if len(added) != 1 || added[0] != "new-model" || len(skipped) != 0 {
t.Fatalf("added %v skipped %v", added, skipped)
}
got, _ := os.ReadFile(p)
if !strings.HasPrefix(string(got), libraryFixture) {
t.Fatal("existing content was changed; entries must only be appended")
}
res, err := gallery.DecodeModelIndex(got)
if err != nil {
t.Fatal(err)
}
if len(res.Entries) != 2 {
t.Fatalf("want 2 entries, got %d", len(res.Entries))
}
e := res.Entries[1]
if e.ID != "new-model" || e.Kind != "model" {
t.Fatalf("unexpected entry %+v", e.Envelope)
}
if e.Model.URL == "" || len(e.Model.Files) != 1 || e.Model.Overrides == nil {
t.Fatalf("model body not filled: %+v", e.Model)
}
if len(e.URLs) != 1 || e.URLs[0] != "https://huggingface.co/acme/new-model-GGUF" {
t.Fatalf("urls: %v", e.URLs)
}
}
func TestAppendLibraryEntriesSkipsAnExistingID(t *testing.T) {
p := writeFixture(t)
dup := strings.Replace(agentEntry, `"new-model"`, `"existing-model"`, 1)
added, skipped, err := appendLibraryEntries(p, []string{dup})
if err != nil {
t.Fatal(err)
}
if len(added) != 0 || len(skipped) != 1 {
t.Fatalf("added %v skipped %v", added, skipped)
}
got, _ := os.ReadFile(p)
if string(got) != libraryFixture {
t.Fatal("file changed although nothing was added")
}
}
func TestDuplicateCheckReadsTheLibrary(t *testing.T) {
p := writeFixture(t)
if _, _, err := appendLibraryEntries(p, []string{agentEntry}); err != nil {
t.Fatal(err)
}
t.Setenv("GALLERY_INDEX_PATH", p)
old := galleryIndexPath
galleryIndexPath = p
defer func() { galleryIndexPath = old }()
set, err := loadGalleryURLSet()
if err != nil {
t.Fatal(err)
}
for _, id := range []string{"acme/existing-GGUF", "acme/new-model-GGUF"} {
if !modelAlreadyInGallery(set, id) {
t.Fatalf("%s should count as already in the library", id)
}
}
}