Fix parse args from file(#13911) (#14085)

Co-authored-by: ronnie_zheng <zl19940307@163.com>
Co-authored-by: Liangsheng Yin <lsyincs@gmail.com>
This commit is contained in:
1874.
2026-01-01 11:37:33 +08:00
committed by GitHub
co-authored by ronnie_zheng Liangsheng Yin
parent 70a769bc56
commit e0e5084802
3 changed files with 67 additions and 46 deletions
+6 -18
View File
@@ -68,7 +68,6 @@ from sglang.utils import is_in_ci
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Define constants # Define constants
SAMPLING_BACKEND_CHOICES = {"flashinfer", "pytorch", "ascend"} SAMPLING_BACKEND_CHOICES = {"flashinfer", "pytorch", "ascend"}
LOAD_FORMAT_CHOICES = [ LOAD_FORMAT_CHOICES = [
@@ -4999,30 +4998,19 @@ def prepare_server_args(argv: List[str]) -> ServerArgs:
Returns: Returns:
The server arguments. The server arguments.
""" """
# Import here to avoid circular imports parser = argparse.ArgumentParser()
from sglang.srt.server_args_config_parser import ConfigArgumentMerger ServerArgs.add_cli_args(parser)
# Check for config file and merge arguments if present # Check for config file and merge arguments if present
if "--config" in argv: if "--config" in argv:
# Import here to avoid circular imports
from sglang.srt.server_args_config_parser import ConfigArgumentMerger
# Extract boolean actions from the parser to handle them correctly # Extract boolean actions from the parser to handle them correctly
parser = argparse.ArgumentParser() config_merger = ConfigArgumentMerger(parser)
ServerArgs.add_cli_args(parser)
# Get boolean action destinations
boolean_actions = []
for action in parser._actions:
if hasattr(action, "dest") and hasattr(action, "action"):
if action.action in ["store_true", "store_false"]:
boolean_actions.append(action.dest)
# Merge config file arguments with CLI arguments
config_merger = ConfigArgumentMerger(boolean_actions=boolean_actions)
argv = config_merger.merge_config_with_args(argv) argv = config_merger.merge_config_with_args(argv)
parser = argparse.ArgumentParser()
ServerArgs.add_cli_args(parser)
raw_args = parser.parse_args(argv) raw_args = parser.parse_args(argv)
return ServerArgs.from_cli_args(raw_args) return ServerArgs.from_cli_args(raw_args)
+52 -26
View File
@@ -3,6 +3,7 @@ Configuration argument parser for command-line applications.
Handles merging of YAML configuration files with command-line arguments. Handles merging of YAML configuration files with command-line arguments.
""" """
import argparse
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List
@@ -15,9 +16,25 @@ logger = logging.getLogger(__name__)
class ConfigArgumentMerger: class ConfigArgumentMerger:
"""Handles merging of configuration file arguments with command-line arguments.""" """Handles merging of configuration file arguments with command-line arguments."""
def __init__(self, boolean_actions: List[str] = None): def __init__(self, parser: argparse.ArgumentParser):
"""Initialize with list of boolean action destinations.""" """Initialize with list of store_true action names."""
self.boolean_actions = boolean_actions or [] # NOTE: The current code does not support actions other than "store_true" and "store".
self.parser = parser
self.store_true_actions = [
action.dest
for action in parser._actions
if isinstance(action, argparse._StoreTrueAction)
]
self.unsupported_actions = {
a.dest: a
for a in parser._actions
if a.option_strings
and not isinstance(a, argparse._StoreTrueAction)
and not isinstance(a, argparse._StoreAction)
and "--config" not in a.option_strings
and "--help" not in a.option_strings
and "-h" not in a.option_strings
}
def merge_config_with_args(self, cli_args: List[str]) -> List[str]: def merge_config_with_args(self, cli_args: List[str]) -> List[str]:
""" """
@@ -39,8 +56,18 @@ class ConfigArgumentMerger:
if not config_file_path: if not config_file_path:
return cli_args return cli_args
config_args = self._parse_yaml_config(config_file_path) config_data = self._parse_yaml_config(config_file_path)
return self._insert_config_args(cli_args, config_args, config_file_path) config_args = self._convert_config_to_args(config_data)
# Merge config args into CLI args
config_index = cli_args.index("--config")
# Split arguments around config file
before_config = cli_args[:config_index]
after_config = cli_args[config_index + 2 :] # Skip --config and file path
# Simple merge: config args + CLI args
return config_args + before_config + after_config
def _extract_config_file_path(self, args: List[str]) -> str: def _extract_config_file_path(self, args: List[str]) -> str:
"""Extract the config file path from arguments.""" """Extract the config file path from arguments."""
@@ -58,20 +85,7 @@ class ConfigArgumentMerger:
return args[config_index + 1] return args[config_index + 1]
def _insert_config_args( def _parse_yaml_config(self, file_path: str) -> Dict[str, Any]:
self, cli_args: List[str], config_args: List[str], config_file_path: str
) -> List[str]:
"""Insert configuration arguments into the CLI argument list."""
config_index = cli_args.index("--config")
# Split arguments around config file
before_config = cli_args[:config_index]
after_config = cli_args[config_index + 2 :] # Skip --config and file path
# Simple merge: config args + CLI args
return config_args + before_config + after_config
def _parse_yaml_config(self, file_path: str) -> List[str]:
""" """
Parse YAML configuration file and convert to argument list. Parse YAML configuration file and convert to argument list.
@@ -100,7 +114,7 @@ class ConfigArgumentMerger:
if not isinstance(config_data, dict): if not isinstance(config_data, dict):
raise ValueError("Config file must contain a dictionary at root level") raise ValueError("Config file must contain a dictionary at root level")
return self._convert_config_to_args(config_data) return config_data
def _validate_yaml_file(self, file_path: str) -> None: def _validate_yaml_file(self, file_path: str) -> None:
"""Validate that the file is a YAML file.""" """Validate that the file is a YAML file."""
@@ -116,6 +130,11 @@ class ConfigArgumentMerger:
args = [] args = []
for key, value in config.items(): for key, value in config.items():
key_norm = key.replace("-", "_")
if key_norm in self.unsupported_actions:
action = self.unsupported_actions[key_norm]
msg = f"Unsupported config option '{key_norm}' with action '{action.__class__.__name__}'"
raise ValueError(msg)
if isinstance(value, bool): if isinstance(value, bool):
self._add_boolean_arg(args, key, value) self._add_boolean_arg(args, key, value)
elif isinstance(value, list): elif isinstance(value, list):
@@ -126,14 +145,21 @@ class ConfigArgumentMerger:
return args return args
def _add_boolean_arg(self, args: List[str], key: str, value: bool) -> None: def _add_boolean_arg(self, args: List[str], key: str, value: bool) -> None:
"""Add boolean argument to the list.""" """
if key in self.boolean_actions: Add boolean argument to the list.
# For boolean actions, always add the flag and value
args.extend([f"--{key}", str(value).lower()]) Only store_true flags:
else: - value True -> add flag
# For regular booleans, only add flag if True - value False -> skip
Regular booleans:
- always add --key true/false
"""
key_norm = key.replace("-", "_")
if key_norm in self.store_true_actions:
if value: if value:
args.append(f"--{key}") args.append(f"--{key}")
else:
args.extend([f"--{key}", str(value).lower()])
def _add_list_arg(self, args: List[str], key: str, value: List[Any]) -> None: def _add_list_arg(self, args: List[str], key: str, value: List[Any]) -> None:
"""Add list argument to the list.""" """Add list argument to the list."""
+9 -2
View File
@@ -2,20 +2,23 @@
Test script to verify SGLang config file integration. Test script to verify SGLang config file integration.
""" """
import argparse
import os import os
import tempfile import tempfile
import pytest import pytest
import yaml import yaml
from sglang.srt.server_args import prepare_server_args from sglang.srt.server_args import ServerArgs, prepare_server_args
from sglang.srt.server_args_config_parser import ConfigArgumentMerger from sglang.srt.server_args_config_parser import ConfigArgumentMerger
@pytest.fixture @pytest.fixture
def merger(): def merger():
"""Fixture providing a ConfigArgumentMerger instance.""" """Fixture providing a ConfigArgumentMerger instance."""
return ConfigArgumentMerger() parser = argparse.ArgumentParser()
ServerArgs.add_cli_args(parser)
return ConfigArgumentMerger(parser)
def test_server_args_config_parser(merger): def test_server_args_config_parser(merger):
@@ -156,3 +159,7 @@ def test_error_handling():
prepare_server_args(argv) prepare_server_args(argv)
finally: finally:
os.unlink(invalid_yaml_file) os.unlink(invalid_yaml_file)
if __name__ == "__main__":
pytest.main([__file__])