[Config] msgspec.Struct for the config tier (#38753)
This commit is contained in:
@@ -17,7 +17,7 @@ register_cpu_ci(est_time=10, suite="base-b-test-cpu-arm64")
|
||||
|
||||
class TestServerArgsCPUBackend(CustomTestCase):
|
||||
def _make_server_args(self, attention_backend=None):
|
||||
server_args = ServerArgs.__new__(ServerArgs)
|
||||
server_args = ServerArgs(model_path="dummy")
|
||||
server_args.device = "cpu"
|
||||
server_args.attention_backend = attention_backend
|
||||
server_args.sampling_backend = None
|
||||
|
||||
@@ -21,6 +21,7 @@ import torch
|
||||
from transformers import AutoConfig, AutoTokenizer
|
||||
|
||||
from sglang.srt.entrypoints.engine import Engine
|
||||
from sglang.srt.server_args import _declared_default
|
||||
from sglang.test.ci.ci_register import register_cuda_ci
|
||||
from sglang.test.test_utils import (
|
||||
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
|
||||
@@ -56,9 +57,8 @@ class TestMISServerArgsValidation(unittest.TestCase):
|
||||
|
||||
def test_enable_mis_default(self):
|
||||
"""Test that enable_mis defaults to False."""
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
|
||||
self.assertEqual(ServerArgs.enable_mis, False)
|
||||
self.assertEqual(_declared_default("enable_mis"), False)
|
||||
|
||||
|
||||
class TestMultiItemScoringOptimization(CustomTestCase):
|
||||
|
||||
@@ -21,11 +21,13 @@ Current coverage:
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import json
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
from sglang.srt.arg_groups.validation_hook import check_load_publish_args
|
||||
from sglang.srt.entrypoints import http_server
|
||||
from sglang.srt.lora.lora_registry import LoRARef
|
||||
@@ -457,7 +459,7 @@ class TestServerInfoExistingFieldsPreserved(CustomTestCase):
|
||||
|
||||
info = _call_server_info_with(args)
|
||||
|
||||
for field in dataclasses.fields(ServerArgs):
|
||||
for field in msgspec.structs.fields(ServerArgs):
|
||||
self.assertIn(
|
||||
field.name,
|
||||
info,
|
||||
|
||||
@@ -10,6 +10,9 @@ import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang
|
||||
from sglang.srt.managers.tokenizer_manager import TokenizerManager
|
||||
from sglang.srt.runtime_context import get_context, publish, reset_context
|
||||
@@ -85,14 +88,15 @@ class TestTokenizerConfigUpdates(CustomTestCase):
|
||||
)
|
||||
|
||||
def test_the_dump_snapshot_identifies_the_running_checkpoint(self):
|
||||
import dataclasses
|
||||
|
||||
manager = _manager(self, load_format="auto")
|
||||
manager.model_path = "at-startup"
|
||||
manager.served_model_name = "at-startup"
|
||||
manager._update_model_path_info("after-reload", "dummy")
|
||||
|
||||
snapshot = manager.resolved_config_dict(dataclasses.asdict(manager.server_args))
|
||||
snapshot = manager.resolved_config_dict(
|
||||
msgspec.structs.asdict(manager.server_args)
|
||||
)
|
||||
self.assertEqual(snapshot["model_path"], "after-reload")
|
||||
self.assertEqual(snapshot["served_model_name"], "after-reload")
|
||||
self.assertEqual(snapshot["load_format"], "dummy")
|
||||
@@ -116,7 +120,6 @@ class TestTokenizerConfigUpdates(CustomTestCase):
|
||||
self.assertIsNone(manager._dump_config_snapshot())
|
||||
|
||||
def test_an_unpickleable_field_does_not_lose_the_dump(self):
|
||||
import dataclasses
|
||||
import pickle
|
||||
|
||||
# What --custom-sigquit-handler leaves on a real ServerArgs.
|
||||
@@ -128,7 +131,7 @@ class TestTokenizerConfigUpdates(CustomTestCase):
|
||||
"server_args": manager.server_args,
|
||||
"config_updates": get_context().overrides_log(),
|
||||
"resolved_config": manager.resolved_config_dict(
|
||||
dataclasses.asdict(manager.server_args)
|
||||
msgspec.structs.asdict(manager.server_args)
|
||||
),
|
||||
"requests": [],
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import unittest
|
||||
from types import ModuleType, SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import msgspec
|
||||
|
||||
from sglang.srt.parser.template_detection import (
|
||||
REASONING_PARSER_RULES,
|
||||
TOOL_CALL_PARSER_RULES,
|
||||
@@ -892,7 +894,9 @@ class TestResolveAutoParsers(unittest.TestCase):
|
||||
reasoning_parser="auto",
|
||||
tool_call_parser="auto",
|
||||
)
|
||||
object.__setattr__(args, "model_path", "nonexistent/model-does-not-exist-xyz")
|
||||
msgspec.Struct.__setattr__(
|
||||
args, "model_path", "nonexistent/model-does-not-exist-xyz"
|
||||
)
|
||||
with _patch_hf_transformers_utils(
|
||||
Mock(side_effect=RuntimeError("tokenizer unavailable")),
|
||||
Mock(side_effect=RuntimeError("config unavailable")),
|
||||
|
||||
@@ -31,6 +31,8 @@ the unified pool today.
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
import msgspec
|
||||
|
||||
from sglang.srt.arg_groups.kv_cache_hook import handle_page_major_kv_layout
|
||||
from sglang.srt.configs.model_config import AttentionArch
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
@@ -50,7 +52,7 @@ def _accepts(
|
||||
) -> bool:
|
||||
"""Run just `handle_page_major_kv_layout` against a minimal stand-in, since
|
||||
ServerArgs' real constructor pulls in a model config."""
|
||||
sa = ServerArgs.__new__(ServerArgs)
|
||||
sa = ServerArgs(model_path="dummy")
|
||||
for name, value in {
|
||||
"enable_unified_memory": unified,
|
||||
# The unified pool sets this itself; without it the flag must be explicit
|
||||
@@ -64,8 +66,8 @@ def _accepts(
|
||||
"linear_attn_prefill_backend": linear_prefill,
|
||||
"mamba_backend": "triton",
|
||||
}.items():
|
||||
object.__setattr__(sa, name, value)
|
||||
object.__setattr__(
|
||||
msgspec.Struct.__setattr__(sa, name, value)
|
||||
setattr(
|
||||
sa,
|
||||
"_model_config",
|
||||
SimpleNamespace(
|
||||
|
||||
@@ -8,13 +8,15 @@ value the caller still holds and the snapshot cannot see it.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
@@ -108,7 +110,7 @@ class TestRecordHoldsTheRawInput(CustomTestCase):
|
||||
|
||||
moved = {
|
||||
field.name: (raw[field.name], getattr(server_args, field.name))
|
||||
for field in dataclasses.fields(server_args)
|
||||
for field in msgspec.structs.fields(server_args)
|
||||
if _moved(getattr(server_args, field.name), raw[field.name])
|
||||
}
|
||||
self.assertEqual(
|
||||
|
||||
@@ -9,7 +9,6 @@ or is projected into the wrong namespace therefore fails on observed state.
|
||||
|
||||
import ast
|
||||
import copy
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
@@ -18,6 +17,9 @@ import tempfile
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang
|
||||
from sglang.srt.arg_groups.overrides import resolution_result
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
@@ -30,7 +32,7 @@ _SRT = pathlib.Path(sglang.__file__).resolve().parent / "srt"
|
||||
|
||||
# Every field of the record: resolution has no bare-assignment writer left, so
|
||||
# the scan states that as a whole rather than a converted-so-far list.
|
||||
_RESOLVED_FIELDS = frozenset(field.name for field in dataclasses.fields(ServerArgs))
|
||||
_RESOLVED_FIELDS = frozenset(field.name for field in msgspec.structs.fields(ServerArgs))
|
||||
|
||||
# Shapes the agreement check runs on. Each needs a real config.json:
|
||||
# `model_path="dummy"` takes the pipeline's early return.
|
||||
@@ -201,14 +203,14 @@ class TestResolutionDeclarations(CustomTestCase):
|
||||
supplied = {"random_seed": 42, **shape}
|
||||
server_args = self._resolve(shape)
|
||||
overlay = _stash_overlay(server_args)
|
||||
for field in dataclasses.fields(server_args):
|
||||
for field in msgspec.structs.fields(server_args):
|
||||
if field.name in ("model_path", "device") or field.name in overlay:
|
||||
continue
|
||||
if field.name in supplied:
|
||||
before = supplied[field.name]
|
||||
elif field.default is not dataclasses.MISSING:
|
||||
elif field.default is not msgspec.NODEFAULT:
|
||||
before = field.default
|
||||
elif field.default_factory is not dataclasses.MISSING:
|
||||
elif field.default_factory is not msgspec.NODEFAULT:
|
||||
before = field.default_factory()
|
||||
else:
|
||||
continue
|
||||
@@ -278,7 +280,7 @@ class TestResolutionDeclarations(CustomTestCase):
|
||||
dump = server_args.resolved_dict()
|
||||
self.assertEqual(
|
||||
sorted(dump),
|
||||
sorted(field.name for field in dataclasses.fields(server_args)),
|
||||
sorted(field.name for field in msgspec.structs.fields(server_args)),
|
||||
"the readback dump is no longer exactly the fields",
|
||||
)
|
||||
leaked = sorted(
|
||||
@@ -532,7 +534,7 @@ class TestResolutionDeclarations(CustomTestCase):
|
||||
overlay = _stash_overlay(server_args)
|
||||
raw_input = getattr(server_args, "_raw_input", None)
|
||||
self.assertTrue(raw_input, f"{shape}: the record kept no raw snapshot")
|
||||
for field in dataclasses.fields(server_args):
|
||||
for field in msgspec.structs.fields(server_args):
|
||||
name = field.name
|
||||
if name in overlay or name not in raw_input:
|
||||
continue
|
||||
|
||||
@@ -33,6 +33,8 @@ import tempfile
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
import torch
|
||||
|
||||
from sglang.srt.arg_groups.overrides import (
|
||||
@@ -270,7 +272,7 @@ class TestResolutionIsReproducible(_RestoresProcessState, CustomTestCase):
|
||||
the one of those that a shared mutable could corrupt.
|
||||
"""
|
||||
out = {}
|
||||
for field in dataclasses.fields(server_args):
|
||||
for field in msgspec.structs.fields(server_args):
|
||||
if field.name in _NOT_COMPARABLE:
|
||||
continue
|
||||
# The resolution result, not the field: a declaration-only resolver
|
||||
@@ -517,7 +519,7 @@ class TestALateDeclarationKeepsTheResolution(_RestoresProcessState, CustomTestCa
|
||||
record arrives resolved, and a copy would throw that away.
|
||||
"""
|
||||
parent = self._resolved()
|
||||
bare = dataclasses.replace(parent, dist_init_addr="1.2.3.4:5000")
|
||||
bare = msgspec.structs.replace(parent, dist_init_addr="1.2.3.4:5000")
|
||||
self.assertFalse(
|
||||
getattr(bare, "_resolution_finished", False),
|
||||
"a bare replace carried the flag; then this test proves nothing",
|
||||
@@ -528,7 +530,7 @@ class TestALateDeclarationKeepsTheResolution(_RestoresProcessState, CustomTestCa
|
||||
resolution_result(parent, field.name),
|
||||
resolution_result(bare, field.name),
|
||||
)
|
||||
for field in dataclasses.fields(parent)
|
||||
for field in msgspec.structs.fields(parent)
|
||||
if field.name not in ("dist_init_addr", "random_seed")
|
||||
and repr(resolution_result(parent, field.name))
|
||||
!= repr(resolution_result(bare, field.name))
|
||||
|
||||
@@ -22,10 +22,12 @@ the two scopes it can derive exactly.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import dataclasses
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
@@ -34,7 +36,7 @@ from sglang.test.test_utils import CustomTestCase
|
||||
register_cpu_ci(est_time=45, suite="base-a-test-cpu")
|
||||
|
||||
_SRT = pathlib.Path(sglang.__file__).resolve().parent / "srt"
|
||||
_FIELDS = frozenset(field.name for field in dataclasses.fields(ServerArgs))
|
||||
_FIELDS = frozenset(field.name for field in msgspec.structs.fields(ServerArgs))
|
||||
|
||||
# Names a config travels under. `args` is included because the platform hooks
|
||||
# use it; a false positive would be a function taking an argparse Namespace and
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import argparse
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
@@ -10,6 +9,9 @@ import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang.srt.server_args as server_args_module
|
||||
from sglang.srt.arg_groups import parallel_hook, pd_disaggregation_hook, serving_hook
|
||||
from sglang.srt.arg_groups.attention_hook import (
|
||||
@@ -267,7 +269,7 @@ class TestPrepareServerArgs(CustomTestCase):
|
||||
# And across the hop that matters: the scheduler and the draft worker
|
||||
# rebuild the record from its fields and resolve again, so the bit has
|
||||
# to survive `asdict` and come back the same the second time.
|
||||
reconstructed = ServerArgs(**dataclasses.asdict(inherited))
|
||||
reconstructed = ServerArgs(**msgspec.structs.asdict(inherited))
|
||||
handle_missing_default_values(reconstructed)
|
||||
|
||||
self.assertFalse(
|
||||
@@ -1046,7 +1048,9 @@ class TestContextParallelServerArgs(CustomTestCase):
|
||||
ServerArgs.add_cli_args(self.parser)
|
||||
|
||||
def _new_cp_args(self, **overrides):
|
||||
server_args = object.__new__(ServerArgs)
|
||||
# Constructed, not conjured: a Struct has no uninitialized form, and
|
||||
# every field this case does not name wants its declared default
|
||||
# anyway.
|
||||
defaults = dict(
|
||||
enable_prefill_cp=False,
|
||||
cp_strategy=None,
|
||||
@@ -1061,9 +1065,7 @@ class TestContextParallelServerArgs(CustomTestCase):
|
||||
enable_aiter_allreduce_fusion=False,
|
||||
)
|
||||
defaults.update(overrides)
|
||||
for key, value in defaults.items():
|
||||
setattr(server_args, key, value)
|
||||
return server_args
|
||||
return ServerArgs(**defaults)
|
||||
|
||||
def test_canonical_prefill_cp_requires_strategy(self):
|
||||
args = self.parser.parse_args(["--model", "dummy", "--enable-prefill-cp"])
|
||||
@@ -2170,7 +2172,7 @@ class TestDeepEPv2Args(CustomTestCase):
|
||||
prefill=PhaseConfig(backend=Backend.FULL, max_bs=512),
|
||||
)
|
||||
server_args._resolved_overrides = []
|
||||
valid = {f.name for f in dataclasses.fields(ServerArgs)}
|
||||
valid = {f.name for f in msgspec.structs.fields(ServerArgs)}
|
||||
for key, value in overrides.items():
|
||||
# Reject stale field names before setattr silently accepts them.
|
||||
assert key in valid, f"{key} is not a ServerArgs field"
|
||||
@@ -2489,7 +2491,7 @@ class TestHandleCrashDumpEnv(CustomTestCase):
|
||||
)
|
||||
|
||||
def _run_handler(self, crash_dump_folder, preset_env=None):
|
||||
server_args = ServerArgs.__new__(ServerArgs)
|
||||
server_args = ServerArgs(model_path="dummy")
|
||||
server_args.crash_dump_folder = crash_dump_folder
|
||||
with patch.dict(os.environ, preset_env or {}):
|
||||
for key in self._COREDUMP_ENV_KEYS:
|
||||
@@ -2868,7 +2870,7 @@ class TestTheInputIsSealedDuringResolution(CustomTestCase):
|
||||
server_args = ServerArgs(model_path="dummy", device="cuda")
|
||||
# The seal is what the pipeline runs under; drive it directly rather
|
||||
# than injecting a violation into a real handler.
|
||||
object.__setattr__(server_args, "_input_frozen", True)
|
||||
msgspec.Struct.__setattr__(server_args, "_input_frozen", True)
|
||||
with self.assertRaisesRegex(AttributeError, "during resolution"):
|
||||
server_args.tp_size = 4
|
||||
# and the message says what to do instead
|
||||
@@ -2901,7 +2903,7 @@ class TestTheInputIsSealedDuringResolution(CustomTestCase):
|
||||
)
|
||||
|
||||
server_args = ServerArgs(model_path="dummy", device="cuda")
|
||||
object.__setattr__(server_args, "_input_frozen", True)
|
||||
msgspec.Struct.__setattr__(server_args, "_input_frozen", True)
|
||||
|
||||
def foreign(config):
|
||||
# What a plugin does: read what is decided, assign a default.
|
||||
@@ -2961,7 +2963,7 @@ class TestLaunchCommand(CustomTestCase):
|
||||
"""It describes how the configuration was asked for, so it is not part
|
||||
of the configuration: no CLI flag, no namespace, not in the bags."""
|
||||
self.assertNotIn(
|
||||
"launch_command", {f.name for f in dataclasses.fields(ServerArgs)}
|
||||
"launch_command", {f.name for f in msgspec.structs.fields(ServerArgs)}
|
||||
)
|
||||
self.assertNotIn(
|
||||
"launch_command", ServerArgs(model_path="/tmp/x").resolved_dict()
|
||||
|
||||
@@ -32,6 +32,8 @@ user's stated intent), and decode capture is untouched either way.
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
import msgspec
|
||||
|
||||
from sglang.srt.arg_groups.kv_cache_hook import handle_unified_memory_pool
|
||||
from sglang.srt.model_executor.cuda_graph_config import Backend
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
@@ -42,7 +44,7 @@ register_cpu_ci(est_time=8, suite="base-a-test-cpu")
|
||||
|
||||
def _run_handler(*, prefill_backend, explicit):
|
||||
"""Run just `handle_unified_memory_pool` over a minimal stand-in."""
|
||||
sa = ServerArgs.__new__(ServerArgs)
|
||||
sa = ServerArgs(model_path="dummy")
|
||||
cg = SimpleNamespace(
|
||||
prefill=SimpleNamespace(backend=prefill_backend),
|
||||
decode=SimpleNamespace(backend=Backend.FULL),
|
||||
@@ -59,7 +61,7 @@ def _run_handler(*, prefill_backend, explicit):
|
||||
"cuda_graph_config": cg,
|
||||
"cuda_graph_backend_prefill": prefill_backend if explicit else None,
|
||||
}.items():
|
||||
object.__setattr__(sa, name, value)
|
||||
msgspec.Struct.__setattr__(sa, name, value)
|
||||
handle_unified_memory_pool(sa)
|
||||
return cg
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ pair, so without this gate a running server crashes mid-serving.
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
import msgspec
|
||||
|
||||
from sglang.srt.arg_groups.kv_cache_hook import handle_unified_memory_pool
|
||||
from sglang.srt.model_executor.cuda_graph_config import Backend
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
@@ -33,7 +35,7 @@ register_cpu_ci(est_time=5, suite="base-a-test-cpu")
|
||||
|
||||
def _run_handler(*, unified, tbo):
|
||||
"""Run just `handle_unified_memory_pool` over a minimal stand-in."""
|
||||
sa = ServerArgs.__new__(ServerArgs)
|
||||
sa = ServerArgs(model_path="dummy")
|
||||
for name, value in {
|
||||
"enable_unified_memory": unified,
|
||||
"enable_two_batch_overlap": tbo,
|
||||
@@ -49,7 +51,7 @@ def _run_handler(*, unified, tbo):
|
||||
),
|
||||
"cuda_graph_backend_prefill": Backend.DISABLED,
|
||||
}.items():
|
||||
object.__setattr__(sa, name, value)
|
||||
msgspec.Struct.__setattr__(sa, name, value)
|
||||
handle_unified_memory_pool(sa)
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ from sglang.srt.runtime_context import (
|
||||
override_platform,
|
||||
reset_context,
|
||||
)
|
||||
from sglang.srt.server_args import _declared_default
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
|
||||
|
||||
@@ -1613,14 +1614,13 @@ class TestGoldenModelOverrides(_IsolatedPublish):
|
||||
from sglang.srt.arg_groups.model_overrides.deepseek_v4 import (
|
||||
_deepseek_v4_overrides,
|
||||
)
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
|
||||
hf = SimpleNamespace(architectures=["DeepseekV4ForCausalLM"])
|
||||
|
||||
def _args(**kw):
|
||||
defaults = dict(
|
||||
device="cuda",
|
||||
swa_full_tokens_ratio=ServerArgs.swa_full_tokens_ratio,
|
||||
swa_full_tokens_ratio=_declared_default("swa_full_tokens_ratio"),
|
||||
moe_a2a_backend="none",
|
||||
moe_runner_backend="auto",
|
||||
_model_config=SimpleNamespace(is_fp4_experts=True, nvfp4_moe_meta=None),
|
||||
|
||||
@@ -14,6 +14,9 @@ import unittest
|
||||
import warnings
|
||||
from unittest.mock import patch
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang as _sglang
|
||||
import sglang.srt.server_args as server_args_module
|
||||
from sglang.srt.arg_groups.arg_utils import NS, A, Arg
|
||||
@@ -438,7 +441,7 @@ class TestServerArgsScopedOverride(_IsolatedServerArgs):
|
||||
from sglang.srt.runtime_context import get_spec
|
||||
|
||||
name = "_speculative_draft_quantization_explicitly_set"
|
||||
self.assertIn(name, ServerArgs.__dataclass_fields__)
|
||||
self.assertIn(name, ServerArgs.__struct_fields__)
|
||||
|
||||
published = get_context().override_server_args(**{name: True}).install()
|
||||
# The record keeps the operator's input, as it does for every other
|
||||
@@ -473,7 +476,6 @@ class TestServerArgsScopedOverride(_IsolatedServerArgs):
|
||||
override.install()
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _FakeCaptureGroup(_FlagGroupBase):
|
||||
gamma: int = 0
|
||||
|
||||
@@ -1639,13 +1641,12 @@ class TestTheDerivedHalfIsDeclared(CustomTestCase):
|
||||
def test_a_declared_quotient_is_not_a_record_field(self):
|
||||
"""It has no operator input to preserve, and the record is what crosses
|
||||
a process boundary."""
|
||||
import dataclasses
|
||||
|
||||
from sglang.srt.arg_groups.arg_utils import Derived
|
||||
from sglang.srt.arg_groups.fields.parallel import Parallel
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
|
||||
fields = {f.name for f in dataclasses.fields(ServerArgs)}
|
||||
fields = {f.name for f in msgspec.structs.fields(ServerArgs)}
|
||||
for name, value in vars(Parallel).items():
|
||||
if isinstance(value, Derived):
|
||||
self.assertNotIn(name, fields)
|
||||
|
||||
@@ -9,6 +9,9 @@ import dataclasses
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
from sglang.srt import runtime_context as rc
|
||||
from sglang.srt.arg_groups.arg_utils import NS, A
|
||||
from sglang.srt.arg_groups.overrides import resolution_result
|
||||
@@ -83,7 +86,7 @@ class TestConfigBags(CustomTestCase):
|
||||
import dataclasses
|
||||
|
||||
sa, reference = self._resolve_published_and_sibling()
|
||||
defaults = {f.name: f.default for f in dataclasses.fields(ServerArgs)}
|
||||
defaults = {f.name: f.default for f in msgspec.structs.fields(ServerArgs)}
|
||||
# Leaves resolution writes on this input on both CI device shapes
|
||||
# (CUDA host and CPU-only runner): each starts at a None default.
|
||||
sampled = (
|
||||
|
||||
@@ -8,6 +8,8 @@ field aborts before any write; provenance is recorded.
|
||||
|
||||
import unittest
|
||||
|
||||
import msgspec
|
||||
|
||||
from sglang.srt import runtime_context as rc
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
@@ -110,7 +112,7 @@ class TestContextOverride(CustomTestCase):
|
||||
# server_args is read-only after resolution: resolved config changes go
|
||||
# to the bags, a per-runner config to a derived variant.
|
||||
sa = ServerArgs(model_path="dummy")
|
||||
object.__setattr__(sa, "_resolution_finished", True)
|
||||
msgspec.Struct.__setattr__(sa, "_resolution_finished", True)
|
||||
with self.assertRaises(AttributeError):
|
||||
sa.page_size = 999
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import argparse
|
||||
import unittest
|
||||
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.srt.server_args import ServerArgs, _declared_default
|
||||
from sglang.srt.utils.common import human_readable_int
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
@@ -23,7 +23,9 @@ class TestServerArgsMigratedCliMetadata(CustomTestCase):
|
||||
}
|
||||
|
||||
def test_argparse_shape_is_preserved_for_representative_migrated_options(self):
|
||||
self.assertEqual(self.actions_by_option["--dtype"].default, ServerArgs.dtype)
|
||||
self.assertEqual(
|
||||
self.actions_by_option["--dtype"].default, _declared_default("dtype")
|
||||
)
|
||||
self.assertEqual(
|
||||
self.actions_by_option["--dtype"].choices,
|
||||
["auto", "half", "float16", "bfloat16", "float", "float32"],
|
||||
|
||||
@@ -11,9 +11,11 @@ This is the guardrail that fails when an upstream PR adds a field to a namespace
|
||||
class that has no ``_NS_PATH``, or adds one outside the taxonomy below.
|
||||
"""
|
||||
|
||||
import dataclasses
|
||||
import unittest
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
from sglang.srt.arg_groups.arg_utils import namespace_of
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
@@ -48,7 +50,7 @@ VALID_NAMESPACES = {
|
||||
|
||||
|
||||
def _field_names():
|
||||
return {f.name for f in dataclasses.fields(ServerArgs)}
|
||||
return {f.name for f in msgspec.structs.fields(ServerArgs)}
|
||||
|
||||
|
||||
class TestServerArgsNamespaces(CustomTestCase):
|
||||
|
||||
@@ -153,13 +153,13 @@ class TestSplitBackendsReachTheDecisions(CustomTestCase):
|
||||
def test_the_flashinfer_version_guard_sees_a_split_launch(self):
|
||||
# The launcher runs before any publish, so it asks the record; the
|
||||
# member and the accessor answer the same pair.
|
||||
args = ServerArgs.__new__(ServerArgs)
|
||||
args = ServerArgs(model_path="dummy")
|
||||
for name, value in (
|
||||
("attention_backend", None),
|
||||
("prefill_attention_backend", None),
|
||||
("decode_attention_backend", "flashinfer"),
|
||||
):
|
||||
object.__setattr__(args, name, value)
|
||||
setattr(args, name, value)
|
||||
self.assertIn("flashinfer", attention_backends_of(resolved_view(args)))
|
||||
|
||||
def test_support_triton_is_the_regression_being_guarded(self):
|
||||
|
||||
@@ -43,7 +43,6 @@ process-wide at all, so neither the read nor the field is on this axis.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
@@ -51,6 +50,9 @@ import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import msgspec
|
||||
import msgspec.structs
|
||||
|
||||
import sglang
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
from sglang.test.ci.ci_register import register_cpu_ci, register_cuda_ci
|
||||
@@ -367,10 +369,10 @@ class TestSuppliedInstanceExposure(CustomTestCase):
|
||||
"would drift"
|
||||
)
|
||||
defaults = {}
|
||||
for field in dataclasses.fields(resolved):
|
||||
if field.default is not dataclasses.MISSING:
|
||||
for field in msgspec.structs.fields(resolved):
|
||||
if field.default is not msgspec.NODEFAULT:
|
||||
defaults[field.name] = field.default
|
||||
elif field.default_factory is not dataclasses.MISSING:
|
||||
elif field.default_factory is not msgspec.NODEFAULT:
|
||||
defaults[field.name] = field.default_factory()
|
||||
for field_name, default in defaults.items():
|
||||
if field_name in _PASSED or field_name in extra:
|
||||
|
||||
Reference in New Issue
Block a user