Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
53c6c92
Use MRotaryEmbedding contrib op for Qwen VL model builders
Copilot Aug 6, 2026
3f3f0e5
Add Qwen-2.5 VL and Qwen-3 VL text-only model types
kunal-vaishnavi Aug 8, 2026
d507b95
Validate Qwen-2.5 and Qwen-3 series of models
kunal-vaishnavi Aug 10, 2026
2e814bf
Validate Qwen-3.5 dense model
kunal-vaishnavi Aug 15, 2026
6ce342b
Simplify LFM models and integrate new caches into base class
kunal-vaishnavi Aug 19, 2026
923854b
Refactor RoPE parameter handling
kunal-vaishnavi Aug 20, 2026
727cf4c
Validate Qwen-3.5 MoE model
kunal-vaishnavi Aug 20, 2026
6c1a4ac
Update todo comments
kunal-vaishnavi Aug 20, 2026
011a747
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 20, 2026
6143f7e
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 20, 2026
fb4852e
Restore MTP changes for now
kunal-vaishnavi Aug 21, 2026
b3f5387
Update Copilot reviewer instructions
kunal-vaishnavi Aug 21, 2026
7282e79
Fix model builder after MTP restore
kunal-vaishnavi Aug 21, 2026
773f723
Consolidate and refactor many things
kunal-vaishnavi Aug 21, 2026
a4aa771
Remove unused variable
kunal-vaishnavi Aug 21, 2026
ba92c77
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 21, 2026
e9f117d
Fix typo in Phi LongRoPE detection
kunal-vaishnavi Aug 22, 2026
aebf034
Refactor quant dtype usage and add new token ids to GenAI config
kunal-vaishnavi Aug 22, 2026
5b290e2
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 22, 2026
30bb75c
Refactor context length changes
kunal-vaishnavi Aug 22, 2026
abbfb06
Fix model builder imports and test fixtures
kunal-vaishnavi Aug 22, 2026
3edd991
Initial refactor of quantized model loaders and MTP building
kunal-vaishnavi Aug 23, 2026
f843ce6
Add loader README to Copilot
kunal-vaishnavi Aug 23, 2026
f48a1ea
Apply code guidelines on loaders
kunal-vaishnavi Aug 23, 2026
4cc5629
Refactor state window into base class for all models to use
kunal-vaishnavi Aug 23, 2026
f4431c9
Move Qwen KV scale handling into base builder
kunal-vaishnavi Aug 23, 2026
acd405d
Normalize quantized linear formats in loaders
kunal-vaishnavi Aug 23, 2026
08386a2
Move native QMoE emission into base builder.
kunal-vaishnavi Aug 23, 2026
d85df93
Move quantization exclusions into loader metadata.
kunal-vaishnavi Aug 23, 2026
2026464
Move MTP checkpoint adaptation into loaders.
kunal-vaishnavi Aug 23, 2026
2909b50
Fix CUTLASS QMoE signed-scale test
kunal-vaishnavi Aug 23, 2026
f41c267
Generalize native packed QMoE experts
kunal-vaishnavi Aug 23, 2026
72c6f87
Add GPT-OSS MXFP4 expert loader
kunal-vaishnavi Aug 23, 2026
75aa270
Move GPT-OSS MXFP4 emission into loader
kunal-vaishnavi Aug 23, 2026
6a7eae9
Normalize Quark packed QMoE experts
kunal-vaishnavi Aug 23, 2026
1feed27
Add GPT-OSS packed expert parity coverage
kunal-vaishnavi Aug 23, 2026
efaf1af
Rename KV cache quant extra options
kunal-vaishnavi Aug 23, 2026
b01aa0d
Update documentation
kunal-vaishnavi Aug 23, 2026
7c2bf39
Fix unit tests
kunal-vaishnavi Aug 23, 2026
0d3dd9f
Derive windowed KV cache behavior
kunal-vaishnavi Aug 24, 2026
633fa55
Route windowed cache policy through context attrs
kunal-vaishnavi Aug 24, 2026
bc020f2
Rename windowed cache context attribute
kunal-vaishnavi Aug 24, 2026
1183848
Document builder attribute dictionaries
kunal-vaishnavi Aug 24, 2026
f043604
Fix CodeQL errors
kunal-vaishnavi Aug 24, 2026
31547da
Fix KV cache quantization metadata
kunal-vaishnavi Aug 24, 2026
681890e
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 24, 2026
ef804c7
Fix PEP 701 error
kunal-vaishnavi Aug 24, 2026
a4468d4
Merge branch 'main' into copilot/copilotmrope-variants-support
kunal-vaishnavi Aug 25, 2026
856c2d3
Fix CI failures
kunal-vaishnavi Aug 25, 2026
ef7a412
Fix unused local variable errors
kunal-vaishnavi Aug 25, 2026
04ae1ec
Make private attribute into public attribute
kunal-vaishnavi Aug 25, 2026
96e6294
Fix CodeQL error
kunal-vaishnavi Aug 25, 2026
6bcb310
Fix CI failure with default RoPE
kunal-vaishnavi Aug 25, 2026
0aa0e93
Fix CI import failure
kunal-vaishnavi Aug 25, 2026
fb99431
Update imports in unit test
kunal-vaishnavi Aug 25, 2026
3281338
Add validation for num hidden layers
kunal-vaishnavi Aug 25, 2026
fae35ae
Fix Whisper sequence length issue
kunal-vaishnavi Aug 25, 2026
2dd3789
Fix clang formatting error
kunal-vaishnavi Aug 25, 2026
22f5932
Fix KV cache name access for Whisper
kunal-vaishnavi Aug 25, 2026
d97f982
Fix removing output names
kunal-vaishnavi Aug 25, 2026
2905f2e
Address PR review comments
Copilot Aug 25, 2026
62cb323
Move DML GatedAdd fallback to EP expansion
Copilot Aug 25, 2026
f9a4e01
Remove fallback from base method now that expansion has it
kunal-vaishnavi Aug 25, 2026
cbf840a
Fix recurrent state test model config
Copilot Aug 26, 2026
6e4eaa2
Fix checked-in recurrent test model config
Copilot Aug 26, 2026
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
4 changes: 4 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ struct DecoderInputs_Element : JSON::Element {
v_.attention_metadata = JSON::Get<std::string_view>(value);
} else if (name == "past_conv_names") {
v_.past_conv_names = JSON::Get<std::string_view>(value);
} else if (name == "past_recurrent_names") {
v_.past_recurrent_names = JSON::Get<std::string_view>(value);
} else if (name == "targets") {
v_.targets = JSON::Get<std::string_view>(value);
} else if (name == "lstm_hidden_state") {
Expand Down Expand Up @@ -390,6 +392,8 @@ struct DecoderOutputs_Element : JSON::Element {
v_.rnn_states = JSON::Get<std::string_view>(value);
} else if (name == "present_conv_names") {
v_.present_conv_names = JSON::Get<std::string_view>(value);
} else if (name == "present_recurrent_names") {
v_.present_recurrent_names = JSON::Get<std::string_view>(value);
} else if (name == "outputs") {
v_.outputs = JSON::Get<std::string_view>(value);
} else if (name == "lstm_hidden_state") {
Expand Down
13 changes: 10 additions & 3 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ struct Config {
static constexpr std::string_view PositionIdsName = "position_ids";
static constexpr std::string_view PastKeyName = "past_key_values.%d.key";
static constexpr std::string_view PastValueName = "past_key_values.%d.value";
static constexpr std::string_view PastConvName = "past.%d.conv";
static constexpr std::string_view PastRecurrentName = "past.%d.recurrent";
static constexpr std::string_view LogitsName = "logits";
static constexpr std::string_view PresentKeyName = "present.%d.key";
static constexpr std::string_view PresentValueName = "present.%d.value";
static constexpr std::string_view PresentConvName = "present.%d.conv";
static constexpr std::string_view PresentRecurrentName = "present.%d.recurrent";
static constexpr std::string_view RnnStatesName = "rnn_states";
static constexpr std::string_view RnnStatesPrevName = "rnn_states_prev";
static constexpr std::string_view CumulativeSequenceLengthsName = "cumulative_sequence_lengths";
Expand All @@ -40,6 +44,7 @@ struct Config {
static constexpr std::string_view AudioProjectionModeName = "audio_projection_mode";
static constexpr std::string_view AudioFeaturesName = "audio_features";
static constexpr std::string_view NumAudioTokens = "num_audio_tokens";
static constexpr std::string_view OutputCrossQKName = "output_cross_qk_%d";

// Vision encoder names
static constexpr std::string_view PixelValuesName = "pixel_values";
Expand Down Expand Up @@ -385,7 +390,8 @@ struct Config {
std::string past_sequence_lengths{Defaults::PastSequenceLengthsName};
std::string block_table{Defaults::BlockTableName};
std::string attention_metadata{Defaults::AttentionMetadataName};
std::string past_conv_names{"past_conv.%d"}; // Conv cache input name template (LFM2)
std::string past_conv_names{Defaults::PastConvName}; // Conv cache input name template (LFM2)
std::string past_recurrent_names{Defaults::PastRecurrentName};

// RNNT decoder inputs
std::string targets;
Expand All @@ -404,9 +410,10 @@ struct Config {
std::string present_key_names{Defaults::PresentKeyName};
std::string present_value_names{Defaults::PresentValueName};
std::string present_names; // When key/value pairs are combined
std::string output_cross_qk_names{"output_cross_qk_%d"};
std::string output_cross_qk_names{Defaults::OutputCrossQKName};
std::string rnn_states{Defaults::RnnStatesName};
std::string present_conv_names{"present_conv.%d"}; // Conv cache output name template (LFM2)
std::string present_conv_names{Defaults::PresentConvName}; // Conv cache output name template (LFM2)
std::string present_recurrent_names{Defaults::PresentRecurrentName};

// RNNT decoder outputs
std::string outputs;
Expand Down
40 changes: 20 additions & 20 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, 27> LLM = {"chatglm", "decoder", "ernie4_5", "gemma", "gemma2", "gemma3_text", "gemma4_text", "gpt2", "gptoss", "granite", "granitemoehybrid", "hunyuandensev1", "internlm2", "lfm2", "llama", "mistral", "nemotron", "olmo", "phi", "phimoe", "phi3", "phi3small", "qwen2", "qwen3", "qwen3_5_moe_text", "qwen3_5_text", "smollm3"};
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"};
return std::find(LLM.begin(), LLM.end(), model_type) != LLM.end();
}

Expand All @@ -27,23 +27,24 @@ struct ModelType {
return std::find(VLM.begin(), VLM.end(), model_type) != VLM.end();
}

inline static bool IsQwenVLFamily(const std::string& model_type) {
// Qwen-VL family: models requiring 3D mRoPE position IDs
static constexpr std::array<std::string_view, 5> QwenVL = {"fara", "qwen2_5_vl", "qwen3_vl", "qwen3_5", "qwen3_5_moe"};
return std::find(QwenVL.begin(), QwenVL.end(), model_type) != QwenVL.end();
}

inline static bool IsPixtralFamily(const std::string& model_type) {
// Pixtral family: per-image vision loop with variable resolution
return model_type == "mistral3";
}

inline static bool IsALM(const std::string& model_type) {
// Audio-language model (ALM)
static constexpr std::array<std::string_view, 1> ALM = {"whisper"};
return std::find(ALM.begin(), ALM.end(), model_type) != ALM.end();
}

inline static bool IsMMM(const std::string& model_type) {
// Multi-modal model (MMM)
static constexpr std::array<std::string_view, 2> MMM = {"gemma4", "phi4mm"};
return std::find(MMM.begin(), MMM.end(), model_type) != MMM.end();
}

inline static bool IsPipe(const std::string& model_type) {
// Pipeline (Pipe)
static constexpr std::array<std::string_view, 1> Pipe = {"decoder-pipeline"};
return std::find(Pipe.begin(), Pipe.end(), model_type) != Pipe.end();
}

inline static bool IsRNNT(const std::string& model_type) {
// RNNT models bypass the search/logits pipeline entirely.
static constexpr std::array<std::string_view, 1> rnnt_types = {"nemotron_speech"};
Expand All @@ -61,16 +62,15 @@ struct ModelType {
return IsRNNT(model_type) || IsTDT(model_type);
}

inline static bool IsMMM(const std::string& model_type) {
// Multi-modal model (MMM)
static constexpr std::array<std::string_view, 2> MMM = {"gemma4", "phi4mm"};
return std::find(MMM.begin(), MMM.end(), model_type) != MMM.end();
inline static bool IsQwenVLFamily(const std::string& model_type) {
// Qwen-VL family: models requiring 3D mRoPE position IDs
static constexpr std::array<std::string_view, 10> QwenVL = {"fara", "qwen2_5_vl_text", "qwen2_5_vl", "qwen3_vl", "qwen3_vl_text", "qwen3_5", "qwen3_5_moe", "qwen3_5_moe_text", "qwen3_5_text", "videochat_flash_qwen"};
return std::find(QwenVL.begin(), QwenVL.end(), model_type) != QwenVL.end();
}

inline static bool IsPipe(const std::string& model_type) {
// Pipeline (Pipe)
static constexpr std::array<std::string_view, 1> Pipe = {"decoder-pipeline"};
return std::find(Pipe.begin(), Pipe.end(), model_type) != Pipe.end();
inline static bool IsPixtralFamily(const std::string& model_type) {
// Pixtral family: per-image vision loop with variable resolution
return model_type == "mistral3";
}

inline static bool IsLFM2(const std::string& model_type) {
Expand Down
49 changes: 24 additions & 25 deletions src/models/recurrent_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,30 @@

namespace Generators {

std::string ComposeCacheName(const std::string& template_string, int index) {
constexpr int32_t CacheNameLength = 64;
char cache_name[CacheNameLength];
if (auto length = snprintf(cache_name, std::size(cache_name), template_string.c_str(), index);
length < 0 || length >= CacheNameLength) {
throw std::runtime_error("Unable to compose cache name from the provided template " + template_string +
". This could be either due to an encoding error or the name being too long.");
}
return std::string(cache_name);
}

RecurrentState::RecurrentState(State& state)
: state_{state} {
const auto& past_key_template = model_.config_->model.decoder.inputs.past_key_names;
const auto& present_key_template = model_.config_->model.decoder.outputs.present_key_names;

// Derive recurrent name templates from KV name templates
auto derive_template = [](const std::string& kv_template, const std::string& suffix) -> std::string {
auto pos = kv_template.rfind('.');
if (pos == std::string::npos) return "";
return kv_template.substr(0, pos + 1) + suffix;
};

std::string past_conv_template = derive_template(past_key_template, "conv_state");
std::string past_recurrent_template = derive_template(past_key_template, "recurrent_state");
std::string present_conv_template = derive_template(present_key_template, "conv_state");
std::string present_recurrent_template = derive_template(present_key_template, "recurrent_state");
// Discover recurrent layer indices by scanning all session input names
const auto& past_recurrent_template = model_.config_->model.decoder.inputs.past_recurrent_names;
const auto placeholder_pos = past_recurrent_template.find("%d");
if (placeholder_pos == std::string::npos) return;

if (past_conv_template.empty()) return;
const auto prefix = past_recurrent_template.substr(0, placeholder_pos);
const auto suffix = past_recurrent_template.substr(placeholder_pos + 2);

// Discover recurrent layer indices by scanning all session input names
for (const auto& name : model_.session_info_.GetInputNames()) {
// Try to match against the conv_state template (e.g. "past_key_values.%d.conv_state")
// Try to match against the recurrent state template (e.g. "past.%d.recurrent")
// Extract the layer index from names that match
auto prefix = past_conv_template.substr(0, past_conv_template.find('%'));
auto suffix = past_conv_template.substr(past_conv_template.find('%') + 2); // skip %d
if (name.size() > prefix.size() + suffix.size() &&
name.compare(0, prefix.size(), prefix) == 0 &&
name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0) {
Expand All @@ -56,10 +55,10 @@ RecurrentState::RecurrentState(State& state)
return s; }() + ")");

for (int idx : layer_indices_) {
input_name_strings_.push_back(ComposeKeyValueName(past_conv_template, idx));
input_name_strings_.push_back(ComposeKeyValueName(past_recurrent_template, idx));
output_name_strings_.push_back(ComposeKeyValueName(present_conv_template, idx));
output_name_strings_.push_back(ComposeKeyValueName(present_recurrent_template, idx));
input_name_strings_.push_back(ComposeCacheName(model_.config_->model.decoder.inputs.past_conv_names, idx));
input_name_strings_.push_back(ComposeCacheName(model_.config_->model.decoder.inputs.past_recurrent_names, idx));
output_name_strings_.push_back(ComposeCacheName(model_.config_->model.decoder.outputs.present_conv_names, idx));
output_name_strings_.push_back(ComposeCacheName(model_.config_->model.decoder.outputs.present_recurrent_names, idx));
}

conv_type_ = model_.session_info_.GetInputDataType(input_name_strings_[0]);
Expand All @@ -83,8 +82,8 @@ RecurrentState::RecurrentState(State& state)
std::to_string(shape[i]) + " at axis " + std::to_string(i));
}
};
validate_shape(conv_shape_, "conv_state");
validate_shape(recurrent_shape_, "recurrent_state");
validate_shape(conv_shape_, "conv");
validate_shape(recurrent_shape_, "recurrent");

const int num_layers = static_cast<int>(layer_indices_.size());

Expand Down
2 changes: 1 addition & 1 deletion src/python/py/models/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ The scale file is produced by the `kv_cache_calibration` module, which runs a ba
python -m onnxruntime_genai.models.builder -i path_to_local_folder_on_disk -o path_to_baseline_folder -p precision -e cuda -c cache_dir_to_store_temp_files

# 2. Calibrate the scales:
python -m onnxruntime_genai.models.kv_cache_calibration --model path_to_baseline_folder --tokenizer path_to_local_folder_on_disk --out path_to_scales.json --quant-type int8_per_channel
python -m onnxruntime_genai.models.quantization.kv_cache_calibration --model path_to_baseline_folder --tokenizer path_to_local_folder_on_disk --out path_to_scales.json --quant-type int8_per_channel
```

Then rebuild with the quantized KV cache:
Expand Down
Loading