Add TML Inkling architecture - #25731
Conversation
|
Hi @danielhanchen, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
d5f48c4 to
1cb0374
Compare
50cde5d to
a015409
Compare
Merge upstream PR ggml-org#25731: TML Inkling architecture (+ upstream master sync)
4d8798a to
f47a88c
Compare
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.
f47a88c to
ce16fff
Compare
|
First: thank you for day-0 support, and it works. We ran inkling UD-Q4_K_XL (587GB) fully resident across two cloud VMs (4x A100 80GB each, 8 total) split over the RPC backend on ordinary Ethernet, and output is coherent and correct (it first-shot passed our hardest structured Terraform generation task). Text only, audio/vision untested here. We also profiled it, and the data below may be useful for the "might need to redesign some parts later" list. Decode runs at roughly 25 percent of what the hardware gives comparable MoEs, and the evidence points at per-token graph rebuild and launch overhead, not compute. Numbers (all this PR at ce16fff, same boxes, sustained single-stream, ctx 8192)
Decode is flat from empty context to 2.2k depth (6.2 to 6.6), so it is not KV/attention scaling. Single box, inkling runs at ~80 percent of the comparable resident MoE. Over RPC it drops to ~27 percent. Where the time goesPer token at 6.45 t/s = 155 ms. During decode:
perf record on llama-server during decode gives a maximally flat profile: 1,783 distinct symbols, top entry 3.5 percent. Named entries in the top 20: cudaStreamSynchronize / ggml_backend_cuda_synchronize, pthread_mutex_lock under cuLaunchKernel, ggml_backend_sched_split_graph, ggml_gallocr_alloc_graph, and ggml-rpc add_tensor + get_alloc_size (the whole graph re-serialized to the worker every token, which is the 5.5 MB). So the shape looks like: a ~9k-node graph rebuilt, re-split, re-allocated, re-serialized and launched node by node every token while the GPUs idle. Our guess is the banded rel-bias / shortconv path defeats the llama-side graph reuse, and the node count does the rest. If graph reuse engages for this arch, the RPC number should improve several-fold on its own. Happy to re-run any branch of this PR on the 2-node A100 cluster or the single box and report the same measurements. Full logs, perf.data and exact commands available on request. |
# Conflicts: # ggml/src/ggml-cuda/mmq.cuh # src/llama-model-saver.cpp # src/llama-vocab.h
…#39) Both pins pointed at commits that no longer merge onto the selected upstream base, so the nightly full release build failed while resolving the PR set. ggml-org#24523 was pinned to 66f43aa6, which was not even the PR head at the time (the resolver logged that the head had moved to 0b78558a). That commit conflicts in common/chat.cpp against both b10107 and b10133. The PR has since been rebased onto current master, so the pin now points at its new head a58a7fa6, which merges cleanly. ggml-org#25731 was pinned to ce16fff, which conflicts on its own in ggml/src/ggml-cuda/mmq.cuh, src/llama-model-saver.cpp and src/llama-vocab.h. This never surfaced in the log because the resolver stops at the first failing entry. Its current head 453c438 merges cleanly by itself. Note that ggml-org#24523 and ggml-org#25731 still conflict with each other in common/chat.cpp and src/llama-arch.h: both append a new llm_arch enum value immediately before LLM_ARCH_UNKNOWN and both add a chat parser in the same region. Reordering does not help, since whichever entry is applied second hits the same conflict. Resolving that needs a decision about which of the two to carry, so it is left alone here. Requires a base of b10133 or newer: b10107 predates the rename of common_chat_params::thinking_end_tag to thinking_end_tags, which the rebased ggml-org#24523 depends on. Co-authored-by: Daniel Han <unslothai@gmail.com>
The 2026-08-02 schedule (run 30767267935) failed in resolve: ggml-org#25731 (02142bb) does not merge cleanly onto b10229 + the PRs listed before it Not the pin rot that broke the previous two nights -- all four pins passed the membership gate this time. Upstream added LLM_ARCH_DEEPSEEK4 to the same fallthrough group in src/llama-arch.cpp that Inkling registers in, between b10226 and b10229, and the resolver has no conflict resolution. Fixed on the branch rather than here: b10229 is merged into add-inkling and both cases are kept, since both arches belong in that group. That also clears ggml-org#25731's own mergeable state against master, which had gone red for the same reason. No other pin moves. #48 needed nothing -- it already carries b10223 and the new arch does not collide with Kimi-K3. Verified against b10229: the four merge in order with zero unmerged paths, and the merged tree builds with -DGGML_CUDA=ON. test-chat passes and test-llama-archs -a kimi-k3 is OK on CUDA and CPU (NMSE 8.77e-08). The new Inkling head also merge-tree's clean against upstream master, which is six commits past b10229, so the next tag should resolve too.
|
Tried this on Intel Arc (SYCL), 6x Arc Pro B70, IntelLLVM 2026.0.0, with unsloth/Inkling-Small-GGUF UD-Q4_K_S. It runs and output is correct, but SYCL has no kernel for GGML_OP_FLASH_ATTN_EXT_BANDED (CUDA and CPU only) so -fa is force-disabled and attention falls back to a full ubatch x ctx score matrix. llama-bench, 6 cards: pp512 409 t/s | pp2048 395 t/s | tg64 16.8 t/s Throughput looks about right for this setup however the context is capped at ~31k instead of the 1M I'd expect. |
# Conflicts: # tests/test-llama-archs.cpp
|
No prompt-cache prefix reuse on Ryzen AI Max+ 395 / gfx1151, ROCm 7.14, this PR at Measured by prefill wall time (
A full re-prefill of B predicts ~37.5 s; prefix reuse predicts ~10.2 s. Observed 39.8 s. On the chat path turn 2 re-processes the entire prompt rather than the ~20 new tokens. Re-sending Control — same binary, same flags, same
So prefix reuse works on this build and these flags; it does not happen for Why it matters beyond a benchmark: in an agentic/tool-calling loop the context grows every turn and each turn re-prefills all of it. A 14-turn recall task here spent its entire 25-minute budget in prefill — per-turn cost climbing 55 s → 163 s — and never reached a final answer, while the same task on another model completes in ~14 minutes. Total thinking across those 14 turns was only ~2.3k characters, so this is prefill, not generation. Speculation, offered as a starting point rather than a finding: Inkling declares Happy to run further diagnostics on this hardware — I still have the build and weights in place. |
|
recommend banning @wowo101 -- they are posting a lot of clearly AI generated content to issues/PRs
|
|
These are all outcomes of pretty lengthy evaluation sessions, reviewed and edited – just trying to be helpful by sharing data. Will stop posting them if not welcome/useful – just let me know, not need for disciplinary action. |
|
I recommend banning @khimaros for being toxic and unhelpful. Don't like AI, then maybe a project that literally lets you run AI locally just isn't for you. If you think someone posts too often, too large of replies or in unhelpful ways, a mature person would engage with them and encourage them to improve rather than chase away the very community that supports the project. @wowo101 I've noticed allot of toxic people on here when it comes to that. Allot of people come wanting to get involved and end up leaving because of it. Don't take it personally, people like that have problems. In general though, I've noticed people tend to be more appreciative if you condense your results down as much as possible. Don't let bullies push you out of open source. Cache is indeed broken and it's for a known problem involving recurrent state. I actually have a fix for the general case on my fork but I need to update it for inkling(Recurrent works, swa works, but I never fixed recurrent+swa). If you're interested I can let you know when I've got it working. I also include auto disk caching and prefix checkpoint features if that's of any use. In short, cache is a known issue not specific to this PR. Lots of people have posted bug reports about it but none of the official project devs have been willing to tackle it and they aren't accepting PRs from new user or anyone who is using any amount of AI to generate code. Hence why I had to fork it. |
Very real, and it's a challenging situation.
I generally recommend against accusing people of this; when it's actually a human, it derails the conversation, and frankly it's stressful and upsetting to get accused of. Plus, it doesn't seem to make much of a difference on actual LLM posts. I've gotten accused of being an LLM in a few places, because I bother to use formatting and full sentences. "You sound like an LLM." Nope - I'm 45; LLMs sound like me (and my cohort) because among other things, they were trained on tons of Reddit posts by people like me. As for if @wowo101 used an LLM in that last response? If they did, it didn't do a very good job, because "just let me know, not need for disciplinary action" is a grammatical mess. (Not intended to be insulting, @wowo101, I'm just pointing out a human mistake. Maybe a usage quirk from a non-native English speaker, or an editing mistake, but there's no way an LLM would let something like that past. Again, no insult intended, the meaning was perfectly clear.) Sure, it's possible that other stuff was LLM-assisted, but this amount of debate and noise isn't helpful; I only point these things out in hopes of defusing this and helping curb some of the reactive finger-pointing. Most of us want the best for these projects; slop submissions are a real problem, but arguments and accusations in comment threads are a problem too. |
|
@wowo101 I got the vram/ram cache for inkling fixed on my fork (see F3zz1k@371c74a ). As I said earlier, I've also got a bunch of other caching patches including auto-caching to disk and multimodal caching on recurrent, which I just updated with latest upstream and latest version of this inkling patch if you want it all applied already (see https://github.com/F3zz1k/llama.cpp ). I only tested the fix on my full fork but it should work off of main too. |
|
I tested this with this and then again with the following fix for the caching bug: F3zz1k@371c74a And ran with 20 tok/s on mt GB10 using this quant: https://huggingface.co/unsloth/Inkling-Small-GGUF The build worked fine for me and the cache fix also sped up my cached turns by about 5x. (works) |
…oj assert) Fleet hardcoded n_embd=6144 (975B width); the Small decoder differs, so the mmproj load asserted. Taken verbatim from upstream PR ggml-org#25731 head (danielhanchen, 02142bb). Verified on Inkling-Small: vision 8/8, audio verbatim transcription.
…nkling preprocessor Upstream ggml-org#26317 deleted tests/get-model.cpp (moved into common). Upstreams mtmd-image.cpp refactor dropped our inkling image preprocessor (from PR ggml-org#25731 snapshot); re-spliced the helper + override, verified build+link.
|
Performance data on gfx1151 (Strix Halo, ROCm) — decode degrades linearly with context depth. Ran a depth sweep with Setup: AMD Ryzen AI Max+ 395 (gfx1151), 128 GB LPDDR5-8000, Ubuntu 26.04 (in-tree amdgpu), ROCm 7.14.0 in container, built from 1e6f9e4 with
The decode slope is constant — roughly 15.5 us of added latency per token of context: So it's O(n), not O(n^2) — but the constant seems high for this architecture. With Possibly Practical impact: a 19.8k-token prompt with ~2k output takes about 18.5 min end-to-end on this hardware. Two other notes for anyone testing on Strix Halo:
Happy to run additional configurations if useful. |
|
Three findings from testing on gfx1151 (Strix Halo, ROCm) that may be useful — in particular, the Setup: AMD Ryzen AI Max+ 395 (gfx1151), 128 GB LPDDR5-8000, Ubuntu 26.04 (in-tree amdgpu), ROCm 7.14.0 in a container, built from 1e6f9e4 with 1.
|
| test | -fa 1 | -fa 0 |
|---|---|---|
| tg32 | 20.96 | — |
| tg32 @ d4096 | 8.86 | — |
| tg32 @ d16384 | 3.36 | 13.85 |
| pp512 @ d16384 | 75.10 | 118.39 |
With FA on, the decode slope is roughly 15.5 us of added latency per token of context, and it is linear:
0 -> 4096 : +65.2 ms / 4096 = 15.9 us/token
4096 -> 16384 : +184.7 ms / 12288 = 15.0 us/token
So O(n), not O(n^2), but the constant looks high. With sliding_window = 512 on most layers I'd expect only the global layers to scale with depth. Rough estimate: even if all 42 layers read their full KV window each step, that's under 1 us/token at this memory bandwidth — so the cost looks compute-bound inside the banded bias rather than bandwidth-bound. Possibly GGML_OP_FLASH_ATTN_EXT_BANDED isn't using rel_extent_swa to bound per-layer work?
Real-world, 8421-token prompt via /v1/chat/completions:
-fa 1 : prompt 102.2 t/s, decode 5.36 t/s
-fa 0 : prompt 111.3 t/s, decode 16.04 t/s
3. But -fa 0 degrades output quality noticeably
This part is subjective, so treat it accordingly — but the difference was consistent and large enough to report.
Same 8421-token prompt, same seed-free sampler settings, same preset except flash-attn. Task: summarise a document into a structured factual account. With -fa 1 the output covered the central facts. With -fa 0 the same prompt produced output that:
- omitted five of the six key facts present in the input (including the one uncontested admission, the date of the event, and the named witness),
- was ~40% shorter in substance,
- in one run, invented a witness who does not appear in the input, along with a fabricated relationship and a paragraph of testimony,
- in the reasoning trace, hallucinated an input detail ("broken glass") that isn't in the prompt and then built the whole analysis around contradicting it.
I don't have a numerical benchmark for this — no perplexity runs — so it may be worth someone reproducing on CUDA before drawing conclusions. But if the non-FA path is numerically diverging rather than just being a different code path, that seems worth knowing, since -fa 0 is currently the only way to get usable decode speed at depth on this hardware.
Also, for anyone else testing on Strix Halo: --load-mode none (or --no-mmap) is required, otherwise loading stalls badly past ~64 GB — looks like #15018.
Happy to run additional configurations or provide full logs if useful.
Prebuilt: repin ggml-org#25731 and unslothai#70 onto post-squash heads
# 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
Prebuilt: repin ggml-org#24423 and ggml-org#25731 onto carry branches
Adds support for the Inkling architecture, a Python safetensors-to-GGUF converter, the graph build, and the kernel changes needed for correct and deterministic inference.
int64_ton some ops since large MoEs would go out of indexI tried to keep changes unbreaking - might need to redesign some parts later.
Used AI for kernels, but hand verified and checked everything carefully.