internal/autogen/discover_test.go
Rama main · réplica de lectura del repositorio original.
package autogen
import (
"os"
"path/filepath"
"testing"
)
// writeStub creates a zero-filled file of the given size. It is sparse: the
// tests only stat the size, and building it with make([]byte, size) cost the
// full size in heap (more under -race) plus the same again in RAM where /tmp is
// tmpfs. Two 4 GB stubs that way ran the ubuntu-26.04 runner out of memory, and
// the runner was shut down mid-test with no failure reported.
func writeStub(t *testing.T, dir, name string, size int) {
t.Helper()
f, err := os.Create(filepath.Join(dir, name))
if err != nil {
t.Fatal(err)
}
defer f.Close()
if err := f.Truncate(int64(size)); err != nil {
t.Fatal(err)
}
}
// A DFlash drafter (filename carries "dflash" as an infix, not a prefix) is
// paired to its target model by dir and not served as its own row.
func TestDiscoverGgufModels_DFlashPairing(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.6-35B-A3B-Q4_K_M.gguf", 1024)
writeStub(t, dir, "Qwen3.6-35B-A3B-DFlash-Q8_0.gguf", 60_000_000) // ~0.06 GB, rounds nonzero at 2dp
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row (draft not served standalone), got %d", len(rows))
}
row := rows[0]
if row.DraftKind != "dflash" {
t.Fatalf("DraftKind = %q, want dflash", row.DraftKind)
}
if row.DraftSizeGB <= 0 {
t.Fatalf("DraftSizeGB not populated: %v", row.DraftSizeGB)
}
if filepath.Base(row.DraftPath) != "Qwen3.6-35B-A3B-DFlash-Q8_0.gguf" {
t.Fatalf("DraftPath = %q", row.DraftPath)
}
}
// The pre-existing MTP prefix convention ("mtp-*.gguf") still pairs correctly
// and is distinguished from a DFlash drafter by DraftKind.
func TestDiscoverGgufModels_MtpPairing(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Gemma-4-12B-it.gguf", 1024)
writeStub(t, dir, "mtp-gemma-4-12B-it.gguf", 256)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row, got %d", len(rows))
}
if rows[0].DraftKind != "mtp" {
t.Fatalf("DraftKind = %q, want mtp", rows[0].DraftKind)
}
}
// Diffusion text encoders / VAEs are components of an image model, not models.
// They used to be emitted as llama-server rows and show up in the UI as LLMs.
func TestDiscoverGgufModels_SkipsImageEncoders(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.6-35B-A3B-Q4_K_M.gguf", 1024)
for _, n := range []string{
"T5-v1_1-xxl-encoder-Q8_0.gguf",
"t5xxl_fp16.gguf",
"umt5-xxl-encoder-Q5_K_M.gguf",
"clip_l.gguf",
"clip-g-Q8_0.gguf",
"flux-vae-f16.gguf",
"ae.gguf",
} {
writeStub(t, dir, n, 1024)
}
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 || rows[0].BaseID != "qwen3.6-35b-a3b" {
var got []string
for _, r := range rows {
got = append(got, r.ID)
}
t.Fatalf("want only the served model, got %v", got)
}
}
// The encoder rule must not eat a real seq2seq LLM that merely starts with t5.
func TestDiscoverGgufModels_KeepsFlanT5(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "flan-t5-large-Q4_K_M.gguf", 1024)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want flan-t5 served, got %d rows", len(rows))
}
}
// A "FastMTP" head is skipped outright: not served as its own model, and not
// paired as a -md draft either. Regression on both halves: it used to surface in
// the catalog as a phantom second model (growing nonsensical 32k/64k ctx
// variants of its own), and pairing it as a draft instead hard-failed the
// llama-server launch, since its reduced 32768-row output.weight + d2t remap is
// a vocab layout no build we ship can load.
func TestDiscoverGgufModels_FastMtpSkipped(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.8-27B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf", 1024)
writeStub(t, dir, "Qwen3.8-27B-Uncensored-HauhauCS-Aggressive-FastMTP-32K.gguf", 256)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row, got %d: %+v", len(rows), rows)
}
if rows[0].DraftPath != "" || rows[0].DraftKind != "" {
t.Fatalf("FastMTP head paired as a draft: kind=%q path=%q", rows[0].DraftKind, rows[0].DraftPath)
}
// The config editor's "is a draft available for this dir" probe must agree.
if p, k, _ := DraftSidecarForDir(dir); p != "" || k != "" {
t.Fatalf("DraftSidecarForDir offered the FastMTP head: kind=%q path=%q", k, p)
}
}
// A model that merely advertises a baked-in MTP head in its file name is a real
// model and must keep being served — the FastMTP rule must not swallow it.
func TestDiscoverGgufModels_NativeMtpIsNotASidecar(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.6-27B-uncensored-heretic-v2-Native-MTP-Preserved-Q4_K_M.gguf", 1024)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row, got %d: %+v", len(rows), rows)
}
if rows[0].DraftPath != "" {
t.Fatalf("DraftPath = %q, want empty", rows[0].DraftPath)
}
}
// An NVFP4 gguf whose quant sits MID-name ("…-NVFP4-MTP-MID-HIGH") is a quant of
// its model, not a model of its own: the row carries the token in Quant, and the
// id is left exactly as the file spells it rather than gaining a second "-nvfp4"
// tail. Both matter downstream - the Models table cuts a row's key at the quant,
// so an unrecognised one gave every ctx tier and the vision twin a row apiece.
func TestDiscoverGgufModels_NVFP4MidNameQuant(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.8-27B-NVFP4-MTP-MID-HIGH.gguf", 1024)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row, got %d: %+v", len(rows), rows)
}
if rows[0].Quant != "NVFP4" {
t.Fatalf("Quant = %q, want NVFP4", rows[0].Quant)
}
const want = "qwen3.8-27b-nvfp4-mtp-mid-high"
if rows[0].ID != want {
t.Fatalf("ID = %q, want %q (no duplicated quant tail)", rows[0].ID, want)
}
if got := ModelBaseKey(rows[0].BaseID); got != "qwen3.8-27b" {
t.Fatalf("ModelBaseKey(%q) = %q, want qwen3.8-27b", rows[0].BaseID, got)
}
}
// A trailing quant is still stripped from the base and re-appended to the id -
// the mid-name rule must not stop the common layout from producing an id.
func TestDiscoverGgufModels_TrailingQuantStillAppended(t *testing.T) {
dir := t.TempDir()
writeStub(t, dir, "Qwen3.8-27B-NVFP4.gguf", 1024)
rows, err := DiscoverGgufModels(dir)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("want 1 served row, got %d", len(rows))
}
if rows[0].BaseID != "qwen3.8-27b" || rows[0].ID != "qwen3.8-27b-nvfp4" {
t.Fatalf("BaseID/ID = %q/%q", rows[0].BaseID, rows[0].ID)
}
}