model: add Hy3 (hy_v3) support with MTP speculative decoding (#25395)

* model: add Hy3 (hy_v3) architecture support

Adds Tencent Hunyuan 3 (HF architecture HYV3ForCausalLM, GGUF arch
hy_v3): a MoE decoder stack with per-head Q/K RMSNorm, a sigmoid
router with expert selection bias, an always-active ungated shared
expert, and leading dense block(s) (first_k_dense_replace).

The base implementation is ported from charlie12345's fork
(https://github.com/charlie12345/ROCmFPX, src/models/hyv3.cpp),
adapted to current mainline APIs (hparams.n_layer(), build_qkv,
build_moe_ffn with fused gate_up + scale tensors, output_s).

Note: blk.N.exp_probs_b is stored without a .bias suffix for
compatibility with existing hy_v3 GGUFs produced by that fork.

Co-Authored-By: charlie12345 <charlie12345@users.noreply.github.com>
Co-authored-by: Piotr Wilkin <ilintar@gmail.com>
Assisted-by: Claude Fable 5
This commit is contained in:
Satinder Grewal
2026-07-14 10:31:04 +12:00
committed by GitHub
parent 6eddde06a4
commit 2969d6d15d
16 changed files with 882 additions and 4 deletions

View File

@@ -1944,6 +1944,9 @@ static void test_role_markers_all_templates(testing & t) {
// MiniMax M2: ]~b]{user|ai}
{ "MiniMax-M2.jinja", "]~b]user", "]~b]ai" },
// HunYuan V3: <hy_User:opensource> / <hy_Assistant:opensource>
{ "tencent-Hy3.jinja", "<hy_User:opensource>", "<hy_Assistant:opensource>" },
// Nemotron Nano v2: <SPECIAL_11>{User|Assistant}; assistant marker
// is followed by a prefilled <think> block that gets included.
{ "NVIDIA-Nemotron-Nano-v2.jinja", "<SPECIAL_11>User", "<SPECIAL_11>Assistant" },

View File

@@ -1376,6 +1376,36 @@ static void test_string_methods(testing & t) {
"bXnXna"
);
test_template(t, "string.format() auto numbering",
"{{ '<{}|{}>'.format(s, 42) }}",
{{"s", "hello"}},
"<hello|42>"
);
test_template(t, "string.format() manual numbering",
"{{ '{1}-{0}-{1}'.format('a', 'b') }}",
json::object(),
"b-a-b"
);
test_template(t, "string.format() named fields",
"{{ '{name} is {age}'.format(name='Bob', age=7) }}",
json::object(),
"Bob is 7"
);
test_template(t, "string.format() escaped braces",
"{{ '{{}} {} {{x}}'.format('mid') }}",
json::object(),
"{} mid {x}"
);
test_template(t, "string.format() no fields",
"{{ 'plain'.format() }}",
json::object(),
"plain"
);
test_template(t, "undefined|capitalize",
"{{ arr|capitalize }}",
json::object(),

View File

@@ -346,6 +346,7 @@ static bool moe_mandatory(const llm_arch arch) {
case LLM_ARCH_ERNIE4_5:
case LLM_ARCH_ERNIE4_5_MOE:
case LLM_ARCH_HUNYUAN_MOE:
case LLM_ARCH_HY_V3:
case LLM_ARCH_OPENAI_MOE:
case LLM_ARCH_LFM2MOE:
case LLM_ARCH_SMALLTHINKER: