Agnes-3.0-Flash-GGUF / build /convert_agnes.py
0xKitkat's picture
Upload provenance, runtime files and measured evaluations
35514b1 verified
Raw History Blame
4.95 kB
"""Convert Agnes with an exact SwiGLU branch merge into the Qwen3.5 GGUF graph.
Run with the same arguments as llama.cpp/convert_hf_to_gguf.py.
The HF checkpoint and its native architecture remain unchanged.
"""
import copy
import json
import runpy
import sys
from pathlib import Path
import torch
ROOT = Path(__file__).resolve().parent
LLAMA = ROOT / "llama.cpp"
sys.path.insert(0, str(LLAMA))
sys.path.insert(0, str(LLAMA / "gguf-py"))
import conversion
from conversion.base import ModelBase, gguf
from conversion.qwen import Qwen3_5TextModel
from conversion.qwen3vl import Qwen3VLVisionModel
@ModelBase.register("AgnesForConditionalGeneration")
class AgnesTextGGUF(Qwen3_5TextModel):
model_arch = gguf.MODEL_ARCH.QWEN35
def get_vocab_base(self):
from transformers import PreTrainedTokenizerFast
tokenizer = PreTrainedTokenizerFast.from_pretrained(self.dir_model, fix_mistral_regex=False)
pre = self.get_vocab_base_pre(tokenizer)
vocab = tokenizer.get_vocab()
size = self.hparams["vocab_size"]
if max(vocab.values()) >= size:
raise ValueError("Tokenizer exceeds embedding vocabulary")
reverse = {idx: token for token, idx in vocab.items()}
added = tokenizer.added_tokens_decoder
tokens, types = [], []
for idx in range(size):
token = reverse.get(idx, f"[PAD{idx}]")
kind = gguf.TokenType.NORMAL
if idx not in reverse:
kind = gguf.TokenType.UNUSED
elif idx in added:
if not added[idx].normalized:
token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False))
kind = gguf.TokenType.CONTROL if added[idx].special or self.does_token_look_special(token) else gguf.TokenType.USER_DEFINED
tokens.append(token)
types.append(kind)
return tokens, types, pre
def __init__(self, dir_model, *args, **kwargs):
config = copy.deepcopy(kwargs.get("hparams") or json.loads((dir_model / "config.json").read_text()))
text = config.get("text_config", config)
text["intermediate_size"] += text["parallel_ffn_intermediate_size"]
text["layer_types"] = [
{"agnes_delta_attention": "linear_attention", "agnes_global_attention": "full_attention"}[k]
for k in text["layer_types"]
]
text["full_attention_interval"] = text.get("global_attention_interval", 4)
text["mtp_num_hidden_layers"] = 0
kwargs["hparams"] = config
type(self).no_mtp = True
super().__init__(dir_model, *args, **kwargs)
@classmethod
def filter_tensors(cls, item):
name, gen = item
if name.startswith(("mtp.", "model.mtp.")):
return None
name = name.replace(".delta_attn.", ".linear_attn.").replace(".global_attn.", ".self_attn.")
return super().filter_tensors((name, gen))
def index_tensors(self, remote_hf_model_id=None):
tensors = super().index_tensors(remote_hf_model_id)
branches = [name for name in tensors if ".mlp.parallel_ffn." in name]
if len(branches) != 3 * self.hparams["text_config"]["num_hidden_layers"]:
raise ValueError("Expected exactly three parallel MLP matrices per decoder layer")
for branch in branches:
main = branch.replace(".parallel_ffn", "")
if main not in tensors:
raise ValueError(f"Missing main branch: {main}")
a, b = tensors[main], tensors.pop(branch)
dim = 1 if main.endswith("down_proj.weight") else 0
tensors[main] = lambda a=a, b=b, dim=dim: torch.cat((a(), b()), dim=dim)
return tensors
def get_vocab_base_pre(self, tokenizer):
backend = json.loads(tokenizer.backend_tokenizer.to_str())
expected = r"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?[\p{L}\p{M}]+|\p{N}| ?[^\s\p{L}\p{M}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+"
sequence = backend["pre_tokenizer"]
if sequence["type"] != "Sequence":
raise ValueError("Unexpected pre-tokenizer structure")
steps = sequence["pretokenizers"]
if len(steps) != 2 or steps[0].get("pattern", {}).get("Regex") != expected:
raise ValueError("Agnes pre-tokenizer differs from verified Qwen3.5 regex")
if steps[1]["type"] != "ByteLevel" or steps[1].get("add_prefix_space") or steps[1].get("use_regex"):
raise ValueError("Unexpected ByteLevel settings")
return "qwen35"
@ModelBase.register("AgnesForConditionalGeneration")
class AgnesVisionGGUF(Qwen3VLVisionModel):
model_arch = Qwen3VLVisionModel.model_arch
conversion.TEXT_MODEL_MAP["AgnesForConditionalGeneration"] = "qwen"
conversion.MMPROJ_MODEL_MAP["AgnesForConditionalGeneration"] = "qwen3vl"
if __name__ == "__main__":
runpy.run_path(str(LLAMA / "convert_hf_to_gguf.py"), run_name="__main__")