internal/autogen/gguf_diffusion_test.go
Rama main · réplica de lectura del repositorio original.
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)
}
}