config: retire ServerArgs.derive; per-runner values are constructor arguments (#33887)
This commit is contained in:
@@ -288,7 +288,12 @@ class MMEncoder:
|
||||
schedule_path=None,
|
||||
dist_init_method=None,
|
||||
rank: int = 0,
|
||||
gpu_id: Optional[int] = None,
|
||||
):
|
||||
"""``gpu_id`` pins this encoder to a device other than
|
||||
``base_gpu_id + rank`` — the DP launcher's per-worker placement. It is
|
||||
this instance's value, not a config change, so it travels as an
|
||||
argument."""
|
||||
logger.info(f"init MMEncoder {rank}/{server_args.tp_size}")
|
||||
self.server_args = server_args
|
||||
publish(server_args, role="encoder")
|
||||
@@ -315,7 +320,7 @@ class MMEncoder:
|
||||
).lower()
|
||||
|
||||
self.device = server_args.device
|
||||
self.gpu_id = server_args.base_gpu_id + rank
|
||||
self.gpu_id = server_args.base_gpu_id + rank if gpu_id is None else gpu_id
|
||||
|
||||
self.device_config = DeviceConfig(
|
||||
device=self.device,
|
||||
@@ -3517,10 +3522,13 @@ async def run_dp_worker(
|
||||
)
|
||||
|
||||
# gpu_id is the device chosen by maybe_reindex_device_id in the parent:
|
||||
# 0 when CVD is pinned to one GPU, else the absolute id. rank=0, so
|
||||
# MMEncoder runs set_device(base_gpu_id).
|
||||
args = server_args.derive("encode_server.dp_worker", base_gpu_id=gpu_id, tp_size=1)
|
||||
enc = MMEncoder(args, dist_init_method=f"tcp://127.0.0.1:{get_free_port()}", rank=0)
|
||||
# 0 when CVD is pinned to one GPU, else the absolute id.
|
||||
enc = MMEncoder(
|
||||
server_args,
|
||||
dist_init_method=f"tcp://127.0.0.1:{get_free_port()}",
|
||||
rank=0,
|
||||
gpu_id=gpu_id,
|
||||
)
|
||||
|
||||
global encoder_metrics_collector
|
||||
if server_args.enable_metrics:
|
||||
|
||||
@@ -1027,9 +1027,19 @@ class _ServerArgsOverride:
|
||||
self._prev_publish_role = ctx._publish_role
|
||||
self._prev_parallel_config = ctx.parallel._config
|
||||
self._prev_capture = ctx.flags.capture.enable_torch_compile
|
||||
from sglang.srt.arg_groups.overrides import _apply_fields
|
||||
|
||||
server_args = ServerArgs(model_path="dummy")
|
||||
if self._fields:
|
||||
server_args = server_args.derive("test-override", **self._fields)
|
||||
# Underscore names seed private property caches (the strict guard
|
||||
# exempts them); everything else must be a real config field.
|
||||
unknown = {name for name in self._fields if not name.startswith("_")} - set(
|
||||
type(server_args).__dataclass_fields__
|
||||
)
|
||||
if unknown:
|
||||
raise ValueError(
|
||||
f"override_server_args: unknown ServerArgs field(s): {sorted(unknown)}"
|
||||
)
|
||||
_apply_fields(server_args, self._fields)
|
||||
# The dummy boundary skips materialization, which would leave the
|
||||
# strict mutation guard unarmed on the published object — mark it
|
||||
# materialized so bare post-publish writes raise like they do on a
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import dataclasses
|
||||
import glob
|
||||
import importlib
|
||||
@@ -8609,52 +8608,12 @@ class ServerArgs:
|
||||
|
||||
declare_late_resolution(self, source, **fields)
|
||||
|
||||
def derive(self, source: str, **fields) -> ServerArgs:
|
||||
"""A copy carrying variant values: a draft worker's context length, an
|
||||
encode worker's device, a launcher's late port pick.
|
||||
|
||||
The receiver is untouched, so a config already published from it -- and
|
||||
the namespace bags projected out of it -- stay true; the variant is a
|
||||
second config, to be published in its own right or handed to whoever
|
||||
owns it. Resolution does not re-run: the values being set are decided
|
||||
*after* it, from inputs resolution never had, and re-resolving a
|
||||
resolved config re-derives conditional decisions from the wrong ones.
|
||||
|
||||
Whitelisted resolvable fields also join the copy's declaration stash so
|
||||
a later re-resolution keeps them; ``source`` is recorded for provenance.
|
||||
"""
|
||||
from sglang.srt.arg_groups.arg_utils import resolvable_fields
|
||||
|
||||
variant = copy.deepcopy(self)
|
||||
whitelist = resolvable_fields(type(variant))
|
||||
declared = {k: v for k, v in fields.items() if k in whitelist}
|
||||
rest = {k: v for k, v in fields.items() if k not in whitelist}
|
||||
if declared:
|
||||
stash = getattr(variant, "_resolved_overrides", None)
|
||||
if stash is None:
|
||||
stash = []
|
||||
object.__setattr__(variant, "_resolved_overrides", stash)
|
||||
stash.append((source, dict(declared)))
|
||||
if rest:
|
||||
log = getattr(variant, "_runtime_mutations", None)
|
||||
if log is None:
|
||||
log = []
|
||||
object.__setattr__(variant, "_runtime_mutations", log)
|
||||
log.append((source, dict(rest)))
|
||||
object.__setattr__(variant, "_internal_write", True)
|
||||
try:
|
||||
for field, value in fields.items():
|
||||
setattr(variant, field, value)
|
||||
finally:
|
||||
object.__setattr__(variant, "_internal_write", False)
|
||||
return variant
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
# After materialization the fields are the resolved startup
|
||||
# configuration -- the pristine, READ-ONLY record that the config bags
|
||||
# were projected from. Resolved config changes go to the bags via
|
||||
# get_context().override(source, ...); a config that differs per runner
|
||||
# or per worker is a separate object, built with derive().
|
||||
# get_context().override(source, ...); a value one runner or worker
|
||||
# owns travels as a constructor argument to it.
|
||||
if (
|
||||
not name.startswith("_")
|
||||
and getattr(self, "_declarations_materialized", False)
|
||||
@@ -8663,8 +8622,8 @@ class ServerArgs:
|
||||
raise AttributeError(
|
||||
f"server_args.{name} assigned after resolution; server_args is "
|
||||
"read-only -- use get_context().override(source, ...) to change "
|
||||
"resolved config, or server_args.derive(source, ...) to build a "
|
||||
"variant for one runner."
|
||||
"resolved config; a value one runner owns travels as a "
|
||||
"constructor argument."
|
||||
)
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
|
||||
@@ -326,10 +326,9 @@ def _configure_runner_for_eagle_draft(
|
||||
"use_mla_backend": runner.use_mla_backend,
|
||||
}
|
||||
from sglang.srt.runtime_context import get_context
|
||||
from sglang.test.test_utils import server_args_variant
|
||||
|
||||
runner.server_args = runner.server_args.derive(
|
||||
"attention-unittest-eagle-draft", **updates
|
||||
)
|
||||
runner.server_args = server_args_variant(runner.server_args, **updates)
|
||||
get_context().set_server_args(runner.server_args)
|
||||
|
||||
runner.spec_algorithm = SpeculativeAlgorithm.EAGLE
|
||||
@@ -392,9 +391,10 @@ def _build_frozen_kv_mtp_fixture(
|
||||
)
|
||||
_configure_runner_for_eagle_draft(fixture.runner, case, settings)
|
||||
from sglang.srt.runtime_context import get_context
|
||||
from sglang.test.test_utils import server_args_variant
|
||||
|
||||
fixture.runner.server_args = fixture.runner.server_args.derive(
|
||||
"attention_unittest.frozen_kv_draft", speculative_algorithm="FROZEN_KV_MTP"
|
||||
fixture.runner.server_args = server_args_variant(
|
||||
fixture.runner.server_args, speculative_algorithm="FROZEN_KV_MTP"
|
||||
)
|
||||
get_context().set_server_args(fixture.runner.server_args)
|
||||
fixture.runner.spec_algorithm = SpeculativeAlgorithm.FROZEN_KV_MTP
|
||||
|
||||
@@ -2345,6 +2345,28 @@ def _wait_for_gpu_idle_in_ci(
|
||||
pass
|
||||
|
||||
|
||||
def server_args_variant(server_args, **fields):
|
||||
"""A modified deep copy of a config, for a test double whose fixture
|
||||
differs from the (possibly published, read-only) config it starts from.
|
||||
The receiver is untouched; the copy keeps its read-only guard.
|
||||
|
||||
A name may also shadow a method with a fixture value (the runner kits set
|
||||
``use_mla_backend``, a method ModelRunner itself overwrites at init);
|
||||
names that exist nowhere on the class fail loudly."""
|
||||
variant = copy.deepcopy(server_args)
|
||||
cls = type(variant)
|
||||
unknown = {
|
||||
name
|
||||
for name in fields
|
||||
if name not in cls.__dataclass_fields__ and not hasattr(cls, name)
|
||||
}
|
||||
if unknown:
|
||||
raise ValueError(f"unknown ServerArgs field(s): {sorted(unknown)}")
|
||||
for name, value in fields.items():
|
||||
object.__setattr__(variant, name, value)
|
||||
return variant
|
||||
|
||||
|
||||
class CustomTestCase(unittest.TestCase):
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user