Skip to content

refactor(grpc): centralize backend×modality capability check - #1914

Merged
slin1237 merged 1 commit into
mainfrom
refactor/backend-modality-capability
Jul 13, 2026
Merged

refactor(grpc): centralize backend×modality capability check#1914
slin1237 merged 1 commit into
mainfrom
refactor/backend-modality-capability

Conversation

@slin1237

@slin1237 slin1237 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Description

Problem

Which engine supports which modality (audio = TokenSpeed-only; video = vLLM/TokenSpeed; image = all-but-MLX) was enforced by scattered bail!s deep in assemble.rs, with inconsistent messages, after media was already fetched and preprocessed.

Solution

Add a single capability matrix and check it early, at worker selection, before any media is fetched — one consistent multimodal_not_supported error. Keep a defense-in-depth assertion in assembly.

Changes

  • multimodal/capability.rs (new): runtime_supports_modality(runtime, modality) + ensure_backend_supports_modalities(...), with a full backend×modality unit test.
  • grpc/client.rs: GrpcClient::runtime_type().
  • common/stages/worker_selection.rs: early rejection using the request's MultimodalIntermediate + selected runtime.
  • multimodal/assemble.rs: merge into_single_image_batch/into_single_vision_batchinto_single_batch; centralize the per-modality check.

Test Plan

cargo test -p smg --lib routers::grpc passes. Behavior-preserving: the same (backend, modality) combinations are rejected as before — just earlier and with one consistent message (verified against each prior bail!).

Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

@github-actions github-actions Bot added grpc gRPC client and router changes model-gateway Model gateway crate changes labels Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b370a267-6aec-4265-bd17-61aaff33ba52

📥 Commits

Reviewing files that changed from the base of the PR and between 0747d71 and 9d29d9f.

📒 Files selected for processing (5)
  • model_gateway/src/routers/grpc/client.rs
  • model_gateway/src/routers/grpc/common/stages/worker_selection.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/multimodal/capability.rs
  • model_gateway/src/routers/grpc/multimodal/mod.rs
📝 Walkthrough

Walkthrough

The PR adds centralized runtime/modality capability validation for multimodal gRPC requests, applies it during worker selection and assembly, and adds runtime derivation helpers plus capability-matrix tests.

Changes

Multimodal capability validation

Layer / File(s) Summary
Capability matrix and validation contract
model_gateway/src/routers/grpc/multimodal/capability.rs, model_gateway/src/routers/grpc/multimodal/mod.rs
Defines runtime modality support, validates intermediate batches, wires the module, and tests supported and rejected combinations.
Worker selection runtime gating
model_gateway/src/routers/grpc/client.rs, model_gateway/src/routers/grpc/common/stages/worker_selection.rs
Derives the selected runtime and rejects unsupported multimodal combinations before request state updates.
Assembly capability re-check
model_gateway/src/routers/grpc/multimodal/assemble.rs
Revalidates the concrete client runtime and uses generic single-batch extraction for non-TokenSpeed backends.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkerSelectionStage
  participant CapabilityMatrix
  participant GrpcClient
  participant MultimodalAssembly
  WorkerSelectionStage->>GrpcClient: derive runtime_type()
  WorkerSelectionStage->>CapabilityMatrix: validate runtime and modalities
  CapabilityMatrix-->>WorkerSelectionStage: allow or return bad_request
  MultimodalAssembly->>GrpcClient: derive concrete runtime
  MultimodalAssembly->>CapabilityMatrix: revalidate intermediate batches
  CapabilityMatrix-->>MultimodalAssembly: allow assembly or return error
Loading

Possibly related PRs

  • lightseekorg/smg#588: Introduced the multimodal assembly and intermediate flow extended by this capability validation.
  • lightseekorg/smg#1034: Added MLX backend handling relevant to runtime and multimodal support decisions.
  • lightseekorg/smg#1515: Added TokenSpeed multimodal support governed by the new capability checks.

Suggested labels: multimodal, tests

Suggested reviewers: catherinesue, key4ng, gongwei-130, xinyuezhang369, claude

Poem

A rabbit checks each image bright,
And guards the batches day and night.
Runtime knows what paths to take,
Unsupported shapes get sent back straight.
Hop, hop—tests confirm the gate!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: centralizing gRPC backend-modality capability checks.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/backend-modality-capability

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request centralizes the backend-by-modality capability mapping into a single source of truth in capability.rs and introduces early validation during worker selection to reject unsupported backend/modality combinations before media preprocessing. It also refactors the assembly code to use a unified into_single_batch helper and adds defense-in-depth checks. The review feedback suggests using absolute paths (starting with crate::) for internal documentation links in capability.rs and assemble.rs. Additionally, it points out that Modality::ImageEmbeds is only supported by TokenSpeed and should be separated from Modality::Image in the capability matrix, with corresponding updates to the unit tests.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +26 to +28
/// `Modality::ImageEmbeds` is treated as image (TokenSpeed folds it into an
/// image batch during assembly); it cannot reach the early check because a
/// [`super::MediaBatch`] only ever yields Image/Video/Audio.

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.

medium

According to the general rules, we should use absolute paths starting with crate:: for internal documentation links instead of relative paths (such as super::) to ensure links remain valid after refactoring or module moves.

Please update [super::MediaBatch] to use the absolute path [crate::routers::grpc::multimodal::MediaBatch].

Suggested change
/// `Modality::ImageEmbeds` is treated as image (TokenSpeed folds it into an
/// image batch during assembly); it cannot reach the early check because a
/// [`super::MediaBatch`] only ever yields Image/Video/Audio.
/// `Modality::ImageEmbeds` is treated as image (TokenSpeed folds it into an
/// image batch during assembly); it cannot reach the early check because a
/// [`crate::routers::grpc::multimodal::MediaBatch`] only ever yields Image/Video/Audio.
References
  1. Use absolute paths starting with crate:: for internal documentation links instead of relative paths (e.g., super::) to ensure links remain valid after refactoring or module moves.

Comment on lines +31 to +34
Modality::Image | Modality::ImageEmbeds => matches!(
runtime,
RuntimeType::Sglang | RuntimeType::Vllm | RuntimeType::Trtllm | RuntimeType::TokenSpeed
),

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.

medium

The capability matrix currently groups Modality::ImageEmbeds with Modality::Image, indicating that SGLang, vLLM, and TRT-LLM support image embeddings. However, these backends only support raw images (Modality::Image) and do not support pre-computed image embeddings (Modality::ImageEmbeds), which is a TokenSpeed-only disaggregated EPD feature.

To prevent potential correctness issues or silent failures if ImageEmbeds is ever routed to these backends, they should be separated in the capability matrix so that only TokenSpeed supports ImageEmbeds.

Suggested change
Modality::Image | Modality::ImageEmbeds => matches!(
runtime,
RuntimeType::Sglang | RuntimeType::Vllm | RuntimeType::Trtllm | RuntimeType::TokenSpeed
),
Modality::Image => matches!(
runtime,
RuntimeType::Sglang | RuntimeType::Vllm | RuntimeType::Trtllm | RuntimeType::TokenSpeed
),
Modality::ImageEmbeds => matches!(runtime, RuntimeType::TokenSpeed),

Comment on lines +86 to +89
// ImageEmbeds tracks image support.
(Sglang, ImageEmbeds, true),
(TokenSpeed, ImageEmbeds, true),
(Mlx, ImageEmbeds, 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.

medium

If Modality::ImageEmbeds is restricted to TokenSpeed (as it is not supported by SGLang, vLLM, or TRT-LLM), the test cases should be updated to assert that ImageEmbeds is unsupported on non-TokenSpeed runtimes.

Suggested change
// ImageEmbeds tracks image support.
(Sglang, ImageEmbeds, true),
(TokenSpeed, ImageEmbeds, true),
(Mlx, ImageEmbeds, false),
// ImageEmbeds is only supported by TokenSpeed.
(Sglang, ImageEmbeds, false),
(TokenSpeed, ImageEmbeds, true),
(Mlx, ImageEmbeds, false),

Comment on lines +160 to +162
/// Defense-in-depth capability assertion mirroring the early worker-selection
/// check, keyed on the concrete backend client. See
/// [`super::capability::runtime_supports_modality`].

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.

medium

According to the general rules, we should use absolute paths starting with crate:: for internal documentation links instead of relative paths (such as super::) to ensure links remain valid after refactoring or module moves.

Please update [super::capability::runtime_supports_modality] to use the absolute path [crate::routers::grpc::multimodal::capability::runtime_supports_modality].

Suggested change
/// Defense-in-depth capability assertion mirroring the early worker-selection
/// check, keyed on the concrete backend client. See
/// [`super::capability::runtime_supports_modality`].
/// Defense-in-depth capability assertion mirroring the early worker-selection
/// check, keyed on the concrete backend client. See
/// [`crate::routers::grpc::multimodal::capability::runtime_supports_modality`].
References
  1. Use absolute paths starting with crate:: for internal documentation links instead of relative paths (e.g., super::) to ensure links remain valid after refactoring or module moves.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Clean, behavior-preserving refactor. The centralized capability matrix in capability.rs is correct and well-tested, the early rejection at worker selection is properly placed (after runtime is known, before media fetch/preprocess), and the defense-in-depth assertion in assembly mirrors the same matrix. The selection_runtime helper correctly picks the prefill leg for disaggregated mode. No issues found.

0 🔴 Important · 0 🟡 Nit · 0 🟣 Pre-existing

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@model_gateway/src/routers/grpc/multimodal/assemble.rs`:
- Around line 163-176: Update ensure_client_supports_intermediate to collect the
intermediate batch modalities and delegate validation to
capability::ensure_backend_supports_modalities using the client’s runtime type,
removing the duplicated loop and anyhow::ensure! logic. Replace the
runtime_supports_modality import with ensure_backend_supports_modalities and
preserve the existing Result<()> behavior.
🪄 Autofix (Beta)

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81654097-218f-4d1d-9ce2-c5687f846e5e

📥 Commits

Reviewing files that changed from the base of the PR and between 1d33392 and 0747d71.

📒 Files selected for processing (5)
  • model_gateway/src/routers/grpc/client.rs
  • model_gateway/src/routers/grpc/common/stages/worker_selection.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/multimodal/capability.rs
  • model_gateway/src/routers/grpc/multimodal/mod.rs

Comment thread model_gateway/src/routers/grpc/multimodal/assemble.rs
The 'which engine supports which modality' truth was implicit and
duplicated across assembly: per-backend into_single_image_batch /
into_single_vision_batch checks plus ad-hoc bail!s with divergent
messages, all firing deep in assembly after media was already
fetched and preprocessed.

Introduce a single capability matrix (runtime_supports_modality) in a
new multimodal::capability module and enforce it once, early, at worker
selection -- so an unsupported (backend, modality) request fails fast
with one consistent message ("backend {X} does not support {modality}
inputs") before any fetch/preprocess. Assembly re-asserts against the
same matrix as defense in depth; the merged into_single_batch helper now
carries only the structural single-batch constraint.

Behavior-preserving: exactly the same (engine, modality) combinations
are rejected (Image: SGLang/vLLM/TRT-LLM/TokenSpeed; Video:
vLLM/TokenSpeed; Audio: TokenSpeed; MLX: none), just earlier and with a
uniform message.

Add GrpcClient::runtime_type() so assembly can key the shared matrix on
the concrete client, and unit tests for the full capability matrix plus
the early-rejection path.

Signed-off-by: Simo Lin <linsimo.mark@gmail.com>
@slin1237
slin1237 force-pushed the refactor/backend-modality-capability branch from 0747d71 to 9d29d9f Compare July 13, 2026 16:29
@slin1237

Copy link
Copy Markdown
Member Author

Addressed all review comments (pushed):

  • coderabbit (Major): ensure_client_supports_intermediate now delegates to capability::ensure_backend_supports_modalities; dropped the duplicated loop and the now-unused runtime_supports_modality import.
  • gemini: ImageEmbeds separated from Image in the matrix — it's now TokenSpeed-only (pre-computed embeddings are an EPD feature); unit-test matrix updated (Sglang/vLLM/TRT-LLM/MLX = false, TokenSpeed = true). Note it can't actually reach the check (a MediaBatch only yields Image/Video/Audio) — kept for correctness/defense-in-depth.
  • gemini: doc links switched from super:: to absolute crate::… paths.

cargo test -p smg --lib routers::grpc::multimodal (capability + assemble) green; clippy -D warnings clean.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d29d9fd02

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +178 to +181
if let Some(intermediate) = multimodal_intermediate(prep) {
if let Err(err) = multimodal::ensure_backend_supports_modalities(
selection_runtime(&workers),
intermediate,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move capability check before media processing

This check still runs after the expensive media path: in the chat/messages pipelines worker selection follows preparation, and multimodal_intermediate is only populated after process_multimodal_plan has fetched and preprocessed the media. For unsupported runtime/modality pairs such as audio routed to SGLang, the request will still download/preprocess the audio before being rejected here, so the intended fail-fast behavior is not achieved; the check needs to use the detected MediaPlan/modalities before full multimodal processing or otherwise run before preparation fetches media.

Useful? React with 👍 / 👎.

#[test]
fn non_grpc_runtimes_support_no_modality() {
for runtime in [RuntimeType::Unspecified, RuntimeType::External] {
for modality in [Modality::Image, Modality::Video, Modality::Audio] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit: ImageEmbeds is missing from the modality list here. The main capability_matrix_matches_backend_support test covers (Unspecified/External, ImageEmbeds) implicitly (those runtimes aren't listed), but this dedicated test for non-gRPC runtimes should be exhaustive for all modalities — especially since the main test doesn't exercise Unspecified/External at all.

Suggested change
for modality in [Modality::Image, Modality::Video, Modality::Audio] {
for modality in [Modality::Image, Modality::ImageEmbeds, Modality::Video, Modality::Audio] {

@slin1237
slin1237 merged commit fe37ff1 into main Jul 13, 2026
22 of 40 checks passed
@slin1237
slin1237 deleted the refactor/backend-modality-capability branch July 13, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes model-gateway Model gateway crate changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant