feat: parse diffusion worker args natively from sglang's diffusion ServerArgs - #13376
Conversation
Signed-off-by: jain-ria <riajain@NVIDIA.com>
Signed-off-by: jain-ria <riajain@NVIDIA.com>
Signed-off-by: jain-ria <riajain@NVIDIA.com>
Signed-off-by: jain-ria <riajain@NVIDIA.com>
Signed-off-by: jain-ria <riajain@NVIDIA.com>
6a32da6 to
08c85e6
Compare
…rverArgs Image/video diffusion workers previously parsed CLI args with the LLM ServerArgs parser, hand-copied 13 fields into a SimpleNamespace stub, and called DiffGenerator.from_pretrained() with 5 hardcoded kwargs. Every other diffusion engine option (torch compile, quantization, batching, warmup, attention backend, ...) was silently stuck at its default, and unknown or LLM-only flags were silently absorbed. Build the worker CLI from sglang's native diffusion ServerArgs instead, mirroring how the LLM path builds its CLI from the LLM ServerArgs: - diffusion_args.py: native parser + DiffusionWorkerArgs adapter. Engine fields delegate to the parsed diffusion ServerArgs; Dynamo-side settings (--served-model-name, --enable-metrics) are registered only when the engine parser does not define them natively, so future native flags win automatically. Fields Dynamo's shared worker code probes are pinned inert. Explicitly-provided flags are communicated through the parser's side channel so resolution never depends on process argv. - args.py: diffusion/video workers branch to the native parser before any LLM-specific processing; the stub is removed. - init_diffusion.py: hand the full parsed config to DiffGenerator.from_server_args() for both image and video workers. BREAKING: LLM-only flags on diffusion worker command lines (previously silently ignored) now fail at startup with "unrecognized arguments". Remove such flags from existing deployment configs. Verification: - 126 engine fields reachable on sglang 0.5.17 (previously 5); invalid values and unknown flags now rejected with actionable errors - unit tests 9/9; live image and video deployments on B200 generate successfully with --warmup-mode server (previously unreachable) confirmed active in the resolved engine config - resolved-config diff vs the stub path: no unintended changes; the stub was also silently dropping --trust-remote-code and nulling dist_timeout, both now honored - assembled check against sglang v0.5.17 + current dynamo wheel: worker boots and serves /v1/images/generations end-to-end Signed-off-by: Marc Karp <mkarp@nvidia.com> Co-Authored-By: Claude <noreply@anthropic.com>
327995a to
010ba75
Compare
08c85e6 to
8c6f5b6
Compare
| explicit_names |= set(parsed._sglang_explicit_arg_names) | ||
| parsed._sglang_explicit_arg_names = tuple(sorted(explicit_names)) | ||
|
|
||
| engine_args = DiffusionServerArgs.from_cli_args(parsed, remaining) |
There was a problem hiding this comment.
Fixed in #13667: --skip-tokenizer-init removed from both launch scripts, and the video launcher now uses the canonical --tp-size. Also swept the repo for other callers passing LLM-only flags to diffusion workers (docs, tests, configs) — these two scripts were the only ones. Both scripts were executed end to end against sglang 0.5.17 and serve generation requests; the old argument vector is rejected with unrecognized arguments: --skip-tokenizer-init.
| # caller that passes an argument list (tests, embedding). Communicate the | ||
| # exact flags we parsed through the side channel the engine parser | ||
| # supports, so resolution never depends on process argv. | ||
| explicit_names = { |
There was a problem hiding this comment.
parse_known_args() can accept an abbreviated or aliased option and store it under a different name, but this records the raw spelling, so then SGLang then may not recognize the corresponding field as explicitly provided. For example, the existing video launcher’ passes --tp 2, accepting it as an abbreviation for --tp-size, but this code records the raw name tp. Because SGLang does not map tp back to tp_size, it treats tp_size as unspecified and falls back to 1.
There was a problem hiding this comment.
Great catch — fixed in #13667. The side channel now resolves each provided flag to its parser destination (exact match or unique prefix, mirroring argparse's own abbreviation rules) instead of recording the raw spelling. Unit-tested: --tp 2 lands as tp_size=2 in both the parsed config and the explicit-args set. Flags not registered on the parser (e.g. dynamic --<component>-path) still fall back to the normalized raw name so the engine's own resolution handles them.
| # server_args is a DiffusionWorkerArgs adapter; engine_args is the | ||
| # natively parsed sglang diffusion ServerArgs, handed over wholesale so | ||
| # every engine option the user set on the CLI takes effect. | ||
| generator = DiffGenerator.from_server_args(server_args.engine_args) |
There was a problem hiding this comment.
This update removed the previous num_gpus = tp_size * dp_size calculation, defaulting to 1. Any call that runs with TP or DP > 1 without also passing --num-gpus will have an inconsistent configuration.
There was a problem hiding this comment.
Confirmed and fixed in #13667 — the engine defaults num_gpus to 1 and does not derive it from parallelism degrees, so this was a real regression. Restored num_gpus = tp_size * dp_size when --num-gpus is not explicitly provided (an explicit --num-gpus wins). Covered by two unit tests.
Overview
Stacked on #13343 — do not merge first. This PR's base is that branch; GitHub will retarget to
mainwhen it merges. Design direction (build on the native diffusion parser, on top of the 0.5.17 bump) was agreed in team review.Image/video diffusion workers currently parse CLI args with the LLM ServerArgs parser, hand-copy 13 fields into a
SimpleNamespacestub, and callDiffGenerator.from_pretrained()with 5 hardcoded kwargs. Consequences:--enable-torch-compilecollides with the same-named LLM flag: it parses into the LLM ServerArgs and never reaches the diffusion config; passing it a value crashes the worker.--trust-remote-codenever reached the engine, anddist_timeoutwas forced toNone, disabling the engine's default distributed-init timeout.What this PR does
Build the worker CLI from sglang's native diffusion ServerArgs, mirroring how the LLM path builds its CLI from the LLM ServerArgs:
diffusion_args.py(new) — native parser +DiffusionWorkerArgsadapter. Engine fields delegate to the natively parsed diffusion ServerArgs; Dynamo-side settings (--served-model-name,--enable-metrics) are registered only when the engine parser does not define them natively, so when sglang ships its own (already on sglang main) it wins automatically. Fields Dynamo's shared worker code probes on anyserver_argsare pinned to inert values. Explicitly-provided flags are passed through the parser's_sglang_explicit_arg_namesside channel so resolution never depends on process argv (keeps the module testable and embeddable). Version-tolerant import for the server_args module/package layout change.args.py— diffusion/video workers branch to the native parser before any LLM-specific processing; the stub is deleted.init_diffusion.py— both image and video inits hand the full parsed config toDiffGenerator.from_server_args(), replacing the 5 hardcoded kwargs.sys.modules; no GPU, no model).Breaking change / migration
LLM-only flags on diffusion worker command lines — previously silently ignored — now fail at startup with
unrecognized arguments. Example: a deployment passing--skip-tokenizer-initto an--image-diffusion-workermust drop that flag. This is the intended contract (same as the LLM path): loud rejection is what makes the newly reachable flags trustworthy. If reviewers prefer a softer landing (warn-and-ignore for one release), that's a small follow-up.Verification
lmsysorg/sglang:v0.5.17-cu130-runtime): 126 engine fields reachable (previously 5); invalid values rejected with actionable messages (--performance-mode: invalid choice: ... choose from 'manual', 'auto', 'speed', 'memory'); unknown flags rejected loudly; Dynamo-side flags kept out of the engine config.--warmup-mode server— previously unreachable — confirmed active in the resolved engine config and engine logs;/v1/images/generationsreturns HTTP 200; warm request 0.97 s at 1024².--video-generation-worker; full pipeline runs and/v1/videosreturns a playable file.trust_remote_code,dist_timeout), and a per-boot random port.image_diffusion.shlaunch-script run that chore(sglang): bump to 0.5.17 #13343's validation had to skip for lack of model access.Notes for reviewers
dllm_algorithm=False, which madediffusion_worker = server_args.dllm_algorithm is not NoneaccidentallyTruefor image/video workers; it was never load-bearing becausemain.pydispatches image/video workers first. The new path sets itFalseexplicitly (dllm = text-diffusion LLMs, unrelated to image/video generation).num_gpusderivation (tp_size * dp_size) moves from Dynamo code into sglang's own ServerArgs resolution, and--num-gpusis now directly settable like any other native flag.nparameter in the SGLang image diffusion handler #12970 and fix: surface image generation errors instead of returning empty HTTP 200 #13000.Part of DYN-3974
🤖 Generated with Claude Code