Skip to content

[None][feat] support streaming KV events with C++ backend - #19359

Open
GuanLuo wants to merge 3 commits into
NVIDIA:mainfrom
GuanLuo:feat/streaming-kv-events-cpp-dto
Open

GuanLuo wants to merge 3 commits into
NVIDIA:mainfrom
GuanLuo:feat/streaming-kv-events-cpp-dto

Conversation

@GuanLuo

@GuanLuo GuanLuo commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Dev Engineer Review

The PR adds native C++ streaming KV-cache events with lifecycle filtering, event coalescing, bounded buffering, statistics, and multimodal metadata. Nanobind exposes the native sink and DTOs. Python converts DTOs into existing BlockStored and BlockRemoved events.

The C++ backend uses the native sink. The Python backend keeps its existing path. Verify event ordering, overflow handling, shutdown flushing, lifecycle selection, multimodal key alignment, and API compatibility.

QA Engineer Review

Tests cover native stored and removed events, event coalescing, payload conversion, lifecycle filtering, statistics, multimodal metadata, backend validation, and attention-layer window filtering. Pipeline- and context-parallelism rejection checks remain covered.

No changed test-list files are present. CI or manual-QA list registration is therefore not required by the changed files.

Coverage verdict: sufficient based on the supplied tests and reported checks.

Per-File QA Perspective

  • CMakeLists.txt: Adds native event sources to the build. Verify compilation and linking.
  • streamingEventSink.cpp: Adds event filtering, coalescing, buffering limits, removal handling, and statistics. Verify invalid, duplicate, multimodal, orphan, and overflow cases.
  • streamingEventSink.h: Defines native DTOs and sink APIs. Verify binding compatibility.
  • eventData.cpp: Decodes tokens, digests, and multimodal metadata. Verify hexadecimal encoding and block context.
  • eventData.h: Adds shared event-token and multimodal-key types. Verify parity with Python structures.
  • eventManager.cpp: Uses shared event decoding for stored blocks. Verify cache metadata and event payloads.
  • eventManager.h: Removes duplicated event-data declarations. Verify dependent C++ interfaces.
  • kvCacheManagerV2.cpp: Exposes sink types, DTOs, statistics, helpers, and sink inputs. Verify bindings and defaults.
  • kv_cache_manager_v2.py: Selects the native sink for the C++ backend. Verify host-tier fallback and attention-layer filtering.
  • kv_cache_events.py: Supports multimodal metadata and native DTO conversion. Verify flushing, validation, digest handling, and removal behavior.
  • runtime/kv_cache_manager_v2/__init__.py: Exports native event types for non-Python backends. Verify imports and __all__.
  • runtime/kv_cache_manager_v2/__init__.pyi: Declares native event types. Verify stub and binding signature parity.
  • _introspection.py: Adds native sink test helpers. Verify forwarding and unavailable-backend errors.
  • kvcache.md: Documents C++ backend support and multimodal events. Verify exclusions and parallelism constraints.
  • test_kv_cache_manager_v2.py: Covers attention-layer window filtering without private backend state. No changed test-list entry applies.
  • test_kv_cache_event_manager.py: Covers native events, payloads, lifecycle filtering, counters, and multimodal metadata. No changed test-list entry applies.
  • test_streaming_kv_events.py: Covers backend support, multimodal events, and invalid parallelism or backend configurations. No changed test-list entry applies.

Description

The Python KV cache manager backend is being removed, but the streaming KV-event publisher introduced by #17023 currently depends on Python-side KV manager events.

This change keeps streaming KV events available with the C++ KV cache manager V2 backend:

  • Add a native StreamingEventSink that captures stored and removed block events as compact C++ DTOs.
  • Expose batched DTO draining through nanobind.
  • Translate the DTOs to the existing Python BlockStored and BlockRemoved msgspec structures, preserving the existing serialization and publisher path.
  • Select the native event lifecycle automatically when the C++ backend is active while preserving the Python backend behavior.
  • Add unit coverage and update the KV-cache documentation.

The boundary intentionally remains semantic rather than serialized:

C++ KV cache manager -> native event DTOs -> nanobind -> Python BlockStored/BlockRemoved -> existing publisher

Test Coverage

  • Native C++ library and nanobindings build: passed.
  • KV-event manager tests: 78 passed.
  • C++ backend lifecycle regression: 1 passed, 112 deselected.
  • Dynamo two-worker KV-aware routing E2E with the C++ backend and ZMQ events: 1 passed in 84.31 seconds; both workers received stored-block state.
  • Relevant formatting, lint, and pre-commit checks: passed.

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-compatible or api-breaking. For api-breaking, include BREAKING in 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, comment /bot help.

@GuanLuo
GuanLuo marked this pull request as ready for review September 21, 2026 08:24
@GuanLuo
GuanLuo requested review from a team as code owners September 21, 2026 08:24
@GuanLuo
GuanLuo force-pushed the feat/streaming-kv-events-cpp-dto branch from c095803 to 4fc3763 Compare September 21, 2026 08:31
@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/TensorRT-LLM/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 242b9ae7-1b70-4d3b-966e-a0528f702194

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc3763 and 3e0a0df.

📒 Files selected for processing (15)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/CMakeLists.txt
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/eventData.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/eventData.h
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/eventManager.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/eventManager.h
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/streamingEventSink.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/streamingEventSink.h
  • cpp/tensorrt_llm/nanobind/batch_manager/kvCacheManagerV2.cpp
  • docs/source/features/kvcache.md
  • tensorrt_llm/_torch/pyexecutor/kv_cache/kv_cache_manager_v2.py
  • tensorrt_llm/_torch/pyexecutor/kv_cache_events.py
  • tensorrt_llm/runtime/kv_cache_manager_v2/__init__.py
  • tensorrt_llm/runtime/kv_cache_manager_v2/__init__.pyi
  • tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_event_manager.py
  • tests/unittest/kv_cache_manager_v2_tests/test_streaming_kv_events.py

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.


Walkthrough

The PR adds native C++ streaming events for KV-cache blocks. It exposes the sink through bindings, integrates native event draining with Python publication, supports both V2 backends, and adds multimodal event handling and tests.

Changes

Native streaming KV-cache events

Layer / File(s) Summary
Event data and collection
cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/*
Defines event payloads, digest and multimodal decoding, lifecycle filtering, event coalescing, bounded pending storage, and statistics.
Native bindings and exports
cpp/tensorrt_llm/nanobind/batch_manager/kvCacheManagerV2.cpp, tensorrt_llm/runtime/kv_cache_manager_v2/*, tensorrt_llm/runtime/kv_cache_manager_v2/_introspection.py
Exposes the sink and event data, adds event-submission helpers, exports runtime symbols, and accepts the common EventSink interface.
Python event integration
tensorrt_llm/_torch/pyexecutor/kv_cache_events.py, tensorrt_llm/_torch/pyexecutor/kv_cache/kv_cache_manager_v2.py
Creates the native sink for the C++ backend, propagates lifecycle selection, drains native events, converts DTOs to wire events, and synchronizes statistics.
Validation and documented backend support
docs/source/features/kvcache.md, tests/unittest/...
Documents C++ backend support and tests multimodal payloads, lifecycle filtering, event counters, event-window filtering, and backend validation.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant KVCacheManager
  participant StreamingEventSink
  participant StreamingKVCacheEventManager
  participant WirePublisher
  KVCacheManager->>StreamingEventSink: submit block lifecycle events
  StreamingEventSink->>StreamingKVCacheEventManager: provide drained native DTOs
  StreamingKVCacheEventManager->>WirePublisher: publish stored or removed wire events
  StreamingKVCacheEventManager->>StreamingEventSink: synchronize native statistics
Loading

Suggested reviewers: juney-nvidia, chzblych

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 86 functions across 15 files. (2 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately summarizes the main change: adding streaming KV events for the C++ backend.
Description check ✅ Passed The description includes the required Description, Test Coverage, and PR Checklist sections. It clearly explains the motivation, implementation, compatibility behavior, tests, and documentation update…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 17.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 86 functions across 15 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@GuanLuo
GuanLuo force-pushed the feat/streaming-kv-events-cpp-dto branch from 9850306 to 003d83f Compare September 21, 2026 08:55
Signed-off-by: Guan Luo <gluo@nvidia.com>
Signed-off-by: Guan Luo <gluo@nvidia.com>
Signed-off-by: Guan Luo <gluo@nvidia.com>
@GuanLuo
GuanLuo force-pushed the feat/streaming-kv-events-cpp-dto branch from 003d83f to 3e0a0df Compare September 21, 2026 08:58
class StreamingEventSink final : public EventSink
{
public:
StreamingEventSink(int tokensPerBlock, int maxEntries, std::optional<int> mmTokenIdOffset = std::nullopt);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tokensPerBlock is stored and validated here but never read anywhere in StreamingEventSinkBlock::isFull() already derives the block size from the block itself — so a wrong value silently does nothing; please drop the parameter. Relatedly, max_entries and mm_token_id_offset now have to be configured identically on both the native sink and StreamingKVCacheEventManager (which keeps its own inert _max_entries/_pending_entries in native mode), and a mismatch fails silently — e.g. a sink with an offset but a facade without one drops the computed mm_keys. Could StreamingKVCacheEventManager construct the native sink itself from its own parameters instead?

block_hashes: list[ExternalBlockHash]
parent_block_hash: ExternalBlockHash | None
token_ids: list[int]
token_ids: list[EventTokenId]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Streaming token_ids was previously guaranteed to be list[int] (multimodal blocks were skipped via multimodal_blocks_suppressed) and can now contain hex digest strings, so existing subscribers that hash it as ints will break on multimodal payloads. This is a separate feature from C++-backend support, and the description's "preserving the existing serialization" no longer holds once mm_keys is added and token_ids is widened — could multimodal enablement be split out, or at least called out in the description?

}
if (block.prev == nullptr)
{
throw std::logic_error("Cannot publish an orphan KV cache block");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The orphan check throws after reserveEntryUnlocked() has already consumed an entry from the per-iteration budget, and unlike the Python path (_add_full_block, which rolls back _pending_entries) nothing releases it. Move the block.prev == nullptr guard above reserveEntryUnlocked().

if mm_token_id_offset is not None and mm_token_id_offset < 0:
raise ValueError("mm_token_id_offset must be non-negative")
self._mm_token_id_offset = mm_token_id_offset
self._native_event_sink = native_event_sink

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In native mode _stored_blocks, _pending_events, _pending_entries, _max_entries and the whole _add_full_block/_decode_block/add_removed_event sink surface are unreachable, and in Python mode _drain_native_events/_sync_native_stats are — consider splitting the two sink implementations and keeping only the shared publisher lifecycle, batching and counters on the facade. In particular, the new Python _decode_block is a hand-maintained twin of C++ decodeEventBlock that only serves the backend this PR describes as being removed. Also worth documenting that stored_blocks/removed_blocks only refresh at flush_iteration_events()/shutdown(), unlike the Python-owned batch counters.


window_sizes: Dict[int, int] = {}
for layer_group_id, layer_ids in enumerate(self.impl.layer_grouping):
layer_config = self.kv_cache_manager_py_config.layers[int(layer_ids[0])]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

layer_config is looked up unconditionally here and then looked up again by the same index inside get_event_window_size. Pass layer_config into the helper instead.

@zhaoyangwang-nvidia zhaoyangwang-nvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve with nits.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants