Skip to content

fix(models): accept an Inkling sub-config that carries a field and its alias - #1561

Merged
inureyes merged 1 commit into
mainfrom
fix/inkling-config-alias-duplicate
Aug 31, 2026
Merged

fix(models): accept an Inkling sub-config that carries a field and its alias#1561
inureyes merged 1 commit into
mainfrom
fix/inkling-config-alias-duplicate

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

inference-optimization/Inkling-0.6B-A0.6B could not be loaded at all. Config parse failed with duplicate field 'text_hidden_size', and once that key was handled, with duplicate field 'num_channels'.

Cause

InklingVisionConfig (src/vision/encoders/inkling_hmlp.rs) and InklingAudioConfig (src/audio/inkling_tower.rs) declare serde aliases:

#[serde(default = "default_channels", alias = "n_channels")]  pub num_channels: usize,
#[serde(default = "default_hidden_size", alias = "decoder_dmodel")]  pub text_hidden_size: usize,

Serde treats a field and its alias as the same field, so an object carrying both spellings is a duplicate-field error and the whole config fails to deserialize, even with the two values in agreement.

Neither spelling can be dropped

Read off the three downloaded checkpoints:

checkpoint decoder_dmodel text_hidden_size n_channels num_channels
mlx-community/Inkling-Small-mlx-4bit 4096 absent present absent
thinkingmachines/Inkling-Small-NVFP4 4096 absent present absent
inference-optimization/Inkling-0.6B-A0.6B 1024 1024 3 3

The Inkling-Small checkpoints ship only the aliases, so the aliases are load-bearing. The 0.6B ships both spellings of every pair at matching values and was rejected outright.

Fix

Equal values collapse to the canonical spelling. Disagreeing values are an error rather than a silent pick: the two keys name one quantity, and choosing either would surface much later as a shape mismatch.

The reconciliation is driven by a list of (canonical, alias) pairs rather than a special case per key, because fixing these one at a time is exactly how num_channels went unnoticed until text_hidden_size was fixed. That reasoning is recorded on the constant and asserted in the test, so the next alias added to those structs has an obvious place to go.

Both parse paths get it: InklingConfig::from_json_with_sidecar for audio_config, and src/loading/vlm_inkling.rs, which deserializes vision_config directly off the raw config value. Fixing only the first leaves the vision half failing.

Test plan

  • inkling_config_accepts_both_text_width_spellings: every declared pair doubled at matching values collapses to the canonical name (the real 0.6B shape); alias-only passes through untouched (the Inkling-Small shape); canonical-only passes through; contradictory values are rejected for both pairs with an error naming the section; an absent, null, or non-object sub-config is not an error.
  • cargo test --lib models::inkling 26, vision::encoders::inkling 5, audio::inkling 11, all passing.
  • cargo clippy --lib --tests -- -D warnings exit 0; cargo fmt --check clean.

Verified on all three real checkpoints, which is what this bug required and what CI could not do:

checkpoint config parse load generate
Inkling-0.6B-A0.6B now passes loads aborts on a separate SWA shape mismatch
Inkling-Small-mlx-4bit passes loads same separate abort
Inkling-Small-NVFP4 passes loads, 23.2 GB resident, 968 bf16 tensors converted same separate abort

NVFP4 matters as its own row: it reaches deserialization through promote_nvfp4_config, a different path, and also passes.

What this does not fix

All three checkpoints still abort during generation with [broadcast_shapes] Shapes (1,32) and (1,8) cannot be broadcast ((1,8) and (1,4) on the 0.6B). Those operands match swa_num_attention_heads and swa_num_key_value_heads exactly on every checkpoint, so it is a sliding-window-attention GQA problem, independent of config parsing and not introduced here. Being able to reach it is the progress this PR makes. Tracked separately.

Note on mlxcel inspect

inspect reported FITS for the 0.6B while it could not load, because it reads the safetensors header and never deserializes the full config. An inspect pass is not evidence that a checkpoint loads.

Closes #1560
Refs #1549

…s alias

inference-optimization/Inkling-0.6B-A0.6B could not be loaded at all. Config
parse failed with `duplicate field 'text_hidden_size'`, and once that key was
handled, with `duplicate field 'num_channels'`.

InklingVisionConfig and InklingAudioConfig declare serde aliases
(decoder_dmodel for text_hidden_size, n_channels for num_channels). Serde treats
a field and its alias as the same field, so an object carrying both spellings is
a duplicate-field error and the whole config fails to deserialize even when the
two values agree.

Neither spelling can be dropped, because published checkpoints disagree on which
they use. Read off the three downloaded configs: Inkling-Small-mlx-4bit and
Inkling-Small-NVFP4 ship only the aliases, so those are load-bearing;
Inkling-0.6B-A0.6B ships both spellings of every pair at matching values and was
rejected outright.

Equal values now collapse to the canonical spelling. Disagreeing values are an
error rather than a silent pick: the two keys name one quantity, and choosing
either would surface much later as a shape mismatch.

The reconciliation is driven by a list of (canonical, alias) pairs rather than a
special case per key, because fixing these one at a time is exactly how
num_channels went unnoticed until text_hidden_size was fixed. Both parse paths
get it: InklingConfig::from_json_with_sidecar for audio_config, and
loading/vlm_inkling.rs which deserializes vision_config directly off the raw
value.

Verified on all three real checkpoints, which now parse and load. They then
abort during generation on a separate sliding-window-attention shape mismatch
that this change does not address and does not introduce.
@inureyes inureyes added type:bug Bug fixes, error corrections, or issue resolutions priority:medium Medium priority area:models Model architectures, weights, loading, metadata status:review Under review labels Aug 31, 2026
@inureyes
inureyes merged commit c3d42a0 into main Aug 31, 2026
13 checks passed
@inureyes
inureyes deleted the fix/inkling-config-alias-duplicate branch August 31, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:models Model architectures, weights, loading, metadata priority:medium Medium priority status:review Under review type:bug Bug fixes, error corrections, or issue resolutions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(models): Inkling config is rejected when a sub-config carries both a field and its serde alias

1 participant