Skip to content

Commit 919682d

Browse files
zhengluo-nvnnshah1
andauthored
fix(backends): backport ModelStreamer object-storage fixes (#10674)
Signed-off-by: Zheng Luo <zheluo@nvidia.com> Signed-off-by: nnshah1 <neelays@nvidia.com> Co-authored-by: Neelay Shah <neelays@nvidia.com>
1 parent c540d22 commit 919682d

4 files changed

Lines changed: 124 additions & 14 deletions

File tree

components/src/dynamo/sglang/register.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@
2828
SGLANG_HICACHE_MOONCAKE_RUNTIME_KEY = "sglang_hicache_mooncake"
2929

3030

31+
def _register_model_source_path(engine: sgl.Engine, server_args: ServerArgs) -> str:
32+
"""Pick the path passed to `register_model` for MDC construction.
33+
34+
When `--model-path` is a remote URI (`s3://...`, `gs://...`), SGLang's
35+
`ModelConfig.maybe_pull_model_tokenizer_from_remote` (sglang/srt/configs/
36+
model_config.py) pulls metadata files (`*config.json`) to a local temp dir
37+
and rewrites the engine's ModelConfig:
38+
39+
- `.model_weights = <original URI>` (preserved for the weight loader)
40+
- `.model_path = <local temp dir>` (now contains the metadata)
41+
42+
`server_args.model_path` itself is NOT mutated. Dynamo's `register_model`
43+
would otherwise pass the raw URI through `hub.rs` -> ModelExpress, which
44+
has no S3 provider and 404s. Returning the post-pull local dir lets
45+
`register_model` take its `fs::exists` shortcut and skip the broken MX
46+
path.
47+
48+
Temporary LLM-only workaround mirroring the vLLM fix in
49+
`components/src/dynamo/vllm/main.py`. Diffusion paths (Images/Videos) skip
50+
the Rust-side HF download entirely (lib/bindings/python/rust/lib.rs:314)
51+
and don't need this rewrite.
52+
"""
53+
try:
54+
mc = engine.tokenizer_manager.model_config
55+
except AttributeError:
56+
return server_args.model_path
57+
weights = getattr(mc, "model_weights", None)
58+
if weights:
59+
return mc.model_path
60+
return server_args.model_path
61+
62+
3163
def _build_media_decoder_and_fetcher():
3264
"""Construct MediaDecoder/MediaFetcher for frontend-decoded multimodal.
3365
@@ -89,7 +121,7 @@ async def _register_model_with_runtime_config(
89121
input_type,
90122
output_type,
91123
endpoint,
92-
server_args.model_path,
124+
_register_model_source_path(engine, server_args),
93125
server_args.served_model_name,
94126
context_length=server_args.context_length,
95127
kv_cache_block_size=server_args.page_size,

components/src/dynamo/vllm/llm_engine.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,13 @@ async def start(self, worker_id: int) -> EngineConfig:
8989
)
9090
os.environ["PROMETHEUS_MULTIPROC_DIR"] = self._prometheus_temp_dir.name
9191

92-
self._default_sampling_params = (
93-
self.engine_args.create_model_config().get_diff_sampling_param()
94-
)
95-
9692
vllm_config = self.engine_args.create_engine_config(
9793
usage_context=UsageContext.OPENAI_API_SERVER
9894
)
9995
self._vllm_config = vllm_config
96+
self._default_sampling_params = (
97+
vllm_config.model_config.get_diff_sampling_param()
98+
)
10099

101100
self.engine_client = AsyncLLM.from_vllm_config(
102101
vllm_config=vllm_config,

components/src/dynamo/vllm/main.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ def should_register_model_ignore_weights(config: Config) -> bool:
7070
return uses_modelexpress_load_format(config)
7171

7272

73+
def _register_model_source_path(config: Config, vllm_config: VllmConfig) -> str:
74+
"""Pick the path passed to `register_model` for MDC construction.
75+
76+
When `--model` is an object-storage URI (`s3://...`, `gs://...`, `az://...`),
77+
vLLM's `maybe_pull_model_tokenizer_for_runai` (vllm/config/model.py) pulls
78+
metadata files to a local temp dir and rewrites `vllm_config.model_config`:
79+
80+
- `.model_weights = <original URI>` (used by runai-streamer / mx plugin)
81+
- `.model = <local temp dir>` (contains config.json, tokenizer, …)
82+
83+
Dynamo's `register_model` would otherwise try to resolve the raw URI via
84+
`hub.rs` → ModelExpress, which has no S3 provider and 404s. Returning the
85+
local dir lets `register_model` take its `fs::exists` shortcut.
86+
87+
Temporary vLLM-only workaround until `hub.rs` learns object-storage routing.
88+
Falls back to `config.model` whenever vLLM did not pull (HF id, local path,
89+
or older vLLM without `model_weights`).
90+
"""
91+
if getattr(vllm_config.model_config, "model_weights", ""):
92+
return vllm_config.model_config.model
93+
return config.model
94+
95+
7396
def build_headless_namespace(config: Config) -> argparse.Namespace:
7497
"""Build an argparse Namespace from engine_args for vLLM's run_headless().
7598
@@ -490,14 +513,6 @@ def setup_vllm_engine(
490513
# Prevents deadlock during TP>1 failover.
491514
configure_gms_lock_mode(engine_args)
492515

493-
# ModelExpress uses vLLM's plugin path with --load-format=modelexpress.
494-
# Dynamo does not register loaders or set a custom worker class here.
495-
496-
# Load default sampling params from `generation_config.json`
497-
default_sampling_params = (
498-
engine_args.create_model_config().get_diff_sampling_param()
499-
)
500-
501516
# Configure ec_both mode with DynamoMultimodalEmbeddingCacheConnector.
502517
# Must happen BEFORE engine setup so vLLM sees ec_transfer_config.
503518
if (
@@ -527,6 +542,7 @@ def setup_vllm_engine(
527542
# Taken from build_async_engine_client_from_engine_args()
528543
usage_context = UsageContext.OPENAI_API_SERVER
529544
vllm_config = engine_args.create_engine_config(usage_context=usage_context)
545+
default_sampling_params = vllm_config.model_config.get_diff_sampling_param()
530546

531547
# Set up consolidator endpoints if KVBM (DynamoConnector) is enabled
532548
consolidator_endpoints = None
@@ -680,7 +696,7 @@ async def register_vllm_model(
680696
model_input,
681697
model_type,
682698
generate_endpoint,
683-
config.model,
699+
_register_model_source_path(config, vllm_config),
684700
config.served_model_name,
685701
context_length=vllm_config.model_config.max_model_len,
686702
kv_cache_block_size=runtime_values["kv_event_block_size"],

components/src/dynamo/vllm/tests/test_vllm_unit.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,69 @@ def test_should_register_model_fetch_weights_for_default_load_format():
336336
assert should_register_model_ignore_weights(config) is False
337337

338338

339+
def test_setup_vllm_engine_reuses_engine_config_model_config(monkeypatch):
340+
from dynamo.vllm import main as vllm_main
341+
342+
class FakeModelConfig:
343+
def get_diff_sampling_param(self):
344+
return {"temperature": 0.7}
345+
346+
vllm_config = SimpleNamespace(
347+
additional_config={},
348+
cache_config=SimpleNamespace(block_size=None),
349+
model_config=FakeModelConfig(),
350+
)
351+
352+
class FakeEngineArgs:
353+
enable_log_requests = False
354+
enable_lora = False
355+
disable_log_stats = True
356+
load_format = "modelexpress"
357+
358+
def create_model_config(self):
359+
raise AssertionError("setup_vllm_engine must not create ModelConfig twice")
360+
361+
def create_engine_config(self, usage_context):
362+
return vllm_config
363+
364+
engine_client = SimpleNamespace(vllm_config=vllm_config)
365+
366+
class FakeAsyncLLM:
367+
@staticmethod
368+
def from_vllm_config(**_kwargs):
369+
return engine_client
370+
371+
class FakeMetrics:
372+
def __init__(self, **_kwargs):
373+
pass
374+
375+
def set_model_load_time(self, _load_time):
376+
pass
377+
378+
monkeypatch.setattr(vllm_main, "setup_multiprocess_prometheus", lambda: None)
379+
monkeypatch.setattr(vllm_main, "LLMBackendMetrics", FakeMetrics)
380+
monkeypatch.setattr(vllm_main, "_uses_dynamo_connector", lambda _args: False)
381+
monkeypatch.setattr(vllm_main, "AsyncLLM", FakeAsyncLLM)
382+
monkeypatch.setattr(
383+
vllm_main,
384+
"get_engine_cache_info",
385+
lambda _engine: {"block_size": 16},
386+
)
387+
388+
config = SimpleNamespace(
389+
component="backend",
390+
engine_args=FakeEngineArgs(),
391+
gms_shadow_mode=False,
392+
multimodal_embedding_cache_capacity_gb=0,
393+
route_to_encoder=False,
394+
served_model_name="Qwen/Qwen3-0.6B",
395+
)
396+
397+
_, _, default_sampling_params, _, _ = vllm_main.setup_vllm_engine(config)
398+
399+
assert default_sampling_params == {"temperature": 0.7}
400+
401+
339402
# --disaggregation-mode tests
340403

341404

0 commit comments

Comments
 (0)