#![cfg_attr(not(feature = "media-ffmpeg"), allow(dead_code))]
mod config;
mod qwen3;
use std::{path::Path, sync::Arc};
use anyhow::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)]
pub(crate) struct QwenVideoProcessorContract {
pub placeholder_target: QwenVideoPlaceholderTarget,
pub resize_mode: QwenVideoResizeMode,
}
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 target_tokens: Vec<TokenIdType>,
pub replacement_tokens: Vec<TokenIdType>,
}
enum SupportedVideoModel {
Qwen3(qwen3::Qwen3VideoRoutingSpec),
}
pub(crate) struct VideoRoutingProcessor {
model: SupportedVideoModel,
}
impl VideoRoutingProcessor {
pub(crate) fn try_new(
model_id: &str,
model_type: &str,
model_dir: &Path,
tokenizer: Arc<dyn Tokenizer>,
qwen_video_contract: QwenVideoProcessorContract,
) -> 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,
qwen_video_contract,
)?)
} 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),
}
}
}