Upload 4 files
Browse files- hy3-llama-patch/0001-add-hyv3-support.patch +383 -0
- hy3-llama-patch/HF_README.md +165 -0
- hy3-llama-patch/README.md +268 -0
- hy3-llama-patch/src/models/hyv3.cpp +388 -0
hy3-llama-patch/0001-add-hyv3-support.patch
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
From: AngelSlim <hy3-ml@github>
|
| 2 |
+
Subject: [PATCH] Add Hy3 (hy_v3) architecture support to llama.cpp
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
Adds the Hunyuan V3 (hy_v3) MoE architecture:
|
| 6 |
+
- 295B MoE, 81 layers, 192 experts (8 active)
|
| 7 |
+
- MTP (Multi-Token Prediction) support
|
| 8 |
+
- Sigmoid expert gating with correction bias
|
| 9 |
+
- Shared expert + routed MoE FFN
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
diff --git a/common/chat.cpp b/common/chat.cpp
|
| 13 |
+
index 24e58ab06..56569383d 100644
|
| 14 |
+
--- a/common/chat.cpp
|
| 15 |
+
+++ b/common/chat.cpp
|
| 16 |
+
@@ -22,6 +22,7 @@
|
| 17 |
+
#include <functional>
|
| 18 |
+
|
| 19 |
+
#include <optional>
|
| 20 |
+
+#include <regex>
|
| 21 |
+
#include <sstream>
|
| 22 |
+
#include <stdexcept>
|
| 23 |
+
#include <string>
|
| 24 |
+
@@ -1608,6 +1609,178 @@ static common_chat_params common_chat_params_init_kimi_k2(const common_chat_temp
|
| 25 |
+
return data;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
+// Hunyuan V3 (hy_v3) parser.
|
| 29 |
+
+// Reasoning: <think{sfx}>{reasoning}</think{sfx}>
|
| 30 |
+
+// Tool calls (constructed / tagged, key-value split):
|
| 31 |
+
+// <tool_calls{sfx}>
|
| 32 |
+
+// <tool_call{sfx}>{func-name}<tool_sep{sfx}>
|
| 33 |
+
+// <arg_key{sfx}>{key}</arg_key{sfx}>
|
| 34 |
+
+// <arg_value{sfx}>{value}</arg_value{sfx}>
|
| 35 |
+
+// ...
|
| 36 |
+
+// </tool_call{sfx}>
|
| 37 |
+
+// </tool_calls{sfx}>
|
| 38 |
+
+// The special tokens carry a per-tokenizer suffix (e.g. ":opensource" or empty),
|
| 39 |
+
+// so we extract the concrete literals from the template source rather than
|
| 40 |
+
+// hard-coding them.
|
| 41 |
+
+static common_chat_params common_chat_params_init_hunyuan_v3(const common_chat_template & tmpl,
|
| 42 |
+
+ const autoparser::generation_params & inputs) {
|
| 43 |
+
+ common_chat_params data;
|
| 44 |
+
+
|
| 45 |
+
+ const std::string & src = tmpl.source();
|
| 46 |
+
+
|
| 47 |
+
+ // Pull the concrete token literal assigned to a jinja variable, e.g.
|
| 48 |
+
+ // {%- set toolsep_token = '<tool_sep:opensource>' %}
|
| 49 |
+
+ auto extract_tok = [&src](const std::string & var, const std::string & fallback) -> std::string {
|
| 50 |
+
+ std::smatch m;
|
| 51 |
+
+ std::regex re(var + R"(\s*=\s*'([^']*)')");
|
| 52 |
+
+ if (std::regex_search(src, m, re)) {
|
| 53 |
+
+ return m[1].str();
|
| 54 |
+
+ }
|
| 55 |
+
+ return fallback;
|
| 56 |
+
+ };
|
| 57 |
+
+
|
| 58 |
+
+ const std::string think_begin = extract_tok("think_begin_token", "<think>");
|
| 59 |
+
+ const std::string think_end = extract_tok("think_end_token", "</think>");
|
| 60 |
+
+ const std::string tcalls_begin = extract_tok("toolcalls_begin_token", "<tool_calls>");
|
| 61 |
+
+ const std::string tcalls_end = extract_tok("toolcalls_end_token", "</tool_calls>");
|
| 62 |
+
+ const std::string tcall_begin = extract_tok("toolcall_begin_token", "<tool_call>");
|
| 63 |
+
+ const std::string tcall_end = extract_tok("toolcall_end_token", "</tool_call>");
|
| 64 |
+
+ const std::string tsep = extract_tok("toolsep_token", "<tool_sep>");
|
| 65 |
+
+ const std::string argkey_begin = extract_tok("argkey_begin_token", "<arg_key>");
|
| 66 |
+
+ const std::string argkey_end = extract_tok("argkey_end_token", "</arg_key>");
|
| 67 |
+
+ const std::string argval_begin = extract_tok("argvalue_begin_token", "<arg_value>");
|
| 68 |
+
+ const std::string argval_end = extract_tok("argvalue_end_token", "</arg_value>");
|
| 69 |
+
+ const std::string assistant_tok = extract_tok("assistant_token", "");
|
| 70 |
+
+
|
| 71 |
+
+ auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
|
| 72 |
+
+ auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
|
| 73 |
+
+ auto include_grammar = has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE;
|
| 74 |
+
+
|
| 75 |
+
+ data.supports_thinking = true;
|
| 76 |
+
+ data.thinking_start_tag = think_begin;
|
| 77 |
+
+ data.thinking_end_tag = think_end;
|
| 78 |
+
+ data.prompt = common_chat_template_direct_apply_impl(tmpl, inputs);
|
| 79 |
+
+ data.generation_prompt = common_chat_template_generation_prompt_impl(tmpl, inputs);
|
| 80 |
+
+ data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;
|
| 81 |
+
+ data.preserved_tokens = {
|
| 82 |
+
+ think_begin, think_end,
|
| 83 |
+
+ tcalls_begin, tcalls_end,
|
| 84 |
+
+ tcall_begin, tcall_end, tsep,
|
| 85 |
+
+ argkey_begin, argkey_end,
|
| 86 |
+
+ argval_begin, argval_end,
|
| 87 |
+
+ };
|
| 88 |
+
+
|
| 89 |
+
+ if (inputs.has_continuation()) {
|
| 90 |
+
+ const auto & msg = inputs.continue_msg;
|
| 91 |
+
+
|
| 92 |
+
+ data.generation_prompt = think_begin + msg.reasoning_content;
|
| 93 |
+
+ if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {
|
| 94 |
+
+ data.generation_prompt += think_end + msg.render_content();
|
| 95 |
+
+ }
|
| 96 |
+
+
|
| 97 |
+
+ data.prompt += data.generation_prompt;
|
| 98 |
+
+ }
|
| 99 |
+
+
|
| 100 |
+
+ auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
|
| 101 |
+
+ // The generation prompt prefix that precedes the model's real output may
|
| 102 |
+
+ // still be present in the decoded text, in one of these shapes:
|
| 103 |
+
+ // {assistant} (default)
|
| 104 |
+
+ // {assistant}{think_begin} (reasoning_effort low/high)
|
| 105 |
+
+ // {assistant}{think_begin}{think_end}(reasoning_effort no_think)
|
| 106 |
+
+ // Consume an optional leading assistant token so it never leaks into content.
|
| 107 |
+
+ auto gen_prefix = assistant_tok.empty() ? p.eps() : p.optional(p.literal(assistant_tok));
|
| 108 |
+
+
|
| 109 |
+
+ // Reasoning block is optional: <think{sfx}>...</think{sfx}>
|
| 110 |
+
+ auto reasoning = extract_reasoning
|
| 111 |
+
+ ? p.optional(p.literal(think_begin) +
|
| 112 |
+
+ p.reasoning(p.until(think_end)) +
|
| 113 |
+
+ p.literal(think_end))
|
| 114 |
+
+ : p.eps();
|
| 115 |
+
+
|
| 116 |
+
+ // Content only (no tools)
|
| 117 |
+
+ if (!has_tools || inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE) {
|
| 118 |
+
+ include_grammar = false;
|
| 119 |
+
+ return gen_prefix + (reasoning << p.content(p.rest()));
|
| 120 |
+
+ }
|
| 121 |
+
+
|
| 122 |
+
+ // One tool call: <tool_call{sfx}>NAME<tool_sep{sfx}> {args} </tool_call{sfx}>
|
| 123 |
+
+ // where {args} is a sequence of
|
| 124 |
+
+ // <arg_key{sfx}>KEY</arg_key{sfx}> <arg_value{sfx}>VALUE</arg_value{sfx}>
|
| 125 |
+
+ auto tool_choice = p.choice();
|
| 126 |
+
+ foreach_function(inputs.tools, [&](const json & tool) {
|
| 127 |
+
+ const auto & function = tool.at("function");
|
| 128 |
+
+ std::string name = function.at("name");
|
| 129 |
+
+ const json & params = function.contains("parameters") ? function.at("parameters") : json::object();
|
| 130 |
+
+
|
| 131 |
+
+ // Build per-argument parsers restricted to schema-declared property names.
|
| 132 |
+
+ auto args = p.eps();
|
| 133 |
+
+ if (params.contains("properties") && !params.at("properties").empty()) {
|
| 134 |
+
+ auto arg_choice = p.choice();
|
| 135 |
+
+ for (const auto & el : params.at("properties").items()) {
|
| 136 |
+
+ const std::string & prop = el.key();
|
| 137 |
+
+ // Values for string-typed params are literal text (must be quoted in
|
| 138 |
+
+ // the emitted JSON); other types are parsed as JSON/python scalars.
|
| 139 |
+
+ std::string ptype;
|
| 140 |
+
+ if (el.value().is_object() && el.value().contains("type") &&
|
| 141 |
+
+ el.value().at("type").is_string()) {
|
| 142 |
+
+ ptype = el.value().at("type").get<std::string>();
|
| 143 |
+
+ }
|
| 144 |
+
+ auto value_node = (ptype == "string")
|
| 145 |
+
+ ? p.tool_arg_string_value(p.until(argval_end))
|
| 146 |
+
+ : p.tool_arg_value(p.until(argval_end));
|
| 147 |
+
+ arg_choice |= p.tool_arg(
|
| 148 |
+
+ p.tool_arg_open(p.literal(argkey_begin)) +
|
| 149 |
+
+ p.tool_arg_name(p.literal(prop)) +
|
| 150 |
+
+ p.literal(argkey_end) + p.space() +
|
| 151 |
+
+ p.literal(argval_begin) +
|
| 152 |
+
+ value_node +
|
| 153 |
+
+ p.tool_arg_close(p.literal(argval_end)));
|
| 154 |
+
+ }
|
| 155 |
+
+ args = p.zero_or_more(arg_choice + p.space());
|
| 156 |
+
+ }
|
| 157 |
+
+
|
| 158 |
+
+ auto tool_parser = p.tool(
|
| 159 |
+
+ p.tool_open(p.literal(tcall_begin) + p.tool_name(p.literal(name)) + p.literal(tsep)) +
|
| 160 |
+
+ p.space() + p.tool_args(args) + p.space() +
|
| 161 |
+
+ p.tool_close(p.literal(tcall_end)));
|
| 162 |
+
+
|
| 163 |
+
+ tool_choice |= p.rule("tool-" + name, tool_parser);
|
| 164 |
+
+ });
|
| 165 |
+
+
|
| 166 |
+
+ auto max_calls = inputs.parallel_tool_calls ? -1 : 1;
|
| 167 |
+
+ auto min_calls = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ? 1 : 0;
|
| 168 |
+
+ // Section: <tool_calls{sfx}> (one or more tool calls) </tool_calls{sfx}>
|
| 169 |
+
+ auto tool_calls = p.trigger_rule("tool-call",
|
| 170 |
+
+ p.literal(tcalls_begin) + p.space() +
|
| 171 |
+
+ p.repeat(tool_choice + p.space(), min_calls, max_calls) +
|
| 172 |
+
+ p.optional(p.literal(tcalls_end)));
|
| 173 |
+
+
|
| 174 |
+
+ auto content_before_tools = p.content(p.until(tcalls_begin));
|
| 175 |
+
+
|
| 176 |
+
+ return gen_prefix + (reasoning << content_before_tools << tool_calls);
|
| 177 |
+
+ });
|
| 178 |
+
+
|
| 179 |
+
+ data.parser = parser.save();
|
| 180 |
+
+
|
| 181 |
+
+ if (include_grammar) {
|
| 182 |
+
+ data.grammar_lazy = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO;
|
| 183 |
+
+ data.grammar = build_grammar([&](const common_grammar_builder & builder) {
|
| 184 |
+
+ foreach_function(inputs.tools, [&](const json & tool) {
|
| 185 |
+
+ const auto & function = tool.at("function");
|
| 186 |
+
+ auto schema = function.at("parameters");
|
| 187 |
+
+ builder.resolve_refs(schema);
|
| 188 |
+
+ });
|
| 189 |
+
+ parser.build_grammar(builder, data.grammar_lazy);
|
| 190 |
+
+ });
|
| 191 |
+
+
|
| 192 |
+
+ data.grammar_triggers = {
|
| 193 |
+
+ { COMMON_GRAMMAR_TRIGGER_TYPE_WORD, tcalls_begin }
|
| 194 |
+
+ };
|
| 195 |
+
+ }
|
| 196 |
+
+
|
| 197 |
+
+ return data;
|
| 198 |
+
+}
|
| 199 |
+
+
|
| 200 |
+
// LFM2/LFM2.5 parser. Tool calls are almost Python-style and parallel-capable
|
| 201 |
+
// (except dotted names and JSON literals true/false/null).
|
| 202 |
+
// Always wrapped in <|tool_call_start|>[name(args)]<|tool_call_end|> with optional <think> reasoning.
|
| 203 |
+
@@ -2197,6 +2370,16 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
|
| 204 |
+
return common_chat_params_init_ministral_3(tmpl, params);
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
+ // Hunyuan V3 (hy_v3) - key/value split tagged tool calls with per-tokenizer
|
| 208 |
+
+ // suffixed special tokens. Detection: template defines the hy_v3-specific
|
| 209 |
+
+ // arg_key/arg_value and tool_sep jinja variables (suffix-independent).
|
| 210 |
+
+ if (src.find("argkey_begin_token") != std::string::npos &&
|
| 211 |
+
+ src.find("argvalue_begin_token") != std::string::npos &&
|
| 212 |
+
+ src.find("toolsep_token") != std::string::npos) {
|
| 213 |
+
+ LOG_DBG("Using specialized template: Hunyuan V3\n");
|
| 214 |
+
+ return common_chat_params_init_hunyuan_v3(tmpl, params);
|
| 215 |
+
+ }
|
| 216 |
+
+
|
| 217 |
+
// GPT-OSS - has unique channel-based structure that needs dedicated handler
|
| 218 |
+
if (src.find("<|channel|>") != std::string::npos) {
|
| 219 |
+
LOG_DBG("Using specialized template: GPT-OSS\n");
|
| 220 |
+
diff --git a/conversion/__init__.py b/conversion/__init__.py
|
| 221 |
+
index 18162976f..fbc701974 100644
|
| 222 |
+
--- a/conversion/__init__.py
|
| 223 |
+
+++ b/conversion/__init__.py
|
| 224 |
+
@@ -101,6 +101,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
| 225 |
+
"HunYuanDenseV1ForCausalLM": "hunyuan",
|
| 226 |
+
"HunYuanMoEV1ForCausalLM": "hunyuan",
|
| 227 |
+
"HunYuanVLForConditionalGeneration": "hunyuan",
|
| 228 |
+
+ "HYV3ForCausalLM": "hyv3",
|
| 229 |
+
"IQuestCoderForCausalLM": "llama",
|
| 230 |
+
"InternLM2ForCausalLM": "internlm",
|
| 231 |
+
"InternLM3ForCausalLM": "internlm",
|
| 232 |
+
diff --git a/gguf-py/gguf/__init__.py b/gguf-py/gguf/__init__.py
|
| 233 |
+
index 243defc4c..a39401789 100644
|
| 234 |
+
--- a/gguf-py/gguf/__init__.py
|
| 235 |
+
+++ b/gguf-py/gguf/__init__.py
|
| 236 |
+
@@ -7,3 +7,5 @@ from .tensor_mapping import *
|
| 237 |
+
from .vocab import *
|
| 238 |
+
from .utility import *
|
| 239 |
+
from .metadata import *
|
| 240 |
+
+
|
| 241 |
+
+__version__ = "0.19.0"
|
| 242 |
+
diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py
|
| 243 |
+
index bd6246137..099e6bb0c 100644
|
| 244 |
+
--- a/gguf-py/gguf/constants.py
|
| 245 |
+
+++ b/gguf-py/gguf/constants.py
|
| 246 |
+
@@ -492,6 +492,7 @@ class MODEL_ARCH(IntEnum):
|
| 247 |
+
ERNIE4_5 = auto()
|
| 248 |
+
ERNIE4_5_MOE = auto()
|
| 249 |
+
HUNYUAN_MOE = auto()
|
| 250 |
+
+ HYV3 = auto()
|
| 251 |
+
HUNYUAN_DENSE = auto()
|
| 252 |
+
HUNYUAN_VL = auto()
|
| 253 |
+
SMOLLM3 = auto()
|
| 254 |
+
@@ -1042,6 +1043,7 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
| 255 |
+
MODEL_ARCH.ERNIE4_5_MOE: "ernie4_5-moe",
|
| 256 |
+
MODEL_ARCH.FALCON_H1: "falcon-h1",
|
| 257 |
+
MODEL_ARCH.HUNYUAN_MOE: "hunyuan-moe",
|
| 258 |
+
+ MODEL_ARCH.HYV3: "hy_v3",
|
| 259 |
+
MODEL_ARCH.HUNYUAN_DENSE: "hunyuan-dense",
|
| 260 |
+
MODEL_ARCH.HUNYUAN_VL: "hunyuan_vl",
|
| 261 |
+
MODEL_ARCH.SMOLLM3: "smollm3",
|
| 262 |
+
@@ -3755,6 +3757,37 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
| 263 |
+
MODEL_TENSOR.FFN_DOWN_SHEXP,
|
| 264 |
+
MODEL_TENSOR.FFN_UP_SHEXP,
|
| 265 |
+
],
|
| 266 |
+
+ MODEL_ARCH.HYV3: [
|
| 267 |
+
+ MODEL_TENSOR.TOKEN_EMBD,
|
| 268 |
+
+ MODEL_TENSOR.OUTPUT_NORM,
|
| 269 |
+
+ MODEL_TENSOR.OUTPUT,
|
| 270 |
+
+ MODEL_TENSOR.ATTN_NORM,
|
| 271 |
+
+ MODEL_TENSOR.ATTN_Q,
|
| 272 |
+
+ MODEL_TENSOR.ATTN_Q_NORM,
|
| 273 |
+
+ MODEL_TENSOR.ATTN_K,
|
| 274 |
+
+ MODEL_TENSOR.ATTN_K_NORM,
|
| 275 |
+
+ MODEL_TENSOR.ATTN_V,
|
| 276 |
+
+ MODEL_TENSOR.ATTN_OUT,
|
| 277 |
+
+ MODEL_TENSOR.FFN_NORM,
|
| 278 |
+
+ MODEL_TENSOR.FFN_GATE,
|
| 279 |
+
+ MODEL_TENSOR.FFN_DOWN,
|
| 280 |
+
+ MODEL_TENSOR.FFN_UP,
|
| 281 |
+
+ MODEL_TENSOR.FFN_GATE_INP,
|
| 282 |
+
+ MODEL_TENSOR.FFN_EXP_PROBS_B,
|
| 283 |
+
+ MODEL_TENSOR.FFN_GATE_EXP,
|
| 284 |
+
+ MODEL_TENSOR.FFN_DOWN_EXP,
|
| 285 |
+
+ MODEL_TENSOR.FFN_UP_EXP,
|
| 286 |
+
+ MODEL_TENSOR.FFN_GATE_UP_EXP,
|
| 287 |
+
+ MODEL_TENSOR.FFN_GATE_SHEXP,
|
| 288 |
+
+ MODEL_TENSOR.FFN_DOWN_SHEXP,
|
| 289 |
+
+ MODEL_TENSOR.FFN_UP_SHEXP,
|
| 290 |
+
+ MODEL_TENSOR.NEXTN_EH_PROJ,
|
| 291 |
+
+ MODEL_TENSOR.NEXTN_EMBED_TOKENS,
|
| 292 |
+
+ MODEL_TENSOR.NEXTN_ENORM,
|
| 293 |
+
+ MODEL_TENSOR.NEXTN_HNORM,
|
| 294 |
+
+ MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
|
| 295 |
+
+ MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
|
| 296 |
+
+ ],
|
| 297 |
+
MODEL_ARCH.HUNYUAN_DENSE: [
|
| 298 |
+
MODEL_TENSOR.TOKEN_EMBD,
|
| 299 |
+
MODEL_TENSOR.OUTPUT_NORM,
|
| 300 |
+
diff --git a/gguf-py/gguf/tensor_mapping.py b/gguf-py/gguf/tensor_mapping.py
|
| 301 |
+
index a9537983d..99cf5158a 100644
|
| 302 |
+
--- a/gguf-py/gguf/tensor_mapping.py
|
| 303 |
+
+++ b/gguf-py/gguf/tensor_mapping.py
|
| 304 |
+
@@ -2397,6 +2397,7 @@ class TensorNameMap:
|
| 305 |
+
|
| 306 |
+
MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM: (
|
| 307 |
+
"model.layers.{bid}.shared_head.norm",
|
| 308 |
+
+ "model.layers.{bid}.final_layernorm", # hy_v3 MTP
|
| 309 |
+
),
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
diff --git a/src/llama-arch.cpp b/src/llama-arch.cpp
|
| 313 |
+
index 6a5d5f8d2..888cafcb2 100644
|
| 314 |
+
--- a/src/llama-arch.cpp
|
| 315 |
+
+++ b/src/llama-arch.cpp
|
| 316 |
+
@@ -110,6 +110,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
| 317 |
+
{ LLM_ARCH_ERNIE4_5, "ernie4_5" },
|
| 318 |
+
{ LLM_ARCH_ERNIE4_5_MOE, "ernie4_5-moe" },
|
| 319 |
+
{ LLM_ARCH_HUNYUAN_MOE, "hunyuan-moe" },
|
| 320 |
+
+ { LLM_ARCH_HYV3, "hy_v3" },
|
| 321 |
+
{ LLM_ARCH_HUNYUAN_DENSE, "hunyuan-dense" },
|
| 322 |
+
{ LLM_ARCH_HUNYUAN_VL, "hunyuan_vl" },
|
| 323 |
+
{ LLM_ARCH_SMOLLM3, "smollm3" },
|
| 324 |
+
diff --git a/src/llama-arch.h b/src/llama-arch.h
|
| 325 |
+
index 03b1a265d..88c9236b5 100644
|
| 326 |
+
--- a/src/llama-arch.h
|
| 327 |
+
+++ b/src/llama-arch.h
|
| 328 |
+
@@ -114,6 +114,7 @@ enum llm_arch {
|
| 329 |
+
LLM_ARCH_ERNIE4_5,
|
| 330 |
+
LLM_ARCH_ERNIE4_5_MOE,
|
| 331 |
+
LLM_ARCH_HUNYUAN_MOE,
|
| 332 |
+
+ LLM_ARCH_HYV3,
|
| 333 |
+
LLM_ARCH_HUNYUAN_DENSE,
|
| 334 |
+
LLM_ARCH_HUNYUAN_VL,
|
| 335 |
+
LLM_ARCH_SMOLLM3,
|
| 336 |
+
diff --git a/src/llama-model.cpp b/src/llama-model.cpp
|
| 337 |
+
index 4f12e0949..a03b39ecc 100644
|
| 338 |
+
--- a/src/llama-model.cpp
|
| 339 |
+
+++ b/src/llama-model.cpp
|
| 340 |
+
@@ -253,6 +253,8 @@ static llama_model * llama_model_mapping(llm_arch arch, const llama_model_params
|
| 341 |
+
return new llama_model_paddleocr(params);
|
| 342 |
+
case LLM_ARCH_HUNYUAN_MOE:
|
| 343 |
+
return new llama_model_hunyuan_moe(params);
|
| 344 |
+
+ case LLM_ARCH_HYV3:
|
| 345 |
+
+ return new llama_model_hyv3(params);
|
| 346 |
+
case LLM_ARCH_HUNYUAN_VL:
|
| 347 |
+
return new llama_model_hunyuan_vl(params);
|
| 348 |
+
case LLM_ARCH_HUNYUAN_DENSE:
|
| 349 |
+
@@ -2479,6 +2481,7 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
|
| 350 |
+
case LLM_ARCH_STEP35:
|
| 351 |
+
case LLM_ARCH_TALKIE:
|
| 352 |
+
case LLM_ARCH_MELLUM:
|
| 353 |
+
+ case LLM_ARCH_HYV3:
|
| 354 |
+
return LLAMA_ROPE_TYPE_NEOX;
|
| 355 |
+
|
| 356 |
+
case LLM_ARCH_QWEN2VL:
|
| 357 |
+
diff --git a/src/models/models.h b/src/models/models.h
|
| 358 |
+
index c137e32e8..641fdf9e9 100644
|
| 359 |
+
--- a/src/models/models.h
|
| 360 |
+
+++ b/src/models/models.h
|
| 361 |
+
@@ -1566,6 +1566,22 @@ struct llama_model_hunyuan_moe : public llama_model_base {
|
| 362 |
+
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
| 363 |
+
};
|
| 364 |
+
|
| 365 |
+
+struct llama_model_hyv3 : public llama_model_base {
|
| 366 |
+
+ llama_model_hyv3(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 367 |
+
+ void load_arch_hparams(llama_model_loader & ml) override;
|
| 368 |
+
+ void load_arch_tensors(llama_model_loader & ml) override;
|
| 369 |
+
+
|
| 370 |
+
+ struct graph : public llm_graph_context {
|
| 371 |
+
+ graph(const llama_model & model, const llm_graph_params & params);
|
| 372 |
+
+ };
|
| 373 |
+
+
|
| 374 |
+
+ struct graph_mtp : public llm_graph_context {
|
| 375 |
+
+ graph_mtp(const llama_model & model, const llm_graph_params & params);
|
| 376 |
+
+ };
|
| 377 |
+
+
|
| 378 |
+
+ std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
| 379 |
+
+};
|
| 380 |
+
+
|
| 381 |
+
|
| 382 |
+
struct llama_model_hunyuan_vl : public llama_model_base {
|
| 383 |
+
llama_model_hunyuan_vl(const struct llama_model_params & params) : llama_model_base(params) {}
|
hy3-llama-patch/HF_README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- zh
|
| 6 |
+
pipeline_tag: text-generation
|
| 7 |
+
tags:
|
| 8 |
+
- moe
|
| 9 |
+
- hy3
|
| 10 |
+
- hy-v3
|
| 11 |
+
- huggingface
|
| 12 |
+
- llama.cpp
|
| 13 |
+
- apple-silicon
|
| 14 |
+
- metal
|
| 15 |
+
- local-ai
|
| 16 |
+
- 295b
|
| 17 |
+
base_model: tencent/Hy3
|
| 18 |
+
datasets:
|
| 19 |
+
- AngelSlim/Hy3-GGUF
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
# Hy3 (hy_v3) — macOS Metal Build
|
| 23 |
+
|
| 24 |
+
**Run Tencent Hy3 295B MoE on a MacBook via llama.cpp + Metal.**
|
| 25 |
+
|
| 26 |
+
Not in the cloud. Not on a cluster. On a laptop.
|
| 27 |
+
|
| 28 |
+
## 🔥 The Numbers
|
| 29 |
+
|
| 30 |
+
| What | Value |
|
| 31 |
+
|------|-------|
|
| 32 |
+
| Architecture | Hy3 (hy_v3) — Hunyuan V3 |
|
| 33 |
+
| Parameters | **295B** |
|
| 34 |
+
| Layers | 81 (80 routed + 1 MTP) |
|
| 35 |
+
| Experts | 192 (8 active per token) |
|
| 36 |
+
| Quantization | IQ1_M (mixed recipe) |
|
| 37 |
+
| File size | ~85 GB |
|
| 38 |
+
| RAM needed | **96-128 GB** (Apple Silicon) |
|
| 39 |
+
| License | Apache 2.0 |
|
| 40 |
+
|
| 41 |
+
## 🏆 Why This Matters
|
| 42 |
+
|
| 43 |
+
A **295B MoE model running on a single MacBook** is a milestone for open local AI:
|
| 44 |
+
|
| 45 |
+
- ✅ No cloud, no API keys, no subscriptions
|
| 46 |
+
- ✅ Total privacy — data never leaves your machine
|
| 47 |
+
- ✅ No dedicated GPU — Apple Silicon unified memory is enough
|
| 48 |
+
- ✅ Portable — runs on a laptop, not a server rack
|
| 49 |
+
|
| 50 |
+
## 📥 Download the GGUF
|
| 51 |
+
|
| 52 |
+
The quantized model comes from [AngelSlim/Hy3-GGUF](https://huggingface.co/AngelSlim/Hy3-GGUF):
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
# IQ1_M with MTP (85 GB) — recommended for 128 GB
|
| 56 |
+
wget https://huggingface.co/AngelSlim/Hy3-GGUF/resolve/main/Hy3-IQ1_M-mtp.gguf
|
| 57 |
+
|
| 58 |
+
# IQ1_M without MTP (84 GB) — for 96 GB
|
| 59 |
+
wget https://huggingface.co/AngelSlim/Hy3-GGUF/resolve/main/Hy3-IQ1_M.gguf
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
## 🛠️ Build & Run
|
| 63 |
+
|
| 64 |
+
### 1. Clone the patched llama.cpp
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
git clone https://github.com/RobZombAI/llama.cpp-metal_hyv3
|
| 68 |
+
cd llama.cpp-metal_hyv3
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
### 2. Build with Metal
|
| 72 |
+
|
| 73 |
+
```bash
|
| 74 |
+
mkdir build && cd build
|
| 75 |
+
cmake .. -DLLAMA_METAL=ON
|
| 76 |
+
make -j$(sysctl -n hw.logicalcpu)
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
### 3. Run inference
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
./build/bin/llama-cli \
|
| 83 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 84 |
+
-c 65536 \
|
| 85 |
+
-ngl 99 \
|
| 86 |
+
-fa on \
|
| 87 |
+
-ctk q8_0 -ctv q8_0 \
|
| 88 |
+
-p "Hello" \
|
| 89 |
+
-n 100 \
|
| 90 |
+
--temp 0.6
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
### With reasoning enabled
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
./build/bin/llama-cli \
|
| 97 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 98 |
+
-c 65536 -ngl 99 -fa on \
|
| 99 |
+
-ctk q8_0 -ctv q8_0 \
|
| 100 |
+
-p "Explain quantum computing" \
|
| 101 |
+
-n 300 --temp 0.6 \
|
| 102 |
+
--reasoning on --reasoning-budget -1
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
### With MTP self-speculative decoding (higher throughput)
|
| 106 |
+
|
| 107 |
+
```bash
|
| 108 |
+
./build/bin/llama-cli \
|
| 109 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 110 |
+
-c 65536 -ngl 99 -fa on \
|
| 111 |
+
--spec-type draft-mtp --spec-draft-n-max 3 --spec-draft-n-min 1 \
|
| 112 |
+
-ctk q8_0 -ctv q8_0 \
|
| 113 |
+
-ctkd q8_0 -ctvd q8_0 \
|
| 114 |
+
-p "Hello" -n 200 --temp 0.6
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
### OpenAI-compatible server
|
| 118 |
+
|
| 119 |
+
```bash
|
| 120 |
+
./build/bin/llama-server \
|
| 121 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 122 |
+
-c 65536 -ngl 99 -fa on \
|
| 123 |
+
-ctk q8_0 -ctv q8_0 \
|
| 124 |
+
--temp 0.6 --port 8080
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
### For 96 GB Macs (no MTP, compressed KV)
|
| 128 |
+
|
| 129 |
+
```bash
|
| 130 |
+
./build/bin/llama-cli \
|
| 131 |
+
-m ~/Downloads/Hy3-IQ1_M.gguf \
|
| 132 |
+
-c 65536 -ngl 99 -fa on \
|
| 133 |
+
-ctk q8_0 -ctv q8_0 \
|
| 134 |
+
-p "Hello" -n 100 --temp 0.6
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
## 📊 Hardware Requirements
|
| 138 |
+
|
| 139 |
+
| Mac | RAM | MTP | Context | Notes |
|
| 140 |
+
|-----|-----|:---:|:--------:|-------|
|
| 141 |
+
| M5/M4/M3 Max | 128 GB | ✅ | 64K | Everything on |
|
| 142 |
+
| MacBook Pro | 128 GB | ✅ | 64K | Runs on a laptop |
|
| 143 |
+
| Mac Studio | 96 GB | ❌ | 64K | KV q8_0, no MTP |
|
| 144 |
+
| MacBook Pro | 96 GB | ❌ | 64K | Same |
|
| 145 |
+
|
| 146 |
+
## 🙏 Credits
|
| 147 |
+
|
| 148 |
+
- **Tencent** — original [Hy3 model](https://huggingface.co/tencent/Hy3) (Apache 2.0)
|
| 149 |
+
- **[AngelSlim](https://huggingface.co/AngelSlim)** — GGUF quantization, mixed recipes, importance matrix, base llama.cpp patches, benchmarks. **The real MVP.**
|
| 150 |
+
- **RobZombAI** — macOS Metal build, patches integration, testing
|
| 151 |
+
|
| 152 |
+
## 📚 Resources
|
| 153 |
+
|
| 154 |
+
- GitHub repo: [github.com/RobZombAI/llama.cpp-metal_hyv3](https://github.com/RobZombAI/llama.cpp-metal_hyv3)
|
| 155 |
+
- AngelSlim's GGUF: [huggingface.co/AngelSlim/Hy3-GGUF](https://huggingface.co/AngelSlim/Hy3-GGUF)
|
| 156 |
+
- Original model: [huggingface.co/tencent/Hy3](https://huggingface.co/tencent/Hy3)
|
| 157 |
+
- llama.cpp: [github.com/ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp)
|
| 158 |
+
|
| 159 |
+
## 📜 License
|
| 160 |
+
|
| 161 |
+
Apache 2.0 — same as the original Tencent Hy3 model and AngelSlim's patches.
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
**Long live open local AI. 🎉**
|
hy3-llama-patch/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hy3 (hy_v3) for llama.cpp — macOS Metal support
|
| 2 |
+
|
| 3 |
+
Patches and binaries to run **Tencent Hy3 (hy_v3) 295B MoE** on **llama.cpp** with **Apple Silicon GPU (Metal)**.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Why this project exists
|
| 8 |
+
|
| 9 |
+
llama.cpp supports dozens of architectures, but **Hy3 (hy_v3) wasn't one of them**. Tencent released Hy3 as open-source (Apache 2.0), AngelSlim quantized it to GGUF, but to run it on a Mac someone had to write the missing piece: hy_v3 architecture support in llama.cpp.
|
| 10 |
+
|
| 11 |
+
This repo contains **the patches that add hy_v3 to llama.cpp** — architecture detection, weight loading, MoE + shared expert forward pass, and MTP self-speculative decoding. All compiled with **Metal** for Apple Silicon GPU.
|
| 12 |
+
|
| 13 |
+
The goal is simple: **run a 295B model on a MacBook**. Not on the cloud, not on a cluster. On a laptop.
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## Credits
|
| 18 |
+
|
| 19 |
+
This starts and ends with **AngelSlim** and their work on HuggingFace:
|
| 20 |
+
|
| 21 |
+
[**AngelSlim/Hy3-GGUF**](https://huggingface.co/AngelSlim/Hy3-GGUF) — GGUF-quantized model (IQ1_M and Q4_K_M), mixed-precision recipes, importance matrix, setup script, benchmarks, chat template. Without this work, this project wouldn't exist.
|
| 22 |
+
|
| 23 |
+
AngelSlim provided:
|
| 24 |
+
- The **base patches** for hy_v3 architecture in llama.cpp
|
| 25 |
+
- The **IQ1_M quantization** with mixed recipe (critical weights in Q8_0/Q6_K, experts in IQ1_M/IQ2_XXS)
|
| 26 |
+
- The **importance matrix** to allocate bits where they matter
|
| 27 |
+
- The **chat template** for tool calling and reasoning
|
| 28 |
+
|
| 29 |
+
This repo takes those patches, applies them to llama.cpp, and **builds them with Metal for macOS**.
|
| 30 |
+
|
| 31 |
+
**Thank you AngelSlim.** 🙌
|
| 32 |
+
|
| 33 |
+
---
|
| 34 |
+
|
| 35 |
+
## The model
|
| 36 |
+
|
| 37 |
+
| Detail | Value |
|
| 38 |
+
|--------|-------|
|
| 39 |
+
| **Architecture** | Hy3 (hy_v3) — Hunyuan V3 |
|
| 40 |
+
| **Developed by** | Tencent |
|
| 41 |
+
| **Parameters** | 295B |
|
| 42 |
+
| **Layers** | 81 (80 routed + 1 MTP) |
|
| 43 |
+
| **Experts** | 192 (8 active per token) |
|
| 44 |
+
| **Gating** | Sigmoid + correction bias + top-8 selection |
|
| 45 |
+
| **Quantization** | IQ1_M (AngelSlim mixed recipe) |
|
| 46 |
+
| **File size** | ~85 GB (with MTP) |
|
| 47 |
+
|
| 48 |
+
> Original HF model: [Tencent/Hy3](https://huggingface.co/tencent/Hy3)
|
| 49 |
+
> AngelSlim GGUF quant: [**AngelSlim/Hy3-GGUF**](https://huggingface.co/AngelSlim/Hy3-GGUF)
|
| 50 |
+
> Download: [Hy3-IQ1_M-mtp.gguf](https://huggingface.co/AngelSlim/Hy3-GGUF/resolve/main/Hy3-IQ1_M-mtp.gguf) (85 GB, IQ1_M with MTP)
|
| 51 |
+
|
| 52 |
+
**IQ1_M vs BF16 quality loss**: ~+0.3% PPL — **imperceptible**. Full benchmarks on [AngelSlim's HF page](https://huggingface.co/AngelSlim/Hy3-GGUF), file `assets/benchmark.png`.
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## Why a MacBook?
|
| 57 |
+
|
| 58 |
+
| Mac | RAM | IQ1_M (85 GB) | MTP | Context | Notes |
|
| 59 |
+
|-----|-----|:---:|:---:|:--------:|-------|
|
| 60 |
+
| **M5 Max** | 128 GB | ✅ | ✅ | 64K | Everything on, comfortable |
|
| 61 |
+
| **M4 Max** | 128 GB | ✅ | ✅ | 64K | Everything on |
|
| 62 |
+
| **M3 Max** | 128 GB | ✅ | ✅ | 64K | Everything on |
|
| 63 |
+
| **MacBook Pro** | 128 GB | ✅ | ✅ | 64K | Runs on a laptop |
|
| 64 |
+
| **Mac Studio** | 96 GB | ✅ | ❌ | 64K | KV q8_0 only, no MTP |
|
| 65 |
+
| **MacBook Pro** | 96 GB | ✅ | ❌ | 64K | Same as above |
|
| 66 |
+
|
| 67 |
+
A **295B MoE running on a MacBook with 128 GB** is a concrete milestone for local AI:
|
| 68 |
+
|
| 69 |
+
- **No cloud**, no API keys, no subscriptions
|
| 70 |
+
- **Total privacy** — data never leaves your machine
|
| 71 |
+
- **No dedicated GPU needed** — Apple Silicon unified memory is enough
|
| 72 |
+
- **Portable** — no server rack, no cluster
|
| 73 |
+
|
| 74 |
+
With 96 GB it still works: the model is ~85 GB, leaving ~11 GB for the system. Just compress the KV cache (`-ctk q8_0 -ctv q8_0`) and skip MTP (which adds ~2 GB of weights plus a draft KV cache). With 128 GB everything runs — MTP included, with headroom.
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## Download
|
| 79 |
+
|
| 80 |
+
### 1. The GGUF model
|
| 81 |
+
|
| 82 |
+
```bash
|
| 83 |
+
# IQ1_M with MTP (85 GB) — recommended for 128 GB
|
| 84 |
+
wget https://huggingface.co/AngelSlim/Hy3-GGUF/resolve/main/Hy3-IQ1_M-mtp.gguf
|
| 85 |
+
|
| 86 |
+
# IQ1_M without MTP (84 GB) — for 96 GB
|
| 87 |
+
wget https://huggingface.co/AngelSlim/Hy3-GGUF/resolve/main/Hy3-IQ1_M.gguf
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
### 2. The code
|
| 91 |
+
|
| 92 |
+
```bash
|
| 93 |
+
git clone https://github.com/ggml-org/llama.cpp
|
| 94 |
+
cd llama.cpp
|
| 95 |
+
git checkout 19bba67c1
|
| 96 |
+
git apply /path/to/0001-add-hyv3-support.patch
|
| 97 |
+
cp /path/to/hyv3.cpp src/models/
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
Or clone the ready-made fork:
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
git clone https://github.com/RobZombAI/llama.cpp-metal_hyv3
|
| 104 |
+
cd llama.cpp-metal_hyv3
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
### 3. Build
|
| 108 |
+
|
| 109 |
+
```bash
|
| 110 |
+
mkdir build && cd build
|
| 111 |
+
cmake .. -DLLAMA_METAL=ON
|
| 112 |
+
make -j$(sysctl -n hw.logicalcpu)
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
---
|
| 116 |
+
|
| 117 |
+
## Commands
|
| 118 |
+
|
| 119 |
+
### CLI (base inference)
|
| 120 |
+
|
| 121 |
+
```bash
|
| 122 |
+
./build/bin/llama-cli \
|
| 123 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 124 |
+
-c 65536 \
|
| 125 |
+
-ngl 99 \
|
| 126 |
+
-fa on \
|
| 127 |
+
-ctk q8_0 -ctv q8_0 \
|
| 128 |
+
-p "Hello" \
|
| 129 |
+
-n 100 \
|
| 130 |
+
--temp 0.6
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
### CLI (with reasoning/thinking)
|
| 134 |
+
|
| 135 |
+
```bash
|
| 136 |
+
./build/bin/llama-cli \
|
| 137 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 138 |
+
-c 65536 \
|
| 139 |
+
-ngl 99 -fa on \
|
| 140 |
+
-ctk q8_0 -ctv q8_0 \
|
| 141 |
+
-p "Hello" \
|
| 142 |
+
-n 200 \
|
| 143 |
+
--temp 0.6 \
|
| 144 |
+
--reasoning on \
|
| 145 |
+
--reasoning-budget -1
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
### CLI (with MTP self-speculative — higher throughput)
|
| 149 |
+
|
| 150 |
+
```bash
|
| 151 |
+
./build/bin/llama-cli \
|
| 152 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 153 |
+
-c 65536 \
|
| 154 |
+
-ngl 99 -fa on \
|
| 155 |
+
--spec-type draft-mtp \
|
| 156 |
+
--spec-draft-n-max 3 \
|
| 157 |
+
--spec-draft-n-min 1 \
|
| 158 |
+
-ctk q8_0 -ctv q8_0 \
|
| 159 |
+
-ctkd q8_0 -ctvd q8_0 \
|
| 160 |
+
-p "Hello" \
|
| 161 |
+
-n 200 \
|
| 162 |
+
--temp 0.6
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
### Server (OpenAI-compatible API)
|
| 166 |
+
|
| 167 |
+
```bash
|
| 168 |
+
./build/bin/llama-server \
|
| 169 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 170 |
+
-c 65536 \
|
| 171 |
+
-ngl 99 -fa on \
|
| 172 |
+
-ctk q8_0 -ctv q8_0 \
|
| 173 |
+
--temp 0.6 \
|
| 174 |
+
--port 8080
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
Test API call:
|
| 178 |
+
|
| 179 |
+
```bash
|
| 180 |
+
curl http://localhost:8080/v1/chat/completions \
|
| 181 |
+
-H "Content-Type: application/json" \
|
| 182 |
+
-d '{
|
| 183 |
+
"model": "Hy3-IQ1_M-mtp",
|
| 184 |
+
"messages": [{"role": "user", "content": "Hello"}],
|
| 185 |
+
"temperature": 0.6,
|
| 186 |
+
"max_tokens": 200
|
| 187 |
+
}'
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
### Server (with reasoning + MTP)
|
| 191 |
+
|
| 192 |
+
```bash
|
| 193 |
+
./build/bin/llama-server \
|
| 194 |
+
-m ~/Downloads/Hy3-IQ1_M-mtp.gguf \
|
| 195 |
+
-c 65536 \
|
| 196 |
+
-ngl 99 -fa on \
|
| 197 |
+
--spec-type draft-mtp \
|
| 198 |
+
--spec-draft-n-max 3 \
|
| 199 |
+
--spec-draft-n-min 1 \
|
| 200 |
+
-ctk q8_0 -ctv q8_0 \
|
| 201 |
+
-ctkd q8_0 -ctvd q8_0 \
|
| 202 |
+
--temp 0.6 \
|
| 203 |
+
--reasoning on \
|
| 204 |
+
--reasoning-budget -1 \
|
| 205 |
+
--port 8080
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
### For Mac with 96 GB RAM
|
| 209 |
+
|
| 210 |
+
```bash
|
| 211 |
+
# No MTP, compressed KV cache
|
| 212 |
+
./build/bin/llama-cli \
|
| 213 |
+
-m ~/Downloads/Hy3-IQ1_M.gguf \
|
| 214 |
+
-c 65536 \
|
| 215 |
+
-ngl 99 -fa on \
|
| 216 |
+
-ctk q8_0 -ctv q8_0 \
|
| 217 |
+
-p "Hello" -n 100 \
|
| 218 |
+
--temp 0.6
|
| 219 |
+
```
|
| 220 |
+
|
| 221 |
+
---
|
| 222 |
+
|
| 223 |
+
## Flag reference
|
| 224 |
+
|
| 225 |
+
| Flag | What it does |
|
| 226 |
+
|------|--------------|
|
| 227 |
+
| `-m PATH` | Path to the GGUF model file |
|
| 228 |
+
| `-c N` | Context size in tokens. `65536` = 64K. Higher = more memory |
|
| 229 |
+
| `-ngl N` | Layers to offload to GPU. `99` = all layers on Metal |
|
| 230 |
+
| `-fa on` | Flash attention — reduces memory and speeds up attention |
|
| 231 |
+
| `-ctk q8_0 -ctv q8_0` | KV cache in q8_0. **Essential for 96 GB** (saves ~20 GB) |
|
| 232 |
+
| `--temp N` | Sampling temperature. `0.0` = deterministic/greedy |
|
| 233 |
+
| `--reasoning on` | Enable thinking/reasoning (tag) |
|
| 234 |
+
| `--reasoning-budget N` | Max tokens for thinking. `-1` = unlimited |
|
| 235 |
+
| `--spec-type draft-mtp` | MTP self-speculative decoding (*-mtp.gguf only) |
|
| 236 |
+
| `--spec-draft-n-max N` | Max draft tokens per MTP step |
|
| 237 |
+
|
| 238 |
+
---
|
| 239 |
+
|
| 240 |
+
## Project structure
|
| 241 |
+
|
| 242 |
+
```
|
| 243 |
+
├── 0001-add-hyv3-support.patch # Patch for 9 llama.cpp files (383 lines)
|
| 244 |
+
├── src/models/hyv3.cpp # hy_v3 model implementation + MTP (388 lines)
|
| 245 |
+
└── README.md # This file
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
## Modified files in llama.cpp
|
| 249 |
+
|
| 250 |
+
| File | Change |
|
| 251 |
+
|------|--------|
|
| 252 |
+
| `src/llama-arch.h` | New enum `LLM_ARCH_HYV3` |
|
| 253 |
+
| `src/llama-arch.cpp` | Architecture name `hy_v3` |
|
| 254 |
+
| `src/llama-model.cpp` | Model mapping + Neox rope type |
|
| 255 |
+
| `src/models/models.h` | `llama_model_hyv3` class declaration |
|
| 256 |
+
| `src/models/hyv3.cpp` | **New** — load, forward, MTP draft head |
|
| 257 |
+
| `gguf-py/gguf/constants.py` | Arch enum + tensor list (28 hy_v3 tensors) |
|
| 258 |
+
| `gguf-py/gguf/tensor_mapping.py` | MTP tensor name mapping |
|
| 259 |
+
| `conversion/__init__.py` | HF → GGUF model name mapping |
|
| 260 |
+
| `common/chat.cpp` | Chat template parser (tool calls + reasoning) |
|
| 261 |
+
|
| 262 |
+
## License
|
| 263 |
+
|
| 264 |
+
**Apache 2.0.** Same as the original [Tencent/Hy3](https://huggingface.co/tencent/Hy3) model and [AngelSlim](https://huggingface.co/AngelSlim)'s patches.
|
| 265 |
+
|
| 266 |
+
---
|
| 267 |
+
|
| 268 |
+
**Long live open local AI. A 295B model running on a MacBook. 🎉**
|
hy3-llama-patch/src/models/hyv3.cpp
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "models.h"
|
| 2 |
+
|
| 3 |
+
void llama_model_hyv3::load_arch_hparams(llama_model_loader & ml) {
|
| 4 |
+
ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
|
| 5 |
+
ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
|
| 6 |
+
ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp, false);
|
| 7 |
+
ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func, false);
|
| 8 |
+
ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale, false);
|
| 9 |
+
ml.get_key(LLM_KV_EXPERT_WEIGHTS_NORM, hparams.expert_weights_norm, false);
|
| 10 |
+
|
| 11 |
+
// MTP / NextN head (optional). Absent in pre-MTP gguf -> n_layer_nextn stays 0
|
| 12 |
+
// and everything below (extra block load, MTP graph, MTP context) is skipped.
|
| 13 |
+
ml.get_key(LLM_KV_NEXTN_PREDICT_LAYERS, hparams.n_layer_nextn, false);
|
| 14 |
+
|
| 15 |
+
if (hparams.expert_gating_func == LLAMA_EXPERT_GATING_FUNC_TYPE_NONE) {
|
| 16 |
+
hparams.expert_gating_func = LLAMA_EXPERT_GATING_FUNC_TYPE_SIGMOID;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
switch (hparams.n_layer()) {
|
| 20 |
+
case 48: type = LLM_TYPE_30B_A3B; break;
|
| 21 |
+
default: type = LLM_TYPE_UNKNOWN;
|
| 22 |
+
}
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
void llama_model_hyv3::load_arch_tensors(llama_model_loader &) {
|
| 26 |
+
LLAMA_LOAD_LOCALS;
|
| 27 |
+
|
| 28 |
+
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
|
| 29 |
+
|
| 30 |
+
output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
|
| 31 |
+
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED);
|
| 32 |
+
if (output == NULL) {
|
| 33 |
+
output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, TENSOR_DUPLICATED);
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
for (int i = 0; i < n_layer; ++i) {
|
| 37 |
+
auto & layer = layers[i];
|
| 38 |
+
const int64_t n_ff_exp = hparams.n_ff_exp ? hparams.n_ff_exp : n_ff / n_expert_used;
|
| 39 |
+
const int64_t n_ff_shexp = hparams.n_ff_shexp ? hparams.n_ff_shexp : n_ff_exp;
|
| 40 |
+
|
| 41 |
+
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
|
| 42 |
+
|
| 43 |
+
create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_k_gqa, n_embd_v_gqa, 0);
|
| 44 |
+
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_embd_head_k * n_head, n_embd}, 0);
|
| 45 |
+
|
| 46 |
+
layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 47 |
+
layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 48 |
+
|
| 49 |
+
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
|
| 50 |
+
|
| 51 |
+
layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff}, TENSOR_NOT_REQUIRED);
|
| 52 |
+
layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), {n_ff, n_embd}, TENSOR_NOT_REQUIRED);
|
| 53 |
+
layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff}, TENSOR_NOT_REQUIRED);
|
| 54 |
+
|
| 55 |
+
layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, TENSOR_NOT_REQUIRED);
|
| 56 |
+
layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, TENSOR_NOT_REQUIRED);
|
| 57 |
+
layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, TENSOR_NOT_REQUIRED);
|
| 58 |
+
create_tensor_gate_up_exps(layer, i, n_embd, n_ff_exp, n_expert, TENSOR_NOT_REQUIRED);
|
| 59 |
+
|
| 60 |
+
layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), {n_embd, n_ff_shexp}, TENSOR_NOT_REQUIRED);
|
| 61 |
+
layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), {n_embd, n_ff_shexp}, TENSOR_NOT_REQUIRED);
|
| 62 |
+
layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), {n_ff_shexp, n_embd}, TENSOR_NOT_REQUIRED);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
// MTP / NextN block(s): loaded as extra decoder blocks beyond the main stack
|
| 66 |
+
// (index range [n_layer, n_layer_all)). Skipped entirely for pre-MTP gguf
|
| 67 |
+
// where n_layer_all == n_layer, so those models load unchanged.
|
| 68 |
+
for (int i = n_layer; i < (int) hparams.n_layer_all; ++i) {
|
| 69 |
+
auto & layer = layers[i];
|
| 70 |
+
const int64_t n_ff_exp = hparams.n_ff_exp ? hparams.n_ff_exp : n_ff / n_expert_used;
|
| 71 |
+
const int64_t n_ff_shexp = hparams.n_ff_shexp ? hparams.n_ff_shexp : n_ff_exp;
|
| 72 |
+
|
| 73 |
+
// Standard hy_v3 MoE decoder block (same layout as a trunk sparse layer).
|
| 74 |
+
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
|
| 75 |
+
|
| 76 |
+
create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_k_gqa, n_embd_v_gqa, 0);
|
| 77 |
+
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), {n_embd_head_k * n_head, n_embd}, 0);
|
| 78 |
+
|
| 79 |
+
layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 80 |
+
layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), {n_embd_head_k}, 0);
|
| 81 |
+
|
| 82 |
+
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
|
| 83 |
+
|
| 84 |
+
layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, 0);
|
| 85 |
+
layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, TENSOR_NOT_REQUIRED);
|
| 86 |
+
layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, 0);
|
| 87 |
+
create_tensor_gate_up_exps(layer, i, n_embd, n_ff_exp, n_expert, 0);
|
| 88 |
+
|
| 89 |
+
layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), {n_embd, n_ff_shexp}, TENSOR_NOT_REQUIRED);
|
| 90 |
+
layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), {n_embd, n_ff_shexp}, TENSOR_NOT_REQUIRED);
|
| 91 |
+
layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), {n_ff_shexp, n_embd}, TENSOR_NOT_REQUIRED);
|
| 92 |
+
|
| 93 |
+
// NextN-specific tensors. eh_proj fuses [enorm(embed), hnorm(hidden)] (2*n_embd -> n_embd).
|
| 94 |
+
// shared_head_head / embed_tokens are tied to the main lm_head / tok_embd in Hy3,
|
| 95 |
+
// so they are optional and fall back in the graph.
|
| 96 |
+
layer.nextn.eh_proj = create_tensor(tn(LLM_TENSOR_NEXTN_EH_PROJ, "weight", i), {2 * n_embd, n_embd}, 0);
|
| 97 |
+
layer.nextn.enorm = create_tensor(tn(LLM_TENSOR_NEXTN_ENORM, "weight", i), {n_embd}, 0);
|
| 98 |
+
layer.nextn.hnorm = create_tensor(tn(LLM_TENSOR_NEXTN_HNORM, "weight", i), {n_embd}, 0);
|
| 99 |
+
layer.nextn.shared_head_norm = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_NORM, "weight", i), {n_embd}, TENSOR_NOT_REQUIRED);
|
| 100 |
+
layer.nextn.shared_head_head = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_HEAD, "weight", i), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED);
|
| 101 |
+
layer.nextn.embed_tokens = create_tensor(tn(LLM_TENSOR_NEXTN_EMBED_TOKENS, "weight", i), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED);
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
std::unique_ptr<llm_graph_context> llama_model_hyv3::build_arch_graph(const llm_graph_params & params) const {
|
| 106 |
+
if (params.gtype == LLM_GRAPH_TYPE_DECODER_MTP) {
|
| 107 |
+
return std::make_unique<graph_mtp>(*this, params);
|
| 108 |
+
}
|
| 109 |
+
return std::make_unique<graph>(*this, params);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
llama_model_hyv3::graph::graph(const llama_model & model, const llm_graph_params & params) : llm_graph_context(params) {
|
| 113 |
+
const int64_t n_embd_head = hparams.n_embd_head_v();
|
| 114 |
+
|
| 115 |
+
GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
|
| 116 |
+
GGML_ASSERT(n_embd_head == n_rot);
|
| 117 |
+
|
| 118 |
+
ggml_tensor * cur;
|
| 119 |
+
ggml_tensor * inpL;
|
| 120 |
+
|
| 121 |
+
inpL = build_inp_embd(model.tok_embd);
|
| 122 |
+
ggml_tensor * inp_pos = build_inp_pos();
|
| 123 |
+
auto * inp_attn = build_attn_inp_kv();
|
| 124 |
+
ggml_tensor * inp_out_ids = build_inp_out_ids();
|
| 125 |
+
|
| 126 |
+
const float kq_scale = 1.0f / sqrtf(float(n_embd_head));
|
| 127 |
+
|
| 128 |
+
for (int il = 0; il < n_layer; ++il) {
|
| 129 |
+
ggml_tensor * inpSA = inpL;
|
| 130 |
+
|
| 131 |
+
cur = build_norm(inpL, model.layers[il].attn_norm, nullptr, LLM_NORM_RMS, il);
|
| 132 |
+
cb(cur, "attn_norm", il);
|
| 133 |
+
|
| 134 |
+
{
|
| 135 |
+
ggml_tensor * rope_factors = model.get_rope_factors(cparams, il);
|
| 136 |
+
|
| 137 |
+
auto [Qcur, Kcur, Vcur] = build_qkv(model.layers[il], cur, n_embd_head, n_head, n_head_kv, il);
|
| 138 |
+
|
| 139 |
+
Qcur = build_norm(Qcur, model.layers[il].attn_q_norm, nullptr, LLM_NORM_RMS, il);
|
| 140 |
+
Kcur = build_norm(Kcur, model.layers[il].attn_k_norm, nullptr, LLM_NORM_RMS, il);
|
| 141 |
+
|
| 142 |
+
Qcur = ggml_rope_ext(ctx0, Qcur, inp_pos, rope_factors,
|
| 143 |
+
n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 144 |
+
ext_factor, attn_factor, beta_fast, beta_slow);
|
| 145 |
+
Kcur = ggml_rope_ext(ctx0, Kcur, inp_pos, rope_factors,
|
| 146 |
+
n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 147 |
+
ext_factor, attn_factor, beta_fast, beta_slow);
|
| 148 |
+
|
| 149 |
+
cur = build_attn(inp_attn,
|
| 150 |
+
model.layers[il].wo, model.layers[il].wo_b, model.layers[il].wo_s,
|
| 151 |
+
Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il);
|
| 152 |
+
cb(cur, "attn_out", il);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
if (il == n_layer - 1 && inp_out_ids && cparams.embeddings_nextn_masked) {
|
| 156 |
+
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 157 |
+
inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
|
| 161 |
+
cb(ffn_inp, "ffn_inp", il);
|
| 162 |
+
|
| 163 |
+
cur = build_norm(ffn_inp, model.layers[il].ffn_norm, nullptr, LLM_NORM_RMS, il);
|
| 164 |
+
cb(cur, "ffn_norm", il);
|
| 165 |
+
|
| 166 |
+
if (model.layers[il].ffn_gate_inp == nullptr) {
|
| 167 |
+
cur = build_ffn(cur,
|
| 168 |
+
model.layers[il].ffn_up, model.layers[il].ffn_up_b, model.layers[il].ffn_up_s,
|
| 169 |
+
model.layers[il].ffn_gate, model.layers[il].ffn_gate_b, model.layers[il].ffn_gate_s,
|
| 170 |
+
model.layers[il].ffn_down, model.layers[il].ffn_down_b, model.layers[il].ffn_down_s,
|
| 171 |
+
nullptr,
|
| 172 |
+
LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 173 |
+
cb(cur, "ffn_dense_out", il);
|
| 174 |
+
} else {
|
| 175 |
+
ggml_tensor * moe_out = build_moe_ffn(cur,
|
| 176 |
+
model.layers[il].ffn_gate_inp,
|
| 177 |
+
model.layers[il].ffn_up_exps,
|
| 178 |
+
model.layers[il].ffn_gate_exps,
|
| 179 |
+
model.layers[il].ffn_down_exps,
|
| 180 |
+
model.layers[il].ffn_exp_probs_b,
|
| 181 |
+
n_expert, n_expert_used,
|
| 182 |
+
LLM_FFN_SILU,
|
| 183 |
+
hparams.expert_weights_norm,
|
| 184 |
+
hparams.expert_weights_scale,
|
| 185 |
+
(llama_expert_gating_func_type) hparams.expert_gating_func,
|
| 186 |
+
il,
|
| 187 |
+
nullptr, model.layers[il].ffn_gate_up_exps,
|
| 188 |
+
model.layers[il].ffn_up_exps_s,
|
| 189 |
+
model.layers[il].ffn_gate_exps_s,
|
| 190 |
+
model.layers[il].ffn_down_exps_s);
|
| 191 |
+
cb(moe_out, "ffn_moe_out", il);
|
| 192 |
+
|
| 193 |
+
ggml_tensor * sh_out = build_ffn(cur,
|
| 194 |
+
model.layers[il].ffn_up_shexp, nullptr, model.layers[il].ffn_up_shexp_s,
|
| 195 |
+
model.layers[il].ffn_gate_shexp, nullptr, model.layers[il].ffn_gate_shexp_s,
|
| 196 |
+
model.layers[il].ffn_down_shexp, nullptr, model.layers[il].ffn_down_shexp_s,
|
| 197 |
+
nullptr,
|
| 198 |
+
LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 199 |
+
cb(sh_out, "ffn_shared_out", il);
|
| 200 |
+
|
| 201 |
+
cur = ggml_add(ctx0, moe_out, sh_out);
|
| 202 |
+
cb(cur, "ffn_out", il);
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
cur = ggml_add(ctx0, cur, ffn_inp);
|
| 206 |
+
cur = build_cvec(cur, il);
|
| 207 |
+
cb(cur, "l_out", il);
|
| 208 |
+
|
| 209 |
+
inpL = cur;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
cur = build_norm(inpL, model.output_norm, nullptr, LLM_NORM_RMS, -1);
|
| 213 |
+
|
| 214 |
+
// post-norm hidden state feeds both the LM head and the MTP seed (t_h_nextn).
|
| 215 |
+
// When masking is off, the MTP path needs the full-width hidden, so defer the
|
| 216 |
+
// output-id gather until after capturing t_h_nextn (matches qwen35moe).
|
| 217 |
+
cb(cur, "h_nextn", -1);
|
| 218 |
+
res->t_h_nextn = cur;
|
| 219 |
+
|
| 220 |
+
if (!cparams.embeddings_nextn_masked && inp_out_ids) {
|
| 221 |
+
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
cb(cur, "result_norm", -1);
|
| 225 |
+
res->t_embd = cur;
|
| 226 |
+
|
| 227 |
+
cur = build_lora_mm(model.output, cur, model.output_s);
|
| 228 |
+
cb(cur, "result_output", -1);
|
| 229 |
+
res->t_logits = cur;
|
| 230 |
+
|
| 231 |
+
ggml_build_forward_expand(gf, cur);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
// LLM_GRAPH_TYPE_DECODER_MTP draft head for Hy3 MoE.
|
| 235 |
+
// Mirrors vLLM HYV3MultiTokenPredictorLayer.forward:
|
| 236 |
+
// e = enorm(inputs_embeds); h = hnorm(previous_hidden);
|
| 237 |
+
// x = eh_proj(cat([e, h])); x = mtp_block(x); x += residual; x = final_ln(x)
|
| 238 |
+
// logits = lm_head(x) (lm_head/embed tied to the main model)
|
| 239 |
+
// Differences vs qwen35moe MTP: default RoPE (not mrope), no attention gate,
|
| 240 |
+
// no shared-expert gate, sigmoid MoE gating with correction bias.
|
| 241 |
+
llama_model_hyv3::graph_mtp::graph_mtp(const llama_model & model, const llm_graph_params & params)
|
| 242 |
+
: llm_graph_context(params) {
|
| 243 |
+
const int64_t n_embd_head = hparams.n_embd_head_v();
|
| 244 |
+
GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
|
| 245 |
+
GGML_ASSERT(n_embd_head == n_rot);
|
| 246 |
+
|
| 247 |
+
GGML_ASSERT(hparams.n_layer_nextn > 0 && "HYV3 MTP requires n_layer_nextn > 0");
|
| 248 |
+
GGML_ASSERT(hparams.n_layer_nextn == 1 && "HYV3 MTP currently only supports a single MTP block");
|
| 249 |
+
|
| 250 |
+
const int il = hparams.n_layer();
|
| 251 |
+
const auto & layer = model.layers[il];
|
| 252 |
+
|
| 253 |
+
GGML_ASSERT(layer.nextn.eh_proj && "HYV3 MTP block missing nextn.eh_proj");
|
| 254 |
+
GGML_ASSERT(layer.nextn.enorm && "HYV3 MTP block missing nextn.enorm");
|
| 255 |
+
GGML_ASSERT(layer.nextn.hnorm && "HYV3 MTP block missing nextn.hnorm");
|
| 256 |
+
GGML_ASSERT(layer.ffn_gate_inp && "HYV3 MTP block missing ffn_gate_inp");
|
| 257 |
+
|
| 258 |
+
const float kq_scale = 1.0f / sqrtf(float(n_embd_head));
|
| 259 |
+
|
| 260 |
+
// Inputs: token ids (-> embedding), and the previous-step hidden state h.
|
| 261 |
+
auto inp = std::make_unique<llm_graph_input_embd_h>(hparams.n_embd);
|
| 262 |
+
|
| 263 |
+
inp->tokens = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_tokens);
|
| 264 |
+
ggml_set_input(inp->tokens);
|
| 265 |
+
|
| 266 |
+
inp->embd = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, hparams.n_embd_inp(), n_tokens);
|
| 267 |
+
ggml_set_input(inp->embd);
|
| 268 |
+
|
| 269 |
+
ggml_tensor * tok_embd;
|
| 270 |
+
if (ubatch.token) {
|
| 271 |
+
ggml_tensor * tok_embd_w = layer.nextn.embed_tokens ? layer.nextn.embed_tokens : model.tok_embd;
|
| 272 |
+
tok_embd = ggml_get_rows(ctx0, tok_embd_w, inp->tokens);
|
| 273 |
+
} else {
|
| 274 |
+
tok_embd = inp->embd;
|
| 275 |
+
}
|
| 276 |
+
cb(tok_embd, "mtp_tok_embd", il);
|
| 277 |
+
|
| 278 |
+
inp->h = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, hparams.n_embd, n_tokens);
|
| 279 |
+
ggml_set_input(inp->h);
|
| 280 |
+
ggml_set_name(inp->h, "mtp_h_input");
|
| 281 |
+
|
| 282 |
+
ggml_tensor * h_embd = inp->h;
|
| 283 |
+
|
| 284 |
+
res->add_input(std::move(inp));
|
| 285 |
+
|
| 286 |
+
ggml_tensor * inp_pos = build_inp_pos();
|
| 287 |
+
ggml_tensor * inp_out_ids = build_inp_out_ids();
|
| 288 |
+
|
| 289 |
+
auto * inp_attn = build_attn_inp_kv();
|
| 290 |
+
|
| 291 |
+
// e = enorm(embed), h = hnorm(hidden); fuse via eh_proj: cat([e, h]) -> n_embd
|
| 292 |
+
ggml_tensor * e_norm = build_norm(tok_embd, layer.nextn.enorm, nullptr, LLM_NORM_RMS, il);
|
| 293 |
+
cb(e_norm, "mtp_enorm", il);
|
| 294 |
+
|
| 295 |
+
ggml_tensor * h_norm = build_norm(h_embd, layer.nextn.hnorm, nullptr, LLM_NORM_RMS, il);
|
| 296 |
+
cb(h_norm, "mtp_hnorm", il);
|
| 297 |
+
|
| 298 |
+
ggml_tensor * concat = ggml_concat(ctx0, e_norm, h_norm, /*dim=*/ 0);
|
| 299 |
+
cb(concat, "mtp_concat", il);
|
| 300 |
+
|
| 301 |
+
ggml_tensor * cur = build_lora_mm(layer.nextn.eh_proj, concat, layer.nextn.eh_proj_s);
|
| 302 |
+
cb(cur, "mtp_eh_proj", il);
|
| 303 |
+
|
| 304 |
+
// --- one standard hy_v3 decoder block on the fused hidden ---
|
| 305 |
+
ggml_tensor * inpSA = cur;
|
| 306 |
+
|
| 307 |
+
cur = build_norm(cur, layer.attn_norm, nullptr, LLM_NORM_RMS, il);
|
| 308 |
+
cb(cur, "mtp_attn_norm", il);
|
| 309 |
+
|
| 310 |
+
{
|
| 311 |
+
ggml_tensor * rope_factors = model.get_rope_factors(cparams, il);
|
| 312 |
+
|
| 313 |
+
auto [Qcur, Kcur, Vcur] = build_qkv(layer, cur, n_embd_head, n_head, n_head_kv, il);
|
| 314 |
+
|
| 315 |
+
Qcur = build_norm(Qcur, layer.attn_q_norm, nullptr, LLM_NORM_RMS, il);
|
| 316 |
+
Kcur = build_norm(Kcur, layer.attn_k_norm, nullptr, LLM_NORM_RMS, il);
|
| 317 |
+
|
| 318 |
+
Qcur = ggml_rope_ext(ctx0, Qcur, inp_pos, rope_factors,
|
| 319 |
+
n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 320 |
+
ext_factor, attn_factor, beta_fast, beta_slow);
|
| 321 |
+
Kcur = ggml_rope_ext(ctx0, Kcur, inp_pos, rope_factors,
|
| 322 |
+
n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 323 |
+
ext_factor, attn_factor, beta_fast, beta_slow);
|
| 324 |
+
|
| 325 |
+
cur = build_attn(inp_attn,
|
| 326 |
+
layer.wo, layer.wo_b, layer.wo_s,
|
| 327 |
+
Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il);
|
| 328 |
+
cb(cur, "mtp_attn_out", il);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
|
| 332 |
+
cb(ffn_inp, "mtp_ffn_inp", il);
|
| 333 |
+
|
| 334 |
+
cur = build_norm(ffn_inp, layer.ffn_norm, nullptr, LLM_NORM_RMS, il);
|
| 335 |
+
cb(cur, "mtp_ffn_norm", il);
|
| 336 |
+
|
| 337 |
+
ggml_tensor * moe_out = build_moe_ffn(cur,
|
| 338 |
+
layer.ffn_gate_inp,
|
| 339 |
+
layer.ffn_up_exps,
|
| 340 |
+
layer.ffn_gate_exps,
|
| 341 |
+
layer.ffn_down_exps,
|
| 342 |
+
layer.ffn_exp_probs_b,
|
| 343 |
+
n_expert, n_expert_used,
|
| 344 |
+
LLM_FFN_SILU,
|
| 345 |
+
hparams.expert_weights_norm,
|
| 346 |
+
hparams.expert_weights_scale,
|
| 347 |
+
(llama_expert_gating_func_type) hparams.expert_gating_func,
|
| 348 |
+
il,
|
| 349 |
+
nullptr, layer.ffn_gate_up_exps,
|
| 350 |
+
layer.ffn_up_exps_s,
|
| 351 |
+
layer.ffn_gate_exps_s,
|
| 352 |
+
layer.ffn_down_exps_s);
|
| 353 |
+
cb(moe_out, "mtp_ffn_moe_out", il);
|
| 354 |
+
|
| 355 |
+
ggml_tensor * sh_out = build_ffn(cur,
|
| 356 |
+
layer.ffn_up_shexp, nullptr, layer.ffn_up_shexp_s,
|
| 357 |
+
layer.ffn_gate_shexp, nullptr, layer.ffn_gate_shexp_s,
|
| 358 |
+
layer.ffn_down_shexp, nullptr, layer.ffn_down_shexp_s,
|
| 359 |
+
nullptr,
|
| 360 |
+
LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 361 |
+
cb(sh_out, "mtp_ffn_shared_out", il);
|
| 362 |
+
|
| 363 |
+
cur = ggml_add(ctx0, moe_out, sh_out);
|
| 364 |
+
cb(cur, "mtp_ffn_out", il);
|
| 365 |
+
|
| 366 |
+
cur = ggml_add(ctx0, cur, ffn_inp);
|
| 367 |
+
cb(cur, "mtp_post_ffn", il);
|
| 368 |
+
|
| 369 |
+
// final_layernorm then LM head (both fall back to the main model when tied)
|
| 370 |
+
ggml_tensor * head_norm_w = layer.nextn.shared_head_norm ? layer.nextn.shared_head_norm : model.output_norm;
|
| 371 |
+
GGML_ASSERT(head_norm_w && "HYV3 MTP: missing both nextn.shared_head_norm and output_norm");
|
| 372 |
+
cur = build_norm(cur, head_norm_w, nullptr, LLM_NORM_RMS, -1);
|
| 373 |
+
|
| 374 |
+
cb(cur, "h_nextn", -1);
|
| 375 |
+
res->t_h_nextn = cur;
|
| 376 |
+
|
| 377 |
+
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 378 |
+
cb(cur, "mtp_final_norm", -1);
|
| 379 |
+
|
| 380 |
+
ggml_tensor * head_w = layer.nextn.shared_head_head ? layer.nextn.shared_head_head : model.output;
|
| 381 |
+
ggml_tensor * head_s = layer.nextn.shared_head_head ? layer.nextn.shared_head_head_s : model.output_s;
|
| 382 |
+
GGML_ASSERT(head_w && "HYV3 MTP: missing LM head (nextn.shared_head_head or model.output)");
|
| 383 |
+
cur = build_lora_mm(head_w, cur, head_s);
|
| 384 |
+
cb(cur, "result_output", -1);
|
| 385 |
+
|
| 386 |
+
res->t_logits = cur;
|
| 387 |
+
ggml_build_forward_expand(gf, cur);
|
| 388 |
+
}
|