Skip to content

[LLM] Fix nested dict to Namespace conversion in vLLM engine initialization#60380

Merged
edoakes merged 3 commits intoray-project:masterfrom
MiXaiLL76:namespace-bug-fix
Jan 22, 2026
Merged

[LLM] Fix nested dict to Namespace conversion in vLLM engine initialization#60380
edoakes merged 3 commits intoray-project:masterfrom
MiXaiLL76:namespace-bug-fix

Conversation

@MiXaiLL76
Copy link
Contributor

Summary

This PR fixes a bug in the vLLM engine initialization where nested dictionaries in engine arguments were not being converted to argparse.Namespace objects, causing AttributeError when accessing nested configuration attributes.

Problem

When merging vllm_frontend_args and vllm_engine_args, nested dictionary values remained as dict objects instead of being converted to Namespace objects. This caused issues when vLLM's initialization code attempted to access nested attributes using dot notation.

Example failing configuration:
When deploying a RayService with nested structured_outputs_config, the service would fail to initialize:

apiVersion: ray.io/v1
kind: RayService
metadata:
  name: qwen-thinking-service
spec:
  serveConfigV2: |
    applications:
      - name: llms
        import_path: ray.serve.llm:build_openai_app
        route_prefix: "/"
        args:
          llm_configs:
            - model_loading_config:
                model_id: qwen3-4b-thinking
                model_source: /models/Qwen3-4B
              engine_kwargs:
                tensor_parallel_size: 2
                trust_remote_code: true
                gpu_memory_utilization: 0.9
                dtype: auto
                max_model_len: 4096
                structured_outputs_config:  # This nested dict caused the issue
                  backend: xgrammar
                  reasoning_parser: qwen3
              deployment_config:
                autoscaling_config:
                  min_replicas: 1
                  max_replicas: 1
                  target_ongoing_requests: 32
                max_ongoing_requests: 64

The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

Solution

  • Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  • Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.init()

Changes

  • Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  • Added recursive conversion logic in VLLMEngine.init() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

Testing

  • Tested with Kubernetes RayService deployment using nested structured_outputs_config
  • Verified that the vLLM engine initializes correctly with nested configuration parameters

@MiXaiLL76 MiXaiLL76 requested a review from a team as a code owner January 21, 2026 20:27
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses a bug where nested dictionaries in vLLM engine arguments were not being converted to argparse.Namespace objects. The introduction of the _dict_to_namespace helper function and its application during engine initialization is a good solution.

My review includes a couple of suggestions to improve code clarity and conciseness:

  1. Adding type hints to the new helper function and translating its docstring to English for consistency.
  2. Refactoring the logic that applies the conversion to be more direct and Pythonic.

Additionally, I noticed some comments in Russian. It would be great to translate them to English to maintain consistency across the codebase.

Overall, the changes are solid and address the issue effectively. Once the suggested improvements are considered, this PR should be good to merge.

MiXaiLL76 and others added 3 commits January 21, 2026 23:38
Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
@MiXaiLL76
Copy link
Contributor Author

@edoakes What do you think about this?

@ray-gardener ray-gardener bot added serve Ray Serve Related Issue llm community-contribution Contributed by the community labels Jan 22, 2026
@edoakes edoakes added the go add ONLY when ready to merge, run all tests label Jan 22, 2026
@edoakes
Copy link
Collaborator

edoakes commented Jan 22, 2026

Looks like a reasonable fix to me, but will defer to @kouroshHakha et al. because I'm not familiar with this code

Copy link
Contributor

@eicherseiji eicherseiji left a comment

Choose a reason for hiding this comment

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

Thanks @MiXaiLL76!

Verified the fix as follows:

"""Minimal reproducer for PR #60380"""
import argparse
from vllm.config.structured_outputs import StructuredOutputsConfig
from typing import Any


# PR #60380 fix: recursive dict→Namespace converter
def _dict_to_namespace(obj: Any) -> Any:
    """Recursively converts dictionaries to argparse.Namespace."""
    if isinstance(obj, dict):
        return argparse.Namespace(**{k: _dict_to_namespace(v) for k, v in obj.items()})
    elif isinstance(obj, list):
        return [_dict_to_namespace(item) for item in obj]
    else:
        return obj


engine_args = argparse.Namespace(
    model="facebook/opt-125m",
    structured_outputs_config={"backend": "xgrammar", "reasoning_parser": "qwen3"}
)
frontend_args = argparse.Namespace(host="0.0.0.0", port=8000)

# Original (broken)
# args = argparse.Namespace(**(frontend_args.__dict__ | engine_args.__dict__))

# PR #60380 fix (uncomment to test)
args = _dict_to_namespace(frontend_args.__dict__ | engine_args.__dict__)

args.structured_outputs_config.backend  # AttributeError: 'dict' object has no attribute 'backend'

I will follow up with a regression test

eicherseiji added a commit to eicherseiji/ray that referenced this pull request Jan 22, 2026
Add a GPU test that verifies vLLM engine can start with nested dicts
in engine_kwargs (e.g., structured_outputs_config). This test will fail
without the fix from PR ray-project#60380 with:

    AttributeError: 'dict' object has no attribute 'backend'

The test ensures that nested dicts are properly converted to
argparse.Namespace objects so vLLM's init_app_state() can access
nested attributes via dot notation.

See: ray-project#60380
Signed-off-by: Seiji Eicher <seiji@anyscale.com>
@edoakes edoakes enabled auto-merge (squash) January 22, 2026 23:24
@edoakes edoakes merged commit 1fad97f into ray-project:master Jan 22, 2026
8 checks passed
xinyuangui2 pushed a commit to xinyuangui2/ray that referenced this pull request Jan 26, 2026
…zation (ray-project#60380)

## Summary
This PR fixes a bug in the vLLM engine initialization where nested
dictionaries in engine arguments were not being converted to
`argparse.Namespace` objects, causing `AttributeError` when accessing
nested configuration attributes.

  ## Problem
When merging `vllm_frontend_args` and `vllm_engine_args`, nested
dictionary values remained as `dict` objects instead of being converted
to `Namespace` objects. This caused issues when vLLM's initialization
code attempted to access nested attributes using dot notation.

  **Example failing configuration:**
When deploying a RayService with nested `structured_outputs_config`, the
service would fail to initialize:

  ```yaml
  apiVersion: ray.io/v1
  kind: RayService
  metadata:
    name: qwen-thinking-service
  spec:
    serveConfigV2: |
      applications:
        - name: llms
          import_path: ray.serve.llm:build_openai_app
          route_prefix: "/"
          args:
            llm_configs:
              - model_loading_config:
                  model_id: qwen3-4b-thinking
                  model_source: /models/Qwen3-4B
                engine_kwargs:
                  tensor_parallel_size: 2
                  trust_remote_code: true
                  gpu_memory_utilization: 0.9
                  dtype: auto
                  max_model_len: 4096
structured_outputs_config: # This nested dict caused the issue
                    backend: xgrammar
                    reasoning_parser: qwen3
                deployment_config:
                  autoscaling_config:
                    min_replicas: 1
                    max_replicas: 1
                    target_ongoing_requests: 32
                  max_ongoing_requests: 64
```
  The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

  Solution

  - Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  - Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.__init__()

  Changes

  - Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  - Added recursive conversion logic in VLLMEngine.__init__() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

  Testing

  - Tested with Kubernetes RayService deployment using nested structured_outputs_config
  - Verified that the vLLM engine initializes correctly with nested configuration parameters

---------

Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
jinbum-kim pushed a commit to jinbum-kim/ray that referenced this pull request Jan 29, 2026
…zation (ray-project#60380)

## Summary
This PR fixes a bug in the vLLM engine initialization where nested
dictionaries in engine arguments were not being converted to
`argparse.Namespace` objects, causing `AttributeError` when accessing
nested configuration attributes.

  ## Problem
When merging `vllm_frontend_args` and `vllm_engine_args`, nested
dictionary values remained as `dict` objects instead of being converted
to `Namespace` objects. This caused issues when vLLM's initialization
code attempted to access nested attributes using dot notation.

  **Example failing configuration:**
When deploying a RayService with nested `structured_outputs_config`, the
service would fail to initialize:

  ```yaml
  apiVersion: ray.io/v1
  kind: RayService
  metadata:
    name: qwen-thinking-service
  spec:
    serveConfigV2: |
      applications:
        - name: llms
          import_path: ray.serve.llm:build_openai_app
          route_prefix: "/"
          args:
            llm_configs:
              - model_loading_config:
                  model_id: qwen3-4b-thinking
                  model_source: /models/Qwen3-4B
                engine_kwargs:
                  tensor_parallel_size: 2
                  trust_remote_code: true
                  gpu_memory_utilization: 0.9
                  dtype: auto
                  max_model_len: 4096
structured_outputs_config: # This nested dict caused the issue
                    backend: xgrammar
                    reasoning_parser: qwen3
                deployment_config:
                  autoscaling_config:
                    min_replicas: 1
                    max_replicas: 1
                    target_ongoing_requests: 32
                  max_ongoing_requests: 64
```
  The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

  Solution

  - Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  - Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.__init__()

  Changes

  - Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  - Added recursive conversion logic in VLLMEngine.__init__() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

  Testing

  - Tested with Kubernetes RayService deployment using nested structured_outputs_config
  - Verified that the vLLM engine initializes correctly with nested configuration parameters

---------

Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: jinbum-kim <jinbum9958@gmail.com>
400Ping pushed a commit to 400Ping/ray that referenced this pull request Feb 1, 2026
…zation (ray-project#60380)

## Summary
This PR fixes a bug in the vLLM engine initialization where nested
dictionaries in engine arguments were not being converted to
`argparse.Namespace` objects, causing `AttributeError` when accessing
nested configuration attributes.

  ## Problem
When merging `vllm_frontend_args` and `vllm_engine_args`, nested
dictionary values remained as `dict` objects instead of being converted
to `Namespace` objects. This caused issues when vLLM's initialization
code attempted to access nested attributes using dot notation.

  **Example failing configuration:**
When deploying a RayService with nested `structured_outputs_config`, the
service would fail to initialize:

  ```yaml
  apiVersion: ray.io/v1
  kind: RayService
  metadata:
    name: qwen-thinking-service
  spec:
    serveConfigV2: |
      applications:
        - name: llms
          import_path: ray.serve.llm:build_openai_app
          route_prefix: "/"
          args:
            llm_configs:
              - model_loading_config:
                  model_id: qwen3-4b-thinking
                  model_source: /models/Qwen3-4B
                engine_kwargs:
                  tensor_parallel_size: 2
                  trust_remote_code: true
                  gpu_memory_utilization: 0.9
                  dtype: auto
                  max_model_len: 4096
structured_outputs_config: # This nested dict caused the issue
                    backend: xgrammar
                    reasoning_parser: qwen3
                deployment_config:
                  autoscaling_config:
                    min_replicas: 1
                    max_replicas: 1
                    target_ongoing_requests: 32
                  max_ongoing_requests: 64
```
  The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

  Solution

  - Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  - Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.__init__()

  Changes

  - Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  - Added recursive conversion logic in VLLMEngine.__init__() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

  Testing

  - Tested with Kubernetes RayService deployment using nested structured_outputs_config
  - Verified that the vLLM engine initializes correctly with nested configuration parameters

---------

Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 400Ping <jiekaichang@apache.org>
peterxcli pushed a commit to peterxcli/ray that referenced this pull request Feb 25, 2026
…zation (ray-project#60380)

## Summary
This PR fixes a bug in the vLLM engine initialization where nested
dictionaries in engine arguments were not being converted to
`argparse.Namespace` objects, causing `AttributeError` when accessing
nested configuration attributes.

  ## Problem
When merging `vllm_frontend_args` and `vllm_engine_args`, nested
dictionary values remained as `dict` objects instead of being converted
to `Namespace` objects. This caused issues when vLLM's initialization
code attempted to access nested attributes using dot notation.

  **Example failing configuration:**
When deploying a RayService with nested `structured_outputs_config`, the
service would fail to initialize:

  ```yaml
  apiVersion: ray.io/v1
  kind: RayService
  metadata:
    name: qwen-thinking-service
  spec:
    serveConfigV2: |
      applications:
        - name: llms
          import_path: ray.serve.llm:build_openai_app
          route_prefix: "/"
          args:
            llm_configs:
              - model_loading_config:
                  model_id: qwen3-4b-thinking
                  model_source: /models/Qwen3-4B
                engine_kwargs:
                  tensor_parallel_size: 2
                  trust_remote_code: true
                  gpu_memory_utilization: 0.9
                  dtype: auto
                  max_model_len: 4096
structured_outputs_config: # This nested dict caused the issue
                    backend: xgrammar
                    reasoning_parser: qwen3
                deployment_config:
                  autoscaling_config:
                    min_replicas: 1
                    max_replicas: 1
                    target_ongoing_requests: 32
                  max_ongoing_requests: 64
```
  The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

  Solution

  - Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  - Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.__init__()

  Changes

  - Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  - Added recursive conversion logic in VLLMEngine.__init__() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

  Testing

  - Tested with Kubernetes RayService deployment using nested structured_outputs_config
  - Verified that the vLLM engine initializes correctly with nested configuration parameters

---------

Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
peterxcli pushed a commit to peterxcli/ray that referenced this pull request Feb 25, 2026
…zation (ray-project#60380)

## Summary
This PR fixes a bug in the vLLM engine initialization where nested
dictionaries in engine arguments were not being converted to
`argparse.Namespace` objects, causing `AttributeError` when accessing
nested configuration attributes.

  ## Problem
When merging `vllm_frontend_args` and `vllm_engine_args`, nested
dictionary values remained as `dict` objects instead of being converted
to `Namespace` objects. This caused issues when vLLM's initialization
code attempted to access nested attributes using dot notation.

  **Example failing configuration:**
When deploying a RayService with nested `structured_outputs_config`, the
service would fail to initialize:

  ```yaml
  apiVersion: ray.io/v1
  kind: RayService
  metadata:
    name: qwen-thinking-service
  spec:
    serveConfigV2: |
      applications:
        - name: llms
          import_path: ray.serve.llm:build_openai_app
          route_prefix: "/"
          args:
            llm_configs:
              - model_loading_config:
                  model_id: qwen3-4b-thinking
                  model_source: /models/Qwen3-4B
                engine_kwargs:
                  tensor_parallel_size: 2
                  trust_remote_code: true
                  gpu_memory_utilization: 0.9
                  dtype: auto
                  max_model_len: 4096
structured_outputs_config: # This nested dict caused the issue
                    backend: xgrammar
                    reasoning_parser: qwen3
                deployment_config:
                  autoscaling_config:
                    min_replicas: 1
                    max_replicas: 1
                    target_ongoing_requests: 32
                  max_ongoing_requests: 64
```
  The error occurred because structured_outputs_config was passed as a dict, but vLLM expected it as a Namespace object to access attributes like args.structured_outputs_config.backend.

  Solution

  - Added _dict_to_namespace() helper function that recursively converts dictionaries and lists of dictionaries to argparse.Namespace objects
  - Applied this conversion to all nested dictionary values in the merged args Namespace after initialization in VLLMEngine.__init__()

  Changes

  - Added _dict_to_namespace() function at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:65
  - Added recursive conversion logic in VLLMEngine.__init__() at python/ray/llm/_internal/serve/engines/vllm/vllm_engine.py:273-276

  Testing

  - Tested with Kubernetes RayService deployment using nested structured_outputs_config
  - Verified that the vLLM engine initializes correctly with nested configuration parameters

---------

Signed-off-by: MiXaiLL76 <mike.milos@yandex.ru>
Signed-off-by: Михаил <mike.milos@yandex.ru>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community go add ONLY when ready to merge, run all tests llm serve Ray Serve Related Issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants