model : fix SWA not being enabled for EXAONE 4.5 - #26848
Merged
Merged
Conversation
load_arch_hparams tests `hparams.n_layer() == 64` before LLM_KV_NEXTN_PREDICT_LAYERS has been read. n_layer() returns n_layer_all - n_layer_nextn and n_layer_nextn defaults to 0, so a GGUF carrying the MTP head (block_count=65, nextn=1) evaluates to 65 and the whole SWA block is skipped. The model type switch further down in the same function reads 64, because by then the key has been loaded. n_swa is still filled in by the unconditional get_key below the block, so llama_model_n_swa() reports 4096 and the logs look correct while only swa_type stays LLAMA_SWA_TYPE_NONE. This affects the official LGAI-EXAONE GGUF release as well. EXAONE 4.0 has no MTP head, so block_count is 64 there and the check matches.
create_tensor asserts on a null buffer type when building from metadata alone, but buft_for_tensor returns null by design for tensors marked TENSOR_SKIP, which is how architectures with nextn/MTP layers mark theirs. Those models cannot be constructed by llama_model_init_from_user at all. The file-backed path below already returns nullptr for the same tensors, so callers see the same thing either way.
Builds a synthetic exaone4 model with the layout the shipped EXAONE 4.5 GGUFs use (block_count 65 + nextn 1). The swa_type check is the one that catches the ordering bug; the n_layer_nextn and n_layer() checks only tell a broken fixture apart from a real regression. Fails before the ordering fix with "swa_type is not STANDARD", passes after.
junmo-kim
requested review from
CISC,
JohannesGaessler and
ggerganov
as code owners
August 10, 2026 14:53
CISC
approved these changes
Aug 10, 2026
CISC
left a comment
Member
There was a problem hiding this comment.
Good catch, thank you for the fix.
Please remove the test though, we would like test-llama-archs to be as agnostic as possible and this should not be necessary.
CISC
approved these changes
Aug 10, 2026
taronaeo
approved these changes
Aug 11, 2026
huaxel
pushed a commit
to huaxel/CachyLLama
that referenced
this pull request
Aug 12, 2026
* model : fix SWA not being enabled for EXAONE 4.5 load_arch_hparams tests `hparams.n_layer() == 64` before LLM_KV_NEXTN_PREDICT_LAYERS has been read. n_layer() returns n_layer_all - n_layer_nextn and n_layer_nextn defaults to 0, so a GGUF carrying the MTP head (block_count=65, nextn=1) evaluates to 65 and the whole SWA block is skipped. The model type switch further down in the same function reads 64, because by then the key has been loaded. n_swa is still filled in by the unconditional get_key below the block, so llama_model_n_swa() reports 4096 and the logs look correct while only swa_type stays LLAMA_SWA_TYPE_NONE. This affects the official LGAI-EXAONE GGUF release as well. EXAONE 4.0 has no MTP head, so block_count is 64 there and the check matches. * model-loader : skip TENSOR_SKIP tensors in the metadata-only path create_tensor asserts on a null buffer type when building from metadata alone, but buft_for_tensor returns null by design for tensors marked TENSOR_SKIP, which is how architectures with nextn/MTP layers mark theirs. Those models cannot be constructed by llama_model_init_from_user at all. The file-backed path below already returns nullptr for the same tensors, so callers see the same thing either way. * tests : cover exaone4 hparams ordering Builds a synthetic exaone4 model with the layout the shipped EXAONE 4.5 GGUFs use (block_count 65 + nextn 1). The swa_type check is the one that catches the ordering bug; the n_layer_nextn and n_layer() checks only tell a broken fixture apart from a real regression. Fails before the ordering fix with "swa_type is not STANDARD", passes after. * Revert "tests : cover exaone4 hparams ordering" This reverts commit d2f3baf. * Revert "model-loader : skip TENSOR_SKIP tensors in the metadata-only path" This reverts commit aecb9bc.
brittlewis12
pushed a commit
to brittlewis12/llama.cpp
that referenced
this pull request
Aug 17, 2026
* model : fix SWA not being enabled for EXAONE 4.5 load_arch_hparams tests `hparams.n_layer() == 64` before LLM_KV_NEXTN_PREDICT_LAYERS has been read. n_layer() returns n_layer_all - n_layer_nextn and n_layer_nextn defaults to 0, so a GGUF carrying the MTP head (block_count=65, nextn=1) evaluates to 65 and the whole SWA block is skipped. The model type switch further down in the same function reads 64, because by then the key has been loaded. n_swa is still filled in by the unconditional get_key below the block, so llama_model_n_swa() reports 4096 and the logs look correct while only swa_type stays LLAMA_SWA_TYPE_NONE. This affects the official LGAI-EXAONE GGUF release as well. EXAONE 4.0 has no MTP head, so block_count is 64 there and the check matches. * model-loader : skip TENSOR_SKIP tensors in the metadata-only path create_tensor asserts on a null buffer type when building from metadata alone, but buft_for_tensor returns null by design for tensors marked TENSOR_SKIP, which is how architectures with nextn/MTP layers mark theirs. Those models cannot be constructed by llama_model_init_from_user at all. The file-backed path below already returns nullptr for the same tensors, so callers see the same thing either way. * tests : cover exaone4 hparams ordering Builds a synthetic exaone4 model with the layout the shipped EXAONE 4.5 GGUFs use (block_count 65 + nextn 1). The swa_type check is the one that catches the ordering bug; the n_layer_nextn and n_layer() checks only tell a broken fixture apart from a real regression. Fails before the ordering fix with "swa_type is not STANDARD", passes after. * Revert "tests : cover exaone4 hparams ordering" This reverts commit d2f3baf. * Revert "model-loader : skip TENSOR_SKIP tensors in the metadata-only path" This reverts commit aecb9bc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
I ran into two problems trying to run EXAONE 4.5 locally with llama.cpp.
Digging in: the model is supposed to use sliding window attention, but the code never takes that path.
load_arch_hparams()insrc/models/exaone4.cppcheckshparams.n_layer() == 64beforeLLM_KV_NEXTN_PREDICT_LAYERShas been read.n_layer()returnsn_layer_all - n_layer_nextnandn_layer_nextndefaults to 0, so for a GGUF carrying the MTP head (block_count=65,nextn=1) it evaluates to 65 at that point and the whole SWA block is skipped.The fix moves that read above the check;
n_layer()then already accounts for the MTP head when the condition runs. The assert that validates the key moves with it, matching how the other nextn architectures keep the two adjacent.Additional information
This affects the official LGAI-EXAONE release as well as third-party conversions. EXAONE 4.0 is unaffected: no MTP head, so
block_countis 64 and the check matches.It is easy to miss because
n_swalooks fine either way. Theml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, ...)call sits outside the block, so it reads 4096 even when SWA is off, andllama_model_n_swa()reports that too. Onlyswa_typestaysLLAMA_SWA_TYPE_NONE.The loader commit
The new test in
tests/test-llama-archs.cppneeds this commit. The metadata-only path ofcreate_tensorasserts on a null buffer type, butbuft_for_tensorreturns nullptr by design forTENSOR_SKIPtensors, which is how architectures with nextn/MTP layers mark theirs. The file-backed path a few lines below already returns nullptr for the same tensors, so this is not a behaviour change from a caller's point of view. It also makesllama_model_init_from_userwork for the other MTP architectures, not only exaone4. I kept it separate since it is in the loader rather than the model, and can split it out if preferred.Before / after
Same GGUF (official Q4_K_M), same prompt (11,271 tokens),
llama-completion:before
after
The prompt plants a fact near the start and asks for it after ~11k tokens, which is past
n_swa.@nuxlear (author of #21733): the config lists
sliding_window: 4096with anLLLGpattern, and the split after the fix comes out 16 global + 48 local + 1 MTP. Is that what the model expects?Tests
Added three cases to
tests/test-llama-archs.cpp, one for each side of the branch. A 32B model with an MTP head (block_count 65) has to come out with SWA enabled, the same model without one (block_count 64) has to as well, and the 1.2B shape (block_count 31) has to not. The first fails before the fix; the other two pin the condition against being widened or dropped, and each of the three is the only one that catches its own case.The full arch walk that
ctest -L mainruns passes: exit 0, no failures.The change is backend-agnostic: it only touches hparams ordering and the loader's metadata-only path. The before/after on a real model was measured on Windows with Vulkan.
Left alone:
sliding_window_patternisarr[bool]in the GGUF while the code reads it into auint32_tviaget_key_or_arrwithrequired=false. It is silently ignored today, and the hardcoded period of 4 happens to match. I can file that separately.Requirements