diff --git a/convert/convert_qwen25vl.go b/convert/convert_qwen25vl.go index 60d6f494..7261458e 100644 --- a/convert/convert_qwen25vl.go +++ b/convert/convert_qwen25vl.go @@ -39,10 +39,6 @@ func (q *qwen25VLModel) KV(t *Tokenizer) KV { } } - if q.VisionModel.FullAttentionBlocks == nil { - kv["qwen25vl.vision.fullatt_block_indexes"] = []int32{7, 15, 23, 31} - } - kv["qwen25vl.vision.block_count"] = cmp.Or(q.VisionModel.Depth, 32) kv["qwen25vl.vision.embedding_length"] = q.VisionModel.HiddenSize kv["qwen25vl.vision.attention.head_count"] = cmp.Or(q.VisionModel.NumHeads, 16) @@ -53,12 +49,19 @@ func (q *qwen25VLModel) KV(t *Tokenizer) KV { kv["qwen25vl.vision.window_size"] = cmp.Or(q.VisionModel.WindowSize, 112) kv["qwen25vl.vision.attention.layer_norm_epsilon"] = cmp.Or(q.VisionModel.RMSNormEps, 1e-6) kv["qwen25vl.vision.rope.freq_base"] = cmp.Or(q.VisionModel.RopeTheta, 1e4) - kv["qwen25vl.vision.fullatt_block_indexes"] = q.VisionModel.FullAttentionBlocks + kv["qwen25vl.vision.fullatt_block_indexes"] = q.fullAttentionBlocks() kv["qwen25vl.vision.temporal_patch_size"] = cmp.Or(q.VisionModel.TemporalPatchSize, 2) return kv } +func (q *qwen25VLModel) fullAttentionBlocks() []int32 { + if len(q.VisionModel.FullAttentionBlocks) > 0 { + return q.VisionModel.FullAttentionBlocks + } + return []int32{7, 15, 23, 31} +} + func (q *qwen25VLModel) Tensors(ts []Tensor) []*ggml.Tensor { var out []*ggml.Tensor diff --git a/convert/convert_qwen25vl_test.go b/convert/convert_qwen25vl_test.go new file mode 100644 index 00000000..66ee691b --- /dev/null +++ b/convert/convert_qwen25vl_test.go @@ -0,0 +1,44 @@ +package convert + +import ( + "slices" + "testing" +) + +func TestQwen25VLFullAttentionBlockDefaults(t *testing.T) { + tokenizer := &Tokenizer{Vocabulary: &Vocabulary{}} + + for _, tt := range []struct { + name string + blocks []int32 + want []int32 + }{ + { + name: "nil", + want: []int32{7, 15, 23, 31}, + }, + { + name: "empty", + blocks: []int32{}, + want: []int32{7, 15, 23, 31}, + }, + { + name: "custom", + blocks: []int32{5, 17}, + want: []int32{5, 17}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + model := &qwen25VLModel{} + model.VisionModel.FullAttentionBlocks = tt.blocks + + got, ok := model.KV(tokenizer)["qwen25vl.vision.fullatt_block_indexes"].([]int32) + if !ok { + t.Fatalf("fullatt_block_indexes has unexpected type %T", got) + } + if !slices.Equal(got, tt.want) { + t.Fatalf("fullatt_block_indexes = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/llama/compat/llama-ollama-compat.cpp b/llama/compat/llama-ollama-compat.cpp index 0f7ad6e0..3c06c3cb 100644 --- a/llama/compat/llama-ollama-compat.cpp +++ b/llama/compat/llama-ollama-compat.cpp @@ -2958,6 +2958,7 @@ void handle_qwen25vl_clip(gguf_context * meta, ggml_context * ctx) { gguf_set_val_u32(meta, "clip.vision.n_wa_pattern", (uint32_t)(arr[0] + 1)); } } + inject_u32_if_missing(meta, "clip.vision.n_wa_pattern", 8); // Default image_size = 560 (Qwen2VLVisionModel default, no image_size in HF config). inject_u32_if_missing(meta, "clip.vision.image_size", 560);