Skip to content

feat(mm-routing): skip MM-aware routing work when KV router is load-balancing only - #10515

Draft
krishung5 wants to merge 4 commits into
mainfrom
krish/skip-mm-routing-when-load-balancing
Draft

feat(mm-routing): skip MM-aware routing work when KV router is load-balancing only#10515
krishung5 wants to merge 4 commits into
mainfrom
krish/skip-mm-routing-when-load-balancing

Conversation

@krishung5

@krishung5 krishung5 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Overview:

When the KV router is enabled but not using prefix overlap — --load-aware, overlap_score_credit=0, use_kv_events=false, or any non-KV mode — the frontend still computed per-image mm_hash + dim fetches for MM-aware routing. That's wasted work: the router ignores prefix overlap, so there's no KV-reuse benefit (e.g. when prefix caching is disabled on the engine) but the routing cost is still paid.

Details:

Signal: KvRouterConfig::should_subscribe_to_kv_events() (use_kv_events && overlap_score_credit > 0). The --load-aware preset sets overlap_score_credit=0, use_kv_events=False, so it evaluates false. The model watcher computes router_mode == KV && should_subscribe_to_kv_events() and threads mm_routing_enabled into the preprocessor.

  • Rust frontend (--dyn-chat-processor dynamo, covers vLLM and SGLang): when disabled, OpenAIPreprocessor skips the URL-passthrough fetch_image_dims (HTTP header fetches), the mm_image_entries build, and gather_mm_exact_routing_info. Media transfer (frontend decode / NIXL) is unaffected.
  • vLLM chat-processor (--dyn-chat-processor vllm): skips building mm_routing_info; process_inputs and the mm_kwargs transfer (feat(multimodal): move MM routing into vLLM frontend processor #8065) are kept — they serve the backend, not routing.
  • Non-KV-mode / no-router-context callers (C API, static pipelines, tests) default to enabled → behavior unchanged.

Follow-up (not in this PR): in the vLLM path, when transfer is also off (DYNAMO_DISABLE_NIXL_MM=1), process_inputs itself could be skipped — needs the backend raw-request path validated.

Closes DIS-2156

Where should the reviewer start?

  • lib/llm/src/discovery/watcher.rs — the mm_routing_enabled computation.
  • lib/llm/src/preprocessor.rs — the gather_multi_modal_data / gather_mm_exact_routing_info gates.
  • components/src/dynamo/frontend/vllm_processor.py — the mm_routing_info gate.

Verification

  • clippy -D warnings in both default + mm-routing feature configs; cargo fmt clean.
  • cargo test -p dynamo-llm --lib --features mm-routing: 1296 passed. New unit test should_subscribe_to_kv_events_gates_overlap_routing pins the gate matrix (default→on, --load-aware/either-knob-off→off).
  • pre-commit on the Python change.
  • e2e (Qwen MM-router, overlap on vs --load-aware) — running.

Related Issues

🚫 This PR is NOT linked to an issue:

  • Confirmed — no related issue

🤖 Generated with Claude Code


Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Introduced multimodal KV routing configuration support enabling conditional control of multimodal routing computations. The system now respects configuration parameters including router mode selection and KV event subscription settings to determine whether to perform multimodal routing-related processing.
  • Tests

    • Added test coverage for multimodal routing subscription gating behavior validation across different configuration combinations.

krishung5 and others added 2 commits June 9, 2026 16:17
…alancing only

When the KV router is enabled but not using prefix overlap (--load-aware,
overlap_score_credit=0, use_kv_events=false, or a non-KV mode), the frontend
still computed per-image mm_hash + dim fetches for MM-aware routing. That's
wasted work: the router ignores prefix overlap, so there's no KV-reuse benefit
(e.g. when prefix caching is disabled on the engine) but the routing cost is
still paid.

Gate the routing-only MM work on whether the router actually uses overlap:
- Signal: KvRouterConfig::should_subscribe_to_kv_events() (use_kv_events &&
  overlap_score_credit > 0). The model watcher computes
  router_mode == KV && that, and passes mm_routing_enabled into OpenAIPreprocessor.
- Rust frontend (covers vLLM + SGLang via --dyn-chat-processor dynamo): when
  disabled, skips the URL-passthrough fetch_image_dims, the mm_image_entries
  build, and gather_mm_exact_routing_info. Media transfer (frontend decode) is
  unaffected.
- vLLM chat-processor (--dyn-chat-processor vllm): skips building mm_routing_info;
  process_inputs and the mm_kwargs transfer (#8065) are kept — they serve the
  backend, not routing.

Non-KV-mode / no-router-context callers (C API, static pipelines, tests) default
to enabled, so behavior is unchanged there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@krishung5
krishung5 requested review from a team as code owners June 9, 2026 23:18
@krishung5
krishung5 requested a review from a team June 9, 2026 23:18
@github-actions github-actions Bot added feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` labels Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR introduces an mm_routing_enabled boolean flag that gates whether multimodal KV-router overlap routing computations are performed. The flag is propagated from configuration through discovery, preprocessor, and frontend layers, controlling whether per-image routing metadata and related work is accumulated during request processing.

Changes

Multimodal routing feature flag gating

Layer / File(s) Summary
OpenAIPreprocessor MM routing flag definition and gating
lib/llm/src/preprocessor.rs
OpenAIPreprocessor adds an mm_routing_enabled field (behind feature gate) that controls whether multimodal image entries and routing info are accumulated during request preprocessing. Flag gates image entry accumulation in both media_loader and URL-passthrough paths, and causes early exit in routing info gathering when disabled.
Watcher MM routing computation and engine propagation
lib/llm/src/discovery/watcher.rs
In do_worker_set_registration, computes mm_routing_enabled from router mode (KV) and KV event subscription config, then passes the flag to both chat and completions engine preprocessor construction.
Static entrypoint and VllmProcessor frontend integration
lib/llm/src/entrypoint/input/common.rs, components/src/dynamo/frontend/vllm_processor.py
Static pipeline preprocessing passes the MM routing flag explicitly as true to OpenAIPreprocessor::new_with_parts. VllmProcessor accepts mm_routing_enabled from configuration, storing it to control whether per-request routing metadata is built. EngineFactory computes the flag from router mode, KV events, and overlap credit configuration.
KV router config subscription gating test
lib/kv-router/src/scheduling/config.rs
New unit test verifies that KvRouterConfig::should_subscribe_to_kv_events() gating correctly disables overlap routing when either overlap_score_credit is zero or use_kv_events is false.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes all required sections, but it contradicts itself by stating "Closes DIS-2156" while confirming that no related issue exists. Clarify the Related Issues section: either link DIS-2156 using the required format or remove the closing reference and keep the no-issue confirmation.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: skipping MM-aware routing work when KV router operates in load-balancing-only mode without prefix overlap.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/src/dynamo/frontend/vllm_processor.py (1)

878-898: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Derive mm_routing_enabled from the effective worker-set router config, not the global frontend config.

lib/llm/src/discovery/watcher.rs resolves router_config = card.router_config.as_ref().unwrap_or(&self.router_config) before it builds the routed engine, but Lines 883-887 recompute the flag from FrontendConfig. If an MDC overrides use_kv_events or overlap_score_credit, the Python preprocessor and the Rust router can disagree: you either keep paying the MM-routing cost unnecessarily or disable MM overlap routing while the router still expects it.

Please source this from the MDC override as well, or thread the already-resolved bool from Rust into chat_engine_factory, so both paths share one gate.

🤖 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 `@components/src/dynamo/frontend/vllm_processor.py` around lines 878 - 898,
mm_routing_enabled is being computed from self.config (FrontendConfig) but must
reflect the effective worker/router config that may be overridden by the MDC;
update the code that sets mm_routing_enabled so it reads the resolved router
config used to build the routed engine (the same boolean resolved in
lib/llm/src/discovery/watcher.rs where router_config =
card.router_config.as_ref().unwrap_or(&self.router_config)), or instead accept
and thread a pre-resolved bool from chat_engine_factory into the VllmProcessor
call; specifically, ensure the mm_routing_enabled passed into VllmProcessor
comes from the worker-set/router_config override (or the threaded resolved flag)
rather than directly from getattr(self.config, ...) so both Python preprocessor
and the Rust router share the same gate.
lib/llm/src/preprocessor.rs (1)

544-554: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate the dim-fetch client warmup on mm_routing_enabled.

Lines 551-554 still force DIM_FETCH_MEDIA_FETCHER / DIM_FETCH_HTTP_CLIENT whenever the model is MM-routable, even when mm_routing_enabled is false. In load-balancing-only or non-KV setups, that means this path still does the TLS/env/media-fetcher initialization the new flag is supposed to skip, and a bad dim-fetch configuration can still fail worker-set registration even though MM overlap routing is disabled.

Suggested fix
-        #[cfg(feature = "mm-routing")]
-        if image_token_counter.is_some() || routing_image_token_id.is_some() {
+        #[cfg(feature = "mm-routing")]
+        if mm_routing_enabled
+            && (image_token_counter.is_some() || routing_image_token_id.is_some())
+        {
             std::sync::LazyLock::force(&DIM_FETCH_MEDIA_FETCHER);
             std::sync::LazyLock::force(&DIM_FETCH_HTTP_CLIENT);
         }
🤖 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 `@lib/llm/src/preprocessor.rs` around lines 544 - 554, The warmup logic
currently forces DIM_FETCH_MEDIA_FETCHER and DIM_FETCH_HTTP_CLIENT when a model
is MM-routable (checks image_token_counter or routing_image_token_id) even if
mm_routing_enabled is false; update the condition that surrounds
std::sync::LazyLock::force(&DIM_FETCH_MEDIA_FETCHER) and
::force(&DIM_FETCH_HTTP_CLIENT) to also require mm_routing_enabled to be true
(i.e., only trigger the dim-fetch client warmup when mm_routing_enabled &&
(image_token_counter.is_some() || routing_image_token_id.is_some())); adjust the
same guarded block that currently uses #[cfg(feature = "mm-routing")] so the
mm_routing_enabled runtime flag prevents initialization in load-balancing/non-KV
setups.
🤖 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.

Outside diff comments:
In `@components/src/dynamo/frontend/vllm_processor.py`:
- Around line 878-898: mm_routing_enabled is being computed from self.config
(FrontendConfig) but must reflect the effective worker/router config that may be
overridden by the MDC; update the code that sets mm_routing_enabled so it reads
the resolved router config used to build the routed engine (the same boolean
resolved in lib/llm/src/discovery/watcher.rs where router_config =
card.router_config.as_ref().unwrap_or(&self.router_config)), or instead accept
and thread a pre-resolved bool from chat_engine_factory into the VllmProcessor
call; specifically, ensure the mm_routing_enabled passed into VllmProcessor
comes from the worker-set/router_config override (or the threaded resolved flag)
rather than directly from getattr(self.config, ...) so both Python preprocessor
and the Rust router share the same gate.

In `@lib/llm/src/preprocessor.rs`:
- Around line 544-554: The warmup logic currently forces DIM_FETCH_MEDIA_FETCHER
and DIM_FETCH_HTTP_CLIENT when a model is MM-routable (checks
image_token_counter or routing_image_token_id) even if mm_routing_enabled is
false; update the condition that surrounds
std::sync::LazyLock::force(&DIM_FETCH_MEDIA_FETCHER) and
::force(&DIM_FETCH_HTTP_CLIENT) to also require mm_routing_enabled to be true
(i.e., only trigger the dim-fetch client warmup when mm_routing_enabled &&
(image_token_counter.is_some() || routing_image_token_id.is_some())); adjust the
same guarded block that currently uses #[cfg(feature = "mm-routing")] so the
mm_routing_enabled runtime flag prevents initialization in load-balancing/non-KV
setups.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 275b4272-6080-4977-a49e-aacfc905eaf3

📥 Commits

Reviewing files that changed from the base of the PR and between fdefcfa and 4defdba.

📒 Files selected for processing (5)
  • components/src/dynamo/frontend/vllm_processor.py
  • lib/kv-router/src/scheduling/config.rs
  • lib/llm/src/discovery/watcher.rs
  • lib/llm/src/entrypoint/input/common.rs
  • lib/llm/src/preprocessor.rs

Comment thread lib/llm/src/discovery/watcher.rs Outdated
let mm_routing_enabled = router_config.router_mode == RouterMode::KV
&& router_config
.kv_router_config
.should_subscribe_to_kv_events();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should_subscribe_to_kv_events() is false for --no-router-kv-events, but approximate KV mode still does prefix-overlap routing from routing decisions, so this disables MM routing while the router still scores prefix overlap. Fix: derive mm_routing_enabled from KV mode plus overlap scoring being active, not from KV event subscription.

🤖 AI Fix

In lib/llm/src/discovery/watcher.rs inside ModelWatcher::do_worker_set_registration, replace the mm_routing_enabled expression that calls router_config.kv_router_config.should_subscribe_to_kv_events() with router_config.router_mode == RouterMode::KV && router_config.kv_router_config.overlap_score_credit > 0.0 or an equivalent KvRouterConfig::uses_prefix_overlap() helper so --no-router-kv-events approximate routing keeps MM routing enabled.

# or any non-KV mode → skip the wasted mm_routing_info build.
mm_routing_enabled = bool(
getattr(self.config, "router_mode", None) == "kv"
and getattr(self.config, "use_kv_events", False)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Including use_kv_events in the gate disables MM routing for --no-router-kv-events approximate KV mode even though that mode still predicts cache state and scores prefix overlap. Fix: keep mm_routing_enabled true for KV mode whenever overlap scoring is enabled, independent of event subscription.

🤖 AI Fix

In components/src/dynamo/frontend/vllm_processor.py in EngineFactory.chat_engine_factory, remove the and getattr(self.config, "use_kv_events", False) term from the mm_routing_enabled expression and leave the gate based on KV router mode and positive overlap_score_credit, or consume the same resolved uses_prefix_overlap boolean threaded from Rust.

@krishung5
krishung5 marked this pull request as draft June 9, 2026 23:35
krishung5 and others added 2 commits June 10, 2026 16:46
The frontend skipped MM-aware routing whenever the KV router was not
subscribed to KV events. That wrongly disabled it for approximate KV
mode (`--no-router-kv-events`), where events are off but the router
still scores prefix overlap — so the per-image `mm_hash` is still needed.

Gate on overlap scoring directly via a new
`KvRouterConfig::uses_prefix_overlap()` (`overlap_score_credit > 0`),
independent of `use_kv_events`. Only `--load-aware` /
`overlap_score_credit=0` or a non-KV mode now skips the work. Apply the
same predicate on the vLLM chat-processor path, and update the unit test
to pin the approximate-KV case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants