Skip to content

Carry ggml-org#25731 (TML Inkling) merged onto b10775 - #172

Closed
danielhanchen wants to merge 23 commits into
base/upstream-de8656bd9from
inkling-25731-b10775
Closed

Carry ggml-org#25731 (TML Inkling) merged onto b10775#172
danielhanchen wants to merge 23 commits into
base/upstream-de8656bd9from
inkling-25731-b10775

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Carries ggml-org#25731 (TML Inkling) onto b10775 so the nightly has something current to pin.

The pin in scripts/unsloth/pr-set.json is 44eb88e9a, which forked from daef7b687 and last moved on 08-31. It stopped merging onto the base tag:

refused ggml/src/ggml-cuda/fattn-mma-f16.cuh: merge base is not empty,
        so at least one side edited existing text

The head repository is ggml-org, so the repin bot correctly leaves it alone. This is our own carry branch instead, base branch base/upstream-67a17c17c, which is b10775 verbatim.

The collision

Upstream added sparse attention to the MMA flash attention kernel in the same weeks ggml-org#25731 added banded relative bias to it. Both thread an extra parameter through flash_attn_ext_f16_iter and flash_attn_ext_f16_process_tile, and they land in the same 13 places:

  • upstream adds a const int32_t * indices parameter, a use_sparse template argument, and splits KV_max_ptr into KV_max / sparse_indices
  • Add TML Inkling architecture ggml-org/llama.cpp#25731 adds const float * rel_f, rel_extent, head_q0 and ne11, and derives banded_bias from max_bias < 0.0f

Every one of the 13 is a union of two disjoint parameter additions. The parameter order is not a choice: flash_attn_ext_f16_process_tile's own declaration merged without a conflict, as mask_h, indices, sinks_f, rel_f, so upstream's addition goes first and every call site follows it.

The one hunk that is not purely mechanical is sinks_f:

const float * sinks_f = sinks && !banded_bias ? (const float *) sinks + zt_Q : nullptr;
const float * rel_f = banded_bias ?
    (const float *) sinks + int64_t(sequence)*ne01.z*ne02*rel_extent : nullptr;
const int32_t * indices = use_sparse ? sparse_indices + (int64_t(sequence % ne33)*ne31 + jt*ncols1)*ne11 : nullptr;

ggml-org#25731 overloads the sinks pointer to carry the relative bias table when max_bias is negative, so its narrower sinks_f has to win; upstream's version would feed the bias table to the sink path. indices reads sparse_indices, which nothing else touches, so the three locals are independent.

ggml/src/ggml-backend.cpp and tests/CMakeLists.txt also conflicted. Both are pure add/add and were resolved by scripts/unsloth/additive_merge.py: a GGML_OP_FLASH_ATTN_EXT_BANDED case next to upstream's GGML_OP_MUL_MAT one, and the two new test targets next to upstream's metallib test block.

Testing

Built CUDA, sm_100, B200, CUDA 13.1.

$ ./build/bin/test-backend-ops test -o FLASH_ATTN_EXT
  2948/2948 tests passed
  Backend CUDA0: OK

$ ./build/bin/test-backend-ops test -o FLASH_ATTN_EXT_BANDED
  13/13 tests passed
  Backend CUDA0: OK

$ ./build/bin/test-flash-attn-bias          # all cases PASS
$ ./build/bin/test-flash-attn-generic-hash  # OK

That covers each feature against the CPU reference: the 2948 cases exercise upstream's sparse and view paths, the 13 banded cases and test-flash-attn-bias exercise ggml-org#25731's. What it does not cover is the two features active in the same kernel launch, because no test builds a banded op with a sparse index list; that combination does not exist in either branch's test set and nothing in the merge creates it, since banded_bias and use_sparse come from different inputs.

git diff b10775..HEAD is 64 files, +4080/-124, the ggml-org#25731 delta.

danielhanchen and others added 21 commits July 18, 2026 08:09
Hybrid attention model: 55 sliding-window plus 11 global layers, banded
content-dependent relative position bias instead of RoPE, per-layer short
convolution state, fine-grained MoE (256 experts top-6 plus 2 shared),
attention log-scaling past 128K, 1M context.

Includes the GGML_OP_FLASH_ATTN_EXT_BANDED operator (CPU and CUDA, fused
into the MMA flash attention kernel with an fp16 accumulator overflow
guard), HF to GGUF conversion, chat template with typed content block
parsing (interleaved thinking, narration and tool calls), mmproj vision
and audio support, and backend op tests at production shapes.
# Conflicts:
#	ggml/src/ggml-cuda/mmq.cuh
#	src/llama-model-saver.cpp
#	src/llama-vocab.h
Upstream 910196f renamed common_chat_params::thinking_end_tag to
thinking_end_tags and changed it from std::string to a vector, so
data.thinking_end_tag = END_MESSAGE no longer compiles.
# Conflicts:
#	src/llama-arch.cpp
# Conflicts:
#	ggml/include/ggml-rpc.h
#	ggml/src/ggml-backend-meta.cpp
#	ggml/src/ggml-cuda/ggml-cuda.cu
#	src/llama-model-saver.cpp
#	tests/test-llama-archs.cpp
# Conflicts:
#	ggml/src/ggml-rpc/ggml-rpc.cpp
# Conflicts:
#	ggml/src/ggml-backend.cpp
#	ggml/src/ggml-cuda/fattn-mma-f16.cuh
#	tests/CMakeLists.txt
@danielhanchen
danielhanchen requested a review from CISC as a code owner September 3, 2026 09:12
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T23:54:20.319000Z f639c77 New commits
🔒 Security Review Completed 2026-09-03T09:19:33.693164Z 5b836b6 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b836b6f1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +10402 to +10406
test_cases.emplace_back(new test_flash_attn_ext_banded(128, 8, 1, 512, 8192, 1024, 1, GGML_TYPE_F16, GGML_TYPE_F32));
test_cases.emplace_back(new test_flash_attn_ext_banded(128, 8, 1, 512, 16384, 1024, 1, GGML_TYPE_F16, GGML_TYPE_F32));
test_cases.emplace_back(new test_flash_attn_ext_banded(128, 8, 1, 512, 16403, 1024, 1, GGML_TYPE_F16, GGML_TYPE_F32));
test_cases.emplace_back(new test_flash_attn_ext_banded(128, 8, 1, 512, 16896, 1024, 1, GGML_TYPE_F16, GGML_TYPE_F32));
test_cases.emplace_back(new test_flash_attn_ext_banded(128, 8, 1, 512, 17024, 1024, 1, GGML_TYPE_F16, GGML_TYPE_F32));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move production-scale cases out of the default test matrix

Every normal test-backend-ops run now executes these stress-sized cases for each applicable backend and its reference; the 32768-token case alone reports about 68.7 billion FLOPs. In the inspected Release CPU build, even the 8192-token case took approximately 20 seconds, so the remaining near-duplicate 16K-32K cases can add several minutes and risk the 900-second CI timeout on slower runners. Keep representative small coverage here and move the boundary stress cases to an opt-in suite.

AGENTS.md reference: AGENTS.md:L87-L87

Useful? React with 👍 / 👎.

Comment thread tests/CMakeLists.txt
Comment on lines +312 to +316
add_test(NAME test-flash-attn-bias COMMAND test-flash-attn-bias)

add_executable(test-flash-attn-generic-hash test-flash-attn-generic-hash.cpp)
target_link_libraries(test-flash-attn-generic-hash PRIVATE ggml)
add_test(NAME test-flash-attn-generic-hash COMMAND test-flash-attn-generic-hash)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the new tests in the main CI suite

The inspected CI entry points invoke CTest with -L main, but these raw add_test calls assign no label, unlike the repository's llama_test helper. Consequently neither new regression test runs in the standard CI suite; register them through the existing helper or explicitly assign the main label.

AGENTS.md reference: AGENTS.md:L87-L87

Useful? React with 👍 / 👎.

Comment on lines +114 to +119
uint64_t fnv = UINT64_C(1469598103934665603);
const uint8_t * bytes = reinterpret_cast<const uint8_t *>(result.data());
for (size_t i = 0; i < result.size()*sizeof(result[0]); ++i) {
fnv ^= bytes[i];
fnv *= UINT64_C(1099511628211);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the generic hash probe assert a result

When this executable is run without an output path, as configured by CTest, it only prints the computed hash and then returns success. Any numerical regression that still completes therefore passes the test, so this adds test-suite cost without validating the advertised generic flash-attention behavior; compare against an expected hash or a reference result and fail on mismatch.

AGENTS.md reference: AGENTS.md:L87-L87

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

Full test-backend-ops on top of this branch, CUDA sm_100, B200:

14694/14694 tests passed
Backend CUDA0: OK
2/2 backends passed
OK

That is every op, not just flash attention, so the two add/add resolutions in ggml/src/ggml-backend.cpp and tests/CMakeLists.txt are covered as well. LIGHTNING_INDEXER and the banded flash attention cases are in there and pass against the CPU reference.

The arch was enumerated and then skipped:

    if (arch == LLM_ARCH_INKLING) {
        return false; // TODO fixture params for the arch-specific hparams
    }

so nothing has ever built an Inkling model, and the row it printed said SKIP
rather than saying nothing, which is the worst of both.

load_arch_hparams reads d_rel, rel_extent, rel_extent_swa, shortconv_kernel,
dense_block_count and logit_scale_denom unconditionally and then GGML_ASSERTs
four of them non-zero, so all six have to be in the fixture; the three optional
log-scaling and unpadded-vocab keys go in with them so the fixture describes a
whole model rather than the minimum that loads.

MoE-mandatory, not MoE-implemented: expert_feed_forward_length,
expert_shared_count and expert_weights_scale are read unconditionally too, so
there is no expert-free config of this arch to build. Dense and MoE are a
per-layer split decided by dense_block_count, not a model-level choice.

Now runs a real forward pass, and on CUDA it is a real comparison:

  |     Model arch.|         Device|Config| NMSE vs. CPU|Roundtrip|
  |         inkling|    NVIDIA B200|   MoE| OK (1.82e-11)|    SKIP|
  |         inkling|Xeon 8559C     |   MoE| OK (0.00e+00)|    SKIP|

Roundtrip stays SKIP because llama_model_saver_supports_arch already lists
INKLING as unsupported; that is this PR's own decision and is untouched here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2cd60b7852

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread conversion/inkling.py
self.gguf_writer.add_float32(f"{arch}.logit_scale_denom", hp["logits_mup_width_multiplier"])
self.gguf_writer.add_uint32(f"{arch}.log_scaling_n_floor", int(hp.get("log_scaling_n_floor") or 0))
self.gguf_writer.add_float32(f"{arch}.log_scaling_alpha", hp.get("log_scaling_alpha", 0.0))
self.gguf_writer.add_uint32(f"{arch}.unpadded_vocab_size", hp["unpadded_vocab_size"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the fallback for a missing unpadded vocab size

When an Inkling config omits unpadded_vocab_size, get_vocab_base() explicitly falls back to vocab_size, but this direct lookup later raises KeyError and aborts conversion. Use the same fallback here (and in the following log statement) so checkpoints without this optional field can be converted consistently.

Useful? React with 👍 / 👎.

The merge is conflict-free and the tree does not compile, in two ways.

n_ff_exp became a per-layer array behind an accessor:

    error: invalid use of non-static member function
           'uint32_t llama_hparams::n_ff_exp(uint32_t) const'

Read into n_ff_exp_arr and take the value through the accessor, the same
way the other MoE archs do since that change. Inkling's expert FF length
is uniform across layers, so the layer-0 value is every layer's value.

The image and audio preprocessor base classes made preprocess() const:

    error: 'mtmd_image_preprocessor_inkling::preprocess(...)'
           marked 'override', but does not override

Both Inkling overrides only read the cache; the writes are in
initialize(), where the base class already puts them, so const is
accurate rather than a cast.
@danielhanchen
danielhanchen changed the base branch from base/upstream-67a17c17c to base/upstream-de8656bd9 September 3, 2026 23:46
danielhanchen pushed a commit that referenced this pull request Sep 3, 2026
The base tag moved from b10775 to b10786 while this was open, and five
pins needed work to survive it. Four of the five merge without a single
conflict and produce a tree that does not compile, which is the failure
mode the compile gate in #175 exists for; three of those four are the
same upstream change.

- #172 inkling, #173 glm5next, #177 diffusion-gemma: n_ff_exp became a
  per-layer array behind an accessor. Reading the old scalar field is a
  compile error, and inside a create_tensor dimension list the member
  function quietly decays to a pointer-to-member instead. #172 and #173
  also override preprocess(), which the mtmd base classes made const.
- #152 per-run buffers: b10786 added a load-ordering pass that reads a
  llama_buf_map entry as one buffer, and this pin made an entry a run of
  buffers.
- #144 qwen4exp MTP: the only one that conflicts, in both places it
  touches, over the same n_ff_exp change.

Verified on b10786: all 13 pins merge (11 clean, 2 additive), the CPU
llama target builds, and the CUDA build plus the feature matrix are in
the PR comment.
@danielhanchen

Copy link
Copy Markdown
Member Author

Refreshed onto b10786, which is the base tonight's nightly will pick. The PR base branch moved to base/upstream-de8656bd9, which is that tag verbatim, so the diff here is still this feature and nothing else.

What the move required is in the merge commit message. Verified with the whole 13-pin set replayed on b10786: every pin merges, pin_contract.py reports all 13 intact, the CUDA sm_100 build is clean, and the feature matrix passes all six features on a B200. The full output is on #174.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f639c770a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread conversion/inkling.py
model_arch = gguf.MODEL_ARCH.INKLING
undo_permute = False

_SKIP_PREFIXES = ("model.visual.", "model.audio.", "model.mtp.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip bare multimodal tensor prefixes

Checkpoints that store the combined towers as visual.* or audio.* cannot be converted as text: the mmproj converter explicitly accepts those bare prefixes, but this filter skips only model.visual.* and model.audio.*, so the text converter passes the tower tensors to map_tensor_name() and aborts with Can not map tensor. Include the bare prefixes here as well.

Useful? React with 👍 / 👎.

Comment thread tools/mtmd/mtmd-image.cpp
Comment on lines +483 to +485
mtmd_inkling_image_preproc_out mtmd_image_preprocess_inkling(
const clip_image_u8 & img,
resize_algo algo) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the duplicate unused image preprocessor

A repo-wide search finds no caller of this newly declared helper outside its declaration and definition; the runtime instead uses mtmd_image_preprocessor_inkling::preprocess() below. The two implementations already disagree on resize selection and BF16 rounding, so retaining this second preprocessing path adds substantial dead code and an ambiguous API that can produce different embeddings if used later; keep only the runtime implementation or make it reuse this helper.

AGENTS.md reference: AGENTS.md:L80-L80

Useful? React with 👍 / 👎.

danielhanchen pushed a commit that referenced this pull request Sep 4, 2026
The preflight on this branch reports that #172 does not merge onto
b10786, and it is right: #172 and #177 both add a fixture block to
tests/test-llama-archs.cpp and share exactly one line, `}`, which
master's additive_merge.py reads as the same change made twice.

#170 fixes that and lands first. Merging it here so the preflight on
this branch tests the combination that will actually exist on master,
rather than a state nobody will ever run.
danielhanchen pushed a commit that referenced this pull request Sep 4, 2026
This branch carries the pin set for the lint, so the preflight runs on it
too, and without #170's resolver fix it reports #172 as not merging. That
is a true statement about a state that will never exist on master: #170
lands first. Merging it through the pin-set branch.
@danielhanchen

Copy link
Copy Markdown
Member Author

Retired. Everything this carry held beyond a replay of ggml-org#25731 is now in the upstream PR itself:

  • the mtmd const-correctness fix, so the Inkling image and audio preprocessors override the base class again and the towers build (36df1bf40 and fe45c899a)
  • the arch fixture hparams in tests/test-llama-archs.cpp, plus removal of upstream's own TODO fixture params exclusion and the moe_mandatory entry

Verified on the merged pin set against b10795: llama, mtmd and test-llama-archs all build, and test-llama-archs -a inkling reports OK (0.00e+00) where it previously printed SKIP.

The pin in scripts/unsloth/pr-set.json now points at ggml-org#25731 directly (#174), so this carry has nothing left to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants