Skip to content

Commit 9bce789

Browse files
committed
feat: MDC decoding params, python bindings
Signed-off-by: Alexandre Milesi <[email protected]>
1 parent 3c4aa12 commit 9bce789

File tree

7 files changed

+141
-3
lines changed

7 files changed

+141
-3
lines changed

lib/bindings/python/Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/bindings/python/rust/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use dynamo_llm::{self as llm_rs};
3737
use dynamo_llm::{entrypoint::RouterConfig, kv_router::KvRouterConfig};
3838

3939
use crate::llm::local_model::ModelRuntimeConfig;
40+
use crate::llm::preprocessor::{MediaDecoder, MediaFetcher};
4041

4142
#[pyclass(eq, eq_int)]
4243
#[derive(Clone, Debug, PartialEq)]
@@ -159,6 +160,8 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
159160
m.add_class::<llm::model_card::ModelDeploymentCard>()?;
160161
m.add_class::<llm::local_model::ModelRuntimeConfig>()?;
161162
m.add_class::<llm::preprocessor::OAIChatPreprocessor>()?;
163+
m.add_class::<llm::preprocessor::MediaDecoder>()?;
164+
m.add_class::<llm::preprocessor::MediaFetcher>()?;
162165
m.add_class::<llm::backend::Backend>()?;
163166
m.add_class::<llm::kv::OverlapScores>()?;
164167
m.add_class::<llm::kv::KvIndexer>()?;
@@ -215,7 +218,7 @@ fn log_message(level: &str, message: &str, module: &str, file: &str, line: u32)
215218
/// Create an engine and attach it to an endpoint to make it visible to the frontend.
216219
/// This is the main way you create a Dynamo worker / backend.
217220
#[pyfunction]
218-
#[pyo3(signature = (model_input, model_type, endpoint, model_path, model_name=None, context_length=None, kv_cache_block_size=None, router_mode=None, migration_limit=0, runtime_config=None, user_data=None, custom_template_path=None))]
221+
#[pyo3(signature = (model_input, model_type, endpoint, model_path, model_name=None, context_length=None, kv_cache_block_size=None, router_mode=None, migration_limit=0, runtime_config=None, user_data=None, custom_template_path=None, media_decoder=None, media_fetcher=None))]
219222
#[allow(clippy::too_many_arguments)]
220223
fn register_llm<'p>(
221224
py: Python<'p>,
@@ -231,6 +234,8 @@ fn register_llm<'p>(
231234
runtime_config: Option<ModelRuntimeConfig>,
232235
user_data: Option<&Bound<'p, PyDict>>,
233236
custom_template_path: Option<&str>,
237+
media_decoder: Option<MediaDecoder>,
238+
media_fetcher: Option<MediaFetcher>,
234239
) -> PyResult<Bound<'p, PyAny>> {
235240
// Validate Prefill model type requirements
236241
if model_type.inner == llm_rs::model_type::ModelType::Prefill {
@@ -303,7 +308,9 @@ fn register_llm<'p>(
303308
.migration_limit(Some(migration_limit))
304309
.runtime_config(runtime_config.unwrap_or_default().inner)
305310
.user_data(user_data_json)
306-
.custom_template_path(custom_template_path_owned);
311+
.custom_template_path(custom_template_path_owned)
312+
.media_decoder(media_decoder.map(|m| m.inner))
313+
.media_fetcher(media_fetcher.map(|m| m.inner));
307314
// Load the ModelDeploymentCard
308315
let mut local_model = builder.build().await.map_err(to_pyerr)?;
309316
// Advertise ourself on etcd so ingress can find us

lib/bindings/python/rust/llm/preprocessor.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::llm::model_card::ModelDeploymentCard;
66

77
use llm_rs::{
88
preprocessor::OpenAIPreprocessor,
9+
preprocessor::media::{MediaDecoder as RsMediaDecoder, MediaFetcher as RsMediaFetcher},
910
protocols::common::llm_backend::{BackendOutput, PreprocessedRequest},
1011
types::{
1112
Annotated,
@@ -74,3 +75,63 @@ impl OAIChatPreprocessor {
7475
})
7576
}
7677
}
78+
79+
80+
#[pyclass]
81+
#[derive(Clone)]
82+
pub struct MediaDecoder {
83+
pub(crate) inner: RsMediaDecoder,
84+
}
85+
86+
#[pymethods]
87+
impl MediaDecoder {
88+
#[new]
89+
fn new() -> Self {
90+
Self {
91+
inner: RsMediaDecoder::default(),
92+
}
93+
}
94+
95+
fn image_decoder(&mut self, image_decoder: &Bound<'_, PyDict>) -> PyResult<()> {
96+
let image_decoder = pythonize::depythonize(image_decoder).map_err(|err| {
97+
PyErr::new::<PyException, _>(format!("Failed to parse image_decoder: {}", err))
98+
})?;
99+
self.inner.image_decoder = image_decoder;
100+
Ok(())
101+
}
102+
}
103+
104+
#[pyclass]
105+
#[derive(Clone)]
106+
pub struct MediaFetcher {
107+
pub(crate) inner: RsMediaFetcher,
108+
}
109+
110+
#[pymethods]
111+
impl MediaFetcher {
112+
#[new]
113+
fn new() -> Self {
114+
Self {
115+
inner: RsMediaFetcher::default(),
116+
}
117+
}
118+
fn user_agent(&mut self, user_agent: String) {
119+
self.inner.user_agent = user_agent;
120+
}
121+
122+
fn allow_direct_ip(&mut self, allow: bool) {
123+
self.inner.allow_direct_ip = allow;
124+
}
125+
126+
fn allow_direct_port(&mut self, allow: bool) {
127+
self.inner.allow_direct_port = allow;
128+
}
129+
130+
fn allowed_media_domains(&mut self, domains: Vec<String>) {
131+
self.inner.allowed_media_domains = Some(domains.into_iter().collect());
132+
}
133+
134+
fn timeout_ms(&mut self, timeout_ms: u64) {
135+
self.inner.timeout = Some(Duration::from_millis(timeout_ms));
136+
}
137+
}

lib/bindings/python/src/dynamo/llm/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from dynamo._core import RouterMode as RouterMode
2929
from dynamo._core import SpecDecodeStats as SpecDecodeStats
3030
from dynamo._core import WorkerMetricsPublisher as WorkerMetricsPublisher
31+
from dynamo._core import MediaDecoder as MediaDecoder
32+
from dynamo._core import MediaFetcher as MediaFetcher
3133
from dynamo._core import WorkerStats as WorkerStats
3234
from dynamo._core import ZmqKvEventListener as ZmqKvEventListener
3335
from dynamo._core import ZmqKvEventPublisher as ZmqKvEventPublisher

lib/llm/src/local_model.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::mocker::protocols::MockEngineArgs;
1515
use crate::model_card::{self, ModelDeploymentCard};
1616
use crate::model_type::{ModelInput, ModelType};
1717
use crate::request_template::RequestTemplate;
18+
use crate::preprocessor::media::{MediaDecoder, MediaFetcher};
1819

1920
pub mod runtime_config;
2021

@@ -52,6 +53,8 @@ pub struct LocalModelBuilder {
5253
namespace: Option<String>,
5354
custom_backend_metrics_endpoint: Option<String>,
5455
custom_backend_metrics_polling_interval: Option<f64>,
56+
media_decoder: Option<MediaDecoder>,
57+
media_fetcher: Option<MediaFetcher>,
5558
}
5659

5760
impl Default for LocalModelBuilder {
@@ -77,6 +80,8 @@ impl Default for LocalModelBuilder {
7780
namespace: Default::default(),
7881
custom_backend_metrics_endpoint: Default::default(),
7982
custom_backend_metrics_polling_interval: Default::default(),
83+
media_decoder: Default::default(),
84+
media_fetcher: Default::default(),
8085
}
8186
}
8287
}
@@ -184,6 +189,16 @@ impl LocalModelBuilder {
184189
self
185190
}
186191

192+
pub fn media_decoder(&mut self, media_decoder: Option<MediaDecoder>) -> &mut Self {
193+
self.media_decoder = media_decoder;
194+
self
195+
}
196+
197+
pub fn media_fetcher(&mut self, media_fetcher: Option<MediaFetcher>) -> &mut Self {
198+
self.media_fetcher = media_fetcher;
199+
self
200+
}
201+
187202
/// Make an LLM ready for use:
188203
/// - Download it from Hugging Face (and NGC in future) if necessary
189204
/// - Resolve the path
@@ -219,6 +234,8 @@ impl LocalModelBuilder {
219234
self.runtime_config.max_num_batched_tokens =
220235
mocker_engine_args.max_num_batched_tokens.map(|v| v as u64);
221236
self.runtime_config.data_parallel_size = mocker_engine_args.dp_size;
237+
self.media_decoder = Some(MediaDecoder::default());
238+
self.media_fetcher = Some(MediaFetcher::default());
222239
}
223240

224241
// frontend and echo engine don't need a path.
@@ -230,6 +247,8 @@ impl LocalModelBuilder {
230247
card.migration_limit = self.migration_limit;
231248
card.user_data = self.user_data.take();
232249
card.runtime_config = self.runtime_config.clone();
250+
card.media_decoder = self.media_decoder.clone();
251+
card.media_fetcher = self.media_fetcher.clone();
233252

234253
return Ok(LocalModel {
235254
card,
@@ -280,6 +299,8 @@ impl LocalModelBuilder {
280299
card.migration_limit = self.migration_limit;
281300
card.user_data = self.user_data.take();
282301
card.runtime_config = self.runtime_config.clone();
302+
card.media_decoder = self.media_decoder.clone();
303+
card.media_fetcher = self.media_fetcher.clone();
283304

284305
Ok(LocalModel {
285306
card,

lib/llm/src/model_card.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use dynamo_runtime::{slug::Slug, storage::key_value_store::Versioned};
2525
use serde::{Deserialize, Serialize};
2626
use tokenizers::Tokenizer as HfTokenizer;
2727

28+
use crate::preprocessor::media::{MediaDecoder, MediaFetcher};
2829
use crate::protocols::TokenIdType;
2930

3031
/// Identify model deployment cards in the key-value store
@@ -217,6 +218,14 @@ pub struct ModelDeploymentCard {
217218
#[serde(default)]
218219
pub runtime_config: ModelRuntimeConfig,
219220

221+
/// Media decoding configuration
222+
#[serde(default)]
223+
pub media_decoder: Option<MediaDecoder>,
224+
225+
/// Media fetching configuration
226+
#[serde(default)]
227+
pub media_fetcher: Option<MediaFetcher>,
228+
220229
#[serde(skip, default)]
221230
checksum: OnceLock<String>,
222231
}
@@ -520,6 +529,8 @@ impl ModelDeploymentCard {
520529
model_input: Default::default(), // set later
521530
user_data: None,
522531
runtime_config: ModelRuntimeConfig::default(),
532+
media_decoder: None,
533+
media_fetcher: None,
523534
checksum: OnceLock::new(),
524535
})
525536
}

lib/llm/src/preprocessor/media.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ mod loader;
77

88
pub use common::EncodedMediaData;
99
pub use decoders::{Decoder, ImageDecoder, MediaDecoder};
10-
pub use loader::MediaLoader;
10+
pub use loader::{MediaLoader, MediaFetcher};

0 commit comments

Comments
 (0)