[None][feat] Support Inkling-NVFP4 model - #17062
Conversation
Add the Inkling NVFP4 model to the _torch stack: modeling_inkling.py, a Triton score_mod attention backend (SWA + global + relative-position bias) over KVCacheManagerV2, HF NVFP4 weight mapper/configs, trtllm-gen blockScaleMoe runner with sink-renorm routing, reasoning-parser/effort rendering, lm_eval post-processing, and the inkling_* unittest suite. Progress (working snapshot): - Component validation passes in isolation on the TP=4 NVFP4 / trtllm-gen MoE stack: weight load & accounting, attention source-activation replay, MoE replay, and full-model source-logit replay. - Baseline (cuda_graph=off, overlap=off) accuracy vs the SGLang reference: GSM8K 0.916 vs 0.972 (-5.6pt), full MMLU 82.22 vs 85.66 (-3.44pt). The gap is dominated by runaway / non-terminating generation on hard prompts: 76% of GSM8K errors are 7k-8.6k-token spirals where the model reaches the answer but never emits EOS, while SGLang commits. Per-layer localization is exhausted; the residual is a diffuse fp4 / kernel-family divergence (bf16 Triton attention + trtllm-gen fp4 MoE vs SGLang flashinfer), not a single fixable layer bug. - Enabled (cuda_graph=on) is blocked by a decode collapse (B2) localized to the global-attention block under CUDA-graph capture/replay at TP=4: h_attn goes non-finite at the first global-attention layer once decode crosses a KV-page boundary; a reduced-model TP=2 harness reproduces it. Next: decode-side termination fix for the baseline runaway (finish_reason-based detection + EOS/stop handling), and resolve B2 in the global-attention-under- graph path. Excludes build artifacts (libtensorrt_llm.so), locks, and the ext/ submodule. Signed-off-by: kleinc <kleinc@nvidia.com>
…(6*448)) Root cause of the baseline NVFP4 accuracy gap vs SGLang. The Inkling checkpoint ships routed-expert activation calibration as a RAW `.input_amax`, but the fused-MoE loader / trtllm `fp4_quantize` expect the ModelOpt per-tensor `input_scale = amax / (E2M1_MAX * E4M3_MAX) = amax / (6*448)`. The mapper renamed `.input_amax` -> `input_scale` WITHOUT the conversion, making the activation global scale 2688x too small: every routed expert's fp4 output was ~0.62 rel_rms from the bf16 ground truth (7.6x SGLang's 0.082). Mirrors sglang inkling.py:1222 / inkling_common/dense_mlp.py:497. Positional bisection confirmed the weight/block-scale/gate-up-interleave layout was already element-wise correct (24/24 L3 experts, 18.8M elems, 0 diverged) -- the defect was ONLY the activation input scale, shared by both TRT MoE backends. Fix: `inkling_weight_mapper._map_expert` divides `.input_amax` by (6*448). Result (baseline: cuda_graph=off, overlap=off, TP=4, CUTLASS MoE, fp4 fix active): - L3 routed-expert vs bf16 truth: rel_rms 0.624 -> 0.081 (== SGLang 0.082) - GSM8K paired 5x100: TRT 0.916 -> 0.968 vs SGLang 0.974; mean_delta -0.056 -> -0.006 (Gate 2 PASS, within +/-0.02); runaway canary passes; collapse 0 - MMLU paired 6x100: TRT ~0.822 -> 0.862 vs SGLang 0.872; gap -3.44pt -> -1.0pt (Gate 3 accuracy PASS); B-bias error-is-B 65.9% -> 40%, TRT-B 33.2% -> 29.8% Residual (not this fix): TRT (CUTLASS) vs SGLang (flashinfer) fp4 kernel-family near-tie noise -- accuracy-neutral, not bit-reproducible at batch>1. The strict within-2pp-of-gold MMLU B-bias criterion is model-inherent (SGLang itself over-picks B) and is left to human adjudication, not a TRT defect. Also adds env-gated per-layer/per-module dump instrumentation (modeling_inkling.py dump_sink; inkling_perlayer_localize_test.py) and the trtllm-gen MoE backend path used to localize the bug. Signed-off-by: kleinc <kleinc@nvidia.com>
…ring graph capture
Root cause of the Inkling TP=4 enabled-runtime (cuda_graph=on + overlap) decode collapse
("B2"): the AutoTuner-selected all-reduce (`tunable_allreduce`, AUTO strategy) is not
CUDA-graph-capture-safe. Frozen into a decode graph, the tuned tactic produces a non-finite
result on replay, so decode goes NaN from the first global-attention layer and collapses to
a token-0 repeat ("Paris!!!!"). Eager is finite at identical metadata; the fault is baked
into the captured graph.
Localized by single-variable determinism isolation (autotuner ON -> collapse, autotuner OFF
-> clean), NOT by op-fingerprints -- any in-graph probe shifts the graph memory pool / tactic
selection and suppresses the bug (a Heisenbug).
Fix (`tensorrt_llm/_torch/distributed/ops.py`, AllReduce.forward): during CUDA-graph capture
(`torch.cuda.is_current_stream_capturing()`), skip `tunable_allreduce` and fall back to the
static, graph-safe `all_reduce_op(AUTO)`. Warm-up / eager (not capturing) still autotune, so
steady-state performance is unchanged. This is a mainline TRT-LLM robustness fix (any TP
model that captures an AUTO all-reduce benefits), not Inkling-specific.
Confirmed: enabled generation_parity is BIT-IDENTICAL to the baseline (cuda_graph=off) --
tf_mismatch/neartie/confident=16/11/5, freerun_collapse=0, identical logit_checksum; enabled
5x100 GSM8K (0.966 vs SGLang 0.974) and MMLU (0.875 vs 0.872) within +/-0.02, zero errors,
no regression vs the accepted CUTLASS baseline.
Also includes env-gated per-layer/per-op B2 localization instrumentation
(`modeling_inkling.py` dump_sink / INKLING_FP*; `inkling_fp_localize_test.py`), zero-cost
when the INKLING_FP* env is unset.
Signed-off-by: kleinc <kleinc@nvidia.com>
…MLP tower, image fusion (WIP) WIP -- Stage-1 progress snapshot on the Inkling NVFP4 multimodal tower, on top of the accepted text tower. Not finished: the vision path is verified clean but the MMMU Accounting gap is still open (decode-side), and audio / MTP are still deferred. Committed to record current progress, not as a complete feature. Text decode/attention/MoE paths are untouched. Model / config: - configs/inkling.py: add image_token_id (200054, the in-vocab chat-template <|unused_200054|>) and audio_token_id. The SGLang-internal -101 sentinel is rejected by TensorRT-LLM's executor token-id validation; the two ids are interchangeable for parity since both are overwritten by vision embeddings. - modeling_inkling_vision.py (new): hMLP vision tower InklingVisionModel and InklingInputProcessor, which expands the <image> placeholder to one token per vision patch and attaches vision_patches_bthwc features. - modeling_inkling.py: InklingForConditionalGeneration registers the input processor, builds the vision tower as a replicated bf16 submodule, and fuses per-patch embeddings into the text stream via fuse_input_embeds with explicit text/mm indices (OOV-safe). Fixes the "image not visible" hallucination: the fused stream must NOT be re-normed, since SGLang scatters raw vision rows in after embed_norm; the extra RMSNorm corrupted the image rows. Tests / diagnostics (tests/unittest/_torch/modeling/, 25 new files): MMMU harness alignment against the SGLang scorer, input-processor and vision-tower unit checks, image e2e / fusion / logit-replay / generation-parity drivers, and the localization probes used to isolate the vision-vs-decode split (vision verified bitwise-clean; the residual Accounting gap is decode-side). These require GPU + the NVFP4 checkpoint (TP=4) and were not run for this commit. Signed-off-by: kleinc <kleinc@nvidia.com>
…s_token SamplingParams._setup() looked for the end-of-generation token in only two places: tokenizer.eos_token_id, then generation_config.eos_token_id. When a checkpoint provides neither, end_id stayed None, nothing could terminate a request, and every generation silently ran to max_tokens. That combination is reachable. Multi-part chat formats have no single terminator -- a message end, an end-of-sampling marker and a document separator are distinct tokens -- so such checkpoints register their control tokens under extra_special_tokens / additional_special_tokens, neither of which populates tokenizer.eos_token_id, and declare the real stop token as eos_token_id in config.json. Those checkpoints also tend to ship no generation_config.json. _setup() already received hf_model_config but never consulted it. Observed on a checkpoint of that shape: responses ran to the token limit while emitting the configured eos_token_id up to 441 times in a single response. Fall back to hf_model_config.eos_token_id when the first two sources yield nothing. A list value sets end_id from its first entry and appends the rest to stop_token_ids, mirroring the existing generation_config path. Priority is otherwise unchanged: an explicit SamplingParams(end_id=...) still wins and tokenizer.eos_token_id still takes precedence over config.json, so models that already resolved an end_id see no behavioural change. Warn when all three sources come up empty. Generating to the cap on every request with no diagnostic is the part that makes this expensive to find. Add unit tests for tokenizer priority, the config fallback, the list form, an explicit end_id not being overridden, the all-empty case, and a missing hf_model_config. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
Adds the audio and video modalities alongside the existing vision path.
Audio:
* InklingAudioPreprocessor -- dMel feature extraction (mel basis, hz<->mel,
per-frame bin quantization) producing int32 [N, 80] bins, one audio token
per frame.
* InklingAudioModel -- codebook encoder (bin m occupies codebook rows
[m*V, (m+1)*V), summed over bins) plus optional final norm, bf16, loaded
strictly from the real checkpoint's audio tensors.
* Placeholder expansion and fail-loud count checks in the input processor.
Video:
* sample_video_frames / sample_video_as_images / DecodedVideo -- frame
sampling ported to match SGLang's sample_video_frames semantics.
* The <image>-per-frame path: every sampled frame becomes its own image
span through the existing vision tower.
Audio and video land together because both wire into the same
InklingInputProcessor.assemble dispatch; splitting them would leave an
intermediate commit whose processor references helpers that do not exist yet.
Tests (all GPU-verified on TP=4):
* inkling_audio_tower_test.py -- 10 passed, incl. real-weight CUDA forward
(AUDIO_TOWER_CUDA_OK, out=(n_frames, 6144) bf16 finite) and a
reference-math allclose(atol=1e-5) check of the codebook sum.
* inkling_video_utils_test.py -- 12 passed, incl. a port of SGLang's
test_video_utils.py::test_sample_video_frames_lengths (same 4 cases and
the same expected frame indices) and a real-weight multi-frame CUDA
forward (VIDEO_TOWER_CUDA_OK, out=(total_patches, 6144)).
* inkling_audio_e2e_test.py / inkling_video_e2e_test.py -- strict TP=4
end-to-end smokes: every prompt must be finite, non-empty and
non-collapsed. Green both baseline (5/5) and with cuda_graph+overlap
enabled (5/5).
Scope note: these prove the modalities run and are shape/dtype/finiteness
correct; they are not a cross-stack numerical parity check against SGLang.
Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
`logits_rows` is a view of the same slice that is written back, so the assignment self-overlaps and torch rejects it with "... refer to a single memory location. Please clone()...". The processors edit in place, so the write-back is redundant anyway; cloning the source makes it overlap-safe. Only reached for requests carrying a py_logits_post_processor, so normal requests are unaffected. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
… parity runs evaluate/interface.py gains summarize_generation_stats/log_generation_stats, and lm_eval emits a greppable GEN_STATS marker per batch. Without it a runaway / no-EOS regression hides behind a parseable answer buried in a wall of repeated text -- the failure mode that cost several bring-up iterations. The MMMU harness/runner changes carry the sharded union runs used for the TRT-vs-SGLang comparison (shard plan, incremental atomic per-item writes so a wall-killed shard keeps everything it scored, cap/max_seq plumbing), plus unit coverage of the answer parser. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
Adds streaming-vs-batch equivalence over arbitrary split points, tool-call and repetition segmentation, and end-tokens split across deltas. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
Standalone probes used to localize the TRT-vs-SGLang vision divergence: prefill logits, transformers-reference decode, termination behaviour, and a per-layer activation dump. Diagnostics only -- not part of any test suite. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
dfc574c to
f5fcaf1
Compare
Two in-progress pieces, neither reached by a runtime path yet. MTP static tier: InklingMTPConfig carries the checkpoint's num_nextn_predict_layers / chain_hidden_post_norm / local_layer_ids plus the per-depth banded-attention geometry injected from the text tower, so draft depths in local_layer_ids get SWA head geometry and the rest stay global. The weight mapper gains inkling_expected_mtp_keys() and accounts model.mtp.* as consumed rather than deferred when an mtp_config is supplied; the default mtp_config=None leaves the text-tower accounting byte-identical. Unit coverage for config parse, weight accounting, the BF16/unquantized requirement, and per-depth banding against the checkpoint shapes. MMMU harness: INKLING_MMMU_TEXT_ONLY reruns the same items as pure text (image placeholder stripped, no image attached) so the shared decoder is exercised at the identical bs / cap / overlap regime without the vision path. Default-off — the vision scoring path is unchanged when unset. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…del code The cuda-graph decode collapse is a defect in symmetric all-reduce: a captured NCCL_SYMMETRIC reduce whose send buffer is unregistered while its recv buffer is a registered NCCL window corrupts the run at a 12288 B message. Inkling hits that size exactly -- hidden 6144, bf16, one decode token -- so the first global-attention layer goes non-finite and decode collapses to a repeated token 0. Revert the shared-code mitigation in distributed/ops.py: that file is now byte-identical to its pre-Inkling state, so no other model's all-reduce path changes on our account. Mitigate in modeling_inkling.py instead. The all-reduces that trigger this are built by generic modules -- attention o_proj, MoE down_proj -- so the strategy cannot be passed at construction without editing shared code; rebuilding each AllReduce after super().__init__() keeps the mitigation model-local. Each rebuilt instance carries the module's own mapping and dtype over, so strategy is the only delta. Pinning ONESHOT also drops the window requirement, since AllReduce only takes an NCCL window under NCCL_SYMMETRIC/NCCL/AUTO -- two of the five trigger conditions go away, not just one. Active by default; INKLING_ALLREDUCE_STRATEGY=AUTO restores stock behaviour, and the defect with it, for A/B runs. Measured on job 5728192, alternating arms in one job against one binary: default 0/3 collapse, AUTO 3/3, 331 modules swapped. Cost: symmetric is disabled on every Inkling all-reduce, eager included, and roughly a third of captured decode all-reduces pick it today. The performance impact is unmeasured and should be measured before this is treated as final. This is containment, not a root-cause fix -- the defect remains for any other model that meets all five conditions. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
f5fcaf1 to
35a4a46
Compare
Removes the bring-up's debug scaffolding and completes the deliverables the Inkling NVFP4 change was missing. No model behaviour changes. Deleted 41 debug/localization test files (divergence probes, dump/isolate helpers, teacher-forcing and graph-capture localizers) that existed to find the bring-up's defects and have no role now that it works. Nothing imports them: every deleted basename was grepped across the tree, zero references remain. The two environment variables the model still reads are the ones worth keeping — INKLING_ALLREDUCE_STRATEGY (the escape hatch for the ONESHOT all-reduce mitigation) and INKLING_MOE_BACKEND (the trtllm-gen MoE kernel select) — and both are documented; the 19 debug-only INKLING_* knobs are gone. Completed the deliverables: - docs/source/models/supported-models.md — architecture row, multimodal feature-matrix row, and footnote [^14] covering modality coverage, the unsupported set (MTP, LoRA, function calling, constrained decoding, EPD, mm-hash caching), and the all-reduce mitigation plus its escape hatch. - TestInkling_NVFP4::test_nvfp4 added to the shared multimodal accuracy file rather than a standalone per-model test, with a sourced MMMU reference. - Registered that id in test-db/l0_b200.yml and qa/llm_function_core.txt. - Dropped the orphaned inkling_vision_tower_artifact.json; the surviving regression test consumes the generated artifact instead. Static checks: git diff --check clean, every modified Python file compiles, no stale references to the deleted files. Runtime coverage as of the last completed suite: unit tiers green (vision 43, audio 10, video 12, text 31, collect 95 with zero residual debug files) and GSM8K cg0ov0 parity at delta=0.0. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
d1649bb to
8a32f22
Compare
The MTP / next-N draft work never reached a runtime path: nothing builds the draft layers and nothing consumes them. Only the config plumbing and the load-accounting existed, so remove them rather than ship dead code. - configs/inkling.py: drop InklingMTPConfig, InklingConfig.has_mtp and _as_mtp_config. mtp_config goes back to a plain retained blob, so the checkpoint still round-trips. - inkling_weight_mapper.py: drop inkling_expected_mtp_keys and the consumed_mtp bucket; inkling_account_checkpoint loses its mtp_config parameter. - test_modeling_inkling.py: drop the four Stage-9 MTP tests. - supported-models.md: the footnote no longer claims the draft weights are weight-accounted. Checkpoint accounting is unaffected: "model.mtp." stays in INKLING_DEFERRED_PREFIXES, so the draft weights are classified as deferred exactly like the audio and vision blocks, and `unaccounted` stays empty. Verified: every touched file compiles, and no reference to any removed symbol remains anywhere in the tree. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…ences MMMU already had a reference entry; the two text benchmarks the bring-up actually measured did not, so the numbers lived only in run logs. Both references are the cached SGLang NVFP4 measurement under the same harness, matching how the MMMU entry was sourced: - GSM8K 95.53 (full set, 0.9553). TRT-Inkling was validated against it with the paired 5x100 protocol: flexible-parse mean 0.968, and all four cuda-graph x overlap-scheduler corners scored 0.98-0.99 on the 100 paired items where SGLang scored 0.99. - MMLU 85.66 (full Hendrycks, 14042 samples, weighted_accuracy 85.6573). The TRT side was measured with the 5-seed text-regression protocol on the harness' 114-item subset (83.33 / 84.21 / 85.09 / 85.96 / 87.72, mean ~85.3). The comment says so explicitly: it tracks the reference, but no full-set TRT-LLM MMLU run has been recorded yet. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…that matters
The bring-up left 22 Inkling files under tests/unittest/_torch/modeling. Most
were per-defect regressions or SGLang-alignment scaffolding whose outcome is
now covered end-to-end by the MMMU/GSM8K accuracy tests. Keep one test per
thing that nothing else covers, drop the rest.
Kept:
- test_modeling_inkling.py config parse, layer classification, weight
accounting over the real checkpoint
- inkling_vision_tower_test.py vision tower
- inkling_audio_tower_test.py audio tower (no accuracy benchmark covers it)
- inkling_video_utils_test.py video utils (no accuracy benchmark covers it)
- inkling_input_processor_test.py multimodal input processor
- inkling_moe_backend_select_test.py the INKLING_MOE_BACKEND kernel select
inkling_mmmu_real_align_test.py was doing double duty: the vision-tower and
input-processor tests import its MMMU item fetch/cache and its importlib
loader. Split that half out as inkling_mmmu_fixtures.py (a fixture module, no
tests) and drop the SGLang-alignment machinery with the rest.
Deleted (16): the mmmu align/harness/run/parser set, image_prompts, the three
per-modality e2e smokes, image_fusion, image_norm_fix, attn_decode_meta,
gate_up_deinterleave, kv_manager_v2, generation_parity and
source_logit_replay.
Every kept file compiles and no reference to a deleted module remains in the
tree.
Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
The multimodal file already carried TestInkling_NVFP4::test_nvfp4 for MMMU, so the vision path had an integration test but the shared text decoder -- what GSM8K and MMLU actually exercise -- had none. Adds TestInkling_NVFP4 to test_llm_api_pytorch.py running both text benchmarks against the references recorded earlier. It mirrors the multimodal class: NVFP4 assert, 16384-token budget for the long chain of thought, and extract_inkling_content as the post-processor so the <|content_thinking|> channel is dropped and only the visible answer is scored. Registered the new id in both lists that already carry the multimodal one: test-db/l0_b200.yml and qa/llm_function_core.txt. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…keep CUTLASS The Inkling routed experts now run only the default CUTLASS MoE backend. The trtllm-gen blockScaleMoe path was an opt-in experiment behind INKLING_MOE_BACKEND=TRTLLM, never the default, and it needed a dedicated routing enum plumbed all the way into the CUDA runner to work. Restored to their pre-bring-up state (the additions were Inkling-only, so these files are now byte-identical to 44f0521): - cpp/.../trtllmGenKernels/blockScaleMoe/runner.h the InklingSinkRenorm = 9 enum value and its name case - cpp/.../trtllmGenKernels/blockScaleMoe/runner.cu the precomputed-routing dispatch branch - _torch/modules/fused_moe/routing.py the matching Python enum value and its autotuner-dummy mapping Removed from the model: - _inkling_trtllm_moe_backend / _moe_config_with_trtllm_backend and the frozen-config copy they needed to retarget moe_backend - InklingMoeRoutingMethod._trtllm_backend, so routing_method_type is always Unspecified and requires_separated_routing goes back to the default - the per-layer INKLING_MOE_SELECT backend-introspection log Also deletes inkling_moe_backend_select_test.py, which existed only to cover that knob, and the stale comment that pointed at it as the fix for the fused combine's cross-row non-determinism. INKLING_ALLREDUCE_STRATEGY is now the only environment variable the model reads. Everything compiles and git diff --check is clean. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
The MMLU comment added in 5f28628 claimed no full-set TRT-LLM run had been recorded. That is wrong. One exists: 2026-07-20, all 14042 Hendrycks samples, weighted_accuracy 82.2176 -- 3.44 points below the 85.66 reference, and it failed the bring-up's 2-point gate. The gap was later traced to an fp4 expert-GEMM family difference (CUTLASS/trtllm-gen vs SGLang's flashinfer) plus non-terminating generation on hard prompts. What is true is narrower: the full set has not been re-measured since those fixes. Post-fix MMLU evidence is subset-only -- a 570-item stratified canary at 84.21 (-1.45, inside the gate) and a 5-seed 114-item regression averaging ~85.3. The comment now says exactly that, including that meeting 85.66 at full scale is unverified. GSM8K's comment was not wrong but was too vague about coverage. TRT-LLM has never been measured on the full 1319-item set: the evidence is the paired 5x100 protocol (flexible mean 0.968), the four cuda-graph x overlap corners at 0.98-0.99 on 100 paired items, and one early full-set attempt that completed only its first 120-item chunk (0.925 vs 0.975). Spelled out. Both files still record the SGLang reference as the accuracy value, so TestInkling_NVFP4::test_nvfp4 grades against a bar the model has not been shown to clear at full scale. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…timodal
The module already carried the image, audio and video paths, so the "vision"
name no longer described it. Rename it and reorganize the contents into four
labelled sections -- vision tower, audio tower, video tower, and the shared
multimodal input processor -- so each modality is easy to locate.
Behavior-preserving. Along the way:
* factor the duplicated tower weight-loading into ``_load_tower_weights``
and the duplicated RMSNorm into a single shared ``InklingRMSNorm``
(was ``InklingVisionRMSNorm``, used by both towers);
* factor the input processor's repeated media-list coercion and its
placeholder/feature-row count checks into small helpers, dropping a
tautological per-item check (``num_tokens`` is built from ``num_patches``);
* trim the docstrings to what the code needs, dropping the development-time
stage/goal references and reference-implementation file paths.
Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
Bring-up leftovers that do not belong in the production model:
* cuda_graph_runner: drop the "[cuda-graph] CAPTURED/REPLAYED" evidence
logging and its once-only gate flag. This was pure instrumentation added
to prove the runtime really captured and replayed a graph, and it sat in
shared (non-Inkling) code.
* modeling_inkling: drop the INKLING_ALLREDUCE_STRATEGY environment knob.
The ONESHOT pin is a correctness mitigation, not a tuning option, so it
is applied unconditionally instead of via an A/B toggle (and the log line
that reported it is gone).
* modeling_inkling: drop the explicit-window short-conv decode path
(InklingShortConv.forward_decode, the decoder layer's third branch, and
the attention's conv_states/return_conv_state arguments). Only the
bring-up replay harness ever passed those; the runtime always drives the
short convs through the per-request state pool.
* modeling_inkling: drop the attention's decode_seq_lens / decode_page_table
/ skip_kv_write arguments. The runtime publishes decode metadata into the
layer's stable GPU buffers before capture, so the pre-supplied static
tensors had no caller left; the eager fallback that builds them from the
host block table stays.
* modeling_inkling: drop InklingConvStateCache.reset (unused) and merge
InklingConvRuntime.from_metadata into build (the split existed only so the
replay harness could publish slots itself).
Also rewrite the comments and docstrings across the Inkling _torch files to
describe the code as it stands: no development stage/goal numbers, job ids,
absolute paths into local reference checkouts, or references to the deleted
replay harness. The stale "audio / vision / MTP are deferred" module docstring
now reflects that only MTP is unimplemented.
Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
The Inkling unit tests were five files carrying a bring-up harness rather than a test suite: they loaded the SGLang reference from an absolute path in a local checkout, downloaded MMMU rows over the network into a gitignored cache, wrote JSON artifacts, hardcoded a personal /lustre checkpoint path, and shipped __main__ runners that printed comparison tables. None of that can run in CI. Fold everything multimodal into test_modeling_inkling_multimodal.py -- one file covering the vision, audio and video paths plus the shared input processor -- with small synthetic configs and inputs: no checkpoint, no GPU, no network, no SGLang import, no artifacts. 27 tests, well under a second. Slim test_modeling_inkling.py the same way. The config and layer-classification tests now build their config explicitly instead of requiring the checkpoint, so they actually run; the weight-accounting and tensor-shape tests keep the checkpoint (index JSON only, no weights) and resolve it through the standard llm_models_root() with an INKLING_CHECKPOINT override, so they skip cleanly instead of always skipping on a path that only existed on one machine. Removed: inkling_vision_tower_test.py, inkling_input_processor_test.py, inkling_audio_tower_test.py, inkling_video_utils_test.py (folded in) inkling_mmmu_fixtures.py (MMMU downloader + SGLang loader; no longer used) Also drop the .gitignore entries for the deleted caches and artifacts. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
…nkling PR Run the repo's pre-commit formatters (isort, yapf, ruff, ruff-format) over the files this PR touches, and fix the eleven ruff-legacy D205/D209 docstring regressions it flags in tensorrt_llm/evaluate/interface.py and tests/unittest/llmapi/test_reasoning_parser.py. Formatting and docstring wording only; no behavior change. Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
Two bring-up leftovers outside _torch:
* evaluate: drop summarize_generation_stats / log_generation_stats and both
call sites (Evaluator.evaluate and LmEvalWrapper). This logged a greppable
GEN_STATS line with the finish_reason mix and generated-token distribution
so a runaway/no-EOS regression would be visible while bringing the model up.
It is observability scaffolding, not part of the eval contract: it never
influenced what was scored, nothing consumed the marker, and no test
covered it.
* docs: drop "The text decoder is also usable standalone (text-only) via the
InklingForCausalLM architecture" from the Inkling footnote. It is not true.
InklingForCausalLM carries no @register_auto_model, so it is not in
MODEL_CLASS_MAPPING and no checkpoint can select it; the config registry has
no inkling_text entry either. The class is the base that
InklingForConditionalGeneration derives from, and the inkling_text handling
in config_utils/_util exists for that nested sub-config, not for standalone
loading. The published checkpoint declares InklingForConditionalGeneration
and the text-only accuracy test loads it through that same architecture, so
registering the class would advertise a path with no checkpoint to exercise
it and no test coverage.
Signed-off-by: KleinBlueC <102355066+KleinBlueC@users.noreply.github.com>
8658713 to
c15285c
Compare
|
PR_Github #63648 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #63713 [ run ] triggered by Bot. Commit: |
|
PR_Github #63713 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #63889 [ run ] triggered by Bot. Commit: |
|
PR_Github #63889 [ run ] completed with state
|
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (8)
tensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.py (1)
329-336: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winDocument the interleaved
w13_weightlayout. The checkpoint stores[g0, u0, ...];_split_interleaved_gate_upcorrectly maps even rows tow1and odd rows tow3. Update lines 331–332 and add a sentinel-row test.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.py` around lines 329 - 336, Update the docstring for the per-expert mapping method to state that w13_weight uses the interleaved [g0, u0, …] layout, with even rows mapped to w1 and odd rows to w3, rather than describing gate and up rows as contiguous halves. Add a sentinel-row test covering _split_interleaved_gate_up to verify the even/odd mapping.tensorrt_llm/_torch/configs/inkling.py (2)
140-142: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReturn a precise element type from
_local_ids.The annotation is the bare
set. Useset[int]so callers get the element type.♻️ Proposed change
`@property` - def _local_ids(self) -> set: + def _local_ids(self) -> set[int]: return set(self.local_layer_ids)As per coding guidelines: "Annotate every function, use
Nonefor procedures, avoid unnecessaryAnyandtype: ignore, prefer built-in generic types".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/configs/inkling.py` around lines 140 - 142, Update the _local_ids property return annotation from bare set to set[int], preserving its existing set(self.local_layer_ids) implementation.Source: Coding guidelines
241-250: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAnnotate
_as_configand the two__init__methods.
_as_confighas no parameter or return annotation. Both__init__methods have no-> None. The guidelines require an annotation on every function.♻️ Proposed change
`@staticmethod` - def _as_config(value): + def _as_config(value: PretrainedConfig | dict | None) -> PretrainedConfig | None: if value is None or isinstance(value, PretrainedConfig): return valueAlso add
-> NonetoInklingTextConfig.__init__(line 89) andInklingConfig.__init__(line 220).As per coding guidelines: "Annotate every function, use
Nonefor procedures".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/configs/inkling.py` around lines 241 - 250, Annotate the static method _as_config with appropriate parameter and return types, and add -> None to both InklingTextConfig.__init__ and InklingConfig.__init__. Preserve the existing conversion and initialization behavior.Source: Coding guidelines
tests/unittest/llmapi/test_reasoning_parser.py (1)
919-941: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCreate a fresh parser for each
parsecall.The test reuses one parser instance for three
parsecalls. That relies onparsebeing stateless. If the parser later keeps buffer state between calls, this test fails for a reason unrelated to the assertions it makes.♻️ Proposed change
- parser = ReasoningParserFactory.create_reasoning_parser("inkling") - r = parser.parse( + def _parse(text): + return ReasoningParserFactory.create_reasoning_parser("inkling").parse(text) + + r = _parse( f'{INK_CH}need tool{INK_EM}{INK_MM}<|content_invoke_tool_json|>{{"n":1}}{INK_EM}{INK_END}' ) assert r.content == '{"n":1}' assert r.reasoning_content == "need tool" - r = parser.parse( + r = _parse( f'{INK_CH}need tool{INK_EM}{INK_MM}<|content_invoke_tool_text|>lookup{INK_EM}{INK_END}' ) assert r.content == "lookup" assert r.reasoning_content == "need tool" rep = f"{INK_CH}reason{INK_EM}" + f"{INK_MM}{INK_CT}Answer: A{INK_EM}{INK_END}" * 6 - r = parser.parse(rep) + r = _parse(rep)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unittest/llmapi/test_reasoning_parser.py` around lines 919 - 941, Update test_inkling_reasoning_parser_tool_and_repetition_segmentation to create a fresh parser via ReasoningParserFactory.create_reasoning_parser("inkling") before each of its three parse calls, rather than reusing one parser instance across calls.tensorrt_llm/_torch/models/modeling_inkling_multimodal.py (1)
436-444: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueReplace the
assertwith an explicit check.Python removes
assertunder-O. Ifn_layers < 1,idxshas one element, andidxs[0] = 0thenidxs[-1] = len(scales) - 1write the same slot. The returned plan then holds a single scale, andInklingVisionModel.__init__builds zero layers. Validaten_layersat the top of the function instead.🐛 Proposed fix
if patch_size <= 1: raise ValueError("patch_size must be greater than 1") + if n_layers < 1: + raise ValueError("n_layers must be at least 1") @@ - assert len(idxs) >= 2 idxs[0] = 0 idxs[-1] = len(scales) - 1🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/models/modeling_inkling_multimodal.py` around lines 436 - 444, Replace the `assert len(idxs) >= 2` in the scale-planning function with an explicit validation of `n_layers` at the function entry, rejecting values below 2 before computing or mutating `idxs`. Preserve the existing index assignment and returned scale plan for valid layer counts.tensorrt_llm/_torch/models/modeling_inkling.py (2)
883-885: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the truncated comment at the end of
__init__.The comment stops mid-sentence and describes buffers that this class no longer owns. The decode metadata buffers now live in
InklingAttentionMetadata(ink_seq_lens/ink_page_table). A reader looks for a statement that does not exist.♻️ Proposed cleanup
self.local_num_heads = num_heads // tp_size - # Stable GPU buffers for the CUDA-graph-safe runtime decode metadata, - # refreshed eagerly (before capture/replay) by the model engine via🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/models/modeling_inkling.py` around lines 883 - 885, Remove the truncated stable GPU buffers comment at the end of __init__, including its continuation, since the buffers are owned by InklingAttentionMetadata rather than this class.
429-435: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winReduce host-side work on the decode path in
InklingConvRuntime.build.
buildruns once per forward.attn_metadata.seq_lens.tolist()forces a device-to-host transfer whenseq_lensis a CUDA tensor, andwrite_state_indicesallocates a freshtorch.tensor(slots, ...)on line 276 for every step. Both land on the decode critical path.Two changes remove them:
- Read the already-materialized host sequence lengths (
attn_metadata.seq_lensis built from a pinned CPU tensor in_prepare_tp_inputs) instead of calling.tolist()on a device tensor.- Fill
state_indices_cpu[:n]in place from the Python list rather than building a temporary tensor.♻️ Proposed change for the staging write
n = len(slots) - self.state_indices_cpu[:n].copy_(torch.tensor(slots, dtype=torch.int32)) + sl_np = self.state_indices_cpu[:n].numpy() + for i, slot in enumerate(slots): + sl_np[i] = slot self.state_indices[:n].copy_(self.state_indices_cpu[:n], non_blocking=True)Measure the decode step time before and after.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/models/modeling_inkling.py` around lines 429 - 435, Reduce decode-path host overhead in InklingConvRuntime.build by reusing the host sequence lengths prepared by _prepare_tp_inputs instead of calling .tolist() on attn_metadata.seq_lens. Update write_state_indices to populate state_indices_cpu[:n] in place from the Python slots list, avoiding a temporary torch.tensor allocation. Measure decode step time before and after the changes.tensorrt_llm/_torch/attention_backend/inkling_triton.py (1)
482-494: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winBuild the eager page table with a single host-to-device copy.
build_page_tableallocates one CUDA tensor per request row inside the loop. Eachtorch.tensor(valid, device=device)is its own H2D transfer. This path is the eager decode fallback, so it runs whenever the metadata was not published.♻️ Proposed refactor
batch = len(block_ids_per_seq) - pt = torch.zeros((batch, max_pages), dtype=torch.int32, device=device) - for i, blocks in enumerate(block_ids_per_seq): - valid = [int(b) for b in blocks if int(b) >= 0] - if valid: - pt[i, : len(valid)] = torch.tensor(valid, dtype=torch.int32, device=device) - return pt + host = torch.zeros((batch, max_pages), dtype=torch.int32) + host_np = host.numpy() + for i, blocks in enumerate(block_ids_per_seq): + valid = [b for b in map(int, blocks) if b >= 0][:max_pages] + if valid: + host_np[i, : len(valid)] = valid + return host.to(device=device, non_blocking=True)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/attention_backend/inkling_triton.py` around lines 482 - 494, Update build_page_table to assemble all filtered valid block IDs into a host-side dense representation first, then create or copy the complete [batch, max_pages] int32 page table to device once outside the row loop. Preserve zero padding and each row’s existing ordering and placement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.py`:
- Around line 260-274: Complete type annotations in the mapper: annotate the
_text_config property with InklingTextConfig, change preprocess_weights and
related interfaces from unparameterized Dict to dict[str, torch.Tensor],
annotate regex match parameters as re.Match[str], and use None for methods
without a return value. Add equivalent parameter and return annotations to the
nested _assign function, covering the additional methods around the later
referenced section without changing behavior.
In `@tensorrt_llm/_torch/models/modeling_inkling_multimodal.py`:
- Around line 900-921: Replace the assert in sample_video_frames with an
explicit ValueError when total_frames is not positive, preventing empty videos
from reaching np.linspace. Also update plan_out_scales at
tensorrt_llm/_torch/models/modeling_inkling_multimodal.py:436-444 to perform a
top-of-function n_layers < 1 check that raises explicitly instead of relying on
assert len(idxs) >= 2.
- Around line 189-197: Update the media-loading boundary around
BaseMediaIO.async_load/load_file so untrusted requests cannot read arbitrary
bare paths or file:// URLs; reject them or enforce an approved media-root
allowlist before opening files. Ensure InklingImagePreprocessor receives only
already-decoded media objects or validated paths, while preserving supported
URL/data handling.
In `@tensorrt_llm/_torch/models/modeling_inkling.py`:
- Around line 1-39: Run pre-commit formatting across all files and apply the
resulting changes: in tensorrt_llm/_torch/models/modeling_inkling.py lines 1-39,
apply ruff-format; in tensorrt_llm/_torch/attention_backend/inkling_triton.py
lines 560-686, apply all seven Ruff auto-fixes and ruff-format, including the
InklingAttentionMetadata body; in
tensorrt_llm/_torch/pyexecutor/conv_state_manager.py lines 55-86, apply
ruff-format to wrap the write_conv_state_indices signature.
In `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 3811-3815: Correct the `_maybe_prepare_inkling_runtime` docstring
to state the actual callers, replacing `_prepare_tp_inputs_no_cache` with
`_prepare_star_attention_inputs` if that is the intended eager path. Do not
claim publication occurs in `_prepare_tp_inputs_no_cache` unless adding the
helper call there is explicitly required.
In `@tensorrt_llm/evaluate/post_processing.py`:
- Around line 223-224: Update the no-marker passthrough condition in the
post-processing logic to use _INK_CONTROL_RE.search(text) is None, so all
Inkling control tokens—including framing-only headers—enter parser handling. Add
coverage for framing-only input and output truncated immediately after the
header, preserving passthrough only when no control token is present.
In `@tensorrt_llm/llmapi/llm_args.py`:
- Around line 5067-5069: Regenerate the LLM args golden manifest using
scripts/generate_llm_args_golden_manifest.py so the attn_backend entry includes
the newly added INKLING telemetry value, then commit the updated generated
manifest.
In `@tensorrt_llm/llmapi/reasoning_parser.py`:
- Around line 719-729: Replace bare list annotations with list[str] for the
content and reasoning accumulator parameters of _emit() and _consume(), and for
the batch-parser accumulators at tensorrt_llm/llmapi/reasoning_parser.py lines
751-752, streaming-parser accumulators at lines 781-782, and finish-parser
accumulators at lines 793-794; preserve the existing string-appending behavior.
In `@tensorrt_llm/sampling_params.py`:
- Around line 554-559: Update the EOS fallback handling around self.end_id and
self.stop_token_ids to skip any stop_token equal to self.end_id, matching the
generation-config path while retaining other unique stop tokens. Add a
regression test covering repeated primary EOS IDs such as [7, 7, 8], asserting 7
is excluded from stop_token_ids.
In `@tests/integration/defs/accuracy/test_llm_api_pytorch_multimodal.py`:
- Around line 721-726: Update TestInkling_NVFP4.test_nvfp4 and its LLM
configuration to use tensor_parallel_size=4, matching MPI and device-memory
gating, and document the MMMU reference configuration. Ensure the same runtime
requirements are reflected for the existing test-list entries in l0_b200.yml and
llm_function_core.txt.
In `@tests/integration/defs/accuracy/test_llm_api_pytorch.py`:
- Around line 8140-8172: Update the test_nvfp4 task loop to pass a fresh
SamplingParams instance to each task.evaluate call instead of reusing the
class-level self.sampling_params object. Preserve the existing MAX_NUM_TOKENS
configuration while allowing each task to independently set
truncate_prompt_tokens without mutating shared state.
In `@tests/unittest/_torch/modeling/test_modeling_inkling.py`:
- Line 1: Run the configured ruff formatter on test_modeling_inkling.py and
commit the resulting formatting changes, without altering its behavior or
unrelated files.
- Around line 90-107: Add a focused test alongside
test_model_defaults_pin_v2_and_disable_block_reuse that constructs an Inkling
configuration with transceiver_runtime set to "CPP" and invokes
KvCacheCreator._fallback_if_unsupported_kv_cache_manager_v2(). Assert it raises
the Inkling-specific NotImplementedError and that the message recommends
transceiver_runtime='PYTHON', preserving the V2-only KV-head geometry
requirement.
---
Nitpick comments:
In `@tensorrt_llm/_torch/attention_backend/inkling_triton.py`:
- Around line 482-494: Update build_page_table to assemble all filtered valid
block IDs into a host-side dense representation first, then create or copy the
complete [batch, max_pages] int32 page table to device once outside the row
loop. Preserve zero padding and each row’s existing ordering and placement.
In `@tensorrt_llm/_torch/configs/inkling.py`:
- Around line 140-142: Update the _local_ids property return annotation from
bare set to set[int], preserving its existing set(self.local_layer_ids)
implementation.
- Around line 241-250: Annotate the static method _as_config with appropriate
parameter and return types, and add -> None to both InklingTextConfig.__init__
and InklingConfig.__init__. Preserve the existing conversion and initialization
behavior.
In `@tensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.py`:
- Around line 329-336: Update the docstring for the per-expert mapping method to
state that w13_weight uses the interleaved [g0, u0, …] layout, with even rows
mapped to w1 and odd rows to w3, rather than describing gate and up rows as
contiguous halves. Add a sentinel-row test covering _split_interleaved_gate_up
to verify the even/odd mapping.
In `@tensorrt_llm/_torch/models/modeling_inkling_multimodal.py`:
- Around line 436-444: Replace the `assert len(idxs) >= 2` in the scale-planning
function with an explicit validation of `n_layers` at the function entry,
rejecting values below 2 before computing or mutating `idxs`. Preserve the
existing index assignment and returned scale plan for valid layer counts.
In `@tensorrt_llm/_torch/models/modeling_inkling.py`:
- Around line 883-885: Remove the truncated stable GPU buffers comment at the
end of __init__, including its continuation, since the buffers are owned by
InklingAttentionMetadata rather than this class.
- Around line 429-435: Reduce decode-path host overhead in
InklingConvRuntime.build by reusing the host sequence lengths prepared by
_prepare_tp_inputs instead of calling .tolist() on attn_metadata.seq_lens.
Update write_state_indices to populate state_indices_cpu[:n] in place from the
Python slots list, avoiding a temporary torch.tensor allocation. Measure decode
step time before and after the changes.
In `@tests/unittest/llmapi/test_reasoning_parser.py`:
- Around line 919-941: Update
test_inkling_reasoning_parser_tool_and_repetition_segmentation to create a fresh
parser via ReasoningParserFactory.create_reasoning_parser("inkling") before each
of its three parse calls, rather than reusing one parser instance across calls.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 54bc7c1e-088b-4aec-9c43-3ed5e6c6cded
📒 Files selected for processing (37)
docs/source/models/supported-models.mdtensorrt_llm/_torch/attention_backend/inkling_triton.pytensorrt_llm/_torch/attention_backend/utils.pytensorrt_llm/_torch/configs/__init__.pytensorrt_llm/_torch/configs/inkling.pytensorrt_llm/_torch/model_config.pytensorrt_llm/_torch/models/__init__.pytensorrt_llm/_torch/models/checkpoints/__init__.pytensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.pytensorrt_llm/_torch/models/modeling_inkling.pytensorrt_llm/_torch/models/modeling_inkling_multimodal.pytensorrt_llm/_torch/pyexecutor/_util.pytensorrt_llm/_torch/pyexecutor/config_utils.pytensorrt_llm/_torch/pyexecutor/conv_state_manager.pytensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor_creator.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytensorrt_llm/evaluate/lm_eval.pytensorrt_llm/evaluate/post_processing.pytensorrt_llm/llmapi/llm_args.pytensorrt_llm/llmapi/llm_utils.pytensorrt_llm/llmapi/reasoning_parser.pytensorrt_llm/sampling_params.pytensorrt_llm/serve/openai_server.pytests/integration/defs/accuracy/references/gsm8k.yamltests/integration/defs/accuracy/references/mmlu.yamltests/integration/defs/accuracy/references/mmmu.yamltests/integration/defs/accuracy/test_llm_api_pytorch.pytests/integration/defs/accuracy/test_llm_api_pytorch_multimodal.pytests/integration/test_lists/qa/llm_function_core.txttests/integration/test_lists/test-db/l0_b200.ymltests/unittest/_torch/executor/test_mamba_cache_manager.pytests/unittest/_torch/modeling/test_modeling_inkling.pytests/unittest/_torch/modeling/test_modeling_inkling_multimodal.pytests/unittest/llmapi/test_reasoning_parser.pytests/unittest/llmapi/test_sampling_params.pytests/unittest/others/test_lm_eval.py
🚧 Files skipped from review as they are similar to previous changes (18)
- tensorrt_llm/_torch/models/checkpoints/init.py
- tensorrt_llm/_torch/models/init.py
- tests/integration/defs/accuracy/references/mmlu.yaml
- tensorrt_llm/_torch/model_config.py
- tests/integration/defs/accuracy/references/mmmu.yaml
- tests/integration/test_lists/qa/llm_function_core.txt
- docs/source/models/supported-models.md
- tests/integration/test_lists/test-db/l0_b200.yml
- tensorrt_llm/_torch/pyexecutor/resource_manager.py
- tensorrt_llm/_torch/pyexecutor/py_executor_creator.py
- tensorrt_llm/llmapi/llm_utils.py
- tensorrt_llm/serve/openai_server.py
- tests/unittest/llmapi/test_sampling_params.py
- tensorrt_llm/_torch/configs/init.py
- tensorrt_llm/_torch/pyexecutor/config_utils.py
- tensorrt_llm/_torch/pyexecutor/_util.py
- tests/unittest/others/test_lm_eval.py
- tensorrt_llm/evaluate/lm_eval.py
| if isinstance(image, str): | ||
| if image.startswith(("http://", "https://", "data:")): | ||
| raise ValueError( | ||
| "InklingImagePreprocessor received a URL/data: image; resolve " | ||
| "it to bytes upstream before preprocessing." | ||
| ) | ||
| path = image[len("file://") :] if image.startswith("file://") else image | ||
| with open(path, "rb") as f: | ||
| return Image.open(io.BytesIO(f.read())).convert("RGB") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Trace how multi_modal_data reaches input processors: decoded objects vs raw strings.
rg -nP --type=py -C 5 'multi_modal_data' tensorrt_llm/serve tensorrt_llm/inputs | head -120
rg -nP --type=py -C 5 'def (load_image|load_audio|async_load_image|default_multimodal_input_loader)' tensorrt_llm/inputs | head -80Repository: NVIDIA/TensorRT-LLM
Length of output: 11861
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- multimodal processor symbols ---'
ast-grep outline tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
echo '--- relevant processor code ---'
sed -n '150,230p' tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
sed -n '640,715p' tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
echo '--- loader implementations ---'
sed -n '55,235p' tensorrt_llm/inputs/utils.py
sed -n '760,850p' tensorrt_llm/inputs/utils.py
echo '--- serving call sites and request conversion ---'
sed -n '1460,1565p' tensorrt_llm/serve/openai_server.py
sed -n '1680,1740p' tensorrt_llm/serve/openai_server.py
rg -n -P --type=py -C 8 'default_multimodal_input_loader|async_load_image|load_image\(|load_audio\(|mm_data_tracker|retrieve_all_sync' tensorrt_llm/inputs tensorrt_llm/serveRepository: NVIDIA/TensorRT-LLM
Length of output: 50375
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- chat media parsing and decoding ---'
sed -n '120,230p' tensorrt_llm/serve/chat_utils.py
rg -n -P -C 8 'def _make_media_io|class .*Media|async_load|read\(\)|retrieve_all|add_data' tensorrt_llm/serve/chat_utils.py tensorrt_llm/inputs/utils.py
echo '--- tracker storage and retrieval ---'
sed -n '300,450p' tensorrt_llm/inputs/utils.py
sed -n '930,975p' tensorrt_llm/inputs/utils.py
echo '--- Inkling input processor dispatch ---'
sed -n '930,1135p' tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
rg -n -P -C 8 'multi_modal_data|data\["|data.get|_to_pil_rgb|_to_waveform|image_preprocessor|audio_preprocessor' tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
echo '--- external request input construction ---'
rg -n -P -C 6 'generate_async\(inputs=|inputs\s*=\s*prompt|multi_modal_data' tensorrt_llm/serve tensorrt_llm/llmapi tests/unittest/_torch/modeling/test_modeling_inkling_multimodal.pyRepository: NVIDIA/TensorRT-LLM
Length of output: 50375
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- media IO selection and async_load implementations ---'
sed -n '90,125p' tensorrt_llm/serve/chat_utils.py
fd -i 'media_io.py' tensorrt_llm
sed -n '1,280p' tensorrt_llm/inputs/media_io.py
echo '--- Inkling text and tokenized call paths ---'
sed -n '1080,1285p' tensorrt_llm/_torch/models/modeling_inkling_multimodal.pyRepository: NVIDIA/TensorRT-LLM
Length of output: 18462
🏁 Script executed:
#!/bin/bash
set -eu
rg -n -P '^class (BaseMediaIO|ImageMediaIO|AudioMediaIO|VideoMediaIO)|def (async_load|load)\(' tensorrt_llm/inputs/media_io.py
sed -n '520,930p' tensorrt_llm/inputs/media_io.pyRepository: NVIDIA/TensorRT-LLM
Length of output: 13322
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- serving entry points that accept multimodal input directly ---'
rg -n -P -C 8 'multi_modal_data|generate_async\(inputs=|generate\(inputs=|prompt_token_ids' \
tensorrt_llm/serve tensorrt_llm/llmapi \
-g '*.py' | head -400
echo '--- direct LLM input normalization ---'
rg -n -P -C 8 'class (LLM|Request)|def generate|TextPrompt|TokenPrompt|multi_modal_data' \
tensorrt_llm/llmapi tensorrt_llm/executor tensorrt_llm/inputs \
-g '*.py' | head -400Repository: NVIDIA/TensorRT-LLM
Length of output: 50375
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import ast
from pathlib import Path
files = {
"inkling": Path("tensorrt_llm/_torch/models/modeling_inkling_multimodal.py"),
"media_io": Path("tensorrt_llm/inputs/media_io.py"),
"llm": Path("tensorrt_llm/llmapi/llm.py"),
}
trees = {name: ast.parse(path.read_text()) for name, path in files.items()}
def find_function(tree, name):
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == name:
return node
raise AssertionError(name)
inkling_call = find_function(trees["inkling"], "call_with_text_prompt")
media_async = find_function(trees["media_io"], "async_load")
media_image_file = find_function(trees["media_io"], "load_file")
llm_preprocess = find_function(trees["llm"], "_preprocess")
def source(node, tree):
return ast.get_source_segment(tree, node) or ""
assert "_to_pil_rgb" not in source(inkling_call, trees["inkling"])
assert "self._text_processor" in source(inkling_call, trees["inkling"])
assert 'parsed.scheme in ("", "file")' in source(media_async, trees["media_io"])
assert "load_file" in source(media_async, trees["media_io"])
assert "Path(_normalize_file_uri(url))" in source(media_image_file, trees["media_io"])
assert '"multi_modal_data" in inputs' in source(llm_preprocess, trees["llm"])
print("Inkling receives media after the serving MediaIO path.")
print("BaseMediaIO.async_load dispatches bare paths and file:// URLs to load_file.")
print("The direct LLM dictionary API forwards multi_modal_data to the input processor.")
PYRepository: NVIDIA/TensorRT-LLM
Length of output: 522
Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Reachability: External · Exploitability: Moderate
Reachability path
● Entry
tensorrt_llm/evaluate/lm_eval.py:530
MultimodalLmEvalWrapper: Multimodal wrapper for lm-evaluation-harness that handles vision-language models. This wrapper extends the base LmEvalWrapper to support …
│
▼
● Sink
tensorrt_llm/_torch/models/modeling_inkling_multimodal.py
Reject untrusted local media paths at the media boundary.
OpenAI requests decode media before Inkling receives it, but BaseMediaIO.async_load accepts bare paths and file:// URLs and calls load_file without an allowlist. An attacker can therefore make the server read a local media file. Reject local paths for untrusted requests, or restrict them to an approved media root and pass only decoded objects to Inkling.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] 195-195: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(path, "rb")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(open-filename-from-request)
[warning] 189-189: Do not make http calls without encryption
Context: "http://"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.
(requests-http)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tensorrt_llm/_torch/models/modeling_inkling_multimodal.py` around lines 189 -
197, Update the media-loading boundary around BaseMediaIO.async_load/load_file
so untrusted requests cannot read arbitrary bare paths or file:// URLs; reject
them or enforce an approved media-root allowlist before opening files. Ensure
InklingImagePreprocessor receives only already-decoded media objects or
validated paths, while preserving supported URL/data handling.
Source: Linters/SAST tools
d38cd08 to
3d079c0
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
tensorrt_llm/_torch/attention_backend/inkling_triton.py (2)
479-491: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winBuild the page table with one host-to-device copy.
Line 490 runs inside the per-request loop, so this helper issues one H2D transfer per request. The eager decode fallback in
tensorrt_llm/_torch/models/modeling_inkling.pycalls it on every decode step, so the cost scales with batch size on the latency-critical path. Assemble a single padded host list and transfer it once.Add annotations for
block_ids_per_seqanddeviceas well.♻️ Proposed fix
-def build_page_table(block_ids_per_seq, max_pages: int, device) -> torch.Tensor: +def build_page_table( + block_ids_per_seq: Sequence[Sequence[int]], + max_pages: int, + device: torch.device, +) -> torch.Tensor: """Pack a ragged ``block_ids_per_seq`` (from ``KVCacheManagerV2.get_batch_cache_indices``) into a dense ``[batch, max_pages]`` int32 page table, padding short rows with 0 (never read: the decode kernel bounds every access by the per-request ``seq_len``). """ batch = len(block_ids_per_seq) - pt = torch.zeros((batch, max_pages), dtype=torch.int32, device=device) - for i, blocks in enumerate(block_ids_per_seq): - valid = [int(b) for b in blocks if int(b) >= 0] - if valid: - pt[i, : len(valid)] = torch.tensor(valid, dtype=torch.int32, device=device) - return pt + rows = [[0] * max_pages for _ in range(batch)] + for row, blocks in zip(rows, block_ids_per_seq): + valid = [int(b) for b in blocks if int(b) >= 0] + row[: len(valid)] = valid[:max_pages] + return torch.tensor(rows, dtype=torch.int32, device=device)Add the import at the top of the file:
from collections.abc import Sequence🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/attention_backend/inkling_triton.py` around lines 479 - 491, Update build_page_table to annotate block_ids_per_seq and device, and replace the per-row tensor assignments with a single host-side padded representation of all valid block IDs, then perform one host-to-device tensor construction/copy for the complete [batch, max_pages] int32 page table. Preserve zero padding and filtering of negative IDs.Source: Coding guidelines
341-347: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winBoth Triton wrappers validate shapes with
assert. Python invoked with-Oremoves everyassert, so the head-divisibility andrel_logitschecks vanish in production. The kernels then mis-mapcur_head // kv_group_numor read the bias tensor out of bounds, with no error. RaiseValueErrorat both sites.
tensorrt_llm/_torch/attention_backend/inkling_triton.py#L341-L347: raiseValueErrorfor a non-divisiblenum_heads/num_kv_headspair, and for a non-contiguous or wrongly shapedrel_logits; include the[total_tokens, num_heads, rel_extent]shape check.tensorrt_llm/_torch/attention_backend/inkling_triton.py#L426-L432: apply the same twoValueErrorchecks againstk_cache.shape[1]and the[batch, num_heads, rel_extent]bias shape.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/attention_backend/inkling_triton.py` around lines 341 - 347, Replace the assert-based validation in both wrapper sites—tensorrt_llm/_torch/attention_backend/inkling_triton.py:341-347 and :426-432—with unconditional ValueError checks. Validate head divisibility using num_kv_heads at the first site and k_cache.shape[1] at the second, and validate rel_logits contiguity plus the required [total_tokens, num_heads, rel_extent] and [batch, num_heads, rel_extent] shapes respectively.tensorrt_llm/_torch/configs/inkling.py (2)
140-142: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueParameterize the
_local_idsreturn type.
setis unparameterized. Useset[int]so the element type is explicit.♻️ Proposed change
`@property` - def _local_ids(self) -> set: + def _local_ids(self) -> set[int]: return set(self.local_layer_ids)As per coding guidelines, "prefer built-in generic types".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/configs/inkling.py` around lines 140 - 142, Update the _local_ids property return annotation from unparameterized set to set[int], preserving its existing return value and behavior.Source: Coding guidelines
241-250: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAnnotate
_as_configand theInklingConfig.__init__sub-config parameters.
_as_confighas no parameter or return annotation. Thetext_config,audio_config,vision_config, andmtp_configparameters are also unannotated. AddPretrainedConfig | dict | Nonestyle annotations.♻️ Proposed change
`@staticmethod` - def _as_config(value): + def _as_config( + value: PretrainedConfig | dict | None, + ) -> PretrainedConfig | None: if value is None or isinstance(value, PretrainedConfig): return valueAs per coding guidelines, "Annotate every function".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/configs/inkling.py` around lines 241 - 250, Annotate the static method _as_config with PretrainedConfig | dict | None for its value parameter and return type, and add the same style of annotations to the text_config, audio_config, vision_config, and mtp_config parameters in InklingConfig.__init__. Preserve the existing conversion and initialization behavior.Source: Coding guidelines
tests/unittest/_torch/modeling/test_modeling_inkling_multimodal.py (1)
316-466: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTest coverage summary (unit tests) and one gap.
- Added test functions:
test_vision_scale_plan_and_module_tree,test_scale_plan_is_a_strictly_growing_progression,test_fold_timespace_to_depth_is_value_preserving,test_vision_tower_emits_one_row_per_patch,test_vision_tower_load_weights_is_strict,test_dmel_preprocess_shape_and_range,test_dmel_multi_clip_concat_and_empty_clip,test_audio_tower_matches_codebook_reference,test_audio_tower_optional_norm_and_bin_count_guard,test_sample_video_frames,test_sample_video_as_images_preserves_count_and_order,test_video_frames_expand_to_one_image_span_each,test_registration,test_serving_and_profiler_interface_contract,test_text_only_passthrough,test_image_placeholder_expands_to_num_patches,test_audio_placeholder_expands_to_num_frames,test_image_and_audio_expand_independently,test_call_with_token_ids_expands_and_passes_text_through,test_mm_token_ids_cover_both_modalities,test_fail_loud_on_placeholder_media_mismatch,test_fail_loud_on_audio_placeholder_mismatch,test_multimodal_hash_prefix_cache_is_refused_per_modality. No test is modified or removed.- Test-list registration: this file lives under
tests/unittest/, so CI runs it throughpytest tests/unittest/. No entry intests/integration/test_lists/is required.- Coverage verdict: sufficient for the vision, audio, video, and placeholder paths, with one gap.
InklingInputProcessor.__init__now rejects a sharedimage_token_id/audio_token_id(modeling_inkling_multimodal.pylines 983-990). No test exercises that branch. Add one case that builds the processor with both ids equal and expectsValueError.💚 Proposed test
+def test_shared_image_audio_token_id_is_rejected(): + with pytest.raises(ValueError, match="must "): + _processor(image_token_id=200054, audio_token_id=200054)As per path instructions, "Always produce a test coverage summary, even if no issues are found."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unittest/_torch/modeling/test_modeling_inkling_multimodal.py` around lines 316 - 466, Add a unit test covering the validation branch in InklingInputProcessor.__init__: construct the processor with identical image_token_id and audio_token_id values and assert that ValueError is raised. Keep the existing processor tests unchanged and use the established _processor setup or constructor arguments to exercise the shared-token rejection.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/unittest/_torch/modeling/test_modeling_inkling.py`:
- Around line 52-68: Update the checkpoint helper annotations: change _index()
to return dict[str, str], _exclude_modules() to return set[str], and
_safetensors_shape() to return tuple[list[int], str]. Keep their existing
implementations and behavior unchanged.
---
Nitpick comments:
In `@tensorrt_llm/_torch/attention_backend/inkling_triton.py`:
- Around line 479-491: Update build_page_table to annotate block_ids_per_seq and
device, and replace the per-row tensor assignments with a single host-side
padded representation of all valid block IDs, then perform one host-to-device
tensor construction/copy for the complete [batch, max_pages] int32 page table.
Preserve zero padding and filtering of negative IDs.
- Around line 341-347: Replace the assert-based validation in both wrapper
sites—tensorrt_llm/_torch/attention_backend/inkling_triton.py:341-347 and
:426-432—with unconditional ValueError checks. Validate head divisibility using
num_kv_heads at the first site and k_cache.shape[1] at the second, and validate
rel_logits contiguity plus the required [total_tokens, num_heads, rel_extent]
and [batch, num_heads, rel_extent] shapes respectively.
In `@tensorrt_llm/_torch/configs/inkling.py`:
- Around line 140-142: Update the _local_ids property return annotation from
unparameterized set to set[int], preserving its existing return value and
behavior.
- Around line 241-250: Annotate the static method _as_config with
PretrainedConfig | dict | None for its value parameter and return type, and add
the same style of annotations to the text_config, audio_config, vision_config,
and mtp_config parameters in InklingConfig.__init__. Preserve the existing
conversion and initialization behavior.
In `@tests/unittest/_torch/modeling/test_modeling_inkling_multimodal.py`:
- Around line 316-466: Add a unit test covering the validation branch in
InklingInputProcessor.__init__: construct the processor with identical
image_token_id and audio_token_id values and assert that ValueError is raised.
Keep the existing processor tests unchanged and use the established _processor
setup or constructor arguments to exercise the shared-token rejection.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 6e31d991-e70c-46cf-afb1-0706305d8372
📒 Files selected for processing (33)
docs/source/models/supported-models.mdtensorrt_llm/_torch/attention_backend/inkling_triton.pytensorrt_llm/_torch/configs/__init__.pytensorrt_llm/_torch/configs/inkling.pytensorrt_llm/_torch/model_config.pytensorrt_llm/_torch/models/__init__.pytensorrt_llm/_torch/models/checkpoints/__init__.pytensorrt_llm/_torch/models/checkpoints/hf/inkling_weight_mapper.pytensorrt_llm/_torch/models/modeling_inkling.pytensorrt_llm/_torch/models/modeling_inkling_multimodal.pytensorrt_llm/_torch/pyexecutor/_util.pytensorrt_llm/_torch/pyexecutor/config_utils.pytensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor_creator.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytensorrt_llm/evaluate/lm_eval.pytensorrt_llm/evaluate/post_processing.pytensorrt_llm/llmapi/llm_utils.pytensorrt_llm/llmapi/reasoning_parser.pytensorrt_llm/sampling_params.pytensorrt_llm/serve/openai_server.pytests/integration/defs/accuracy/references/gsm8k.yamltests/integration/defs/accuracy/references/mmlu.yamltests/integration/defs/accuracy/references/mmmu.yamltests/integration/defs/accuracy/test_llm_api_pytorch.pytests/integration/defs/accuracy/test_llm_api_pytorch_multimodal.pytests/integration/test_lists/qa/llm_function_core.txttests/integration/test_lists/test-db/l0_b200.ymltests/unittest/_torch/modeling/test_modeling_inkling.pytests/unittest/_torch/modeling/test_modeling_inkling_multimodal.pytests/unittest/llmapi/test_reasoning_parser.pytests/unittest/llmapi/test_sampling_params.pytests/unittest/others/test_lm_eval.py
🚧 Files skipped from review as they are similar to previous changes (22)
- tests/integration/defs/accuracy/references/gsm8k.yaml
- tensorrt_llm/_torch/model_config.py
- tests/integration/defs/accuracy/references/mmmu.yaml
- tensorrt_llm/llmapi/llm_utils.py
- tensorrt_llm/_torch/models/checkpoints/init.py
- tests/integration/test_lists/test-db/l0_b200.yml
- tensorrt_llm/_torch/models/init.py
- tensorrt_llm/_torch/pyexecutor/resource_manager.py
- docs/source/models/supported-models.md
- tensorrt_llm/_torch/pyexecutor/_util.py
- tensorrt_llm/serve/openai_server.py
- tests/unittest/llmapi/test_sampling_params.py
- tests/integration/defs/accuracy/references/mmlu.yaml
- tests/integration/test_lists/qa/llm_function_core.txt
- tensorrt_llm/_torch/pyexecutor/config_utils.py
- tensorrt_llm/_torch/configs/init.py
- tensorrt_llm/_torch/pyexecutor/py_executor_creator.py
- tests/unittest/others/test_lm_eval.py
- tensorrt_llm/sampling_params.py
- tensorrt_llm/_torch/pyexecutor/model_engine.py
- tensorrt_llm/evaluate/lm_eval.py
- tensorrt_llm/_torch/models/modeling_inkling.py
…ned models BindKvCacheTransceiver constructs CacheTransceiverCpp from ``kv_cache_manager.impl`` and expects the nanobind KVCacheManagerCpp. KVCacheManagerV2's ``.impl`` is the Python KVCacheManagerPy, so enabling disaggregated serving on a V2 manager dies inside a nanobind constructor with a TypeError that lists a C++ signature and names neither the model nor disagg. Add cache_transceiver_config to the V2-incompatible-feature list, where the manager class is chosen. Models structurally pinned to V2 -- sparse attention, Gemma4 hybrid, Inkling -- then hit their existing NotImplementedError with the feature named, and plain V2 falls back to V1 as it already does for the other entries. This is not Inkling-specific: all three V2-pinned families fail on the same line today, so the guard goes in the shared list rather than behind a per-model branch. Scoped to the C++ runtime. KvCacheTransceiverV2 takes the manager object rather than ``.impl``, so the Python runtime is left working and the message points at it as the alternative. ``transceiver_runtime`` may still hold the unresolved "auto" sentinel on paths that skip _resolve_transceiver_runtime_auto, and create_kv_cache_transceiver maps that to the C++ runtime, so anything that is not an explicit "PYTHON" is treated as C++. The gate runs after the model is loaded, so this trades an opaque nanobind error for a named one; it does not move the failure before the checkpoint load.
… supports it Each Inkling decoder layer carries four depthwise short convolutions whose ``kernel_size - 1`` token window is per-request state living outside the KV cache. ``InklingConvRuntime.build`` seeds every context request with ``has_initial_state=False``, so when a prefill reuses cached KV blocks attention resumes from the correct history while the convolutions restart from zeros: the first ``kernel_size - 1`` tokens of each reused chunk convolve against padding instead of the real preceding activations. That is a wrong-output bug, not a cache miss. ``enable_block_reuse`` defaults to True framework-wide and nothing turned it off for Inkling, so the accuracy runs to date were measured with the reuse active on a heavily-overlapping few-shot prefix. Default it off in get_model_defaults and say so in the supported-models footnote. MixedMambaHybridCacheManager documents the same limitation for mamba states, so this follows an established precedent rather than inventing one. The deep-merge still lets an explicit user setting win, which keeps this a safe default rather than a hard block; restoring reuse needs has_initial_state computed from num_cached_tokens_per_seq plus a per-block save/restore of the conv window, and the comment at the seeding site now says so.
…h off model_engine
Inkling's Triton decode kernel needs, per generation request, the total KV
length and the physical page table. Both are host-derived and must land in
fixed-pointer GPU buffers before CUDA-graph capture, so the bring-up published
them from three call sites inside PyTorchModelEngine plus a helper, and held
them in a per-layer InklingDecodeMeta guarded by a publication epoch.
AttentionMetadata.prepare() is the framework hook for exactly this ("Hook to
be called before the forward step of the model") and is already called on all
three input-preparation paths, including the steady-generation fast path. Add
InklingAttentionMetadata, which publishes into its own stable buffers there,
and InklingTritonAttention to carry it; register an INKLING backend and select
it from get_model_defaults.
Verified before moving: CUDAGraphRunner.pad_batch wraps _prepare_inputs, so
request_ids already holds the padded rows when prepare() runs, and
super().prepare() has re-clamped num_cached_tokens_per_seq by then. The hook
sees exactly the data the model-engine call site saw.
InklingTritonAttention subclasses TrtllmAttention and overrides nothing but
Metadata, so the backend switch changes no computation: Inkling's attention
has always been InklingAttention.forward, which overrides the base module
outright and never reaches a backend forward.
InklingDecodeMeta and its epoch counter go with it. The epoch existed because
the buffers outlived the step that filled them; a per-step metadata object
cannot have that problem, since _prepare_inkling_decode resets ink_num_gen
before any early return.
Two things this has to get right, both covered by tests:
* Page tables stage through ONE pinned row per layer. A single shared buffer
refilled per layer races its own non_blocking copies, so every layer past
the first lands a torn table and decode collapses to repeated tokens.
* An attn_backend override silently removes this metadata, and the run then
dies inside CUDA-graph capture with "Cannot copy between CPU and CUDA
tensors", naming neither Inkling nor the setting. Reject it at load.
"INKLING" is deliberately not added to the attn_backend telemetry list:
touching llmapi/llm_args.py trips the API-stability label gate and stales the
golden manifest, for a value no user ever supplies.
…e intents The identity test in _non_hybrid_kv_cache_manager_cls reads like a duplicate of the use_kv_cache_manager_v2 declared in get_model_defaults, and a reader reasonably concludes the model default is not taking effect. It is a backstop: the KV cache is sized before the model is instantiated, so an explicit use_kv_cache_manager_v2=False would otherwise select V1 and a mis-sized per-layer pool. Say so where the branch is. Also list the three is_inkling call sites in the predicate's docstring with what each one is actually asking. They are the same test with three different intents -- select V2, refuse to downgrade from V2, and find the decoder geometry in a multimodal config -- and only the first two are capability questions, which matters when auditing whether any can become a model hook. The other two cleanups in this group needed no change: the duplicated _orig_mod unwrap and the private prepare_inkling_attn_decode hook name both disappeared with the move to InklingAttentionMetadata, and the epoch comment went with the class.
The pool was a standalone ResourceManager under its own ResourceManagerType, published from three call sites plus a helper inside PyTorchModelEngine and released by a fourth branch in the dummy-batch cleanup. Make the cache manager own it, the way CppMambaHybridCacheManager owns mamba conv/SSM state: InklingHybridCacheManager(KVCacheManagerV2, BaseConvStateManager) BaseConvStateManager is a new capability protocol, deliberately not BaseMambaCacheManager: that one mandates get_ssm_states, is_speculative and mamba_layer_cache, and a convolution-only model has no selective-scan state to back them with. Delivery is now a standard AttentionMetadata field. InklingAttentionMetadata .prepare() publishes the pool rows when its kv_cache_manager implements the capability -- an isinstance test on the protocol, matching _prepare_mamba_metadata, not a model identity check -- and the decoder reads them off attn_metadata. No conv kwargs on model.forward and no ResourceManager lookup from inside it. Release rides on the manager's own free_resources, which every caller already invokes, including the warmup/estimation dummy-batch path that previously needed its own Inkling branch. Main-path footprint, measured against the merge base: pyexecutor/model_engine.py 6 hunks / +62 -> 0 pyexecutor/resource_manager.py 1 hunk / +4 -> 0 pyexecutor/py_executor_creator.py 2 hunks / +14 -> 0 What remains under pyexecutor is _util.py and config_utils.py -- the before-model-instantiation KV sizing and manager selection that cannot be a model hook -- plus the new conv_state_manager.py. Beyond the line count this removes a failure mode: conv rows are now freed by the same call that frees the request's KV blocks, so the pool and the KV cache cannot drift apart on request lifetime, and a future block-reuse implementation has one place to decide the semantics rather than two. Costs: the pool is also allocated for the throwaway manager built during KV-cache size estimation (tens of MB, freed with it). And the pool takes its dtype from the model config, NOT the dtype= kwarg _create_kv_cache_manager passes -- that one is the KV cache dtype, a C++ bindings.DataType that torch.zeros rejects outright.
…tor free of it
Three placement mistakes from the previous commits, fixed together because they
are one mistake in three directories.
1. Kernels, per-step metadata and the backend class were all appended to a flat
738-line inkling_triton.py, because the metadata and backend were added to a
file that already held the kernels instead of restructuring at the same time.
sparse/minimax_m3 already shows the shape -- triton_kernels.py,
triton_metadata.py, msa_backend.py, cache_manager.py -- so adopt it:
attention_backend/inkling/
kernels.py the two Triton kernels and their wrappers
metadata.py InklingAttentionMetadata
backend.py InklingTritonAttention
cache_manager.py InklingHybridCacheManager
NOT under sparse/. That package is gated on sparse_attention_config /
SparseParams and its machinery -- index caches, top-k block masks,
per-sparse-layer INDEX_KEY pools -- assumes only part of the KV is scored.
Inkling's attention is dense: full causal on global layers, a 512-token
sliding window on local ones, with a learned relative-bias score_mod.
2. InklingHybridCacheManager sat in pyexecutor, a shared framework directory --
exactly what this work removed from model_engine.py, resource_manager.py and
py_executor_creator.py. MiniMaxM3KVCacheManagerV2 lives in
sparse/minimax_m3/cache_manager.py and _util.py selects it from there, so a
model-owned location is already precedent.
3. BaseConvStateManager is deleted rather than moved. It claimed to be a
capability protocol but both of its useful methods returned Inkling's own
pool and runtime types, so it had exactly one possible implementer and
abstracted nothing -- while planting an Inkling-specific module under
pyexecutor. InklingAttentionMetadata now type-tests the concrete manager.
That is not the model-identity branching the framework warns about: that
rule is about SHARED code, and this metadata class only ever serves Inkling.
Per-request short-conv state is not new -- BaseMambaCacheManager already
declares get_conv_states(layer_idx) and nemotron_h / qwen3_next / qwen3_5
implement it -- but that protocol also mandates get_ssm_states,
is_speculative, mamba_layer_cache and replay metadata, and its
one-tensor-per-layer accessor cannot express Inkling's four convs per layer
at two different widths. Widening it would mean touching a file five manager
classes and three shipped models share, so the reason not to invent a
parallel protocol is recorded in cache_manager.py for whoever adds the second
short-conv model.
pyexecutor now carries zero new files and zero hunks in model_engine.py,
resource_manager.py and py_executor_creator.py; what remains is _util.py and
config_utils.py, the before-model-instantiation manager selection and predicate
that cannot be a model hook.
Pure moves and import updates; no logic changed. Tests assert the layout so it
cannot drift back.
…ling MoE Prerequisite for both expert parallelism and attention data parallelism, and the one piece both need before either can work. FusedMoE sets ``use_dp`` from ``mapping.enable_attention_dp`` and reads ``all_rank_num_tokens`` to pad and gather activations across ranks. InklingMoE called ``self.experts(hidden_states, router_logits)`` and never passed it, so a DP or EP-with-DP layout would have no way to know how much each peer contributed. Models that already support attention DP (DeepSeek-V3, for one) thread exactly this list from the model forward down to the expert call. The value comes off ``attn_metadata``, which PyTorchModelEngine fills only when attention DP is on; the model does not invent it. Dense layers 0 and 1 are InklingDenseMLP, whose forward takes activations only, so the decoder layer dispatches on the mlp type rather than passing the list blindly. Behaviour-preserving on today's paths: with attention DP off the attribute is absent, the list is None, and the expert call is byte-identical to before.
Expert parallelism needs no Inkling-specific implementation. The routed experts go through the generic create_moe factory, so Mapping derives moe_ep_size / moe_tp_size, FusedMoE slices the 256 experts with _compute_ep_partition, and CutlassFusedMoE remaps the NVFP4 per-expert scales onto each rank's local slot range. Checked while scoping this: nothing in the model reimplements expert sharding, and the routed + shared combine stays correct because the shared experts are replicated rather than TP-sharded, so only the routed half is all-reduced. What was missing is a check that the requested split is one the backend can serve. FusedMoE._supports_non_divisible_ep is opt-in and the CUTLASS backend -- Inkling's only routed-expert backend -- does not opt in, so a moe_expert_parallel_size that does not divide 256 fails somewhere inside expert-slot bookkeeping instead of at load. 256 divides by every power of two, so this only bites on values like 3, 5 or 6, which is exactly when a clear message is worth having. The two checks are ordered deliberately: more ranks than experts is the more fundamental problem and subsumes non-divisibility, so it is reported first. Checking divisibility first told a user with 8 ranks and 4 experts to "pick a divisor of 4", which is not the advice they need -- caught by the test that pins the order. Deliberately does not constrain moe_tp_size: with moe_ep_size = 1, the default, the experts are TP-sharded, and that is the layout every Inkling accuracy run to date measured. Tests cover every divisor of 256, the rejected non-divisors, zero-expert ranks, that the guard is inert when EP is off, that the model does not grow a second source of truth for expert sharding, and that the shared experts stay replicated -- if they ever became TP-sharded the routed + shared sum would double-count under EP.
…rash Ran the layouts on 4 GPUs against the golden GSM8K run: ep 1 / 2, cuda_graph on acc 0.9667, zero score flips ep 4, cuda_graph OFF acc 0.9667, zero score flips ep 4, cuda_graph on SIGSEGV during warmup, all four ranks So expert parallelism is result-preserving at every size tried, including moe_tp_size 1: pure EP reproduces the TP-only answer per item. What crashes is ep_size 4 together with CUDA-graph capture. Varying max_batch_size and max_num_tokens does not move it, which rules out the expert GEMM shape as the cause. Root cause not yet found. Refuse that one combination at load and name both escapes -- disable CUDA graphs, or halve ep_size -- rather than hand the user an unexplained SIGSEGV. Two things this deliberately does not do. It does not reject moe_tp_size 1 outright: that would remove a layout measured to work. And it does not repeat the first guess: the initial version blamed whole-width experts in the CUTLASS NVFP4 expert GEMM, which the cuda_graph-off run disproves, so the comment now records what was ruled out as well as what was seen. The guard before this was inferred rather than measured -- it admitted every divisor of 256 on the strength of reading Mapping, _compute_ep_partition and the CUTLASS scale remap. None of that reading predicted a CUDA-graph crash. The tests now assert the measured matrix.
…urce gates CodeRabbit, unresolved: TestInkling_NVFP4.test_nvfp4 defaulted to TP=1 with no MPI or device-memory gate. Inkling-NVFP4 does not fit on one Blackwell GPU at free_gpu_memory_fraction=0.6, so the test could not run -- while being listed in tests/integration/test_lists/test-db/l0_b200.yml and the QA function list. Coverage that looks present and is not is worse than no coverage. The review flagged the multimodal test. The text-side TestInkling_NVFP4 in test_llm_api_pytorch.py has the identical defect -- same checkpoint, same KV fraction, same missing gates -- so both are fixed here. Both now request tensor_parallel_size=4 and carry skip_less_mpi_world_size(4) plus skip_less_device_memory(183000), matching the idiom the other multi-GPU tests in these files use. TP=4 also matches how the cached references were measured; at any other parallelism the numbers are not comparable, and the docstrings now say so. Also fixes a second unresolved finding in the same function: sampling_params is a class attribute and AccuracyTask.evaluate sets truncate_prompt_tokens on it in place when it is None. The loop handed the same object to GSM8K first, so it kept GSM8K's input budget when MMLU ran and MMLU never applied its own. Each task now gets its own copy.
…through CodeRabbit, unresolved: extract_inkling_content only tested for <|content_text|> / <|content_thinking|> before returning the text unchanged. Output that carried framing but never opened a visible block -- a <|message_model|> header with no later content marker, or a generation truncated right after the header -- therefore came back verbatim, and the evaluator scored raw special tokens, or text sitting outside any visible block, as if it were the model's answer. InklingReasoningParser treats that output as framing and drops it, so the offline path has to as well. Test the whole control-token set instead, via _INK_CONTROL_RE, which is the same set the extraction loop already walks. Passthrough for every other model and benchmark is unchanged: text carrying no Inkling control token at all still returns unchanged, now on a strictly wider test. Three regression tests: framing-only output, truncated-after-header output, and the unchanged passthrough.
CodeRabbit, unresolved: when eos_token_id comes from config.json as a list, the first entry becomes end_id and the rest become stop tokens -- with no check that the rest are not the primary EOS. A config listing it twice, e.g. [7, 7, 8], put 7 in both end_id and stop_token_ids. The generation_config path a few lines below already skips self.end_id; this path did not. Two regression tests: the repeated-primary case, and a single-element list producing no stop tokens at all.
…d test helpers Three unresolved CodeRabbit findings, all the same kind, so they land together rather than as three near-identical commits. inkling_weight_mapper.py: _text_config had no return annotation, preprocess_weights and _map_expert used unparameterized Dict, and the nested _assign was unannotated. The file already carries `from __future__ import annotations`, so these use built-in generics (dict[str, torch.Tensor], re.Match[str]) per the coding guidelines. An AST sweep confirms no function in the file is left with a missing return or parameter annotation. reasoning_parser.py: _emit and _consume took bare `list` accumulators and the three parser entry points declared bare `list` locals, all of which only ever hold str. Now list[str] throughout. test_modeling_inkling.py: the checkpoint helpers now return dict[str, str], set[str] and tuple[list[int], str]. No behaviour change.
Under attention DP every rank runs the FULL attention over its OWN requests and
only the routed experts stay sharded. The base Attention already scopes itself
to that (modules/attention.py builds qkv_proj / o_proj from an internal
tp_size=1 mapping), KVCacheManagerV2 already sizes the paged pool the same way,
Embedding forces tensor_parallel_mode=None, and lm_head is replicated by
DecoderModelForCausalLM. What was missing was every Inkling-only tensor that
hangs off the same split and still read the GLOBAL mapping.tp_size:
* r_proj sharded num_heads * d_rel by tp_size while the base kept full heads
* the k/v short convs took tp_shard=True unconditionally, so they convolved a
quarter of a full-width k/v stream
* local_num_heads divided by the global tp_size
* the conv-state pool sized its k/v rows by the global tp_size
None of these fail loudly. They produce a rank that disagrees with its own qkv
projection about how many heads it owns.
InklingDenseMLP (layers 0/1) is a correctness fix rather than a partitioning
choice: its row-parallel down_proj all-reduces a partial sum across the TP
group, and under ADP the peers' partials belong to DIFFERENT requests, so the
reduce would add unrelated tokens together. It now replicates, matching
DeepSeek-V3's _compute_mlp_tp_size, which returns 1 under ADP for this reason.
InklingMoE needs no change: FusedMoE.reducescatter_or_allreduce tests use_dp
before reduce_results, so the reduce_results=True the experts are built with is
correctly superseded by a reduce-scatter under ADP. A test pins that upstream
contract, since Inkling's correctness depends on it with nothing local to blame
if it changed.
__init__ cross-checks its head count against the base rather than trusting two
copies of the attention-TP rule to stay in step.
Tests: 88 pass. Each fix was verified by re-injecting the bug it removes and
confirming the matching test goes from passing to FAILING (not erroring) --
attention head/channel widths, dense-MLP replication, and conv-pool sizing all
caught. Real modules are constructed on a miniature config with the collective
stubbed, because AllReduce's constructor validates the MPI world size and would
otherwise refuse a 4-rank Linear in a single-process pytest.
The entry said "Attention data parallelism is untested". It is now tested, and the two things a user needs before enabling it are both non-obvious. It costs memory rather than saving it. Attention, the embedding, the LM head and the two dense MLP layers replicate under ADP, so what was a 1/tp slice is a full copy per rank: measured 145.98 -> 162.68 GB of weights per rank at TP=4. On a 184 GiB device that leaves little for the KV cache, and the two knobs pull in OPPOSITE directions -- max_num_tokens / max_batch_size bound the KV-cache estimation forward, which OOMs before the pool is sized (so free_gpu_memory_fraction cannot help it), while free_gpu_memory_fraction is what keeps the pool from starving the scheduler. Turning both down, the intuitive move, trades an OOM for a scheduler deadlock. It is also not bit-identical to pure TP, and cannot be: under TP the attention output is an all-reduced sum of four partials, under ADP it is computed whole on one rank, and float addition is not associative -- enough to fork a long chain-of-thought. So the bar is result equivalence, not token identity. Measured: on 30 GSM8K items, ADP with either expert TP or moe_expert_parallel_size=2 reproduces the TP-only answer and correct/incorrect flag on every item (accuracy 0.9667, zero score flips). On 30 MMMU items paired against a TP run at identical runtime settings, per-item results do move -- 6 score flips, 4 of them in ADP's favour, net +2 items or 0.76 sigma, which n=30 cannot distinguish from noise. The MMMU pairing needed its own control first. Two pure-TP runs differing only in free_gpu_memory_fraction are byte-identical (0/30), but dropping max_batch_size from 8 to 1 moves 6 scores on its own -- so the arms were held at the same max_batch_size and only the memory fraction was allowed to differ, which is what makes an ADP-vs-TP comparison possible at all.
Dev Engineer Review
InklingForConditionalGenerationwith hybrid attention, relative-position bias, short-convolution state, sigmoid-gated MoE routing, NVFP4 decoding, and BF16 vision/audio towers.KVCacheManagerV2, CUDA-graph handling, reasoning parsing, and evaluation integration.CODING_GUIDELINES.md.QA Engineer Review
TestInkling_NVFP4.test_nvfp4in text and multimodal accuracy suites.tests/integration/test_lists/qa/llm_function_core.txttests/integration/test_lists/test-db/l0_b200.ymlDescription
Adds PyTorch-backend support for Inkling (
thinkingmachines/Inkling-NVFP4), aRoPE-free hybrid-attention MoE reasoning model with vision and audio towers.
Registered as
InklingForConditionalGeneration.Architecture
pre-softmax, per (query token, head, relative distance).
interleaved with 11 global full-causal layers (8 KV heads).
on the residual stream, carrying per-request state across decode steps.
bias, then a log-sigmoid renorm spanning the routed and two shared-expert logits.
logits_mup_width_multiplierbefore the head; logits sliced from 201024 down to 200058.
fusing one row per patch / per frame into the text embedding stream. Video is
multi-frame images; there is no separate video encoder.
NVFP4 covers the text decoder and routed experts (layers 3–65); layer-2 experts,
attention, shared experts, and both towers stay BF16 per the checkpoint's
hf_quant_config.json.Implementation
modeling_inkling.pyattention_backend/inkling_triton.pyscore_mod. No existing fused backend exposes such a hook (context FMHA is disabled forkRELATIVE, trtllm-gen rejects a relative bias, FlashInfer has no additive per-token bias).rel_logitsis static-shape, so the decode kernel is CUDA-graph capturablemodeling_inkling_multimodal.py<image>placeholder per patch and one<audio>per dMel frame. Pure numpy + torch preprocessingconfigs/inkling.pycheckpoints/hf/inkling_weight_mapper.pypyexecutor/*CONV_STATE_MANAGER. Pool rows and attention decode metadata are published into stable CUDA buffers eagerly from input prep, so the captured decode forward does no host→device copy and each replay reads the current batch (same stable-pointer pattern asMamba2Metadata)KV cache: the per-layer KV-head split (local 16 / global 8) structurally requires
KVCacheManagerV2— V1's unified pool would coerce it to one value and mis-size theper-layer KV bytes, a correctness bug. The model defaults to V2 and the incompatible
path raises rather than silently downgrading.
Serving / eval plumbing (small, each needed end to end):
--reasoning_parser inklingfor Inkling's typed-content blocks, with streaming.needs_raw_special_tokensso a delimiter-based reasoning parser actually sees itsmarkers (previously only the tool-parser path preserved them).
end_idfalls back to the config'seos_token_id— checkpoints whose terminatorlives only in
config.jsonotherwise never stop and run tomax_tokens.hf_quant_config.jsonmay spell "no quantization" as the string"none", whichpreviously reached
QuantAlgo("none")and raised.vocab otherwise fails KV-cache estimation with "Token ID out of range".
--post_process_fn inkling/inkling_mmmufor trtllm-eval.Accuracy
Measured on the complete datasets, TRT-LLM and SGLang side by side at TP=4 with
CUDA graph and the overlap scheduler on, batch 8, driven by the same client so
prompt rendering and scoring are shared code:
Not supported in this release
MTP / speculative decoding (the checkpoint ships next-N draft weights; nothing
builds or loads them), LoRA, function calling, constrained/guided decoding, EPD
disaggregated serving, and multimodal-hash prefix caching (refused loudly per
modality, so multimodal requests still run — just uncached).
One workaround worth flagging for review: every Inkling all-reduce is rebuilt with
ONESHOTafter construction. Under CUDA-graph capture a symmetric all-reducecorrupts the run when its send buffer is unregistered while its recv buffer is a
registered NCCL window at a 12288 B message — which Inkling hits exactly (hidden
6144, bf16, one decode token), sending the first global-attention layer non-finite.
The all-reduces involved are built by generic modules (attention
o_proj, MoEdown_proj), so pinning the strategy afterwards keeps the mitigation model-local.Test Coverage
Accuracy (integration) — added to
l0_b200andllm_function_core:accuracy/test_llm_api_pytorch.py::TestInkling_NVFP4::test_nvfp4— GSM8K + MMLUon the text decoder.
accuracy/test_llm_api_pytorch_multimodal.py::TestInkling_NVFP4::test_nvfp4—MMMU on the vision path.
Unit — CPU-only, no checkpoint / GPU / network needed, all well under a second:
unittest/_torch/modeling/test_modeling_inkling.py— config parsing,registration, per-layer classification; plus checkpoint-gated weight accounting
and tensor-shape checks that read only the safetensors index and skip cleanly
when the checkpoint is absent.
unittest/_torch/modeling/test_modeling_inkling_multimodal.py— the three mediapaths and the input processor on synthetic configs: hMLP scale plan and module
tree, the fold's value preservation, dMel preprocessing, the audio codebook
forward against a reference, frame sampling, and the fail-loud placeholder
contract.
unittest/llmapi/test_reasoning_parser.py— full-parse and streaming equivalencefor the Inkling parser, including control tokens split across delta boundaries.
unittest/llmapi/test_sampling_params.py— theend_idconfig fallback.unittest/others/test_lm_eval.py— the offline post-processing hook.PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.