Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/generator/generators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ SpeculativeStats Generator::GetSpeculativeStats() const {
}

void Generator::RewindToLength(size_t new_length) {
if (model_->config_->model.type == "whisper" || model_->config_->model.type == "phi3v" || model_->config_->model.type == "decoder-pipeline" || model_->config_->model.type == "lfm2")
if (model_->config_->model.type == "whisper" || model_->config_->model.type == "phi3v" || model_->config_->model.type == "decoder-pipeline" || ModelType::IsLFM2(model_->config_->model.type))
throw std::runtime_error("RewindTo is currently not supported for " + model_->config_->model.type + ".");
const size_t current_length = search_->GetSequenceLength();
if (new_length > current_length)
Expand Down
2 changes: 1 addition & 1 deletion src/models/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ std::shared_ptr<Model> CreateModel(OrtEnv& ort_env, std::unique_ptr<Config> conf
// Check if it's a pipeline model by checking if decoder.pipeline is configured
if ((config->model.type == "fara" || config->model.type == "qwen2_5_vl" || config->model.type == "qwen3_vl") && !config->model.decoder.pipeline.empty())
return std::make_shared<Qwen2_5_VL_PipelineModel>(std::move(config), ort_env);
if (config->model.type == "lfm2")
if (ModelType::IsLFM2(config->model.type))
return std::make_shared<LFM2_Model>(std::move(config), ort_env);
if (config->model.type == "gpt2")
return std::make_shared<Gpt_Model>(std::move(config), ort_env);
Expand Down
7 changes: 4 additions & 3 deletions src/models/model_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Generators {
struct ModelType {
inline static bool IsLLM(const std::string& model_type) {
// Large-language model (LLM)
static constexpr std::array<std::string_view, 31> LLM = {"chatglm", "decoder", "ernie4_5", "gemma", "gemma2", "gemma3_text", "gemma3_vl_text", "gemma4_text", "gpt2", "gptoss", "granite", "granitemoehybrid", "hunyuandensev1", "internlm2", "lfm2", "llama", "mistral", "mistral3_text", "nemotron", "olmo", "phi", "phimoe", "phi3", "phi3small", "qwen2", "qwen2_5_vl_text", "qwen3", "qwen3_vl_text", "qwen3_5_moe_text", "qwen3_5_text", "smollm3"};
static constexpr std::array<std::string_view, 32> LLM = {"chatglm", "decoder", "ernie4_5", "gemma", "gemma2", "gemma3_text", "gemma3_vl_text", "gemma4_text", "gpt2", "gptoss", "granite", "granitemoehybrid", "hunyuandensev1", "internlm2", "lfm2", "lfm2_moe", "llama", "mistral", "mistral3_text", "nemotron", "olmo", "phi", "phimoe", "phi3", "phi3small", "qwen2", "qwen2_5_vl_text", "qwen3", "qwen3_vl_text", "qwen3_5_moe_text", "qwen3_5_text", "smollm3"};
return std::find(LLM.begin(), LLM.end(), model_type) != LLM.end();
}

Expand Down Expand Up @@ -74,8 +74,9 @@ struct ModelType {
}

inline static bool IsLFM2(const std::string& model_type) {
// Liquid Foundation Model 2: hybrid attention/conv architecture with conv state cache
return model_type == "lfm2";
// Liquid Foundation Model 2: hybrid attention/conv architecture with conv state cache.
// The MoE variant (LFM2-8B-A1B, LFM2.5-8B-A1B, LFM2-24B-A2B) has the same runtime shape.
return model_type == "lfm2" || model_type == "lfm2_moe";
}
};

Expand Down
8 changes: 8 additions & 0 deletions src/python/py/models/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ The tool currently supports the following model architectures.
- Granite MoE Hybrid
- HunYuan Dense V1
- InternLM2
- LFM2
- LFM2 MoE
- Llama
- Mistral
- Nemotron
Expand Down Expand Up @@ -591,6 +593,12 @@ python -m onnxruntime_genai.models.builder -m model_name -o path_to_output_folde
python builder.py -m model_name -o path_to_output_folder -p int4 -e execution_provider --extra_options qmoe_block_size=128
```

On CPU and WebGPU the block-wise expert weights use ONNX Runtime's MatMulNBits encoding with signed block scales: each block's max-magnitude element maps exactly to the lowest quantized value, so no extreme is clipped. This encoding has been validated end to end against the Hugging Face reference on CPU, and the WebGPU QMoE kernel consumes the MatMulNBits layout directly. TRT-RTX keeps the builder's original symmetric encoding with positive block scales until the signed-scale grid has been measured on that execution provider.

The CPU `QMoE` kernel dequantizes the experts to fp32 on every call unless the environment variable `ORT_USE_MLAS_Q4_GEMM_MOE=1` is set at runtime, which enables its AVX-512 MLAS Q4 fast path. That fast path re-quantizes to the same grid as this encoding, so enabling it only changes results at fp32 rounding level.

ONNX Runtime builds that include [microsoft/onnxruntime#32644](https://github.com/microsoft/onnxruntime/pull/32644) run block-wise experts on the MLAS QNBit GEMM (`MatMulNBits`) kernels directly, without the environment variable. For LFM2.5-8B-A1B int4 on a 30-core x86 server, CPU decode went from about 0.8 s per token with the default dequantize-then-GEMM path to 35 ms per token with `ORT_USE_MLAS_Q4_GEMM_MOE=1` and 15 ms per token with #32644.

##### QMoE Weights Prepacked

This scenario is for when you want to control the CUDA QMoE expert weight layout. The default value is `-1`, which lets the builder choose the layout automatically. Use `0` to export raw weights and let CUDA prepack them at runtime, or `1` to export CUTLASS-prepacked weights.
Expand Down
4 changes: 4 additions & 0 deletions src/python/py/models/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
HunyuanDenseV1Model,
InternLM2Model,
LFM2Model,
LFM2MoEModel,
LlamaModel,
Mistral3TextModel,
MistralModel,
Expand Down Expand Up @@ -536,6 +537,9 @@ def create_model(
onnx_model = InternLM2Model(config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options)
elif config.architectures[0] == "Lfm2ForCausalLM":
onnx_model = LFM2Model(config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options)
elif config.architectures[0] == "Lfm2MoeForCausalLM":
onnx_model = LFM2MoEModel(config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options)
onnx_model.model_type = "lfm2_moe"
elif config.architectures[0] == "LlamaForCausalLM":
onnx_model = LlamaModel(config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options)
elif config.architectures[0] == "MistralForCausalLM":
Expand Down
3 changes: 2 additions & 1 deletion src/python/py/models/builders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .granite import GraniteModel, GraniteMoEHybridModel
from .hunyuan import HunyuanDenseV1Model
from .internlm import InternLM2Model
from .lfm2 import LFM2Model
from .lfm2 import LFM2Model, LFM2MoEModel
from .llama import LlamaModel
from .mistral import Mistral3TextModel, MistralModel
from .mtp import MTPModel
Expand Down Expand Up @@ -58,6 +58,7 @@
"HunyuanDenseV1Model",
"InternLM2Model",
"LFM2Model",
"LFM2MoEModel",
"LlamaModel",
"MTPModel",
"Mistral3TextModel",
Expand Down
Loading
Loading