Ctrl K
ComunidadGuías…

internal/autogen/trellis_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package autogen

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// writeFileAt writes a stub file, creating parent directories.
func writeFileAt(t *testing.T, path string, size int) {
	t.Helper()
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(path, make([]byte, size), 0o644); err != nil {
		t.Fatal(err)
	}
}

// A TRELLIS.2 package is a DIRECTORY (pipeline.json plus ckpts/), discovered as
// a 3D model, emitted as a trellis2-server entry with the DINOv3 encoder and the
// BiRefNet remover found beside it in the same tree - and with nothing inside
// the package turned into a model of its own.
func TestAutogen_trellisDiscoverEmit(t *testing.T) {
	root := t.TempDir()
	base := filepath.Join(root, "3D", "TRELLIS.2")
	pkg := filepath.Join(base, "TRELLIS.2-4B")
	dino := filepath.Join(base, "dinov3-vitl16-pretrain-lvd1689m")
	remover := filepath.Join(base, "BiRefNet", "BiRefNet-F16.gguf")
	// A copy vendored INSIDE the package wins over the sibling one below: the
	// package-local layout is the more specific answer.
	pkgRemover := filepath.Join(pkg, "BiRefNet", "BiRefNet-F16.gguf")

	writeFileAt(t, filepath.Join(pkg, "pipeline.json"), 128)
	writeFileAt(t, filepath.Join(pkg, "ckpts", "shape.safetensors"), 60_000_000) // ~0.06 GB, nonzero at 2dp
	// Inside the package: served by the trellis server, never as a llama model.
	writeFileAt(t, filepath.Join(pkg, "BiRefNet", "BiRefNet-F16.gguf"), 1024)
	writeFileAt(t, filepath.Join(dino, "model.safetensors"), 2048)
	writeFileAt(t, filepath.Join(dino, "config.json"), 64)
	writeFileAt(t, remover, 1024)
	writeFileAt(t, filepath.Join(root, "Qwen3-4B-Q4_K_M.gguf"), 4096)

	rows, err := DiscoverGgufModels(root)
	if err != nil {
		t.Fatal(err)
	}
	var trellis *GgufRow
	for i := range rows {
		if rows[i].IsTrellis {
			trellis = &rows[i]
		}
		if strings.Contains(strings.ToLower(rows[i].FileName), "birefnet") {
			t.Fatalf("a BiRefNet gguf must never be a model: %+v", rows[i])
		}
	}
	if trellis == nil {
		t.Fatalf("TRELLIS.2 package not discovered: %+v", rows)
	}
	if trellis.FullPath != pkg {
		t.Fatalf("FullPath = %q, want the package directory %q", trellis.FullPath, pkg)
	}
	if trellis.ID != "trellis-2-4b" {
		t.Fatalf("unexpected id %q", trellis.ID)
	}
	if trellis.SizeGB <= 0 {
		t.Fatalf("package size not measured: %v", trellis.SizeGB)
	}
	if len(rows) != 2 {
		t.Fatalf("want the llama gguf and the package, got %d rows: %+v", len(rows), rows)
	}

	out, err := Generate(GenerateFile{Settings: Settings{ModelsRoot: root}}, "test")
	if err != nil {
		t.Fatal(err)
	}
	for _, want := range []string{
		`"trellis-2-4b":`,
		"trellis2-server",
		"--model " + `"` + filepath.ToSlash(pkg) + `"`,
		"--dino " + `"` + filepath.ToSlash(dino) + `"`,
		"--birefnet " + `"` + filepath.ToSlash(pkgRemover) + `"`,
		"--model-cache-budget-mib 8192",
		"--port ${PORT}",
		// The full mesh is ~5.5M triangles / ~180 MB; a served model wants the
		// simplified one. Extra args land after this and win.
		"--mesh-postprocess-simplify",
		"checkEndpoint: /health",
		"in: [image]",
		"out: [3d]",
		"estVramGB: 11",
		// Not a coexist group: ~10 GB of VRAM has to swap like any other model.
		"  exclusive:",
	} {
		if !strings.Contains(out, want) {
			t.Fatalf("generated config missing %q in:\n%s", want, out)
		}
	}
}

// The hub's own download layout is `<models root>/<owner>/<repo>`, so a browser
// install puts the package under one owner folder and the gated encoder under
// another. That must still emit a runnable command: the user opted in by
// downloading the files, and moving folders afterwards is not part of the deal.
func TestAutogen_trellisHubLayout(t *testing.T) {
	root := t.TempDir()
	pkg := filepath.Join(root, "microsoft", "TRELLIS.2-4B")
	dino := filepath.Join(root, "facebook", "dinov3-vitl16-pretrain-lvd1689m")
	remover := filepath.Join(root, "Acly", "BiRefNet-GGUF", "BiRefNet-F16.gguf")
	writeFileAt(t, filepath.Join(pkg, "pipeline.json"), 128)
	writeFileAt(t, filepath.Join(pkg, "ckpts", "shape.safetensors"), 60_000_000)
	writeFileAt(t, filepath.Join(dino, "model.safetensors"), 2048)
	writeFileAt(t, filepath.Join(dino, "config.json"), 64)
	writeFileAt(t, remover, 1024)

	out, err := Generate(GenerateFile{Settings: Settings{ModelsRoot: root}}, "test")
	if err != nil {
		t.Fatal(err)
	}
	for _, want := range []string{
		`"trellis-2-4b":`,
		"--model " + `"` + filepath.ToSlash(pkg) + `"`,
		"--dino " + `"` + filepath.ToSlash(dino) + `"`,
		"--birefnet " + `"` + filepath.ToSlash(remover) + `"`,
	} {
		if !strings.Contains(out, want) {
			t.Fatalf("generated config missing %q in:\n%s", want, out)
		}
	}
	if strings.Contains(out, "SKIPPED") {
		t.Fatalf("a hub-layout package must not be skipped:\n%s", out)
	}
}

// A package whose DINOv3 encoder is absent is skipped with a reason rather than
// emitted as an entry that can only fail at request time: the shape stage cannot
// run without the encoder, so the server would refuse every generation.
func TestAutogen_trellisSkipsWithoutDino(t *testing.T) {
	root := t.TempDir()
	pkg := filepath.Join(root, "TRELLIS.2-4B")
	writeFileAt(t, filepath.Join(pkg, "pipeline.json"), 128)
	writeFileAt(t, filepath.Join(pkg, "ckpts", "shape.safetensors"), 4096)

	out, err := Generate(GenerateFile{Settings: Settings{ModelsRoot: root}}, "test")
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(out, `# SKIPPED "trellis-2-4b": no DINOv3 encoder`) {
		t.Fatalf("expected the package to be skipped with a reason, got:\n%s", out)
	}
	if strings.Contains(out, "trellis2-server") {
		t.Fatalf("a package without its encoder must not emit a command:\n%s", out)
	}
}