Ctrl K
ComunidadGuías…

internal/autogen/gguf_diffusion_test.go

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

Archivo original ↗Preguntar a KiBot
package autogen

import (
	"encoding/binary"
	"os"
	"path/filepath"
	"testing"
)

type ggufTensor struct {
	name string
	dims []uint64
}

// writeGgufTensors lays down a gguf with the given string KVs and a tensor table
// (F32, offsets all 0, no payload). The tensor walk reads names and shapes only,
// so this exercises the same path as a multi-GB DiT.
func writeGgufTensors(t *testing.T, path string, kvs [][2]string, tensors []ggufTensor) {
	t.Helper()
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(path)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	le := binary.LittleEndian
	str := func(s string) {
		binary.Write(f, le, uint64(len(s)))
		f.WriteString(s)
	}
	f.WriteString("GGUF")
	binary.Write(f, le, uint32(3))
	binary.Write(f, le, uint64(len(tensors)))
	binary.Write(f, le, uint64(len(kvs)))
	for _, kv := range kvs {
		str(kv[0])
		binary.Write(f, le, uint32(8))
		str(kv[1])
	}
	for _, tn := range tensors {
		str(tn.name)
		binary.Write(f, le, uint32(len(tn.dims)))
		for _, d := range tn.dims {
			binary.Write(f, le, d)
		}
		binary.Write(f, le, uint32(0)) // F32
		binary.Write(f, le, uint64(0))
	}
}

// qwenImage21Tensors is the non-block tensor set of a real Qwen-Image 2.1 Turbo
// gguf (caption projection 4096 wide), with every name under prefix.
func qwenImage21Tensors(prefix string) []ggufTensor {
	names := []ggufTensor{
		{"img_in.weight", []uint64{64, 4096}},
		{"txt_in.in_layer.weight", []uint64{4096, 4096}},
		{"txt_in.out_layer.weight", []uint64{4096, 4096}},
		{"transformer_blocks.0.attn.to_q.weight", []uint64{4096, 4096}},
		{"proj_out.weight", []uint64{4096, 64}},
	}
	for i := range names {
		names[i].name = prefix + names[i].name
	}
	return names
}

// unsloth's and AtomicChat's Qwen-Image 2.1 quants keep ComfyUI's
// model.diffusion_model. prefix, and AtomicChat's carry no KVs at all. Both
// must classify as qwen_image with the caption width read, or the first loads
// with the wrong encoder/VAE and the second is served by llama-server as chat.
func TestReadGgufMetadata_QwenImageComfyLayouts(t *testing.T) {
	dir := t.TempDir()
	cases := []struct {
		name string
		kvs  [][2]string
		pfx  string
	}{
		{"leejet bare names", [][2]string{{"general.architecture", "qwen_image"}}, ""},
		{"unsloth prefixed", [][2]string{{"general.architecture", "qwen_image"}}, "model.diffusion_model."},
		{"atomicchat prefixed, zero kvs", nil, "model.diffusion_model."},
	}
	for i, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			p := filepath.Join(dir, string(rune('a'+i))+".gguf")
			writeGgufTensors(t, p, tc.kvs, qwenImage21Tensors(tc.pfx))
			meta, err := ReadGgufMetadata(p)
			if err != nil {
				t.Fatal(err)
			}
			if got := effectiveImageArch(meta); got != "qwen_image" {
				t.Errorf("effectiveImageArch = %q, want qwen_image", got)
			}
			if meta.CondHidden != 4096 {
				t.Errorf("CondHidden = %d, want 4096", meta.CondHidden)
			}
		})
	}
}

// The qwen_image sniff must not claim a BFL flux file, which also carries
// img_in/txt_in but names its blocks double_blocks.
func TestReadGgufMetadata_FluxNotSniffedAsQwenImage(t *testing.T) {
	p := filepath.Join(t.TempDir(), "flux.gguf")
	writeGgufTensors(t, p, nil, []ggufTensor{
		{"img_in.weight", []uint64{64, 3072}},
		{"txt_in.weight", []uint64{4096, 3072}},
		{"double_blocks.0.img_attn.qkv.weight", []uint64{3072, 9216}},
	})
	meta, err := ReadGgufMetadata(p)
	if err != nil {
		t.Fatal(err)
	}
	if meta.DiffusionKind != "flux" {
		t.Errorf("DiffusionKind = %q, want flux", meta.DiffusionKind)
	}
}