Skip to content

feat: sglang V4 (DeepSeek-V4) compatibility for decode handler - #8678

Closed
davilu-nvidia wants to merge 1 commit into
ai-dynamo:mainfrom
davilu-nvidia:dsv4-h20
Closed

feat: sglang V4 (DeepSeek-V4) compatibility for decode handler#8678
davilu-nvidia wants to merge 1 commit into
ai-dynamo:mainfrom
davilu-nvidia:dsv4-h20

Conversation

@davilu-nvidia

@davilu-nvidia davilu-nvidia commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Compatibility fixes for DecodeWorkerHandler to support sglang V4 (DeepSeek-V4-Flash) disaggregated serving on H20 GPUs.

Overlap with #8671

Additional fixes beyond #8671

  1. use_sglang_tokenizer flag: Replaces the removed skip_tokenizer_init attribute. sglang V4 no longer exposes skip_tokenizer_init on the engine; without this flag, all tokenizer-dependent code paths break.
  2. _get_guided_decoding_params() stub: sglang V4's request flow calls this method for guided decoding support. Missing method causes AttributeError at runtime.
  3. _resolve_lora() stub: sglang V4 invokes LoRA resolution per request. Without the stub, requests fail with AttributeError.
  4. _session_kwargs() stub: sglang V4 passes session-level kwargs to async_generate. Missing method breaks the generate call.
  5. lora_path pass-through: Forwards resolved LoRA path to engine.async_generate() in both disaggregated and aggregated code paths.
  6. stop_token_ids merging: Merges stop_token_ids and stop_token_ids_hidden from stop_conditions, fixing incomplete stop condition handling.

Verified

  • E2E disaggregated inference (prefill/decode via Mooncake RDMA) of DeepSeek-V4-Flash (284B MoE) on 8x H20 GPUs
  • Benchmarked with aiperf: KV router vs round-robin, no regression observed

Signed-off-by: David Lu davilu@nvidia.com

Add use_sglang_tokenizer, _get_guided_decoding_params, _resolve_lora,
_session_kwargs stubs to DecodeWorkerHandler for sglang V4 (DeepSeek-V4)
disaggregated serving on H20 GPUs.

Signed-off-by: David Lu <davilu@nvidia.com>
@davilu-nvidia
davilu-nvidia requested review from a team as code owners April 24, 2026 14:18
@copy-pr-bot

copy-pr-bot Bot commented Apr 24, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Hi davilu-nvidia! Thank you for contributing to ai-dynamo/dynamo.

Just a reminder: The NVIDIA Test Github Validation CI runs an essential subset of the testing framework to quickly catch errors.Your PR reviewers may elect to test the changes comprehensively before approving your changes.

🚀

@github-actions github-actions Bot added feat external-contribution Pull request is from an external contributor backend::sglang Relates to the sglang backend labels Apr 24, 2026
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The DecodeWorkerHandler class in the decode handler module is modified to hard-disable use_sglang_tokenizer, introduce three new placeholder static methods for guided decoding parameters, LoRA resolution, and session kwargs configuration, and remove forwarding of the return_routed_experts argument in engine generation calls.

Changes

Cohort / File(s) Summary
Decode Handler Updates
components/src/dynamo/sglang/request_handlers/llm/decode_handler.py
Hard-disables use_sglang_tokenizer in constructor; adds three static placeholder methods (_get_guided_decoding_params, _resolve_lora, _session_kwargs) returning empty dicts; removes return_routed_experts argument forwarding from engine.async_generate calls in both disaggregated and aggregated generation paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding sglang V4 compatibility (specifically for DeepSeek-V4) to the decode handler, which is the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description comprehensively covers all required template sections including overview, detailed changes, specific files for review, and related issues.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/src/dynamo/sglang/request_handlers/llm/decode_handler.py (1)

324-339: ⚠️ Potential issue | 🟡 Minor

Either restore return_routed_experts or delete the dead plumbing.

Commenting out the kwarg makes enable_return_routed_experts a no-op, and Ruff already reports F841 on the now-unused local. If routed-expert payloads are still expected downstream, forward the flag in both async_generate calls; otherwise remove the assignment and the commented-out lines entirely.

🧹 Suggested fix
-        return_routed_experts = getattr(
-            self.config.server_args, "enable_return_routed_experts", False
-        )
...
-                # return_routed_experts=return_routed_experts,

Also applies to: 360-374

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/src/dynamo/sglang/request_handlers/llm/decode_handler.py` around
lines 324 - 339, The commented-out return_routed_experts kwarg in the
async_generate call makes enable_return_routed_experts a no-op and leaves an
unused local; either re-enable forwarding of the flag by adding
return_routed_experts=return_routed_experts to both async_generate calls (the
one around decode and the other at lines ~360-374) so routed-expert payloads
propagate, or remove the enable_return_routed_experts/local
return_routed_experts assignment and the commented-out kwarg lines entirely (and
any downstream handling that expects routed experts) to eliminate dead plumbing
and the F841 unused-variable warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/src/dynamo/sglang/request_handlers/llm/decode_handler.py`:
- Around line 69-70: The handler currently overwrites self.use_sglang_tokenizer
= False after calling BaseWorkerHandler.__init__(), which breaks the
config-driven request-parsing state; update DecodeHandler so the tokenizer flag
is set consistently by either assigning use_sglang_tokenizer from the config
before calling BaseWorkerHandler.__init__() or, if it must be forced, rebuild
any dependent state (the request-parsing state initialized in
BaseWorkerHandler.__init__) immediately after setting it; reference the
DecodeHandler.__init__ method and BaseWorkerHandler.__init__ and ensure no
post-init hard-coded assignment leaves the handler inconsistent.
- Around line 78-91: The three static method stubs _get_guided_decoding_params,
_resolve_lora, and _session_kwargs in decode_handler.py override parent
implementations and return incorrect types/lose access to instance methods
(e.g., self.lora_id_for_name) causing LoRA resolution and guided decoding to
break; remove these three method definitions so the class will inherit the
correct implementations from LoraMixin and BaseGenerativeHandler, restoring
proper Optional[str] return behavior for _resolve_lora and the routing/guided
decoding logic for _session_kwargs and _get_guided_decoding_params.

---

Outside diff comments:
In `@components/src/dynamo/sglang/request_handlers/llm/decode_handler.py`:
- Around line 324-339: The commented-out return_routed_experts kwarg in the
async_generate call makes enable_return_routed_experts a no-op and leaves an
unused local; either re-enable forwarding of the flag by adding
return_routed_experts=return_routed_experts to both async_generate calls (the
one around decode and the other at lines ~360-374) so routed-expert payloads
propagate, or remove the enable_return_routed_experts/local
return_routed_experts assignment and the commented-out kwarg lines entirely (and
any downstream handling that expects routed experts) to eliminate dead plumbing
and the F841 unused-variable warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c1313256-84ea-4b3e-9b9b-3a75735bc42c

📥 Commits

Reviewing files that changed from the base of the PR and between 50ce49b and 4068057.

📒 Files selected for processing (1)
  • components/src/dynamo/sglang/request_handlers/llm/decode_handler.py

Comment on lines +69 to +70
# H20 patch: set use_sglang_tokenizer from config
self.use_sglang_tokenizer = False # disagg mode

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't hard-code use_sglang_tokenizer after super().__init__().

BaseWorkerHandler.__init__() already used the config value to build request-parsing state, so forcing this field to False here makes the handler internally inconsistent and bypasses the new flag for aggregated serving. If decode mode truly needs a fixed tokenizer setting, apply it before super().__init__() or rebuild the dependent state afterward.

🛠️ Suggested fix
-        # H20 patch: set use_sglang_tokenizer from config
-        self.use_sglang_tokenizer = False  # disagg mode
+        # Preserve the configured tokenizer mode; the base class already
+        # initialized its dependent state from this value.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# H20 patch: set use_sglang_tokenizer from config
self.use_sglang_tokenizer = False # disagg mode
# Preserve the configured tokenizer mode; the base class already
# initialized its dependent state from this value.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/src/dynamo/sglang/request_handlers/llm/decode_handler.py` around
lines 69 - 70, The handler currently overwrites self.use_sglang_tokenizer =
False after calling BaseWorkerHandler.__init__(), which breaks the config-driven
request-parsing state; update DecodeHandler so the tokenizer flag is set
consistently by either assigning use_sglang_tokenizer from the config before
calling BaseWorkerHandler.__init__() or, if it must be forced, rebuild any
dependent state (the request-parsing state initialized in
BaseWorkerHandler.__init__) immediately after setting it; reference the
DecodeHandler.__init__ method and BaseWorkerHandler.__init__ and ensure no
post-init hard-coded assignment leaves the handler inconsistent.

Comment on lines +78 to +91
@staticmethod
def _get_guided_decoding_params(guided_decoding=None):
"""Stub for guided decoding - not needed for E2E test."""
return {}

@staticmethod
def _resolve_lora(request):
"""Stub for LoRA resolution."""
return {}

@staticmethod
def _session_kwargs(request):
"""Stub for session kwargs."""
return {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n components/src/dynamo/sglang/request_handlers/llm/decode_handler.py | head -120

Repository: ai-dynamo/dynamo

Length of output: 4903


🏁 Script executed:

# Check if there's a parent class definition in the file
rg "class.*\(" components/src/dynamo/sglang/request_handlers/llm/decode_handler.py -A 3

Repository: ai-dynamo/dynamo

Length of output: 214


🏁 Script executed:

# Look for imports to understand inheritance
rg "^from|^import" components/src/dynamo/sglang/request_handlers/llm/decode_handler.py | head -20

Repository: ai-dynamo/dynamo

Length of output: 589


🏁 Script executed:

# Find and examine the parent class BaseWorkerHandler
find . -name "handler_base.py" -path "*/sglang/*" | head -5

Repository: ai-dynamo/dynamo

Length of output: 124


🏁 Script executed:

# Search for the parent class and its method implementations
rg "def _get_guided_decoding_params|def _resolve_lora|def _session_kwargs" --type py -B 2 -A 5

Repository: ai-dynamo/dynamo

Length of output: 4948


🏁 Script executed:

# Search for calls to these stub methods to understand expected contract
rg "_get_guided_decoding_params|_resolve_lora|_session_kwargs" --type py -B 2 -A 2

Repository: ai-dynamo/dynamo

Length of output: 8601


🏁 Script executed:

# Get more context from the base class implementation
sed -n '1,250p' components/src/dynamo/sglang/request_handlers/handler_base.py | grep -A 20 "_resolve_lora"

Repository: ai-dynamo/dynamo

Length of output: 42


🏁 Script executed:

# Check the full implementations in base class
sed -n '1,400p' components/src/dynamo/sglang/request_handlers/handler_base.py | grep -A 15 "def _session_kwargs"

Repository: ai-dynamo/dynamo

Length of output: 42


🏁 Script executed:

# Check the full implementation of _get_guided_decoding_params in base
sed -n '1,400p' components/src/dynamo/sglang/request_handlers/handler_base.py | grep -A 15 "def _get_guided_decoding_params"

Repository: ai-dynamo/dynamo

Length of output: 42


🏁 Script executed:

# Get the size and read the handler_base.py file
wc -l components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 127


🏁 Script executed:

# Read the handler_base.py file to find the method implementations
cat components/src/dynamo/sglang/request_handlers/handler_base.py | head -500

Repository: ai-dynamo/dynamo

Length of output: 18776


🏁 Script executed:

# Search for the implementations of these methods in handler_base.py
grep -n "def _resolve_lora\|def _session_kwargs\|def _get_guided_decoding_params" components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 256


🏁 Script executed:

# Get the implementation of _resolve_lora method starting from the line
sed -n '780,810p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 1122


🏁 Script executed:

# Get the implementation of _session_kwargs method
sed -n '810,850p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 1647


🏁 Script executed:

# Get the implementation of _get_guided_decoding_params method
sed -n '850,900p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 2241


🏁 Script executed:

# Get the actual implementations starting from the correct line numbers
sed -n '302,320p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 759


🏁 Script executed:

# Get _session_kwargs implementation
sed -n '1044,1070p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 1334


🏁 Script executed:

# Get _get_guided_decoding_params implementation
sed -n '1057,1080p' components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 1036


🏁 Script executed:

# Verify the calls to these methods in DecodeWorkerHandler and check if they're used correctly
sed -n '78,300p' components/src/dynamo/sglang/request_handlers/llm/decode_handler.py | grep -A 5 "_resolve_lora\|_session_kwargs\|_get_guided_decoding_params"

Repository: ai-dynamo/dynamo

Length of output: 1061


🏁 Script executed:

# Check return type annotations and what lora_path is used for
grep -n "lora_path" components/src/dynamo/sglang/request_handlers/llm/decode_handler.py | head -20

Repository: ai-dynamo/dynamo

Length of output: 312


🏁 Script executed:

# Check if DecodeWorkerHandler uses LoraMixin
grep -n "class DecodeWorkerHandler\|LoraMixin\|_init_lora_tracking" components/src/dynamo/sglang/request_handlers/llm/decode_handler.py

Repository: ai-dynamo/dynamo

Length of output: 109


🏁 Script executed:

# Check BaseWorkerHandler class definition and its mixins
sed -n '1100,1150p' components/src/dynamo/sglang/request_handlers/handler_base.py | grep -A 20 "class BaseWorkerHandler"

Repository: ai-dynamo/dynamo

Length of output: 42


🏁 Script executed:

# Search for BaseWorkerHandler class definition in the full file
grep -n "^class BaseWorkerHandler" components/src/dynamo/sglang/request_handlers/handler_base.py

Repository: ai-dynamo/dynamo

Length of output: 153


Remove these method stubs—they return incorrect types and break LoRA resolution.

The stubs override working parent methods from LoraMixin and BaseGenerativeHandler. Specifically:

  • _resolve_lora() returns {} instead of Optional[str], causing if lora_path: to evaluate as truthy even for empty dicts; it also cannot access self.lora_id_for_name because it is @staticmethod.
  • _session_kwargs() and _get_guided_decoding_params() drop routing and guided decoding logic.

Delete these three methods to inherit the parent implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/src/dynamo/sglang/request_handlers/llm/decode_handler.py` around
lines 78 - 91, The three static method stubs _get_guided_decoding_params,
_resolve_lora, and _session_kwargs in decode_handler.py override parent
implementations and return incorrect types/lose access to instance methods
(e.g., self.lora_id_for_name) causing LoRA resolution and guided decoding to
break; remove these three method definitions so the class will inherit the
correct implementations from LoraMixin and BaseGenerativeHandler, restoring
proper Optional[str] return behavior for _resolve_lora and the routing/guided
decoding logic for _session_kwargs and _get_guided_decoding_params.

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions Bot added the Stale label May 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed due to inactivity. If you believe this PR is still relevant, please feel free to reopen it with additional context or information.

@github-actions github-actions Bot closed this May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend::sglang Relates to the sglang backend external-contribution Pull request is from an external contributor feat size/S Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant