Skip to content

feat(zmq): materialize precomputed multimodal inputs on msgpack ingest - #1081

Open
slin1237 wants to merge 1 commit into
mainfrom
zmq-mm-ingest
Open

feat(zmq): materialize precomputed multimodal inputs on msgpack ingest#1081
slin1237 wants to merge 1 commit into
mainfrom
zmq-mm-ingest

Conversation

@slin1237

Copy link
Copy Markdown
Contributor

The msgpack ZMQ path decodes multimodal payloads (the wire type is
mm-complete and the decoder is tensor-aware) but bypasses the frontend
InputProcessor, so three derived pieces the scheduler requires never
get produced: per-item pad_values, M-RoPE positions, and the
pad_input_tokens substitution that makes distinct images
prefix-compare unequal. A precomputed-mm request over this wire would
reach the scheduler unmaterialized.

Extract the InputProcessor's precomputed-mm block into
materialize_precomputed_inputs (runtime/multimodal/materialize.py) and
run it from both admission paths: the in-process frontend as before,
and now MsgpackRecvSocket on ingest, alongside the existing sampling
validation. The engine owns the mm math on every path; an external
frontend owes only expanded-placeholder input_ids plus items carrying
feature/hash/offsets (and grid metadata).

Ingest semantics:

  • a payload with input_ids_unpadded set is taken as fully materialized
    upstream and passed through untouched;
  • M-RoPE is computed only when ALL mrope fields are unset — a payload
    carrying any of them (including the documented scalar-only contract)
    owns its positions. The frontend path previously keyed only on
    mrope_positions, which would have recomputed over a scalar-only
    payload; both paths now honor the decode-side contract;
  • a malformed mm payload (or mm on a text-only model) is marked via
    validation_error like any invalid request, so the frontend gets a
    terminal abort instead of a dropped stream or a dead scheduler.

model_config threads into the recv socket the same way vocab_size does.

Tests (CPU-only, riding the scalar-only M-RoPE contract): ingest
materializes pad values and padded ids while preserving the unpadded
originals; text-only model rejection flows pre-marked; upstream-
materialized payloads pass through untouched; text requests unaffected.

The msgpack ZMQ path decodes multimodal payloads (the wire type is
mm-complete and the decoder is tensor-aware) but bypasses the frontend
InputProcessor, so three derived pieces the scheduler requires never
get produced: per-item pad_values, M-RoPE positions, and the
pad_input_tokens substitution that makes distinct images
prefix-compare unequal. A precomputed-mm request over this wire would
reach the scheduler unmaterialized.

Extract the InputProcessor's precomputed-mm block into
materialize_precomputed_inputs (runtime/multimodal/materialize.py) and
run it from both admission paths: the in-process frontend as before,
and now MsgpackRecvSocket on ingest, alongside the existing sampling
validation. The engine owns the mm math on every path; an external
frontend owes only expanded-placeholder input_ids plus items carrying
feature/hash/offsets (and grid metadata).

Ingest semantics:
- a payload with input_ids_unpadded set is taken as fully materialized
  upstream and passed through untouched;
- M-RoPE is computed only when ALL mrope fields are unset — a payload
  carrying any of them (including the documented scalar-only contract)
  owns its positions. The frontend path previously keyed only on
  mrope_positions, which would have recomputed over a scalar-only
  payload; both paths now honor the decode-side contract;
- a malformed mm payload (or mm on a text-only model) is marked via
  validation_error like any invalid request, so the frontend gets a
  terminal abort instead of a dropped stream or a dead scheduler.

model_config threads into the recv socket the same way vocab_size does.

Tests (CPU-only, riding the scalar-only M-RoPE contract): ingest
materializes pad values and padded ids while preserving the unpadded
originals; text-only model rejection flows pre-marked; upstream-
materialized payloads pass through untouched; text requests unaffected.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@slin1237
slin1237 requested a review from a team as a code owner August 13, 2026 13:10

@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: 406905cc92

ℹ️ 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 +112 to +115
if self._model_config is None or not getattr(
self._model_config, "is_multimodal_active", False
):
return "multimodal_inputs were provided for a text-only model"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep LM-only decode multimodal requests admissible

In disaggregated decode with --language-model-only, ModelConfig.is_multimodal_active is false even for a multimodal checkpoint, and the frontend path explicitly treats precomputed multimodal inputs as absent so the decode worker can use the expanded input_ids length without running the encoder. With this new check, the msgpack path marks the same request invalid whenever SMG includes multimodal_inputs, so decode-side workers abort valid multimodal requests instead of receiving remote KV. Please mirror the InputProcessor decode/language-model-only exemption rather than treating inactive multimodal as text-only.

Useful? React with 👍 / 👎.

Comment on lines +68 to +70
multimodal_inputs.mrope_positions is None
and multimodal_inputs.mrope_position_delta is None
and multimodal_inputs.mrope_position_delta_scalar is None

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 Compute M-RoPE when only deltas are present

When an upstream payload carries only mrope_position_delta or mrope_position_delta_scalar, this condition skips compute_mrope_positions even though mrope_positions is still absent. The prefill builder only consumes per-token mrope_positions for image/token sections; with positions missing it either applies a constant delta or falls back to linear positions, so Qwen-VL/Omni prefill loses the image-aware M-RoPE that the previous path computed whenever mrope_positions was None. Gate the skip on mrope_positions being present, or otherwise materialize the per-token positions.

Useful? React with 👍 / 👎.

# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

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 Restore the full MIT license header

The new file drops the MIT header paragraph that requires the copyright and permission notice to be included in copies; repository guidance requires the full MIT license header rather than a shortened copyright-only form, so this should match the existing file headers before merging.

AGENTS.md reference: AGENTS.md:L15-L16

Useful? React with 👍 / 👎.

@lightseek-bot
lightseek-bot requested a review from FC-Li August 13, 2026 18:31
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.

1 participant