Repository navigation
[https://nvbugs/6858845][fix] Let no-KV-cache runners use FlashInfer attention #20901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,6 +46,7 @@ | |||||||||||||||||||||||||||||||||||
| _QWEN_3_VL_DIR = llm_models_root() / "Qwen3" / "Qwen3-VL-2B-Instruct" | ||||||||||||||||||||||||||||||||||||
| _QWEN_3_VL_30B_A3B_FP8_DIR = llm_models_root( | ||||||||||||||||||||||||||||||||||||
| ) / "Qwen3" / "Qwen3-VL-30B-A3B-Instruct-FP8" | ||||||||||||||||||||||||||||||||||||
| _QWEN_2_5_VL_3B_DIR = llm_models_root() / "Qwen2.5-VL-3B-Instruct" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| _FAKE_QWEN3_VL_30B_A3B_FP8_SENTINEL = "qwen3_vl_30b_a3b_fp8_fake" | ||||||||||||||||||||||||||||||||||||
| _FAKE_CHECKPOINT_MARKER = ".tllm_fake_checkpoint" | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1330,3 +1331,41 @@ def test_pd_disagg_multimodal_no_reuse_when_disabled(): | |||||||||||||||||||||||||||||||||||
| assert len(decode_outputs[0].outputs) > 0 | ||||||||||||||||||||||||||||||||||||
| text = decode_outputs[0].outputs[0].text | ||||||||||||||||||||||||||||||||||||
| assert len(text) > 0, f"[req {req_num}] empty output" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def _encode_images(model_dir: Path, attn_backend: str, | ||||||||||||||||||||||||||||||||||||
| media: list[list[str]]) -> list[torch.Tensor]: | ||||||||||||||||||||||||||||||||||||
| encoder = MultimodalEncoder(model=model_dir, | ||||||||||||||||||||||||||||||||||||
| attn_backend=attn_backend, | ||||||||||||||||||||||||||||||||||||
| max_batch_size=len(media)) | ||||||||||||||||||||||||||||||||||||
| with encoder: | ||||||||||||||||||||||||||||||||||||
| inputs = _load_inputs(encoder, ["Describe the image."] * len(media), | ||||||||||||||||||||||||||||||||||||
| media) | ||||||||||||||||||||||||||||||||||||
| outputs = encoder.generate(inputs) | ||||||||||||||||||||||||||||||||||||
| embeddings = [] | ||||||||||||||||||||||||||||||||||||
| for output in outputs: | ||||||||||||||||||||||||||||||||||||
| handles = output.disaggregated_params.multimodal_embedding_handles | ||||||||||||||||||||||||||||||||||||
| assert handles is not None | ||||||||||||||||||||||||||||||||||||
| embeddings.append( | ||||||||||||||||||||||||||||||||||||
| torch.cat([ | ||||||||||||||||||||||||||||||||||||
| SharedTensorContainer.from_dict( | ||||||||||||||||||||||||||||||||||||
| handle).get_local_view().float().cpu() | ||||||||||||||||||||||||||||||||||||
| for handle in handles | ||||||||||||||||||||||||||||||||||||
| ])) | ||||||||||||||||||||||||||||||||||||
| return embeddings | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @pytest.mark.threadleak(enabled=False) | ||||||||||||||||||||||||||||||||||||
| def test_encoder_flashinfer_attention_matches_trtllm(): | ||||||||||||||||||||||||||||||||||||
| """The encoder-only engine runs with FLASHINFER attention and matches TRTLLM.""" | ||||||||||||||||||||||||||||||||||||
| media = [[image] for image in example_images] | ||||||||||||||||||||||||||||||||||||
| reference = _encode_images(_QWEN_2_5_VL_3B_DIR, "TRTLLM", media) | ||||||||||||||||||||||||||||||||||||
| flashinfer = _encode_images(_QWEN_2_5_VL_3B_DIR, "FLASHINFER", media) | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1358
to
+1363
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis
Skip when the checkpoint or FlashInfer is unavailable. The H100 selector provides the GPU CI target, but it does not check the Qwen2.5-VL checkpoint or the optional FlashInfer dependency. Suggested fix def test_encoder_flashinfer_attention_matches_trtllm():
"""The encoder-only engine runs with FLASHINFER attention and matches TRTLLM."""
+ pytest.importorskip("flashinfer")
+ if not torch.cuda.is_available():
+ pytest.skip("CUDA is required")
+ if not _QWEN_2_5_VL_3B_DIR.is_dir():
+ pytest.skip(f"Checkpoint not found: {_QWEN_2_5_VL_3B_DIR}")
media = [[image] for image in example_images]📝 Committable suggestion
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| assert len(flashinfer) == len(reference) == len(media) | ||||||||||||||||||||||||||||||||||||
| for ref, test in zip(reference, flashinfer): | ||||||||||||||||||||||||||||||||||||
| assert test.shape == ref.shape | ||||||||||||||||||||||||||||||||||||
| cos = torch.nn.functional.cosine_similarity(ref.flatten(), | ||||||||||||||||||||||||||||||||||||
| test.flatten(), | ||||||||||||||||||||||||||||||||||||
| dim=0) | ||||||||||||||||||||||||||||||||||||
| assert cos.item() > 0.99, f"cosine similarity {cos.item():.6f}" | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two things would let this test catch a regression:
l0_b200.ymlentries for this file are-k "qwen3_30b_a3b_fp8"and-k "test_kv_event_mm_keys and uuid"). Please add an entry so it runs pre-merge.get_attention_backend(attention/backends/utils.py:56) logs a warning and returnsTrtllmAttention. In that case this test compares TRTLLM against itself and passes. Addpytest.importorskip("flashinfer"), or assert that the encoder's attention metadata isFlashInferAttentionMetadata, so it can't pass without testing anything.