#![cfg_attr(not(feature = "media-ffmpeg"), allow(dead_code))]
mod config;
pub mod image;
pub(super) mod nemotron;
mod qwen3;
use std::{path::Path, sync::Arc};
use anyhow::{Context, Result};
use serde::Deserialize;
use crate::{protocols::TokenIdType, tokenizers::traits::Tokenizer};
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum QwenVideoPlaceholderTarget {
BareVideoToken,
VisionWrappedVideoToken,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum QwenVideoResizeMode {
LegacyCeil,
RoundTiesEven,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum QwenVideoRunlessBoundaryHash {
MmMetadata,
TokensOnly,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct QwenVideoProcessorContract {
pub placeholder_target: QwenVideoPlaceholderTarget,
pub resize_mode: QwenVideoResizeMode,
pub runless_boundary_hash: QwenVideoRunlessBoundaryHash,
pub sglang_preprocess: Option<SglangQwenVideoPreprocessContract>,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq)]
pub(crate) struct VllmQwenVideoProcessorContract {
pub placeholder_target: QwenVideoPlaceholderTarget,
pub resize_mode: QwenVideoResizeMode,
#[serde(default)]
runless_boundary_hash: Option<QwenVideoRunlessBoundaryHash>,
#[serde(default)]
sglang_preprocess: Option<SglangQwenVideoPreprocessContract>,
}
impl TryFrom<VllmQwenVideoProcessorContract> for QwenVideoProcessorContract {
type Error = anyhow::Error;
fn try_from(contract: VllmQwenVideoProcessorContract) -> Result<Self> {
anyhow::ensure!(
contract.sglang_preprocess.is_none(),
"mm-routing: vLLM Qwen contract must not publish SGLang preprocessing"
);
anyhow::ensure!(
contract
.runless_boundary_hash
.unwrap_or(QwenVideoRunlessBoundaryHash::MmMetadata)
== QwenVideoRunlessBoundaryHash::MmMetadata,
"mm-routing: vLLM Qwen contract must use MM metadata KV-event identity"
);
Ok(Self {
placeholder_target: contract.placeholder_target,
resize_mode: contract.resize_mode,
runless_boundary_hash: QwenVideoRunlessBoundaryHash::MmMetadata,
sglang_preprocess: None,
})
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq)]
pub(crate) struct SglangQwenVideoProcessorContract {
pub placeholder_target: QwenVideoPlaceholderTarget,
pub resize_mode: QwenVideoResizeMode,
pub runless_boundary_hash: QwenVideoRunlessBoundaryHash,
pub sglang_preprocess: SglangQwenVideoPreprocessContract,
}
impl TryFrom<SglangQwenVideoProcessorContract> for QwenVideoProcessorContract {
type Error = anyhow::Error;
fn try_from(contract: SglangQwenVideoProcessorContract) -> Result<Self> {
anyhow::ensure!(
contract.runless_boundary_hash == QwenVideoRunlessBoundaryHash::TokensOnly,
"mm-routing: SGLang Qwen contract must use pad-value token KV-event identity"
);
Ok(Self {
placeholder_target: contract.placeholder_target,
resize_mode: contract.resize_mode,
runless_boundary_hash: contract.runless_boundary_hash,
sglang_preprocess: Some(contract.sglang_preprocess),
})
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq)]
pub(crate) struct SglangQwenVideoPreprocessContract {
pub image_factor: usize,
pub video_min_pixels: usize,
pub video_max_pixels: usize,
pub video_total_pixels: usize,
pub frame_factor: usize,
pub fps: f64,
pub min_frames: usize,
pub max_frames: usize,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq)]
pub(crate) struct NemotronVideoProcessorContract {
pub video_pruning_rate: f64,
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct VideoProcessorContracts {
pub qwen: Option<QwenVideoProcessorContract>,
pub nemotron: Option<NemotronVideoProcessorContract>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum KvEventMmIdentity {
MmMetadata,
PadValueTokens,
}
pub(crate) struct VideoRoutingInput<'a> {
pub frame_count: usize,
pub width: u32,
pub height: u32,
pub source_fps: f64,
pub sampled_timestamps: &'a [f64],
}
pub(crate) struct VideoRoutingReplacement {
pub placeholder_token_id: TokenIdType,
pub event_video_token_id: Option<TokenIdType>,
pub target_tokens: Vec<TokenIdType>,
pub replacement_tokens: Vec<TokenIdType>,
}
enum SupportedVideoModel {
Qwen3(qwen3::Qwen3VideoRoutingSpec),
Nemotron(nemotron::NemotronVideoRoutingSpec),
#[cfg(test)]
TestStub,
}
pub(crate) struct VideoRoutingProcessor {
model: SupportedVideoModel,
}
impl VideoRoutingProcessor {
#[cfg(test)]
pub(crate) fn test_stub() -> Self {
Self {
model: SupportedVideoModel::TestStub,
}
}
pub(crate) fn try_new(
model_id: &str,
model_type: &str,
model_dir: &Path,
tokenizer: Arc<dyn Tokenizer>,
contracts: VideoProcessorContracts,
) -> Result<Option<Self>> {
let model = if qwen3::supports_model_type(model_type) {
SupportedVideoModel::Qwen3(qwen3::Qwen3VideoRoutingSpec::from_model_dir(
model_id,
model_type,
model_dir,
tokenizer,
contracts
.qwen
.context("mm-routing: Qwen video worker contract is missing")?,
)?)
} else if nemotron::supports_model_type(Some(model_type)) {
SupportedVideoModel::Nemotron(nemotron::NemotronVideoRoutingSpec::from_model_dir(
model_id,
model_type,
model_dir,
tokenizer,
contracts
.nemotron
.context("mm-routing: Nemotron video worker contract is missing")?,
)?)
} else {
return Ok(None);
};
Ok(Some(Self { model }))
}
pub(crate) fn build_replacement(
&self,
input: &VideoRoutingInput<'_>,
) -> Result<VideoRoutingReplacement> {
match &self.model {
SupportedVideoModel::Qwen3(spec) => spec.build_replacement(input),
SupportedVideoModel::Nemotron(spec) => spec.build_replacement(input),
#[cfg(test)]
SupportedVideoModel::TestStub => anyhow::bail!("test video routing processor stub"),
}
}
pub(crate) fn kv_event_mm_identity(&self) -> KvEventMmIdentity {
match &self.model {
SupportedVideoModel::Qwen3(spec) => spec.kv_event_mm_identity(),
SupportedVideoModel::Nemotron(_) => KvEventMmIdentity::MmMetadata,
#[cfg(test)]
SupportedVideoModel::TestStub => KvEventMmIdentity::MmMetadata,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const PUBLISHED_SGLANG_QWEN_CONTRACT: &str = r#"{
"placeholder_target": "bare_video_token",
"resize_mode": "legacy_ceil",
"runless_boundary_hash": "tokens_only",
"sglang_preprocess": {
"image_factor": 28,
"video_min_pixels": 100352,
"video_max_pixels": 602112,
"video_total_pixels": 90316800,
"frame_factor": 2,
"fps": 2.0,
"min_frames": 4,
"max_frames": 768
}
}"#;
#[test]
fn parses_published_sglang_qwen_contract() {
let published: SglangQwenVideoProcessorContract =
serde_json::from_str(PUBLISHED_SGLANG_QWEN_CONTRACT).unwrap();
let contract = QwenVideoProcessorContract::try_from(published).unwrap();
assert_eq!(
contract,
QwenVideoProcessorContract {
placeholder_target: QwenVideoPlaceholderTarget::BareVideoToken,
resize_mode: QwenVideoResizeMode::LegacyCeil,
runless_boundary_hash: QwenVideoRunlessBoundaryHash::TokensOnly,
sglang_preprocess: Some(SglangQwenVideoPreprocessContract {
image_factor: 28,
video_min_pixels: 100_352,
video_max_pixels: 602_112,
video_total_pixels: 90_316_800,
frame_factor: 2,
fps: 2.0,
min_frames: 4,
max_frames: 768,
}),
}
);
}
#[test]
fn rejects_incomplete_sglang_qwen_contract() {
let missing_preprocess = r#"{
"placeholder_target": "bare_video_token",
"resize_mode": "legacy_ceil",
"runless_boundary_hash": "tokens_only"
}"#;
let missing_identity = r#"{
"placeholder_target": "bare_video_token",
"resize_mode": "legacy_ceil",
"sglang_preprocess": {
"image_factor": 28,
"video_min_pixels": 100352,
"video_max_pixels": 602112,
"video_total_pixels": 90316800,
"frame_factor": 2,
"fps": 2.0,
"min_frames": 4,
"max_frames": 768
}
}"#;
assert!(
serde_json::from_str::<SglangQwenVideoProcessorContract>(missing_preprocess).is_err()
);
assert!(
serde_json::from_str::<SglangQwenVideoProcessorContract>(missing_identity).is_err()
);
}
#[test]
fn rejects_cross_engine_qwen_contract_semantics() {
let vllm_with_sglang_preprocess: VllmQwenVideoProcessorContract =
serde_json::from_str(PUBLISHED_SGLANG_QWEN_CONTRACT).unwrap();
assert!(QwenVideoProcessorContract::try_from(vllm_with_sglang_preprocess).is_err());
let sglang_with_vllm_identity =
PUBLISHED_SGLANG_QWEN_CONTRACT.replace("\"tokens_only\"", "\"mm_metadata\"");
let sglang_with_vllm_identity: SglangQwenVideoProcessorContract =
serde_json::from_str(&sglang_with_vllm_identity).unwrap();
assert!(QwenVideoProcessorContract::try_from(sglang_with_vllm_identity).is_err());
}
}