Image-Text-to-Text
GGUF
llama.cpp
rocm
rocmfpx
amd
strix-halo
gfx1151
imatrix
multimodal
muse-glimmer
conversational
dflash
speculative-decoding
Instructions to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16 # Run inference directly in the terminal: llama cli -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16 # Run inference directly in the terminal: llama cli -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16 # Run inference directly in the terminal: ./llama-cli -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Use Docker
docker model run hf.co/vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
- LM Studio
- Jan
- vLLM
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
- Ollama
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Ollama:
ollama run hf.co/vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
- Unsloth Studio
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF to start chatting
- Pi
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Docker Model Runner:
docker model run hf.co/vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
- Lemonade
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Run and chat with the model
lemonade run user.Muse-Glimmer-30B-ROCmFPX-GGUF-BF16
List all available models
lemonade list
- Hermes Agent
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF:BF16" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Add model card, checksums, and Muse ROCmFPX runtime patch
Browse files- README.md +182 -0
- ROCmFPX-Muse-Glimmer.patch +1339 -0
- SHA256SUMS +4 -0
README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: meta-models/Muse-Glimmer-30B
|
| 3 |
+
base_model_relation: quantized
|
| 4 |
+
library_name: llama.cpp
|
| 5 |
+
pipeline_tag: image-text-to-text
|
| 6 |
+
license: apache-2.0
|
| 7 |
+
tags:
|
| 8 |
+
- gguf
|
| 9 |
+
- llama.cpp
|
| 10 |
+
- rocm
|
| 11 |
+
- rocmfpx
|
| 12 |
+
- amd
|
| 13 |
+
- strix-halo
|
| 14 |
+
- gfx1151
|
| 15 |
+
- imatrix
|
| 16 |
+
- multimodal
|
| 17 |
+
- muse-glimmer
|
| 18 |
+
- conversational
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
# Muse-Glimmer-30B ROCmFPX GGUF
|
| 22 |
+
|
| 23 |
+
ROCmFP4 and ROCmFP8 builds of
|
| 24 |
+
[meta-models/Muse-Glimmer-30B](https://huggingface.co/meta-models/Muse-Glimmer-30B),
|
| 25 |
+
targeted and tested on AMD Strix Halo (`gfx1151`). Rawr. 🦖
|
| 26 |
+
|
| 27 |
+
At publication time, a Hub search returned no other Muse Glimmer ROCmFPX
|
| 28 |
+
repositories. These are custom ROCmFPX formats, not ordinary llama.cpp Q4/Q8
|
| 29 |
+
files; read the compatibility section before downloading.
|
| 30 |
+
|
| 31 |
+
> **Experimental runtime required:** these GGUFs need a patched build of
|
| 32 |
+
> [charlie12345/ROCmFPX](https://github.com/charlie12345/ROCmFPX). Stock
|
| 33 |
+
> llama.cpp does not implement the ROCmFP4/ROCmFP8 tensor layouts, while the
|
| 34 |
+
> pinned ROCmFPX base predates Muse Glimmer support. Apply the included
|
| 35 |
+
> `ROCmFPX-Muse-Glimmer.patch` to ROCmFPX commit `00d54526e…`, then build that
|
| 36 |
+
> checkout. The patch adds the upstream Muse text and vision implementation,
|
| 37 |
+
> ports it to ROCmFPX's older APIs, and supplies the FP16 sparse-attention mask
|
| 38 |
+
> required by its multimodal flash-attention path.
|
| 39 |
+
|
| 40 |
+
## Files
|
| 41 |
+
|
| 42 |
+
| File | ROCmFPX preset | Size | BPW | iMatrix | Measured decode |
|
| 43 |
+
| --- | --- | ---: | ---: | --- | ---: |
|
| 44 |
+
| `Muse-Glimmer-30B-ROCmFP4.gguf` | `Q4_0_ROCMFP4_STRIX` | 14.17 GiB | 4.36 | Yes | 14.9 tok/s |
|
| 45 |
+
| `Muse-Glimmer-30B-ROCmFP4-Q6-QUALITY.gguf` | `Q4_0_ROCMFP4_COHERENT` | 14.94 GiB | 4.60 | Yes | 14.0 tok/s |
|
| 46 |
+
| `Muse-Glimmer-30B-ROCmFP8.gguf` | `Q8_0_ROCMFPX` | 26.77 GiB | 8.25 | No | 7.8 tok/s |
|
| 47 |
+
| `mmproj-Muse-Glimmer-30B-BF16.gguf` | BF16 vision projector | 3.59 GiB | — | — | — |
|
| 48 |
+
|
| 49 |
+
Suggested choices:
|
| 50 |
+
|
| 51 |
+
- **ROCmFP4:** default Strix Halo speed/quality build. Fast FP4 body, dual-scale
|
| 52 |
+
FP4 attention K/V, and Q6_K token embeddings.
|
| 53 |
+
- **ROCmFP4-Q6-QUALITY:** coherence-biased build. Dual-scale FP4 throughout the
|
| 54 |
+
body with Q6_K token embeddings.
|
| 55 |
+
- **ROCmFP8:** high-fidelity 8.25-bpw reference build.
|
| 56 |
+
|
| 57 |
+
The BF16 projector works with all three text models.
|
| 58 |
+
|
| 59 |
+
## iMatrix
|
| 60 |
+
|
| 61 |
+
Both FP4 models use the same GGUF importance matrix:
|
| 62 |
+
|
| 63 |
+
- 500 chunks × 512 tokens (approximately 256k calibration tokens)
|
| 64 |
+
- 416 tensor importance entries consumed by each quantizer
|
| 65 |
+
- varied narrative/general-language calibration corpus
|
| 66 |
+
- checkpoints saved every 100 chunks
|
| 67 |
+
|
| 68 |
+
`Q8_0_ROCMFPX` does not consume importance weights, so the FP8 reference was
|
| 69 |
+
intentionally built without an iMatrix.
|
| 70 |
+
|
| 71 |
+
## Compatibility
|
| 72 |
+
|
| 73 |
+
These files use experimental ROCmFPX tensor types and **will not load in stock
|
| 74 |
+
llama.cpp**.
|
| 75 |
+
|
| 76 |
+
The validated runtime was built from:
|
| 77 |
+
|
| 78 |
+
- [ROCmFPX](https://github.com/charlie12345/ROCmFPX) base commit
|
| 79 |
+
`00d54526e24e3aba4c76474e3147cbf9c7cc034c`
|
| 80 |
+
- upstream llama.cpp Muse support commit
|
| 81 |
+
[`62bf73d25c53b8161f8a22894d4f90c4aebbd7d0`](https://github.com/ggml-org/llama.cpp/commit/62bf73d25c53b8161f8a22894d4f90c4aebbd7d0)
|
| 82 |
+
- small compatibility adaptations for the older ROCmFPX chat, model, and
|
| 83 |
+
multimodal APIs
|
| 84 |
+
|
| 85 |
+
`ROCmFPX-Muse-Glimmer.patch` contains the complete patch against the pinned
|
| 86 |
+
ROCmFPX base. The runtime was built with ROCm and Vulkan backends; the reported
|
| 87 |
+
generation tests used `ROCm0` on `gfx1151`.
|
| 88 |
+
|
| 89 |
+
Minimal runtime setup:
|
| 90 |
+
|
| 91 |
+
```bash
|
| 92 |
+
git clone https://github.com/charlie12345/ROCmFPX.git
|
| 93 |
+
cd ROCmFPX
|
| 94 |
+
git checkout 00d54526e24e3aba4c76474e3147cbf9c7cc034c
|
| 95 |
+
|
| 96 |
+
hf download vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF \
|
| 97 |
+
ROCmFPX-Muse-Glimmer.patch \
|
| 98 |
+
--local-dir /tmp/muse-glimmer-rocmfpx
|
| 99 |
+
git apply /tmp/muse-glimmer-rocmfpx/ROCmFPX-Muse-Glimmer.patch
|
| 100 |
+
|
| 101 |
+
BUILD_DIR=build-muse-rocmfpx \
|
| 102 |
+
JOBS=16 \
|
| 103 |
+
CMAKE_HIP_COMPILER=/opt/rocm-7.2.0/lib/llvm/bin/clang++ \
|
| 104 |
+
GGML_HIP_ROCWMMA_FATTN=OFF \
|
| 105 |
+
./scripts/build-strix-rocmfp4-mtp.sh
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
Adjust `CMAKE_HIP_COMPILER` for the installed ROCm version. The resulting
|
| 109 |
+
runtime binaries are under `build-muse-rocmfpx/bin/`. The patch must be applied
|
| 110 |
+
to the exact pinned commit; `git apply --check` was verified before publishing.
|
| 111 |
+
|
| 112 |
+
## Download and run
|
| 113 |
+
|
| 114 |
+
```bash
|
| 115 |
+
hf download vmlinux/Muse-Glimmer-30B-ROCmFPX-GGUF \
|
| 116 |
+
Muse-Glimmer-30B-ROCmFP4.gguf \
|
| 117 |
+
mmproj-Muse-Glimmer-30B-BF16.gguf \
|
| 118 |
+
--local-dir ./Muse-Glimmer-30B-ROCmFPX
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
Text:
|
| 122 |
+
|
| 123 |
+
```bash
|
| 124 |
+
./llama-cli \
|
| 125 |
+
-m ./Muse-Glimmer-30B-ROCmFPX/Muse-Glimmer-30B-ROCmFP4.gguf \
|
| 126 |
+
-dev ROCm0 -ngl all -c 8192 -cnv
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
Vision:
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
./llama-cli \
|
| 133 |
+
-m ./Muse-Glimmer-30B-ROCmFPX/Muse-Glimmer-30B-ROCmFP4.gguf \
|
| 134 |
+
-mm ./Muse-Glimmer-30B-ROCmFPX/mmproj-Muse-Glimmer-30B-BF16.gguf \
|
| 135 |
+
--image ./image.png \
|
| 136 |
+
-p "Describe this image." \
|
| 137 |
+
-dev ROCm0 -ngl all -c 8192 -cnv -st
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
## Validation
|
| 141 |
+
|
| 142 |
+
All three files completed clean, single-turn ROCm generation with every layer
|
| 143 |
+
offloaded. The projector completed an end-to-end image test and correctly
|
| 144 |
+
identified the test image as a folder icon.
|
| 145 |
+
|
| 146 |
+
| Model | Prompt processing | Token generation |
|
| 147 |
+
| --- | ---: | ---: |
|
| 148 |
+
| ROCmFP4 | 113.7 tok/s | 14.9 tok/s |
|
| 149 |
+
| ROCmFP4-Q6-QUALITY | 39.0 tok/s | 14.0 tok/s |
|
| 150 |
+
| ROCmFP8 | 96.7 tok/s | 7.8 tok/s |
|
| 151 |
+
| ROCmFP4 + BF16 projector | 81.7 tok/s | 14.9 tok/s |
|
| 152 |
+
|
| 153 |
+
These are short smoke-test measurements, not a formal benchmark. Host: AMD
|
| 154 |
+
Strix Halo `gfx1151`, 128 GiB unified memory, ROCm backend, 1,024-token text
|
| 155 |
+
context (2,048 for vision).
|
| 156 |
+
|
| 157 |
+
Additional verification:
|
| 158 |
+
|
| 159 |
+
- 731 tensors and `muse-glimmer` architecture in every text GGUF
|
| 160 |
+
- 809 tensors and 50 vision blocks in the projector
|
| 161 |
+
- ROCmFP4 kernel copy/conversion tests: 34/34 passed on Vulkan
|
| 162 |
+
- `test-quantize-fns` and architecture tests passed
|
| 163 |
+
- SHA-256 hashes supplied in `SHA256SUMS`
|
| 164 |
+
|
| 165 |
+
## MTP / speculative decoding
|
| 166 |
+
|
| 167 |
+
No MTP or DFlash drafter is included. The downloaded base repository contained
|
| 168 |
+
the main Muse Glimmer weights but no separate `MuseGlimmerAssistantModel`
|
| 169 |
+
checkpoint. The patched runtime includes Muse DFlash loader support for use if
|
| 170 |
+
a compatible drafter is published separately.
|
| 171 |
+
|
| 172 |
+
## Provenance
|
| 173 |
+
|
| 174 |
+
- Source revision: `f84ecc3a0ea984a4c04542a84269e3d065350a6e`
|
| 175 |
+
- Conversion: upstream llama.cpp `d2f83055dca6dd009d8a52bdff792fbb286f4444`
|
| 176 |
+
- Every published GGUF is covered by `SHA256SUMS`; source, intermediate, and
|
| 177 |
+
calibration hashes are retained in `PROVENANCE_SHA256SUMS`
|
| 178 |
+
- Detailed local build report: `BUILD_RESULTS.md`
|
| 179 |
+
|
| 180 |
+
The original model license and usage policy apply. See the
|
| 181 |
+
[source model card](https://huggingface.co/meta-models/Muse-Glimmer-30B) before
|
| 182 |
+
use.
|
ROCmFPX-Muse-Glimmer.patch
ADDED
|
@@ -0,0 +1,1339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/common/chat.cpp b/common/chat.cpp
|
| 2 |
+
index 58a193f77..01e4166ee 100644
|
| 3 |
+
--- a/common/chat.cpp
|
| 4 |
+
+++ b/common/chat.cpp
|
| 5 |
+
@@ -2081,6 +2081,133 @@ static void func_args_not_string(json & messages) {
|
| 6 |
+
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
+// An assistant turn is rendered as one or more messages, each
|
| 10 |
+
+// "<|start|>assistant to=<recipient><|message|>{content}{END}" where END is
|
| 11 |
+
+// <|eom|> (more messages follow) or <|eot|> (end of turn):
|
| 12 |
+
+// - chain-of-thought: to=self, terminated by <|eom|>
|
| 13 |
+
+// - final answer: to=user, terminated by <|eot|>
|
| 14 |
+
+// The generation prompt is just "<|start|>assistant"; the model emits its own
|
| 15 |
+
+// " to=...<|message|>".
|
| 16 |
+
+static common_chat_params common_chat_params_init_muse_glimmer(const common_chat_template & tmpl,
|
| 17 |
+
+ const autoparser::generation_params & inputs) {
|
| 18 |
+
+ common_chat_params data;
|
| 19 |
+
+
|
| 20 |
+
+ data.prompt = common_chat_template_direct_apply_impl(tmpl, inputs);
|
| 21 |
+
+ data.generation_prompt = "<|start|>assistant";
|
| 22 |
+
+ data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;
|
| 23 |
+
+ data.supports_thinking = true;
|
| 24 |
+
+
|
| 25 |
+
+ data.preserved_tokens = {
|
| 26 |
+
+ "<|start|>", "<|message|>", "<|eom|>", "<|eot|>",
|
| 27 |
+
+ // ATEM tool-call markup emitted on " to=<tool>" turns.
|
| 28 |
+
+ "<atem:function_calls>", "<atem:invoke", "<atem:parameter", "</atem:parameter>",
|
| 29 |
+
+ "</atem:invoke>", "</atem:function_calls>",
|
| 30 |
+
+ };
|
| 31 |
+
+
|
| 32 |
+
+ auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
|
| 33 |
+
+
|
| 34 |
+
+ auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
|
| 35 |
+
+ // Constrained grammar whenever tools are offered.
|
| 36 |
+
+ auto include_grammar = has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE;
|
| 37 |
+
+
|
| 38 |
+
+ auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
|
| 39 |
+
+ auto start = p.rule("start", p.literal("<|start|>assistant"));
|
| 40 |
+
+
|
| 41 |
+
+ if (!extract_reasoning && !include_grammar) {
|
| 42 |
+
+ return start + p.content(p.rest());
|
| 43 |
+
+ }
|
| 44 |
+
+
|
| 45 |
+
+ if (extract_reasoning) {
|
| 46 |
+
+ p.rule("analysis", p.literal(" to=self<|message|>") + p.reasoning(p.until("<|eom|>")) + p.literal("<|eom|>"));
|
| 47 |
+
+ } else {
|
| 48 |
+
+ p.rule("analysis", p.literal(" to=self<|message|>") + p.content(p.until("<|eom|>")) + p.literal("<|eom|>"));
|
| 49 |
+
+ }
|
| 50 |
+
+ auto analysis = p.ref("analysis");
|
| 51 |
+
+
|
| 52 |
+
+ auto recipient = p.optional(p.literal(" to=user"));
|
| 53 |
+
+ auto final_msg = p.rule("final", recipient + p.literal("<|message|>") + p.content(p.until("<|eot|>")));
|
| 54 |
+
+
|
| 55 |
+
+ if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE) {
|
| 56 |
+
+ auto string_value =
|
| 57 |
+
+ p.tool_arg_string_value(p.until("</atem:parameter>")) +
|
| 58 |
+
+ p.tool_arg_close(p.literal("</atem:parameter>"));
|
| 59 |
+
+
|
| 60 |
+
+ auto tool_choice = p.choice();
|
| 61 |
+
+ foreach_function(inputs.tools, [&](const json & tool) {
|
| 62 |
+
+ const auto & function = tool.at("function");
|
| 63 |
+
+ const std::string name = function.at("name");
|
| 64 |
+
+ auto params = function.contains("parameters") ? function.at("parameters") : json::object();
|
| 65 |
+
+
|
| 66 |
+
+ auto args = p.eps();
|
| 67 |
+
+ if (params.contains("properties") && params.at("properties").is_object() && !params.at("properties").empty()) {
|
| 68 |
+
+ auto schema_info = common_schema_info();
|
| 69 |
+
+ schema_info.resolve_refs(params);
|
| 70 |
+
+
|
| 71 |
+
+ auto arg_choice = p.choice();
|
| 72 |
+
+ for (const auto & [prop_name, prop_schema] : params.at("properties").items()) {
|
| 73 |
+
+ auto value_parser = p.eps();
|
| 74 |
+
+ if (schema_info.resolves_to_string(prop_schema)) {
|
| 75 |
+
+ value_parser = string_value;
|
| 76 |
+
+ } else {
|
| 77 |
+
+ value_parser = p.tool_arg_json_value(
|
| 78 |
+
+ p.schema(p.json(), "tool-" + name + "-arg-" + prop_name + "-schema", prop_schema, false))
|
| 79 |
+
+ + p.tool_arg_close(p.literal("</atem:parameter>"));
|
| 80 |
+
+ }
|
| 81 |
+
+
|
| 82 |
+
+ auto arg_rule = p.tool_arg(
|
| 83 |
+
+ p.tool_arg_open(p.literal("<atem:parameter name=\"") + p.tool_arg_name(p.literal(prop_name)) + p.literal("\">")) +
|
| 84 |
+
+ value_parser);
|
| 85 |
+
+
|
| 86 |
+
+ arg_choice |= arg_rule;
|
| 87 |
+
+ }
|
| 88 |
+
+ args = p.zero_or_more(arg_choice + p.space());
|
| 89 |
+
+ }
|
| 90 |
+
+
|
| 91 |
+
+ auto tool_parser = p.tool(
|
| 92 |
+
+ p.tool_open(p.literal(" to=") + p.until("<|message|>") +
|
| 93 |
+
+ p.literal("<|message|><atem:function_calls>") + p.space() +
|
| 94 |
+
+ p.literal("<atem:invoke name=\"") + p.tool_name(p.literal(name)) + p.literal("\">") + p.space())
|
| 95 |
+
+ << p.tool_args(args)
|
| 96 |
+
+ << p.tool_close(p.literal("</atem:invoke>") + p.space() + p.literal("</atem:function_calls>")));
|
| 97 |
+
+
|
| 98 |
+
+ tool_choice |= p.rule("tool-" + name, tool_parser);
|
| 99 |
+
+ });
|
| 100 |
+
+
|
| 101 |
+
+ auto tool_calls = inputs.parallel_tool_calls
|
| 102 |
+
+ ? p.trigger_rule("tool-call", tool_choice + p.zero_or_more(p.literal("<|eom|>") + start + tool_choice))
|
| 103 |
+
+ : p.trigger_rule("tool-call", tool_choice);
|
| 104 |
+
+
|
| 105 |
+
+
|
| 106 |
+
+ if (inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED) {
|
| 107 |
+
+ return p.zero_or_more(start + analysis) + start + tool_calls;
|
| 108 |
+
+ }
|
| 109 |
+
+ return p.zero_or_more(start + analysis) + start + (tool_calls | final_msg);
|
| 110 |
+
+ }
|
| 111 |
+
+
|
| 112 |
+
+ return p.zero_or_more(start + analysis) + start + final_msg;
|
| 113 |
+
+ });
|
| 114 |
+
+
|
| 115 |
+
+ data.parser = parser.save();
|
| 116 |
+
+
|
| 117 |
+
+ if (include_grammar) {
|
| 118 |
+
+ data.grammar_lazy = inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_REQUIRED;
|
| 119 |
+
+ data.grammar = build_grammar([&](const common_grammar_builder & builder) {
|
| 120 |
+
+ foreach_function(inputs.tools, [&](const json & tool) {
|
| 121 |
+
+ const auto & function = tool.at("function");
|
| 122 |
+
+ auto schema = function.contains("parameters") ? function.at("parameters") : json::object();
|
| 123 |
+
+ builder.resolve_refs(schema);
|
| 124 |
+
+ });
|
| 125 |
+
+ parser.build_grammar(builder, data.grammar_lazy);
|
| 126 |
+
+ });
|
| 127 |
+
+ data.grammar_triggers = {
|
| 128 |
+
+ { COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN,
|
| 129 |
+
+ "<\\|start\\|>assistant( to=(?!self<\\|message\\|>)(?!user<\\|message\\|>)[^<]*?<\\|message\\|>)" },
|
| 130 |
+
+ };
|
| 131 |
+
+ }
|
| 132 |
+
+
|
| 133 |
+
+ return data;
|
| 134 |
+
+}
|
| 135 |
+
+
|
| 136 |
+
static json common_chat_extra_context() {
|
| 137 |
+
json ctx = json::object();
|
| 138 |
+
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
| 139 |
+
@@ -2109,6 +2236,12 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
|
| 140 |
+
return common_chat_params_init_gpt_oss(tmpl, params);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
+ // Muse Glimmer format using " to=<recipient>" recipients and <|eom|>/<|eot|> message terminators.
|
| 144 |
+
+ if (src.find("<atem:function_calls>") != std::string::npos && src.find("<|eom|>") != std::string::npos) {
|
| 145 |
+
+ LOG_DBG("Using specialized template: Muse Glimmer\n");
|
| 146 |
+
+ return common_chat_params_init_muse_glimmer(tmpl, params);
|
| 147 |
+
+ }
|
| 148 |
+
+
|
| 149 |
+
// Functionary v3.2 - uses recipient-based format with >>>recipient\n{content}
|
| 150 |
+
// Detection: template has ">>>all" for content and ">>>" prefix for tool calls
|
| 151 |
+
if (src.find(">>>all") != std::string::npos && src.find(">>>${recipient}") != std::string::npos) {
|
| 152 |
+
diff --git a/common/speculative.cpp b/common/speculative.cpp
|
| 153 |
+
index bbfd349ac..abd2e7fdd 100644
|
| 154 |
+
--- a/common/speculative.cpp
|
| 155 |
+
+++ b/common/speculative.cpp
|
| 156 |
+
@@ -1105,7 +1105,14 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
| 157 |
+
return true;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
- if (batch_in.token == nullptr || batch_in.embd != nullptr) {
|
| 161 |
+
+ // Target prefill may contain token IDs or multimodal embeddings. Both
|
| 162 |
+
+ // produce the target-layer features used to seed the draft KV cache, so
|
| 163 |
+
+ // skipping the embedding batches leaves a hole in the draft's cache and
|
| 164 |
+
+ // the next injection fails to initialize.
|
| 165 |
+
+ // TODO: revisit after https://github.com/ggml-org/llama.cpp/pull/24669 is merged
|
| 166 |
+
+ const bool has_tokens = batch_in.token != nullptr;
|
| 167 |
+
+ const bool has_embeddings = batch_in.embd != nullptr;
|
| 168 |
+
+ if (has_tokens == has_embeddings) {
|
| 169 |
+
return true;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
diff --git a/conversion/__init__.py b/conversion/__init__.py
|
| 173 |
+
index 46618905a..7aebf9dc7 100644
|
| 174 |
+
--- a/conversion/__init__.py
|
| 175 |
+
+++ b/conversion/__init__.py
|
| 176 |
+
@@ -170,6 +170,8 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
| 177 |
+
"Olmo3ForCausalLM": "olmo",
|
| 178 |
+
"OlmoForCausalLM": "olmo",
|
| 179 |
+
"OlmoeForCausalLM": "olmo",
|
| 180 |
+
+ "MuseGlimmerAssistantModel": "muse_glimmer",
|
| 181 |
+
+ "MuseGlimmerForConditionalGeneration": "muse_glimmer",
|
| 182 |
+
"OpenELMForCausalLM": "openelm",
|
| 183 |
+
"OrionForCausalLM": "orion",
|
| 184 |
+
"PLMForCausalLM": "plm",
|
| 185 |
+
@@ -280,6 +282,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
| 186 |
+
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
| 187 |
+
"Mistral3ForConditionalGeneration": "llava",
|
| 188 |
+
"NemotronH_Nano_VL_V2": "nemotron",
|
| 189 |
+
+ "MuseGlimmerForConditionalGeneration": "muse_glimmer",
|
| 190 |
+
"PaddleOCRVisionModel": "ernie",
|
| 191 |
+
"Phi4ForCausalLMV": "phi",
|
| 192 |
+
"Qwen2AudioForConditionalGeneration": "ultravox",
|
| 193 |
+
diff --git a/conversion/muse_glimmer.py b/conversion/muse_glimmer.py
|
| 194 |
+
new file mode 100644
|
| 195 |
+
index 000000000..cc588e832
|
| 196 |
+
--- /dev/null
|
| 197 |
+
+++ b/conversion/muse_glimmer.py
|
| 198 |
+
@@ -0,0 +1,179 @@
|
| 199 |
+
+from __future__ import annotations
|
| 200 |
+
+
|
| 201 |
+
+import json
|
| 202 |
+
+from typing import Any, Iterable, TYPE_CHECKING
|
| 203 |
+
+
|
| 204 |
+
+import torch
|
| 205 |
+
+
|
| 206 |
+
+if TYPE_CHECKING:
|
| 207 |
+
+ from torch import Tensor
|
| 208 |
+
+
|
| 209 |
+
+from .base import MmprojModel, ModelBase, TextModel, gguf
|
| 210 |
+
+
|
| 211 |
+
+
|
| 212 |
+
+def _unpermute_for_rope(tensor: "Tensor", n_heads: int) -> "Tensor":
|
| 213 |
+
+ """Invert transformers' `_permute_for_rope`: HF stores Q/K in rotate_half layout,
|
| 214 |
+
+ llama.cpp consumes the interleaved (NORM) layout."""
|
| 215 |
+
+ if tensor.ndim == 2:
|
| 216 |
+
+ dim1, dim2 = tensor.shape
|
| 217 |
+
+ return tensor.view(n_heads, 2, dim1 // n_heads // 2, dim2).transpose(1, 2).reshape(dim1, dim2)
|
| 218 |
+
+ if tensor.ndim == 1:
|
| 219 |
+
+ (dim1,) = tensor.shape
|
| 220 |
+
+ return tensor.view(n_heads, 2, dim1 // n_heads // 2).transpose(1, 2).reshape(dim1)
|
| 221 |
+
+ raise ValueError(f"_unpermute_for_rope: unexpected shape {tuple(tensor.shape)}")
|
| 222 |
+
+
|
| 223 |
+
+
|
| 224 |
+
+@ModelBase.register("MuseGlimmerForConditionalGeneration")
|
| 225 |
+
+class MuseGlimmerModel(TextModel):
|
| 226 |
+
+ model_arch = gguf.MODEL_ARCH.MUSE_GLIMMER
|
| 227 |
+
+
|
| 228 |
+
+ def norm_shift(self, name: str) -> float:
|
| 229 |
+
+ # All four layer norms use 1, the final norm uses 0.
|
| 230 |
+
+ return 1.0 if name.endswith("layernorm.weight") else 0.0
|
| 231 |
+
+
|
| 232 |
+
+ def set_vocab(self):
|
| 233 |
+
+ self._set_vocab_gpt2()
|
| 234 |
+
+
|
| 235 |
+
+ from transformers import AutoTokenizer
|
| 236 |
+
+ tok = AutoTokenizer.from_pretrained(self.dir_model)
|
| 237 |
+
+ eot_id = tok.convert_tokens_to_ids("<|eot|>")
|
| 238 |
+
+ if isinstance(eot_id, int) and eot_id >= 0:
|
| 239 |
+
+ self.gguf_writer.add_eot_token_id(eot_id)
|
| 240 |
+
+
|
| 241 |
+
+ def set_gguf_parameters(self):
|
| 242 |
+
+ super().set_gguf_parameters()
|
| 243 |
+
+ hparams = self.hparams
|
| 244 |
+
+
|
| 245 |
+
+ self.gguf_writer.add_final_logit_softcapping(hparams["final_logit_softcapping"])
|
| 246 |
+
+ self.gguf_writer.add_logit_scale(hparams["output_multiplier"])
|
| 247 |
+
+ self.gguf_writer.add_sliding_window(hparams["sliding_window"])
|
| 248 |
+
+ self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]])
|
| 249 |
+
+
|
| 250 |
+
+ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
| 251 |
+
+ shift = self.norm_shift(name)
|
| 252 |
+
+ if shift != 0.0:
|
| 253 |
+
+ data_torch = data_torch + shift
|
| 254 |
+
+
|
| 255 |
+
+ # Invert transformers' `_permute_for_rope` on Q/K, we keep ggml's NORM (interleaved) rope
|
| 256 |
+
+ if ".self_attn.q_proj." in name:
|
| 257 |
+
+ data_torch = _unpermute_for_rope(data_torch, int(self.hparams["num_attention_heads"]))
|
| 258 |
+
+ elif ".self_attn.k_proj." in name:
|
| 259 |
+
+ data_torch = _unpermute_for_rope(data_torch, int(self.hparams["num_key_value_heads"]))
|
| 260 |
+
+
|
| 261 |
+
+ # Synthesize QK-norm weights to absorb qk_scale_factor.
|
| 262 |
+
+ # MuseGlimmer implementation: scaleless RMSNorm followed by qk_scale_factor..
|
| 263 |
+
+ if bid is not None and name.endswith(f"model.layers.{bid}.self_attn.q_proj.weight"):
|
| 264 |
+
+ head_dim = self.hparams["head_dim"]
|
| 265 |
+
+ q_scale = float(self.hparams["qk_scale_factor"])
|
| 266 |
+
+ yield (
|
| 267 |
+
+ self.map_tensor_name(f"model.layers.{bid}.self_attn.q_norm.weight"),
|
| 268 |
+
+ torch.full((head_dim,), q_scale, dtype=torch.float32),
|
| 269 |
+
+ )
|
| 270 |
+
+ yield (
|
| 271 |
+
+ self.map_tensor_name(f"model.layers.{bid}.self_attn.k_norm.weight"),
|
| 272 |
+
+ torch.ones((head_dim,), dtype=torch.float32),
|
| 273 |
+
+ )
|
| 274 |
+
+
|
| 275 |
+
+ yield from super().modify_tensors(data_torch, name, bid)
|
| 276 |
+
+
|
| 277 |
+
+
|
| 278 |
+
+@ModelBase.register("MuseGlimmerForConditionalGeneration")
|
| 279 |
+
+class MuseGlimmerVisionModel(MmprojModel):
|
| 280 |
+
+ def get_vision_config(self) -> dict[str, Any] | None:
|
| 281 |
+
+ c = self.global_config.get("vision_config")
|
| 282 |
+
+ if not c:
|
| 283 |
+
+ return None
|
| 284 |
+
+ # MuseGlimmer actually uses dynamic size, initialize with nominal size
|
| 285 |
+
+ image_size = c["pos_emb_height"] * c["patch_size"] * c["merge_size"]
|
| 286 |
+
+ return {**c, "image_size": image_size}
|
| 287 |
+
+
|
| 288 |
+
+ def set_gguf_parameters(self):
|
| 289 |
+
+ super().set_gguf_parameters()
|
| 290 |
+
+ assert self.hparams_vision is not None
|
| 291 |
+
+ c = self.hparams_vision # enriched vision_config from get_vision_config()
|
| 292 |
+
+
|
| 293 |
+
+ self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MUSE_GLIMMER)
|
| 294 |
+
+ self.gguf_writer.add_vision_attention_layernorm_eps(float(c["layer_norm_eps"]))
|
| 295 |
+
+ self.gguf_writer.add_vision_spatial_merge_size(int(c["merge_size"]))
|
| 296 |
+
+
|
| 297 |
+
+ @classmethod
|
| 298 |
+
+ def filter_tensors(cls, item):
|
| 299 |
+
+ name, gen = item
|
| 300 |
+
+ keep = ("model.vision_tower.", "model.vision_adapter.", "model.vision_projection.")
|
| 301 |
+
+ if not any(name.startswith(k) for k in keep):
|
| 302 |
+
+ return None
|
| 303 |
+
+ return super().filter_tensors((name, gen))
|
| 304 |
+
+
|
| 305 |
+
+ # 3-layer projector MLP
|
| 306 |
+
+ _MM_MLP_MAP = {
|
| 307 |
+
+ "model.vision_adapter.fc1": (gguf.MODEL_TENSOR.V_MMPROJ, 0),
|
| 308 |
+
+ "model.vision_adapter.fc2": (gguf.MODEL_TENSOR.V_MMPROJ, 1),
|
| 309 |
+
+ "model.vision_projection": (gguf.MODEL_TENSOR.V_MMPROJ, 2),
|
| 310 |
+
+ }
|
| 311 |
+
+
|
| 312 |
+
+ def modify_tensors(self, data_torch, name, bid):
|
| 313 |
+
+ assert self.hparams_vision is not None
|
| 314 |
+
+ if ".attn.q_proj." in name or ".attn.k_proj." in name:
|
| 315 |
+
+ n_heads = int(self.hparams_vision["num_attention_heads"])
|
| 316 |
+
+ data_torch = _unpermute_for_rope(data_torch, n_heads)
|
| 317 |
+
+ # Lay out the pt=2 temporal slabs of the patch embedding as a conv2d for build_inp()
|
| 318 |
+
+ if name.endswith("patch_embedder.patch_embedding.weight"):
|
| 319 |
+
+ n_embd = data_torch.shape[0]
|
| 320 |
+
+ pt = int(self.hparams_vision["patch_temporal"])
|
| 321 |
+
+ ps = int(self.hparams_vision["patch_size"])
|
| 322 |
+
+ data_torch = data_torch.view(n_embd, pt, 3, ps, ps).sum(dim=1) # (n_embd, 3, ps, ps)
|
| 323 |
+
+ stem, _, suffix = name.rpartition(".")
|
| 324 |
+
+ if stem in self._MM_MLP_MAP:
|
| 325 |
+
+ tensor_key, idx = self._MM_MLP_MAP[stem]
|
| 326 |
+
+ yield (self.format_tensor_name(tensor_key, bid=idx, suffix="." + suffix), data_torch)
|
| 327 |
+
+ return
|
| 328 |
+
+ yield (self.map_tensor_name(name), data_torch)
|
| 329 |
+
+
|
| 330 |
+
+
|
| 331 |
+
+@ModelBase.register("MuseGlimmerAssistantModel")
|
| 332 |
+
+class MuseGlimmerAssistantModel(TextModel):
|
| 333 |
+
+ model_arch = gguf.MODEL_ARCH.DFLASH
|
| 334 |
+
+
|
| 335 |
+
+ def set_vocab(self):
|
| 336 |
+
+ if self.target_model_dir is None:
|
| 337 |
+
+ raise ValueError(
|
| 338 |
+
+ "MuseGlimmerAssistant (DFlash drafter) requires --target-model-dir pointing to the "
|
| 339 |
+
+ "target MuseGlimmer HF directory"
|
| 340 |
+
+ )
|
| 341 |
+
+
|
| 342 |
+
+ original_dir = self.dir_model
|
| 343 |
+
+ self.dir_model = self.target_model_dir
|
| 344 |
+
+
|
| 345 |
+
+ from . import get_model_class
|
| 346 |
+
+ with open(self.target_model_dir / "config.json", "r", encoding="utf-8") as f:
|
| 347 |
+
+ target_arch = json.load(f)["architectures"][0]
|
| 348 |
+
+ target_cls = get_model_class(target_arch)
|
| 349 |
+
+ if target_cls is not type(self):
|
| 350 |
+
+ target_cls.set_vocab(self) # ty: ignore[unresolved-attribute]
|
| 351 |
+
+ else:
|
| 352 |
+
+ super().set_vocab()
|
| 353 |
+
+
|
| 354 |
+
+ self.dir_model = original_dir
|
| 355 |
+
+
|
| 356 |
+
+ mask_token_id = self.hparams.get("mask_token_id")
|
| 357 |
+
+ if mask_token_id is not None:
|
| 358 |
+
+ self.gguf_writer.add_mask_token_id(int(mask_token_id))
|
| 359 |
+
+
|
| 360 |
+
+ def set_gguf_parameters(self):
|
| 361 |
+
+ super().set_gguf_parameters()
|
| 362 |
+
+ h = self.hparams
|
| 363 |
+
+
|
| 364 |
+
+ self.gguf_writer.add_block_size(int(h["block_size"]))
|
| 365 |
+
+
|
| 366 |
+
+ # dflash.target_layers[k] refers to the inputs going into the ith layer, which come from the (i-1)th layer's output.
|
| 367 |
+
+ # The transformers configuration refers to the outputs being recorded.
|
| 368 |
+
+ self.gguf_writer.add_target_layers([int(x) + 1 for x in h["target_layer_ids"]])
|
| 369 |
+
+
|
| 370 |
+
+ if h.get("sliding_window") and h.get("layer_types"):
|
| 371 |
+
+ self.gguf_writer.add_sliding_window(int(h["sliding_window"]))
|
| 372 |
+
+ self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in h["layer_types"]])
|
| 373 |
+
+
|
| 374 |
+
+ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
| 375 |
+
+ # DFlash defaults to NEOX (rotate_half) rope, matching transformers HF layout for Q/K, QK-norms
|
| 376 |
+
+ # no permutation needed.
|
| 377 |
+
+ yield (self.map_tensor_name(name), data_torch)
|
| 378 |
+
diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py
|
| 379 |
+
index 85f2ca488..8ec605c1f 100644
|
| 380 |
+
--- a/gguf-py/gguf/constants.py
|
| 381 |
+
+++ b/gguf-py/gguf/constants.py
|
| 382 |
+
@@ -471,6 +471,7 @@ class MODEL_ARCH(IntEnum):
|
| 383 |
+
OLMO = auto()
|
| 384 |
+
OLMO2 = auto()
|
| 385 |
+
OLMOE = auto()
|
| 386 |
+
+ MUSE_GLIMMER = auto()
|
| 387 |
+
OPENELM = auto()
|
| 388 |
+
ARCTIC = auto()
|
| 389 |
+
DEEPSEEK = auto()
|
| 390 |
+
@@ -1037,6 +1038,7 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
| 391 |
+
MODEL_ARCH.OLMO: "olmo",
|
| 392 |
+
MODEL_ARCH.OLMO2: "olmo2",
|
| 393 |
+
MODEL_ARCH.OLMOE: "olmoe",
|
| 394 |
+
+ MODEL_ARCH.MUSE_GLIMMER: "muse-glimmer",
|
| 395 |
+
MODEL_ARCH.OPENELM: "openelm",
|
| 396 |
+
MODEL_ARCH.ARCTIC: "arctic",
|
| 397 |
+
MODEL_ARCH.DEEPSEEK: "deepseek",
|
| 398 |
+
@@ -2983,6 +2985,25 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
| 399 |
+
MODEL_TENSOR.FFN_UP_EXP,
|
| 400 |
+
MODEL_TENSOR.FFN_DOWN_EXP,
|
| 401 |
+
],
|
| 402 |
+
+ MODEL_ARCH.MUSE_GLIMMER: [
|
| 403 |
+
+ MODEL_TENSOR.TOKEN_EMBD,
|
| 404 |
+
+ MODEL_TENSOR.OUTPUT,
|
| 405 |
+
+ MODEL_TENSOR.OUTPUT_NORM,
|
| 406 |
+
+ MODEL_TENSOR.ATTN_Q,
|
| 407 |
+
+ MODEL_TENSOR.ATTN_Q_NORM,
|
| 408 |
+
+ MODEL_TENSOR.ATTN_K,
|
| 409 |
+
+ MODEL_TENSOR.ATTN_K_NORM,
|
| 410 |
+
+ MODEL_TENSOR.ATTN_V,
|
| 411 |
+
+ MODEL_TENSOR.ATTN_OUT,
|
| 412 |
+
+ MODEL_TENSOR.ATTN_GATE,
|
| 413 |
+
+ MODEL_TENSOR.FFN_GATE,
|
| 414 |
+
+ MODEL_TENSOR.FFN_DOWN,
|
| 415 |
+
+ MODEL_TENSOR.FFN_UP,
|
| 416 |
+
+ MODEL_TENSOR.ATTN_NORM,
|
| 417 |
+
+ MODEL_TENSOR.ATTN_POST_NORM,
|
| 418 |
+
+ MODEL_TENSOR.FFN_PRE_NORM,
|
| 419 |
+
+ MODEL_TENSOR.FFN_POST_NORM,
|
| 420 |
+
+ ],
|
| 421 |
+
MODEL_ARCH.OPENELM: [
|
| 422 |
+
MODEL_TENSOR.TOKEN_EMBD,
|
| 423 |
+
MODEL_TENSOR.OUTPUT_NORM,
|
| 424 |
+
@@ -4680,6 +4701,7 @@ class VisionProjectorType:
|
| 425 |
+
MINICPMV4_6 = "minicpmv4_6"
|
| 426 |
+
GRANITE_SPEECH = "granite_speech" # audio
|
| 427 |
+
MIMOVL = "mimovl"
|
| 428 |
+
+ MUSE_GLIMMER = "muse-glimmer"
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
# Items here are (block size, type size)
|
| 432 |
+
diff --git a/gguf-py/gguf/tensor_mapping.py b/gguf-py/gguf/tensor_mapping.py
|
| 433 |
+
index 3200a993b..5b2e0b7ab 100644
|
| 434 |
+
--- a/gguf-py/gguf/tensor_mapping.py
|
| 435 |
+
+++ b/gguf-py/gguf/tensor_mapping.py
|
| 436 |
+
@@ -377,7 +377,7 @@ class TensorNameMap:
|
| 437 |
+
),
|
| 438 |
+
|
| 439 |
+
MODEL_TENSOR.ATTN_GATE: (
|
| 440 |
+
- "model.layers.{bid}.self_attn.gate_proj", # afmoe
|
| 441 |
+
+ "model.layers.{bid}.self_attn.gate_proj", # afmoe muse-glimmer
|
| 442 |
+
"model.layers.{bid}.linear_attn.in_proj_z", # qwen3.5
|
| 443 |
+
"model.layers.{bid}.self_attn.g_proj", # step3.5 head-wise attention gate
|
| 444 |
+
),
|
| 445 |
+
@@ -1275,10 +1275,12 @@ class TensorNameMap:
|
| 446 |
+
"encoder.final_layer_norm", # t5
|
| 447 |
+
"layer_norm", # neobert
|
| 448 |
+
"model.hidden_norm", # dflash
|
| 449 |
+
+ "encoder.output_norm_enc", # dflash (transformers MuseGlimmerAssistant)
|
| 450 |
+
),
|
| 451 |
+
|
| 452 |
+
MODEL_TENSOR.FC: (
|
| 453 |
+
- "model.fc", # dflash
|
| 454 |
+
+ "model.fc", # dflash
|
| 455 |
+
+ "encoder.fc", # dflash (transformers MuseGlimmerAssistant)
|
| 456 |
+
),
|
| 457 |
+
|
| 458 |
+
MODEL_TENSOR.DSPARK_MARKOV_W1: (
|
| 459 |
+
@@ -1443,6 +1445,7 @@ class TensorNameMap:
|
| 460 |
+
"vision_tower.patch_embed.patchifier.proj", # dots.ocr
|
| 461 |
+
"vision_model.conv1", # Step3-VL
|
| 462 |
+
"model.vision_embedder.patch_dense", # gemma4 unified
|
| 463 |
+
+ "model.vision_tower.patch_embedder.patch_embedding", # muse-glimmer
|
| 464 |
+
),
|
| 465 |
+
|
| 466 |
+
MODEL_TENSOR.V_ENC_EMBD_NORM: (
|
| 467 |
+
@@ -1507,6 +1510,7 @@ class TensorNameMap:
|
| 468 |
+
"siglip2.vision_model.encoder.layers.{bid}.self_attn.q_proj", # youtuvl
|
| 469 |
+
"model.vision_model.transformer.layers.{bid}.self_attn.q_proj", # Deepseek-OCR CLIP, generated
|
| 470 |
+
"vision_model.model.layers.{bid}.self_attn.q_proj.linear", # gemma4
|
| 471 |
+
+ "model.vision_tower.layers.{bid}.attn.q_proj", # muse-glimmer
|
| 472 |
+
),
|
| 473 |
+
|
| 474 |
+
MODEL_TENSOR.V_ENC_ATTN_Q_NORM: (
|
| 475 |
+
@@ -1531,6 +1535,7 @@ class TensorNameMap:
|
| 476 |
+
"model.vision_model.transformer.layers.{bid}.self_attn.k_proj", # Deepseek-OCR CLIP, generated
|
| 477 |
+
"siglip2.vision_model.encoder.layers.{bid}.self_attn.k_proj",
|
| 478 |
+
"vision_model.model.layers.{bid}.self_attn.k_proj.linear", # gemma4
|
| 479 |
+
+ "model.vision_tower.layers.{bid}.attn.k_proj", # muse-glimmer
|
| 480 |
+
),
|
| 481 |
+
|
| 482 |
+
MODEL_TENSOR.V_ENC_ATTN_K_NORM: (
|
| 483 |
+
@@ -1555,6 +1560,7 @@ class TensorNameMap:
|
| 484 |
+
"siglip2.vision_model.encoder.layers.{bid}.self_attn.v_proj",
|
| 485 |
+
"model.vision_model.transformer.layers.{bid}.self_attn.v_proj", # Deepseek-OCR CLIP, generated
|
| 486 |
+
"vision_model.model.layers.{bid}.self_attn.v_proj.linear", # gemma4
|
| 487 |
+
+ "model.vision_tower.layers.{bid}.attn.v_proj", # muse-glimmer
|
| 488 |
+
),
|
| 489 |
+
|
| 490 |
+
MODEL_TENSOR.V_ENC_INPUT_NORM: (
|
| 491 |
+
@@ -1576,6 +1582,7 @@ class TensorNameMap:
|
| 492 |
+
"vision_model.radio_model.model.blocks.{bid}.norm1", # Nemotron Nano v2 VL
|
| 493 |
+
"vision_tower.blocks.{bid}.norm1", # dots.ocr
|
| 494 |
+
"vision_model.transformer.resblocks.{bid}.ln_1", # Step3-VL
|
| 495 |
+
+ "model.vision_tower.layers.{bid}.norm1", # muse-glimmer
|
| 496 |
+
),
|
| 497 |
+
|
| 498 |
+
MODEL_TENSOR.V_ENC_ATTN_O: (
|
| 499 |
+
@@ -1599,6 +1606,7 @@ class TensorNameMap:
|
| 500 |
+
"vision_model.model.layers.{bid}.self_attn.o_proj.linear", # gemma4
|
| 501 |
+
"vision_tower.blocks.{bid}.attn.proj", # dots.ocr
|
| 502 |
+
"vision_model.transformer.resblocks.{bid}.attn.out_proj", # Step3-VL
|
| 503 |
+
+ "model.vision_tower.layers.{bid}.attn.proj", # muse-glimmer
|
| 504 |
+
),
|
| 505 |
+
|
| 506 |
+
MODEL_TENSOR.V_ENC_ATTN_SINKS: (
|
| 507 |
+
@@ -1625,6 +1633,7 @@ class TensorNameMap:
|
| 508 |
+
"vision_model.model.layers.{bid}.pre_feedforward_layernorm", # gemma4
|
| 509 |
+
"vision_tower.blocks.{bid}.norm2", # dots.ocr
|
| 510 |
+
"vision_model.transformer.resblocks.{bid}.ln_2", # Step3-VL
|
| 511 |
+
+ "model.vision_tower.layers.{bid}.norm2", # muse-glimmer
|
| 512 |
+
),
|
| 513 |
+
|
| 514 |
+
MODEL_TENSOR.V_ENC_FFN_UP: (
|
| 515 |
+
@@ -1647,6 +1656,7 @@ class TensorNameMap:
|
| 516 |
+
"vision_model.radio_model.model.blocks.{bid}.mlp.fc1", # Nemotron Nano v2 VL
|
| 517 |
+
"vision_model.model.layers.{bid}.mlp.up_proj", # gemma4
|
| 518 |
+
"vision_model.transformer.resblocks.{bid}.mlp.c_fc", # Step3-VL
|
| 519 |
+
+ "model.vision_tower.layers.{bid}.mlp.fc1", # muse-glimmer
|
| 520 |
+
),
|
| 521 |
+
|
| 522 |
+
MODEL_TENSOR.V_ENC_FFN_GATE: (
|
| 523 |
+
@@ -1676,6 +1686,7 @@ class TensorNameMap:
|
| 524 |
+
"vision_model.radio_model.model.blocks.{bid}.mlp.fc2", # Nemotron Nano v2 VL
|
| 525 |
+
"vision_model.model.layers.{bid}.mlp.down_proj", # gemma4
|
| 526 |
+
"vision_model.transformer.resblocks.{bid}.mlp.c_proj", # Step3-VL
|
| 527 |
+
+ "model.vision_tower.layers.{bid}.mlp.fc2", # muse-glimmer
|
| 528 |
+
),
|
| 529 |
+
|
| 530 |
+
MODEL_TENSOR.V_ENC_ATTN_POST_NORM: (
|
| 531 |
+
@@ -1710,6 +1721,7 @@ class TensorNameMap:
|
| 532 |
+
"model.vision_model.pre_layrnorm", # Deepseek-OCR CLIP
|
| 533 |
+
"vision_tower.patch_embed.patchifier.norm", # dots.ocr
|
| 534 |
+
"vision_model.ln_pre", # Step3-VL
|
| 535 |
+
+ "model.vision_tower.ln_pre", # muse-glimmer
|
| 536 |
+
),
|
| 537 |
+
|
| 538 |
+
MODEL_TENSOR.V_POST_NORM: (
|
| 539 |
+
@@ -1721,6 +1733,7 @@ class TensorNameMap:
|
| 540 |
+
"vision_tower.encoder.final_layernorm", # kimi-vl
|
| 541 |
+
"visual.post_layernorm", # glm4v
|
| 542 |
+
"siglip2.vision_model.post_layernorm",
|
| 543 |
+
+ "model.vision_tower.ln_post", # muse-glimmer
|
| 544 |
+
),
|
| 545 |
+
|
| 546 |
+
MODEL_TENSOR.V_MM_POST_NORM: (
|
| 547 |
+
diff --git a/src/llama-arch.cpp b/src/llama-arch.cpp
|
| 548 |
+
index 17908449b..1d9552074 100644
|
| 549 |
+
--- a/src/llama-arch.cpp
|
| 550 |
+
+++ b/src/llama-arch.cpp
|
| 551 |
+
@@ -71,6 +71,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
| 552 |
+
{ LLM_ARCH_OLMO, "olmo" },
|
| 553 |
+
{ LLM_ARCH_OLMO2, "olmo2" },
|
| 554 |
+
{ LLM_ARCH_OLMOE, "olmoe" },
|
| 555 |
+
+ { LLM_ARCH_MUSE_GLIMMER, "muse-glimmer" },
|
| 556 |
+
{ LLM_ARCH_OPENELM, "openelm" },
|
| 557 |
+
{ LLM_ARCH_ARCTIC, "arctic" },
|
| 558 |
+
{ LLM_ARCH_DEEPSEEK, "deepseek" },
|
| 559 |
+
diff --git a/src/llama-arch.h b/src/llama-arch.h
|
| 560 |
+
index d2f948e84..9daeb9931 100644
|
| 561 |
+
--- a/src/llama-arch.h
|
| 562 |
+
+++ b/src/llama-arch.h
|
| 563 |
+
@@ -76,6 +76,7 @@ enum llm_arch {
|
| 564 |
+
LLM_ARCH_OLMO,
|
| 565 |
+
LLM_ARCH_OLMO2,
|
| 566 |
+
LLM_ARCH_OLMOE,
|
| 567 |
+
+ LLM_ARCH_MUSE_GLIMMER,
|
| 568 |
+
LLM_ARCH_OPENELM,
|
| 569 |
+
LLM_ARCH_ARCTIC,
|
| 570 |
+
LLM_ARCH_DEEPSEEK,
|
| 571 |
+
diff --git a/src/llama-model-saver.cpp b/src/llama-model-saver.cpp
|
| 572 |
+
index 19ab54378..d93ebfd6d 100644
|
| 573 |
+
--- a/src/llama-model-saver.cpp
|
| 574 |
+
+++ b/src/llama-model-saver.cpp
|
| 575 |
+
@@ -29,6 +29,7 @@ bool llama_model_saver_supports_arch(llm_arch arch) {
|
| 576 |
+
case LLM_ARCH_APERTUS:
|
| 577 |
+
case LLM_ARCH_MIMO2:
|
| 578 |
+
case LLM_ARCH_STEP35:
|
| 579 |
+
+ case LLM_ARCH_MUSE_GLIMMER:
|
| 580 |
+
case LLM_ARCH_LAGUNA:
|
| 581 |
+
return false;
|
| 582 |
+
default:
|
| 583 |
+
diff --git a/src/llama-model.cpp b/src/llama-model.cpp
|
| 584 |
+
index c33f8c247..594affda4 100644
|
| 585 |
+
--- a/src/llama-model.cpp
|
| 586 |
+
+++ b/src/llama-model.cpp
|
| 587 |
+
@@ -163,6 +163,8 @@ static llama_model * llama_model_mapping(llm_arch arch, const llama_model_params
|
| 588 |
+
return new llama_model_olmo2(params);
|
| 589 |
+
case LLM_ARCH_OLMOE:
|
| 590 |
+
return new llama_model_olmoe(params);
|
| 591 |
+
+ case LLM_ARCH_MUSE_GLIMMER:
|
| 592 |
+
+ return new llama_model_muse_glimmer(params);
|
| 593 |
+
case LLM_ARCH_OPENELM:
|
| 594 |
+
return new llama_model_openelm(params);
|
| 595 |
+
case LLM_ARCH_GPTNEOX:
|
| 596 |
+
@@ -2372,6 +2374,7 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
|
| 597 |
+
case LLM_ARCH_DEEPSEEK2OCR:
|
| 598 |
+
case LLM_ARCH_DEEPSEEK32:
|
| 599 |
+
case LLM_ARCH_DEEPSEEK4:
|
| 600 |
+
+ case LLM_ARCH_MUSE_GLIMMER:
|
| 601 |
+
case LLM_ARCH_PLM:
|
| 602 |
+
case LLM_ARCH_CHATGLM:
|
| 603 |
+
case LLM_ARCH_GRANITE:
|
| 604 |
+
diff --git a/src/models/models.h b/src/models/models.h
|
| 605 |
+
index 5cc93e6b9..cb3b4d4ad 100644
|
| 606 |
+
--- a/src/models/models.h
|
| 607 |
+
+++ b/src/models/models.h
|
| 608 |
+
@@ -1017,6 +1017,19 @@ struct llama_model_olmoe : public llama_model_base {
|
| 609 |
+
};
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
+struct llama_model_muse_glimmer : public llama_model_base {
|
| 613 |
+
+ llama_model_muse_glimmer(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 614 |
+
+ void load_arch_hparams(llama_model_loader & ml) override;
|
| 615 |
+
+ void load_arch_tensors(llama_model_loader & ml) override;
|
| 616 |
+
+
|
| 617 |
+
+ struct graph : public llm_graph_context {
|
| 618 |
+
+ graph(const llama_model & model, const llm_graph_params & params);
|
| 619 |
+
+ };
|
| 620 |
+
+
|
| 621 |
+
+ std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
| 622 |
+
+};
|
| 623 |
+
+
|
| 624 |
+
+
|
| 625 |
+
struct llama_model_openelm : public llama_model_base {
|
| 626 |
+
llama_model_openelm(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 627 |
+
void load_arch_hparams(llama_model_loader & ml) override;
|
| 628 |
+
diff --git a/src/models/muse-glimmer.cpp b/src/models/muse-glimmer.cpp
|
| 629 |
+
new file mode 100644
|
| 630 |
+
index 000000000..6e860b55d
|
| 631 |
+
--- /dev/null
|
| 632 |
+
+++ b/src/models/muse-glimmer.cpp
|
| 633 |
+
@@ -0,0 +1,208 @@
|
| 634 |
+
+#include "models.h"
|
| 635 |
+
+
|
| 636 |
+
+void llama_model_muse_glimmer::load_arch_hparams(llama_model_loader & ml) {
|
| 637 |
+
+ ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
|
| 638 |
+
+ ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
|
| 639 |
+
+ ml.get_key(LLM_KV_FINAL_LOGIT_SOFTCAPPING, hparams.f_final_logit_softcapping, false);
|
| 640 |
+
+ ml.get_key(LLM_KV_LOGIT_SCALE, hparams.f_logit_scale);
|
| 641 |
+
+
|
| 642 |
+
+ hparams.rope_freq_base_train_swa = hparams.rope_freq_base_train;
|
| 643 |
+
+ ml.get_key(LLM_KV_ROPE_FREQ_BASE_SWA, hparams.rope_freq_base_train_swa, false);
|
| 644 |
+
+
|
| 645 |
+
+ hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
|
| 646 |
+
+ uint32_t swa_period = 4;
|
| 647 |
+
+ if (ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, swa_period, false)) {
|
| 648 |
+
+ hparams.set_swa_pattern(swa_period);
|
| 649 |
+
+ } else {
|
| 650 |
+
+ ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.swa_layers, hparams.n_layer);
|
| 651 |
+
+ }
|
| 652 |
+
+
|
| 653 |
+
+ switch (hparams.n_layer) {
|
| 654 |
+
+ case 52: type = LLM_TYPE_30B; break;
|
| 655 |
+
+ default: type = LLM_TYPE_UNKNOWN;
|
| 656 |
+
+ }
|
| 657 |
+
+}
|
| 658 |
+
+
|
| 659 |
+
+void llama_model_muse_glimmer::load_arch_tensors(llama_model_loader &) {
|
| 660 |
+
+ LLAMA_LOAD_LOCALS;
|
| 661 |
+
+
|
| 662 |
+
+ tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
|
| 663 |
+
+ output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
|
| 664 |
+
+ output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
|
| 665 |
+
+
|
| 666 |
+
+ for (int i = 0; i < n_layer; ++i) {
|
| 667 |
+
+ auto & layer = layers[i];
|
| 668 |
+
+
|
| 669 |
+
+ // Pre/post-attention norms (Muse Glimmer's `weight + 1` applied at conversion time).
|
| 670 |
+
+ layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
|
| 671 |
+
+ layer.attn_post_norm = create_tensor(tn(LLM_TENSOR_ATTN_POST_NORM, "weight", i), {n_embd}, 0);
|
| 672 |
+
+
|
| 673 |
+
+ // Q/K/V/O projections.
|
| 674 |
+
+ create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_k_gqa, n_embd_v_gqa, 0);
|
| 675 |
+
+ layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_embd_head_k * n_head, n_embd}, 0);
|
| 676 |
+
+
|
| 677 |
+
+ // QK-norm. Weights are synthesized at conversion time to absorb `qk_scale_factor`.
|
| 678 |
+
+ layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 679 |
+
+ layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 680 |
+
+
|
| 681 |
+
+ // Attention output gate: sigmoid(gate) * attn_out before o_proj (same as afmoe).
|
| 682 |
+
+ layer.wqkv_gate = create_tensor(tn(LLM_TENSOR_ATTN_GATE, "weight", i), {n_embd, n_embd_head_k * n_head}, 0);
|
| 683 |
+
+
|
| 684 |
+
+ // Pre/post-FFN norms (FFN_PRE_NORM is aliased to LLM_TENSOR_FFN_NORM).
|
| 685 |
+
+ layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
|
| 686 |
+
+ layer.ffn_post_norm = create_tensor(tn(LLM_TENSOR_FFN_POST_NORM, "weight", i), {n_embd}, 0);
|
| 687 |
+
+
|
| 688 |
+
+ // Dense FFN (unlike afmoe, no MoE branches).
|
| 689 |
+
+ layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff}, 0);
|
| 690 |
+
+ layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), {n_ff, n_embd}, 0);
|
| 691 |
+
+ layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff}, 0);
|
| 692 |
+
+ }
|
| 693 |
+
+}
|
| 694 |
+
+
|
| 695 |
+
+llama_model_muse_glimmer::graph::graph(const llama_model & model, const llm_graph_params & params)
|
| 696 |
+
+ : llm_graph_context(params) {
|
| 697 |
+
+ const int64_t n_embd_head = hparams.n_embd_head_v();
|
| 698 |
+
+ GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
|
| 699 |
+
+
|
| 700 |
+
+ // Different to f_norm_rms_eps for post-attn / post-FFN norms
|
| 701 |
+
+ const float post_norm_eps = 1e-8f;
|
| 702 |
+
+
|
| 703 |
+
+ ggml_tensor * cur;
|
| 704 |
+
+ ggml_tensor * inpL;
|
| 705 |
+
+
|
| 706 |
+
+ inpL = build_inp_embd(model.tok_embd);
|
| 707 |
+
+ inpL = build_norm(inpL, nullptr, nullptr, LLM_NORM_RMS, -1);
|
| 708 |
+
+ cb(inpL, "embd_norm", -1);
|
| 709 |
+
+
|
| 710 |
+
+ ggml_tensor * inp_pos = build_inp_pos();
|
| 711 |
+
+ auto * inp_attn = build_attn_inp_kv_iswa();
|
| 712 |
+
+ ggml_tensor * inp_out_ids = build_inp_out_ids();
|
| 713 |
+
+
|
| 714 |
+
+ const float kq_scale = 1.0f / sqrtf(float(n_embd_head));
|
| 715 |
+
+
|
| 716 |
+
+ for (int il = 0; il < n_layer; ++il) {
|
| 717 |
+
+ // expose per-layer residual for speculative drafts (see LLM_KV_TARGET_LAYERS).
|
| 718 |
+
+ res->t_layer_inp[il] = inpL;
|
| 719 |
+
+
|
| 720 |
+
+ const float freq_base_l = model.get_rope_freq_base (cparams, il);
|
| 721 |
+
+ const float freq_scale_l = model.get_rope_freq_scale(cparams, il);
|
| 722 |
+
+
|
| 723 |
+
+ ggml_tensor * inpSA = inpL;
|
| 724 |
+
+
|
| 725 |
+
+ // RoPE runs on the SWA layers, NoPE on full ones.
|
| 726 |
+
+ const bool use_rope = hparams.is_swa(il);
|
| 727 |
+
+
|
| 728 |
+
+ // pre-attention norm (weight+1 folded at conversion time)
|
| 729 |
+
+ cur = build_norm(inpL, model.layers[il].attn_norm, NULL, LLM_NORM_RMS, il);
|
| 730 |
+
+ cb(cur, "attn_norm", il);
|
| 731 |
+
+
|
| 732 |
+
+ // self-attention: attention output gate around SDPA (afmoe.cpp:147-191)
|
| 733 |
+
+ {
|
| 734 |
+
+ ggml_tensor * attn_inp = cur; // save input for gate computation
|
| 735 |
+
+
|
| 736 |
+
+ auto [Qcur, Kcur, Vcur] = build_qkv(model.layers[il], cur,
|
| 737 |
+
+ n_embd_head, n_head, n_head_kv, il);
|
| 738 |
+
+
|
| 739 |
+
+ // gate = wqkv_gate @ attn_inp (from pre-attn hidden state)
|
| 740 |
+
+ ggml_tensor * gate = build_lora_mm(model.layers[il].wqkv_gate, attn_inp);
|
| 741 |
+
+ cb(gate, "attn_gate_proj", il);
|
| 742 |
+
+
|
| 743 |
+
+ // QK-norm. attn_q_norm weight was synthesized at conversion to broadcast
|
| 744 |
+
+ // qk_scale_factor across head_dim; attn_k_norm is identity (ones).
|
| 745 |
+
+ Qcur = build_norm(Qcur, model.layers[il].attn_q_norm, NULL, LLM_NORM_RMS, il);
|
| 746 |
+
+ Kcur = build_norm(Kcur, model.layers[il].attn_k_norm, NULL, LLM_NORM_RMS, il);
|
| 747 |
+
+ cb(Qcur, "Qcur_normed", il);
|
| 748 |
+
+ cb(Kcur, "Kcur_normed", il);
|
| 749 |
+
+
|
| 750 |
+
+ if (use_rope) {
|
| 751 |
+
+ Qcur = ggml_rope_ext(
|
| 752 |
+
+ ctx0, Qcur, inp_pos, nullptr,
|
| 753 |
+
+ n_rot, rope_type, n_ctx_orig, freq_base_l, freq_scale_l,
|
| 754 |
+
+ ext_factor, attn_factor, beta_fast, beta_slow);
|
| 755 |
+
+ cb(Qcur, "Qcur_rope", il);
|
| 756 |
+
+
|
| 757 |
+
+ Kcur = ggml_rope_ext(
|
| 758 |
+
+ ctx0, Kcur, inp_pos, nullptr,
|
| 759 |
+
+ n_rot, rope_type, n_ctx_orig, freq_base_l, freq_scale_l,
|
| 760 |
+
+ ext_factor, attn_factor, beta_fast, beta_slow);
|
| 761 |
+
+ cb(Kcur, "Kcur_rope", il);
|
| 762 |
+
+ }
|
| 763 |
+
+
|
| 764 |
+
+ // SDPA. wo is deferred; the gate goes between attn_out and o_proj.
|
| 765 |
+
+ cur = build_attn(inp_attn,
|
| 766 |
+
+ NULL, NULL, NULL,
|
| 767 |
+
+ Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il);
|
| 768 |
+
+ cb(cur, "attn_out", il);
|
| 769 |
+
+
|
| 770 |
+
+ gate = ggml_sigmoid(ctx0, gate);
|
| 771 |
+
+ cb(gate, "attn_gate_sig", il);
|
| 772 |
+
+ cur = ggml_mul(ctx0, cur, gate);
|
| 773 |
+
+ cb(cur, "attn_gated", il);
|
| 774 |
+
+
|
| 775 |
+
+ cur = build_lora_mm(model.layers[il].wo, cur, model.layers[il].wo_s);
|
| 776 |
+
+ cb(cur, "attn_o_proj", il);
|
| 777 |
+
+ }
|
| 778 |
+
+
|
| 779 |
+
+ cur = ggml_rms_norm(ctx0, cur, post_norm_eps);
|
| 780 |
+
+ cur = ggml_mul(ctx0, cur, model.layers[il].attn_post_norm);
|
| 781 |
+
+ cb(cur, "attn_post_norm", il);
|
| 782 |
+
+
|
| 783 |
+
+ if (il == n_layer - 1 && inp_out_ids) {
|
| 784 |
+
+ cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 785 |
+
+ inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids);
|
| 786 |
+
+ }
|
| 787 |
+
+
|
| 788 |
+
+ ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
|
| 789 |
+
+ cb(ffn_inp, "ffn_inp", il);
|
| 790 |
+
+
|
| 791 |
+
+ // pre-FFN norm
|
| 792 |
+
+ cur = build_norm(ffn_inp, model.layers[il].ffn_norm, NULL, LLM_NORM_RMS, il);
|
| 793 |
+
+ cb(cur, "ffn_norm", il);
|
| 794 |
+
+
|
| 795 |
+
+ // SwiGLU dense FFN
|
| 796 |
+
+ cur = build_ffn(cur,
|
| 797 |
+
+ model.layers[il].ffn_up, NULL, NULL,
|
| 798 |
+
+ model.layers[il].ffn_gate, NULL, NULL,
|
| 799 |
+
+ model.layers[il].ffn_down, NULL, NULL,
|
| 800 |
+
+ NULL,
|
| 801 |
+
+ LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 802 |
+
+ cb(cur, "ffn_out", il);
|
| 803 |
+
+
|
| 804 |
+
+ cur = ggml_rms_norm(ctx0, cur, post_norm_eps);
|
| 805 |
+
+ cur = ggml_mul(ctx0, cur, model.layers[il].ffn_post_norm);
|
| 806 |
+
+ cb(cur, "ffn_post_norm", il);
|
| 807 |
+
+
|
| 808 |
+
+ cur = ggml_add(ctx0, cur, ffn_inp);
|
| 809 |
+
+ cur = build_cvec(cur, il);
|
| 810 |
+
+ cb(cur, "l_out", il);
|
| 811 |
+
+
|
| 812 |
+
+ inpL = cur;
|
| 813 |
+
+ }
|
| 814 |
+
+
|
| 815 |
+
+ cur = inpL;
|
| 816 |
+
+
|
| 817 |
+
+ // final norm
|
| 818 |
+
+ cur = build_norm(cur, model.output_norm, NULL, LLM_NORM_RMS, -1);
|
| 819 |
+
+ cb(cur, "result_norm", -1);
|
| 820 |
+
+ res->t_embd = cur;
|
| 821 |
+
+
|
| 822 |
+
+ // lm_head, followed by output multiplier
|
| 823 |
+
+ cur = build_lora_mm(model.output, cur);
|
| 824 |
+
+ cur = ggml_scale(ctx0, cur, hparams.f_logit_scale);
|
| 825 |
+
+
|
| 826 |
+
+ // Final logit tanh softcap (from gemma3.cpp).
|
| 827 |
+
+ if (hparams.f_final_logit_softcapping) {
|
| 828 |
+
+ cur = ggml_scale(ctx0, cur, 1.0f / hparams.f_final_logit_softcapping);
|
| 829 |
+
+ cur = ggml_tanh(ctx0, cur);
|
| 830 |
+
+ cur = ggml_scale(ctx0, cur, hparams.f_final_logit_softcapping);
|
| 831 |
+
+ }
|
| 832 |
+
+
|
| 833 |
+
+ cb(cur, "result_output", -1);
|
| 834 |
+
+ res->t_logits = cur;
|
| 835 |
+
+
|
| 836 |
+
+ ggml_build_forward_expand(gf, cur);
|
| 837 |
+
+}
|
| 838 |
+
+
|
| 839 |
+
+std::unique_ptr<llm_graph_context> llama_model_muse_glimmer::build_arch_graph(const llm_graph_params & params) const {
|
| 840 |
+
+ return std::make_unique<graph>(*this, params);
|
| 841 |
+
+}
|
| 842 |
+
diff --git a/tests/test-llama-archs.cpp b/tests/test-llama-archs.cpp
|
| 843 |
+
index 29eae5590..617a12b38 100644
|
| 844 |
+
--- a/tests/test-llama-archs.cpp
|
| 845 |
+
+++ b/tests/test-llama-archs.cpp
|
| 846 |
+
@@ -187,7 +187,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
| 847 |
+
ms.add_kv(LLM_KV_ROPE_FREQ_BASE_SWA, 10000.0f);
|
| 848 |
+
// SWA pattern: every 5th layer is full attention (matches E2B layer_types)
|
| 849 |
+
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, uint32_t(5));
|
| 850 |
+
- } else if (arch == LLM_ARCH_MIMO2 || arch == LLM_ARCH_STEP35) {
|
| 851 |
+
+ } else if (arch == LLM_ARCH_MIMO2 || arch == LLM_ARCH_STEP35 || arch == LLM_ARCH_MUSE_GLIMMER) {
|
| 852 |
+
std::vector<uint32_t> pattern;
|
| 853 |
+
pattern.reserve(n_layer);
|
| 854 |
+
for (uint32_t il = 0; il < n_layer; il++) {
|
| 855 |
+
diff --git a/tools/mtmd/CMakeLists.txt b/tools/mtmd/CMakeLists.txt
|
| 856 |
+
index 3ff313ee8..d312312a6 100644
|
| 857 |
+
--- a/tools/mtmd/CMakeLists.txt
|
| 858 |
+
+++ b/tools/mtmd/CMakeLists.txt
|
| 859 |
+
@@ -31,6 +31,7 @@ add_library(mtmd
|
| 860 |
+
models/kimivl.cpp
|
| 861 |
+
models/kimik25.cpp
|
| 862 |
+
models/nemotron-v2-vl.cpp
|
| 863 |
+
+ models/muse-glimmer.cpp
|
| 864 |
+
models/llama4.cpp
|
| 865 |
+
models/llava.cpp
|
| 866 |
+
models/minicpmv.cpp
|
| 867 |
+
diff --git a/tools/mtmd/clip-graph.h b/tools/mtmd/clip-graph.h
|
| 868 |
+
index 951480be9..38ae6d4b0 100644
|
| 869 |
+
--- a/tools/mtmd/clip-graph.h
|
| 870 |
+
+++ b/tools/mtmd/clip-graph.h
|
| 871 |
+
@@ -11,6 +11,11 @@
|
| 872 |
+
|
| 873 |
+
#define DEFAULT_INTERPOLATION_MODE (GGML_SCALE_MODE_BILINEAR | GGML_SCALE_FLAG_ANTIALIAS)
|
| 874 |
+
|
| 875 |
+
+struct build_vit_opts {
|
| 876 |
+
+ ggml_tensor * attn_mask = nullptr;
|
| 877 |
+
+ std::vector<ggml_tensor *> attn_mask_layers;
|
| 878 |
+
+};
|
| 879 |
+
+
|
| 880 |
+
struct clip_graph {
|
| 881 |
+
const clip_model & model;
|
| 882 |
+
const clip_hparams & hparams;
|
| 883 |
+
@@ -67,7 +72,8 @@ struct clip_graph {
|
| 884 |
+
norm_type norm_t,
|
| 885 |
+
ffn_op_type ffn_t,
|
| 886 |
+
ggml_tensor * learned_pos_embd,
|
| 887 |
+
- std::function<ggml_tensor *(ggml_tensor *, const clip_layer &)> add_pos);
|
| 888 |
+
+ std::function<ggml_tensor *(ggml_tensor *, const clip_layer &)> add_pos,
|
| 889 |
+
+ const build_vit_opts & opts = {});
|
| 890 |
+
|
| 891 |
+
// build the input after conv2d (inp_raw --> patches)
|
| 892 |
+
// returns tensor with shape [n_embd, n_patches]
|
| 893 |
+
diff --git a/tools/mtmd/clip-impl.h b/tools/mtmd/clip-impl.h
|
| 894 |
+
index bc0165e6a..9fb761041 100644
|
| 895 |
+
--- a/tools/mtmd/clip-impl.h
|
| 896 |
+
+++ b/tools/mtmd/clip-impl.h
|
| 897 |
+
@@ -351,6 +351,7 @@ enum projector_type {
|
| 898 |
+
PROJECTOR_TYPE_MINICPMV4_6,
|
| 899 |
+
PROJECTOR_TYPE_GRANITE_SPEECH,
|
| 900 |
+
PROJECTOR_TYPE_MIMOVL,
|
| 901 |
+
+ PROJECTOR_TYPE_MUSE_GLIMMER,
|
| 902 |
+
PROJECTOR_TYPE_UNKNOWN,
|
| 903 |
+
};
|
| 904 |
+
|
| 905 |
+
@@ -403,6 +404,7 @@ static std::map<projector_type, std::string> PROJECTOR_TYPE_NAMES = {
|
| 906 |
+
{ PROJECTOR_TYPE_MINICPMV4_6, "minicpmv4_6"},
|
| 907 |
+
{ PROJECTOR_TYPE_GRANITE_SPEECH, "granite_speech"},
|
| 908 |
+
{ PROJECTOR_TYPE_MIMOVL, "mimovl"},
|
| 909 |
+
+ { PROJECTOR_TYPE_MUSE_GLIMMER, "muse-glimmer"},
|
| 910 |
+
};
|
| 911 |
+
|
| 912 |
+
static projector_type clip_projector_type_from_string(const std::string & str) {
|
| 913 |
+
diff --git a/tools/mtmd/clip-model.h b/tools/mtmd/clip-model.h
|
| 914 |
+
index c06d9f77b..d68ac631f 100644
|
| 915 |
+
--- a/tools/mtmd/clip-model.h
|
| 916 |
+
+++ b/tools/mtmd/clip-model.h
|
| 917 |
+
@@ -91,6 +91,10 @@ struct clip_hparams {
|
| 918 |
+
int32_t sam_n_head = 0;
|
| 919 |
+
int32_t sam_n_embd = 0;
|
| 920 |
+
|
| 921 |
+
+ // Muse Glimmer vision (per-block sparse-window pattern, learned pos-emb, patch-temporal)
|
| 922 |
+
+ // NOTE: these perhaps shouldn't have the architecture prefix
|
| 923 |
+
+ int32_t muse_glimmer_patch_temporal = 0;
|
| 924 |
+
+ int32_t muse_glimmer_sparse_factor = 0;
|
| 925 |
+
// audio
|
| 926 |
+
int32_t n_mel_bins = 0; // whisper preprocessor
|
| 927 |
+
int32_t proj_stack_factor = 0; // ultravox
|
| 928 |
+
diff --git a/tools/mtmd/clip.cpp b/tools/mtmd/clip.cpp
|
| 929 |
+
index fa5d4f90c..5222f5630 100644
|
| 930 |
+
--- a/tools/mtmd/clip.cpp
|
| 931 |
+
+++ b/tools/mtmd/clip.cpp
|
| 932 |
+
@@ -301,7 +301,8 @@ ggml_tensor * clip_graph::build_vit(
|
| 933 |
+
norm_type norm_t,
|
| 934 |
+
ffn_op_type ffn_t,
|
| 935 |
+
ggml_tensor * learned_pos_embd,
|
| 936 |
+
- std::function<ggml_tensor *(ggml_tensor *, const clip_layer &)> add_pos
|
| 937 |
+
+ std::function<ggml_tensor *(ggml_tensor *, const clip_layer &)> add_pos,
|
| 938 |
+
+ const build_vit_opts & opts
|
| 939 |
+
) {
|
| 940 |
+
// batch dim: inp is [n_embd, n_pos] (B==1) or [n_embd, n_pos, B] (multi-tile encode)
|
| 941 |
+
const int64_t B = inp->ne[2];
|
| 942 |
+
@@ -327,6 +328,11 @@ ggml_tensor * clip_graph::build_vit(
|
| 943 |
+
auto & layer = model.layers[il];
|
| 944 |
+
ggml_tensor * cur = inpL; // inpL = residual, cur = hidden_states
|
| 945 |
+
|
| 946 |
+
+ ggml_tensor * attn_mask = opts.attn_mask;
|
| 947 |
+
+ if (opts.attn_mask_layers.size() > (size_t) il) {
|
| 948 |
+
+ attn_mask = opts.attn_mask_layers[il];
|
| 949 |
+
+ }
|
| 950 |
+
+
|
| 951 |
+
// layernorm1
|
| 952 |
+
cur = build_norm(cur, layer.ln_1_w, layer.ln_1_b, norm_t, eps, il);
|
| 953 |
+
cb(cur, "layer_inp_normed", il);
|
| 954 |
+
@@ -439,7 +445,7 @@ ggml_tensor * clip_graph::build_vit(
|
| 955 |
+
|
| 956 |
+
// build_attn returns a flat 2D [n_embd, n_pos*B]
|
| 957 |
+
cur = build_attn(layer.o_w, layer.o_b,
|
| 958 |
+
- Qcur, Kcur, Vcur, nullptr, kq_scale, il);
|
| 959 |
+
+ Qcur, Kcur, Vcur, attn_mask, kq_scale, il);
|
| 960 |
+
cb(cur, "attn_out", il);
|
| 961 |
+
}
|
| 962 |
+
|
| 963 |
+
@@ -897,6 +903,10 @@ static ggml_cgraph * clip_image_build_graph(clip_ctx * ctx, const clip_image_f32
|
| 964 |
+
{
|
| 965 |
+
builder = std::make_unique<clip_graph_mimovl>(ctx, img);
|
| 966 |
+
} break;
|
| 967 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 968 |
+
+ {
|
| 969 |
+
+ builder = std::make_unique<clip_graph_muse_glimmer>(ctx, img);
|
| 970 |
+
+ } break;
|
| 971 |
+
case PROJECTOR_TYPE_STEP3VL:
|
| 972 |
+
{
|
| 973 |
+
builder = std::make_unique<clip_graph_step3vl>(ctx, img);
|
| 974 |
+
@@ -1435,6 +1445,19 @@ struct clip_model_loader {
|
| 975 |
+
LOG_WRN("%s: more info: https://github.com/ggml-org/llama.cpp/issues/16842\n\n", __func__);
|
| 976 |
+
}
|
| 977 |
+
} break;
|
| 978 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 979 |
+
+ {
|
| 980 |
+
+ hparams.n_merge = 2; // pixel-shuffle downsample after the ViT
|
| 981 |
+
+ // This ROCmFPX base predates the Lanczos resize enum; Pillow-style
|
| 982 |
+
+ // bicubic is the closest available high-quality dynamic resize.
|
| 983 |
+
+ hparams.image_resize_algo = RESIZE_ALGO_BICUBIC_PILLOW;
|
| 984 |
+
+ hparams.rope_theta = 10000.0f;
|
| 985 |
+
+ hparams.muse_glimmer_patch_temporal = 2;
|
| 986 |
+
+ hparams.muse_glimmer_sparse_factor = 4; // 3 sparse layers + 1 global, repeating
|
| 987 |
+
+ get_u32(KEY_SPATIAL_MERGE_SIZE, hparams.n_merge, false);
|
| 988 |
+
+ hparams.set_limit_image_tokens(1, 4096);
|
| 989 |
+
+ hparams.set_warmup_n_tokens(32*32);
|
| 990 |
+
+ } break;
|
| 991 |
+
case PROJECTOR_TYPE_MIMOVL:
|
| 992 |
+
{
|
| 993 |
+
hparams.n_merge = 2; // spatial_merge_size
|
| 994 |
+
@@ -1994,6 +2017,13 @@ struct clip_model_loader {
|
| 995 |
+
model.mm_1_w = get_tensor(string_format(TN_LLAVA_PROJ, 2, "weight"));
|
| 996 |
+
model.mm_1_b = get_tensor(string_format(TN_LLAVA_PROJ, 2, "bias"), false);
|
| 997 |
+
} break;
|
| 998 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 999 |
+
+ {
|
| 1000 |
+
+ // 3-linear MLP: fc -> erf-GELU -> proj -> erf-GELU -> vision_proj (into LLM residual dim)
|
| 1001 |
+
+ model.mm_0_w = get_tensor(string_format(TN_LLAVA_PROJ, 0, "weight"));
|
| 1002 |
+
+ model.mm_1_w = get_tensor(string_format(TN_LLAVA_PROJ, 1, "weight"));
|
| 1003 |
+
+ model.mm_2_w = get_tensor(string_format(TN_LLAVA_PROJ, 2, "weight"));
|
| 1004 |
+
+ } break;
|
| 1005 |
+
case PROJECTOR_TYPE_STEP3VL:
|
| 1006 |
+
{
|
| 1007 |
+
model.mm_0_w = get_tensor(string_format(TN_LLAVA_PROJ, 0, "weight"));
|
| 1008 |
+
@@ -3120,6 +3150,7 @@ int clip_n_output_tokens_x(const struct clip_ctx * ctx, struct clip_image_f32 *
|
| 1009 |
+
case PROJECTOR_TYPE_HUNYUANOCR:
|
| 1010 |
+
case PROJECTOR_TYPE_HUNYUANVL:
|
| 1011 |
+
case PROJECTOR_TYPE_YOUTUVL:
|
| 1012 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1013 |
+
return (img->nx / params.patch_size) / 2;
|
| 1014 |
+
case PROJECTOR_TYPE_STEP3VL:
|
| 1015 |
+
return img->nx / (params.patch_size * params.n_merge);
|
| 1016 |
+
@@ -3141,6 +3172,7 @@ int clip_n_output_tokens_y(const struct clip_ctx * ctx, struct clip_image_f32 *
|
| 1017 |
+
case PROJECTOR_TYPE_PADDLEOCR:
|
| 1018 |
+
case PROJECTOR_TYPE_HUNYUANVL:
|
| 1019 |
+
case PROJECTOR_TYPE_YOUTUVL:
|
| 1020 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1021 |
+
return (img->ny / params.patch_size) / 2;
|
| 1022 |
+
case PROJECTOR_TYPE_STEP3VL:
|
| 1023 |
+
return img->ny / (params.patch_size * params.n_merge);
|
| 1024 |
+
@@ -3218,6 +3250,7 @@ int clip_n_output_tokens(const struct clip_ctx * ctx, struct clip_image_f32 * im
|
| 1025 |
+
case PROJECTOR_TYPE_MIMOVL:
|
| 1026 |
+
case PROJECTOR_TYPE_GLM4V:
|
| 1027 |
+
case PROJECTOR_TYPE_YOUTUVL:
|
| 1028 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1029 |
+
{
|
| 1030 |
+
// dynamic size (2 conv, so double patch size)
|
| 1031 |
+
int x_patch = img->nx / (params.patch_size * 2);
|
| 1032 |
+
@@ -3441,6 +3474,15 @@ bool clip_image_batch_encode(clip_ctx * ctx, const int n_threads, const clip_ima
|
| 1033 |
+
ggml_backend_tensor_set(cur, values.data(), 0, ggml_nbytes(cur));
|
| 1034 |
+
};
|
| 1035 |
+
|
| 1036 |
+
+ auto set_input_f16 = [&get_inp_tensor](const char * name, const std::vector<float> & values) {
|
| 1037 |
+
+ ggml_tensor * cur = get_inp_tensor(name);
|
| 1038 |
+
+ GGML_ASSERT(cur->type == GGML_TYPE_F16);
|
| 1039 |
+
+ GGML_ASSERT(ggml_nelements(cur) == (int64_t) values.size());
|
| 1040 |
+
+ std::vector<ggml_fp16_t> values_f16(values.size());
|
| 1041 |
+
+ ggml_fp32_to_fp16_row(values.data(), values_f16.data(), values.size());
|
| 1042 |
+
+ ggml_backend_tensor_set(cur, values_f16.data(), 0, ggml_nbytes(cur));
|
| 1043 |
+
+ };
|
| 1044 |
+
+
|
| 1045 |
+
auto set_input_i32 = [&get_inp_tensor](const char * name, std::vector<int32_t> & values) {
|
| 1046 |
+
ggml_tensor * cur = get_inp_tensor(name);
|
| 1047 |
+
GGML_ASSERT(cur->type == GGML_TYPE_I32);
|
| 1048 |
+
@@ -3500,6 +3542,70 @@ bool clip_image_batch_encode(clip_ctx * ctx, const int n_threads, const clip_ima
|
| 1049 |
+
|
| 1050 |
+
// set input per projector
|
| 1051 |
+
switch (ctx->model.proj_type) {
|
| 1052 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1053 |
+
+ {
|
| 1054 |
+
+ const int grid_w = pos_w; // image_size_width / patch_size
|
| 1055 |
+
+ const int grid_h = pos_h; // image_size_height / patch_size
|
| 1056 |
+
+ const int n_tok = grid_w * grid_h;
|
| 1057 |
+
+ const int pgrid = (int) std::sqrt((double) ctx->model.position_embeddings->ne[1]); // 32
|
| 1058 |
+
+ const int f = hparams.n_merge; // downsample 2
|
| 1059 |
+
+
|
| 1060 |
+
+ // pixel patchify runs inside the graph via build_inp() (ggml_conv_2d);
|
| 1061 |
+
+ // pos-emb bilinear interp via resize_position_embeddings().
|
| 1062 |
+
+
|
| 1063 |
+
+ // --- sparse window grouping (pgrid x pgrid windows) ---
|
| 1064 |
+
+ const int win = pgrid;
|
| 1065 |
+
+ const int nwin_h = (grid_h + win - 1) / win;
|
| 1066 |
+
+ const int nwin_w = (grid_w + win - 1) / win;
|
| 1067 |
+
+ std::vector<int32_t> sp_perm; sp_perm.reserve(n_tok);
|
| 1068 |
+
+ std::vector<int> sp_slens;
|
| 1069 |
+
+ for (int wy = 0; wy < nwin_h; wy++) {
|
| 1070 |
+
+ for (int wx = 0; wx < nwin_w; wx++) {
|
| 1071 |
+
+ int cnt = 0;
|
| 1072 |
+
+ for (int hh = 0; hh < win; hh++) {
|
| 1073 |
+
+ for (int ww = 0; ww < win; ww++) {
|
| 1074 |
+
+ const int gy = wy * win + hh;
|
| 1075 |
+
+ const int gx = wx * win + ww;
|
| 1076 |
+
+ if (gy < grid_h && gx < grid_w) { sp_perm.push_back(gy * grid_w + gx); cnt++; }
|
| 1077 |
+
+ }
|
| 1078 |
+
+ }
|
| 1079 |
+
+ if (cnt > 0) sp_slens.push_back(cnt);
|
| 1080 |
+
+ }
|
| 1081 |
+
+ }
|
| 1082 |
+
+ std::vector<int32_t> rpos_w(n_tok), rpos_h(n_tok), inv_perm(n_tok);
|
| 1083 |
+
+ for (int i = 0; i < n_tok; i++) {
|
| 1084 |
+
+ const int orig = sp_perm[i];
|
| 1085 |
+
+ rpos_w[i] = (orig % grid_w) + 1; // 1-indexed
|
| 1086 |
+
+ rpos_h[i] = (orig / grid_w) + 1;
|
| 1087 |
+
+ inv_perm[orig] = i;
|
| 1088 |
+
+ }
|
| 1089 |
+
+ set_input_i32("muse_glimmer_sp_perm", sp_perm);
|
| 1090 |
+
+ set_input_i32("muse_glimmer_inv_perm", inv_perm);
|
| 1091 |
+
+ set_input_i32("muse_glimmer_pos_w", rpos_w);
|
| 1092 |
+
+ set_input_i32("muse_glimmer_pos_h", rpos_h);
|
| 1093 |
+
+
|
| 1094 |
+
+ // block-diagonal window mask (permuted order)
|
| 1095 |
+
+ std::vector<float> sp_mask((size_t) n_tok * n_tok, -INFINITY);
|
| 1096 |
+
+ {
|
| 1097 |
+
+ int off = 0;
|
| 1098 |
+
+ for (int s : sp_slens) {
|
| 1099 |
+
+ for (int a = 0; a < s; a++)
|
| 1100 |
+
+ for (int b = 0; b < s; b++)
|
| 1101 |
+
+ sp_mask[(size_t) (off + a) * n_tok + (off + b)] = 0.0f;
|
| 1102 |
+
+ off += s;
|
| 1103 |
+
+ }
|
| 1104 |
+
+ }
|
| 1105 |
+
+ set_input_f16("muse_glimmer_sp_mask", sp_mask);
|
| 1106 |
+
+
|
| 1107 |
+
+ // pixel-shuffle gather (original order): f*f spatial neighbours grouped
|
| 1108 |
+
+ std::vector<int32_t> dsp; dsp.reserve(n_tok);
|
| 1109 |
+
+ for (int oy = 0; oy < grid_h / f; oy++)
|
| 1110 |
+
+ for (int ox = 0; ox < grid_w / f; ox++)
|
| 1111 |
+
+ for (int ry = 0; ry < f; ry++)
|
| 1112 |
+
+ for (int rx = 0; rx < f; rx++)
|
| 1113 |
+
+ dsp.push_back((oy * f + ry) * grid_w + (ox * f + rx));
|
| 1114 |
+
+ set_input_i32("muse_glimmer_ds_perm", dsp);
|
| 1115 |
+
+ } break;
|
| 1116 |
+
case PROJECTOR_TYPE_MINICPMV:
|
| 1117 |
+
{
|
| 1118 |
+
// inspired from siglip:
|
| 1119 |
+
@@ -4273,6 +4379,8 @@ int clip_n_mmproj_embd(const struct clip_ctx * ctx) {
|
| 1120 |
+
return ctx->model.mm_ffn_down_w->ne[1];
|
| 1121 |
+
case PROJECTOR_TYPE_GLM_EDGE:
|
| 1122 |
+
return ctx->model.mm_model_mlp_3_w->ne[1];
|
| 1123 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1124 |
+
+ return ctx->model.mm_2_w->ne[1];
|
| 1125 |
+
case PROJECTOR_TYPE_QWEN2VL:
|
| 1126 |
+
case PROJECTOR_TYPE_QWEN25VL:
|
| 1127 |
+
case PROJECTOR_TYPE_JANUS_PRO:
|
| 1128 |
+
diff --git a/tools/mtmd/models/models.h b/tools/mtmd/models/models.h
|
| 1129 |
+
index 33b485a4a..b91979db3 100644
|
| 1130 |
+
--- a/tools/mtmd/models/models.h
|
| 1131 |
+
+++ b/tools/mtmd/models/models.h
|
| 1132 |
+
@@ -201,3 +201,7 @@ struct clip_graph_kimik25 : clip_graph {
|
| 1133 |
+
|
| 1134 |
+
ggml_tensor * resize_position_embeddings_3d(uint32_t interpolation_mode);
|
| 1135 |
+
};
|
| 1136 |
+
+struct clip_graph_muse_glimmer : clip_graph {
|
| 1137 |
+
+ clip_graph_muse_glimmer(clip_ctx * ctx, const clip_image_f32 & img) : clip_graph(ctx, img) {}
|
| 1138 |
+
+ ggml_cgraph * build() override;
|
| 1139 |
+
+};
|
| 1140 |
+
diff --git a/tools/mtmd/models/muse-glimmer.cpp b/tools/mtmd/models/muse-glimmer.cpp
|
| 1141 |
+
new file mode 100644
|
| 1142 |
+
index 000000000..d2f0a7e02
|
| 1143 |
+
--- /dev/null
|
| 1144 |
+
+++ b/tools/mtmd/models/muse-glimmer.cpp
|
| 1145 |
+
@@ -0,0 +1,90 @@
|
| 1146 |
+
+#include "models.h"
|
| 1147 |
+
+
|
| 1148 |
+
+// MuseGlimmer vision encoder: 50-layer ViT with 2D RoPE, sparse block-diagonal
|
| 1149 |
+
+// window attention (every 4th + last layer global), pixel-shuffle downsample, then
|
| 1150 |
+
+// adapter MLP + LLM's vision_projection.
|
| 1151 |
+
+//
|
| 1152 |
+
+// Several quantities are precomputed on host and fed as named graph inputs (filled in
|
| 1153 |
+
+// clip.cpp set_input, PROJECTOR_TYPE_MUSE_GLIMMER branch):
|
| 1154 |
+
+// muse_glimmer_pos_w/_h [n_tok] i32 : 1-indexed RoPE positions (sparse-permuted order)
|
| 1155 |
+
+// muse_glimmer_sp_perm [n_tok] i32 : window grouping permutation (applied after ln_pre)
|
| 1156 |
+
+// muse_glimmer_inv_perm [n_tok] i32 : inverse of sp_perm (applied after blocks)
|
| 1157 |
+
+// muse_glimmer_ds_perm [n_tok] i32 : pixel-shuffle gather (original order)
|
| 1158 |
+
+// muse_glimmer_sp_mask [n_tok, n_tok] f16 : block-diagonal window mask (sparse layers)
|
| 1159 |
+
+ggml_cgraph * clip_graph_muse_glimmer::build() {
|
| 1160 |
+
+ const int ds = hparams.n_merge; // downsample factor (2)
|
| 1161 |
+
+ const int sf = hparams.muse_glimmer_sparse_factor; // 4
|
| 1162 |
+
+ const int n_tok = n_patches;
|
| 1163 |
+
+ const int n_out = (n_patches_x / ds) * (n_patches_y / ds);
|
| 1164 |
+
+ const float rope_base = hparams.rope_theta; // 10000
|
| 1165 |
+
+
|
| 1166 |
+
+ auto inp_i32 = [&](const char * name, int64_t n) {
|
| 1167 |
+
+ ggml_tensor * t = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n);
|
| 1168 |
+
+ ggml_set_name(t, name);
|
| 1169 |
+
+ ggml_set_input(t);
|
| 1170 |
+
+ return t;
|
| 1171 |
+
+ };
|
| 1172 |
+
+
|
| 1173 |
+
+ ggml_tensor * pos_w = inp_i32("muse_glimmer_pos_w", n_tok);
|
| 1174 |
+
+ ggml_tensor * pos_h = inp_i32("muse_glimmer_pos_h", n_tok);
|
| 1175 |
+
+ ggml_tensor * sp_perm = inp_i32("muse_glimmer_sp_perm", n_tok);
|
| 1176 |
+
+ ggml_tensor * inv_perm = inp_i32("muse_glimmer_inv_perm", n_tok);
|
| 1177 |
+
+ ggml_tensor * ds_perm = inp_i32("muse_glimmer_ds_perm", n_tok);
|
| 1178 |
+
+
|
| 1179 |
+
+ // This ROCmFPX base predates the F32 flash-attention mask support used by
|
| 1180 |
+
+ // current upstream, so keep the same values in the required F16 format.
|
| 1181 |
+
+ ggml_tensor * sp_mask = ggml_new_tensor_2d(ctx0, GGML_TYPE_F16, n_tok, n_tok);
|
| 1182 |
+
+ ggml_set_name(sp_mask, "muse_glimmer_sp_mask");
|
| 1183 |
+
+ ggml_set_input(sp_mask);
|
| 1184 |
+
+
|
| 1185 |
+
+ // patchify via build_inp (conv2d over raw pixels) + bilinear-resized learned pos-emb
|
| 1186 |
+
+ ggml_tensor * x = build_inp(); // [n_embd, n_tok, 1]
|
| 1187 |
+
+ x = ggml_add(ctx0, x, resize_position_embeddings(GGML_SCALE_MODE_BILINEAR));
|
| 1188 |
+
+ cb(x, "after_posemb", -1);
|
| 1189 |
+
+
|
| 1190 |
+
+ // group patches into pgrid x pgrid windows (sparse attention order)
|
| 1191 |
+
+ x = ggml_get_rows(ctx0, x, sp_perm);
|
| 1192 |
+
+ cb(x, "after_sp_perm", -1);
|
| 1193 |
+
+
|
| 1194 |
+
+ // per-layer mask: sparse layers get sp_mask, global layers (every sf-th and last) get none
|
| 1195 |
+
+ std::vector<ggml_tensor *> attn_mask_layers(n_layer);
|
| 1196 |
+
+ for (int il = 0; il < n_layer; ++il) {
|
| 1197 |
+
+ const bool is_global = (il == n_layer - 1) || ((il + 1) % sf == 0);
|
| 1198 |
+
+ attn_mask_layers[il] = is_global ? nullptr : sp_mask;
|
| 1199 |
+
+ }
|
| 1200 |
+
+
|
| 1201 |
+
+ // 2D RoPE: first half of head_dim uses width pos, second half uses height pos
|
| 1202 |
+
+ auto add_pos = [&](ggml_tensor * cur, const clip_layer &) {
|
| 1203 |
+
+ return build_rope_2d(ctx0, cur, pos_w, pos_h, rope_base, false);
|
| 1204 |
+
+ };
|
| 1205 |
+
+
|
| 1206 |
+
+ build_vit_opts opts;
|
| 1207 |
+
+ opts.attn_mask_layers = std::move(attn_mask_layers);
|
| 1208 |
+
+
|
| 1209 |
+
+ // pre_ln, per-layer transformer, post_ln (all inside build_vit); reference uses exact (erf) GELU
|
| 1210 |
+
+ x = build_vit(x, n_tok, NORM_TYPE_NORMAL, FFN_GELU_ERF, nullptr, add_pos, opts);
|
| 1211 |
+
+
|
| 1212 |
+
+ // un-permute back to original grid order
|
| 1213 |
+
+ x = ggml_get_rows(ctx0, x, inv_perm);
|
| 1214 |
+
+ cb(x, "after_inv_perm", -1);
|
| 1215 |
+
+
|
| 1216 |
+
+ // pixel-shuffle downsample: gather f*f spatial neighbors then concat channel-outer.
|
| 1217 |
+
+ // out[c*(ds*ds)+s, o] = x[ds_perm gathered][o*(ds*ds)+s, c]
|
| 1218 |
+
+ x = ggml_get_rows(ctx0, x, ds_perm); // [n_embd, n_tok], grouped
|
| 1219 |
+
+ x = ggml_reshape_3d(ctx0, x, n_embd, ds * ds, n_out);// [c, s, o]
|
| 1220 |
+
+ x = ggml_permute(ctx0, x, 1, 0, 2, 3); // [s, c, o]
|
| 1221 |
+
+ x = ggml_cont(ctx0, x);
|
| 1222 |
+
+ x = ggml_reshape_2d(ctx0, x, n_embd * ds * ds, n_out); // [6144, n_out]
|
| 1223 |
+
+ cb(x, "encoder_out", -1);
|
| 1224 |
+
+
|
| 1225 |
+
+ // adapter (6144->4096->4096, exact GELU each) + LLM vision_projection (4096->6656)
|
| 1226 |
+
+ x = build_mm(model.mm_0_w, x);
|
| 1227 |
+
+ x = ggml_gelu_erf(ctx0, x);
|
| 1228 |
+
+ x = build_mm(model.mm_1_w, x);
|
| 1229 |
+
+ x = ggml_gelu_erf(ctx0, x);
|
| 1230 |
+
+ x = build_mm(model.mm_2_w, x); // [6656, n_out]
|
| 1231 |
+
+ cb(x, "projected", -1);
|
| 1232 |
+
+
|
| 1233 |
+
+ ggml_build_forward_expand(gf, x);
|
| 1234 |
+
+ return gf;
|
| 1235 |
+
+}
|
| 1236 |
+
diff --git a/tools/mtmd/mtmd-image.cpp b/tools/mtmd/mtmd-image.cpp
|
| 1237 |
+
index 1b058e026..dff9c1bd0 100644
|
| 1238 |
+
--- a/tools/mtmd/mtmd-image.cpp
|
| 1239 |
+
+++ b/tools/mtmd/mtmd-image.cpp
|
| 1240 |
+
@@ -1427,3 +1427,68 @@ bool mtmd_image_preprocessor_youtuvl::preprocess(const clip_image_u8 & img, clip
|
| 1241 |
+
output.entries.push_back(std::move(img_f32));
|
| 1242 |
+
return true;
|
| 1243 |
+
}
|
| 1244 |
+
+
|
| 1245 |
+
+//
|
| 1246 |
+
+// mtmd_image_preprocessor_muse_glimmer
|
| 1247 |
+
+//
|
| 1248 |
+
+
|
| 1249 |
+
+// Replicates transformers' get_aspect_ratio_preserving_size
|
| 1250 |
+
+static clip_image_size muse_glimmer_grid_size(int img_w, int img_h, int patch_hw, int max_tokens) {
|
| 1251 |
+
+ double i_nph = (double) img_h / patch_hw;
|
| 1252 |
+
+ double i_npw = (double) img_w / patch_hw;
|
| 1253 |
+
+ const double ratio = i_nph > 0.0 ? i_npw / i_nph : 1.0;
|
| 1254 |
+
+ if (i_nph * i_npw > (double) max_tokens) {
|
| 1255 |
+
+ i_nph = std::sqrt((double) max_tokens / ratio);
|
| 1256 |
+
+ i_npw = i_nph * ratio;
|
| 1257 |
+
+ }
|
| 1258 |
+
+ const int hs[2] = { (int) std::floor(i_nph), (int) std::ceil(i_nph) };
|
| 1259 |
+
+ const int ws[2] = { (int) std::floor(i_npw), (int) std::ceil(i_npw) };
|
| 1260 |
+
+ const double target_ar = (double) img_h / (double) img_w;
|
| 1261 |
+
+ int best_nph = -1;
|
| 1262 |
+
+ int best_npw = -1;
|
| 1263 |
+
+ double best_d = 0.0;
|
| 1264 |
+
+ for (int a = 0; a < 2; ++a) {
|
| 1265 |
+
+ for (int b = 0; b < 2; ++b) {
|
| 1266 |
+
+ const int nph = hs[a];
|
| 1267 |
+
+ const int npw = ws[b];
|
| 1268 |
+
+ if (nph < 1 || npw < 1 || nph * npw > max_tokens) {
|
| 1269 |
+
+ continue;
|
| 1270 |
+
+ }
|
| 1271 |
+
+ const double d = std::fabs((double) nph / (double) npw - target_ar);
|
| 1272 |
+
+ const int n_tokens = nph * npw;
|
| 1273 |
+
+ const int best_n_tokens = best_nph * best_npw;
|
| 1274 |
+
+ if (best_nph < 0 || d < best_d || (d == best_d && n_tokens > best_n_tokens)) {
|
| 1275 |
+
+ best_nph = nph;
|
| 1276 |
+
+ best_npw = npw;
|
| 1277 |
+
+ best_d = d;
|
| 1278 |
+
+ }
|
| 1279 |
+
+ }
|
| 1280 |
+
+ }
|
| 1281 |
+
+ if (best_nph < 0) { // no candidate fit under the cap: round and clamp
|
| 1282 |
+
+ best_nph = std::max(1, (int) std::lround(i_nph));
|
| 1283 |
+
+ best_npw = std::max(1, (int) std::lround(i_npw));
|
| 1284 |
+
+ }
|
| 1285 |
+
+ return clip_image_size{ best_npw * patch_hw, best_nph * patch_hw };
|
| 1286 |
+
+}
|
| 1287 |
+
+
|
| 1288 |
+
+bool mtmd_image_preprocessor_muse_glimmer::preprocess(
|
| 1289 |
+
+ const clip_image_u8 & img,
|
| 1290 |
+
+ clip_image_f32_batch & output) {
|
| 1291 |
+
+ const int patch_hw = hparams.patch_size * hparams.n_merge;
|
| 1292 |
+
+ const int patch_area = hparams.patch_size * hparams.patch_size * hparams.n_merge * hparams.n_merge;
|
| 1293 |
+
+ GGML_ASSERT(patch_area > 0 && hparams.image_max_pixels > 0);
|
| 1294 |
+
+ const int max_tokens = hparams.image_max_pixels / patch_area;
|
| 1295 |
+
+
|
| 1296 |
+
+ const clip_image_size original_size = { img.nx, img.ny };
|
| 1297 |
+
+ const clip_image_size target_size = muse_glimmer_grid_size(
|
| 1298 |
+
+ original_size.width, original_size.height, patch_hw, max_tokens);
|
| 1299 |
+
+
|
| 1300 |
+
+ // PIL resizes directly to (target_w, target_h) -- a stretch, no padding.
|
| 1301 |
+
+ clip_image_u8 resized_image;
|
| 1302 |
+
+ img_tool::resize(img, resized_image, target_size, hparams.image_resize_algo, false);
|
| 1303 |
+
+
|
| 1304 |
+
+ clip_image_f32_ptr img_f32(clip_image_f32_init());
|
| 1305 |
+
+ img_u8_to_f32(resized_image, *img_f32, hparams.image_mean, hparams.image_std);
|
| 1306 |
+
+ output.entries.push_back(std::move(img_f32));
|
| 1307 |
+
+ return true;
|
| 1308 |
+
+}
|
| 1309 |
+
diff --git a/tools/mtmd/mtmd-image.h b/tools/mtmd/mtmd-image.h
|
| 1310 |
+
index 08129a08e..fdfcaaf2c 100644
|
| 1311 |
+
--- a/tools/mtmd/mtmd-image.h
|
| 1312 |
+
+++ b/tools/mtmd/mtmd-image.h
|
| 1313 |
+
@@ -177,3 +177,9 @@ struct mtmd_image_preprocessor_youtuvl : mtmd_image_preprocessor {
|
| 1314 |
+
mtmd_image_preprocessor_youtuvl(const clip_ctx * ctx) : mtmd_image_preprocessor(ctx) {}
|
| 1315 |
+
bool preprocess(const clip_image_u8 & img, clip_image_f32_batch & output) override;
|
| 1316 |
+
};
|
| 1317 |
+
+
|
| 1318 |
+
+// pick the patch grid closest to the input aspect ratio under the per-image token cap, stretch-resize.
|
| 1319 |
+
+struct mtmd_image_preprocessor_muse_glimmer : mtmd_image_preprocessor {
|
| 1320 |
+
+ mtmd_image_preprocessor_muse_glimmer(const clip_ctx * ctx) : mtmd_image_preprocessor(ctx) {}
|
| 1321 |
+
+ bool preprocess(const clip_image_u8 & img, clip_image_f32_batch & output) override;
|
| 1322 |
+
+};
|
| 1323 |
+
diff --git a/tools/mtmd/mtmd.cpp b/tools/mtmd/mtmd.cpp
|
| 1324 |
+
index 8e3e5e013..9f7964828 100644
|
| 1325 |
+
--- a/tools/mtmd/mtmd.cpp
|
| 1326 |
+
+++ b/tools/mtmd/mtmd.cpp
|
| 1327 |
+
@@ -336,6 +336,12 @@ struct mtmd_context {
|
| 1328 |
+
img_end = "<|vision_end|>";
|
| 1329 |
+
image_preproc = std::make_unique<mtmd_image_preprocessor_dyn_size>(ctx_v);
|
| 1330 |
+
} break;
|
| 1331 |
+
+ case PROJECTOR_TYPE_MUSE_GLIMMER:
|
| 1332 |
+
+ {
|
| 1333 |
+
+ img_beg = "<|image_start|>";
|
| 1334 |
+
+ img_end = "<|image_end|>";
|
| 1335 |
+
+ image_preproc = std::make_unique<mtmd_image_preprocessor_muse_glimmer>(ctx_v);
|
| 1336 |
+
+ } break;
|
| 1337 |
+
case PROJECTOR_TYPE_YOUTUVL:
|
| 1338 |
+
{
|
| 1339 |
+
// <|vision_start|> ... (image embeddings) ... <|vision_end|>
|
SHA256SUMS
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
733b6eee371dfe2c49bd338dbf597bcdae1e5297de53cfa98f61a4d7e6e196ae Muse-Glimmer-30B-ROCmFP4.gguf
|
| 2 |
+
6b80db6b044d9a3999b539eec140dc4d29279d53c18f42f80a557d3e6736123a Muse-Glimmer-30B-ROCmFP4-Q6-QUALITY.gguf
|
| 3 |
+
74a5f469f17420d01a837400a2ef19d8191dcb1b479fd7c0be0c862901879915 Muse-Glimmer-30B-ROCmFP8.gguf
|
| 4 |
+
3727a47650ae65be12e91aae49f9b9f4bef09dc6c401a54ca1bd3d1a6160f35a mmproj-Muse-Glimmer-30B-BF16.gguf
|