diff --git a/mkdocs/docs/concepts/services.md b/mkdocs/docs/concepts/services.md
index 461ef530d6..a7d24fe9a6 100644
--- a/mkdocs/docs/concepts/services.md
+++ b/mkdocs/docs/concepts/services.md
@@ -197,7 +197,7 @@ Below is an example for running `Qwen/Qwen3.8-Flash-Next` on `H200`:
resources:
cpu: 4
router:
- type: sglang
+ type: smg
- replicas: 2
commands:
@@ -363,7 +363,7 @@ Below is an example for running `zai-org/GLM-4.5-Air-FP8` on `H200`:
resources:
cpu: 4
router:
- type: sglang
+ type: smg
- replicas: 4
commands:
@@ -537,7 +537,7 @@ groups:
resources:
cpu: 4..
router:
- type: sglang
+ type: smg
- replicas: 2
commands:
diff --git a/mkdocs/docs/concepts/tasks.md b/mkdocs/docs/concepts/tasks.md
index 6c4d36b8aa..34e68698b9 100644
--- a/mkdocs/docs/concepts/tasks.md
+++ b/mkdocs/docs/concepts/tasks.md
@@ -209,7 +209,7 @@ and the [NCCL/RCCL tests](../examples/clusters/nccl-rccl-tests.md) example for r
master node.
> In a service, worker registration happens automatically when the router group sets
- > [`router`](services.md#router) to `type: sglang`.
+ > [`router`](services.md#router) to `type: smg`.
diff --git a/mkdocs/docs/examples/inference/sglang.md b/mkdocs/docs/examples/inference/sglang.md
index 94789a2afa..8ca795a250 100644
--- a/mkdocs/docs/examples/inference/sglang.md
+++ b/mkdocs/docs/examples/inference/sglang.md
@@ -166,7 +166,7 @@ To run SGLang with [PD disaggregation](https://docs.sglang.io/advanced_features/
resources:
cpu: 4
router:
- type: sglang
+ type: smg
- replicas: 1..4
scaling:
@@ -240,7 +240,7 @@ To run SGLang with [PD disaggregation](https://docs.sglang.io/advanced_features/
--port 8000 \
--prefill-policy cache_aware
router:
- type: sglang
+ type: smg
resources:
cpu: 4
@@ -321,7 +321,7 @@ To run SGLang with [PD disaggregation](https://docs.sglang.io/advanced_features/
resources:
cpu: 4..
router:
- type: sglang
+ type: smg
- replicas: 1..2
scaling:
diff --git a/mkdocs/docs/examples/inference/vllm.md b/mkdocs/docs/examples/inference/vllm.md
index 0fac3d2370..08f45f82ab 100644
--- a/mkdocs/docs/examples/inference/vllm.md
+++ b/mkdocs/docs/examples/inference/vllm.md
@@ -154,7 +154,7 @@ groups:
--port 8000 \
--prefill-policy cache_aware
router:
- type: sglang
+ type: smg
resources:
cpu: 4
diff --git a/mkdocs/docs/reference/dstack.yml/service.md b/mkdocs/docs/reference/dstack.yml/service.md
index ad4e5585af..315278a1d7 100644
--- a/mkdocs/docs/reference/dstack.yml/service.md
+++ b/mkdocs/docs/reference/dstack.yml/service.md
@@ -89,8 +89,12 @@ The `service` configuration type allows running [services](../../concepts/servic
#SCHEMA# dstack._internal.core.models.configurations.ReplicaGroup
overrides:
show_root_heading: false
- type:
- required: true
+
+##### `groups[n].router` { #router data-toc-label="router" }
+
+#SCHEMA# dstack._internal.core.models.routers.ReplicaGroupRouterConfig
+ overrides:
+ show_root_heading: false
### `retry`
diff --git a/src/dstack/_internal/core/models/configurations.py b/src/dstack/_internal/core/models/configurations.py
index 81d5d6226a..ba7c62af0c 100644
--- a/src/dstack/_internal/core/models/configurations.py
+++ b/src/dstack/_internal/core/models/configurations.py
@@ -1066,7 +1066,7 @@ class ReplicaGroup(CoreModel):
router: Annotated[
Optional[ReplicaGroupRouterConfig],
Field(
- description="When set, replicas in this group run the in-service HTTP router (e.g. SGLang).",
+ description="When set, replicas in this group run the in-service HTTP router (e.g. SMG)",
),
] = None
diff --git a/src/dstack/_internal/core/models/routers.py b/src/dstack/_internal/core/models/routers.py
index 7d4acd169a..647c62b2cf 100644
--- a/src/dstack/_internal/core/models/routers.py
+++ b/src/dstack/_internal/core/models/routers.py
@@ -1,26 +1,51 @@
from enum import Enum
-from typing import Literal
+from typing import Any
-from pydantic import Field
+from pydantic import Field, field_serializer, field_validator
from typing_extensions import Annotated
from dstack._internal.core.models.common import CoreModel
class RouterType(str, Enum):
- SGLANG = "sglang"
+ SMG = "smg"
DYNAMO = "dynamo"
+# The value `RouterType.SMG` had before the rename
+_LEGACY_SMG_VALUE = "sglang"
+
+
class ReplicaGroupRouterConfig(CoreModel):
type: Annotated[
- Literal["sglang", "dynamo"],
+ RouterType,
Field(
description=(
- "The router implementation for this replica group. "
- "`sglang` runs the SGLang router and dstack syncs worker URLs to it. "
- "`dynamo` runs the NVIDIA Dynamo frontend, which discovers workers "
- "itself via etcd/NATS."
+ "The router implementation for this replica group."
+ " `smg` runs the Shepherd Model Gateway (SMG) and dstack syncs worker URLs to it."
+ " `dynamo` runs the NVIDIA Dynamo frontend, which discovers workers"
+ " itself via etcd/NATS"
),
),
- ] = "sglang"
+ ] = RouterType.SMG
+
+ @field_validator("type", mode="before")
+ @classmethod
+ def _accept_legacy_smg_value(cls, v: Any) -> Any:
+ # Stored run specs and older clients' requests use the legacy value
+ if v == _LEGACY_SMG_VALUE:
+ return RouterType.SMG
+ return v
+
+ @field_serializer("type", when_used="json")
+ def _serialize_type(self, v: RouterType) -> str:
+ # Servers and clients < 0.22.4 reject `smg`, so keep sending and storing the legacy value
+ # for one minor release. This covers client requests, server responses, and the run specs
+ # stored in the DB, which older server replicas read during a rolling deployment.
+ # TODO(0.23.0): Drop this serializer. When doing so, patch run specs in server responses
+ # back to the legacy value for clients < 0.22.4 (see `server/compatibility/runs.py`),
+ # since 0.23.* servers still support 0.22.* clients. Keep `_accept_legacy_smg_value`:
+ # run specs stored with the legacy value stay in the DB.
+ if v == RouterType.SMG:
+ return _LEGACY_SMG_VALUE
+ return v.value
diff --git a/src/dstack/_internal/proxy/gateway/services/registry.py b/src/dstack/_internal/proxy/gateway/services/registry.py
index e0f67868bb..1bc47d7195 100644
--- a/src/dstack/_internal/proxy/gateway/services/registry.py
+++ b/src/dstack/_internal/proxy/gateway/services/registry.py
@@ -349,8 +349,8 @@ async def get_nginx_service_config(
) -> ServiceConfig:
limit_req_zones: list[LimitReqZoneConfig] = []
locations: list[LocationConfig] = []
- sglang_limits: dict[str, LimitReqConfig] = {}
- sglang_prefix_lengths: dict[str, int] = {} # Track prefix lengths for most-specific selection
+ router_limits: dict[str, LimitReqConfig] = {}
+ router_prefix_lengths: dict[str, int] = {} # Track prefix lengths for most-specific selection
for i, rate_limit in enumerate(service.rate_limits):
zone_name = f"{i}.{service.domain_safe}"
@@ -368,13 +368,13 @@ async def get_nginx_service_config(
if rate_limit.prefix == path or path.startswith(rate_limit.prefix):
# Use the longest prefix if multiple prefixes match the same path
current_prefix_len = len(rate_limit.prefix)
- if path not in sglang_limits or current_prefix_len > sglang_prefix_lengths.get(
+ if path not in router_limits or current_prefix_len > router_prefix_lengths.get(
path, 0
):
- sglang_limits[path] = LimitReqConfig(
+ router_limits[path] = LimitReqConfig(
zone=zone_name, burst=rate_limit.burst
)
- sglang_prefix_lengths[path] = current_prefix_len
+ router_prefix_lengths[path] = current_prefix_len
else:
locations.append(
LocationConfig(
@@ -388,10 +388,10 @@ async def get_nginx_service_config(
for path in ROUTER_WHITELISTED_PATHS:
# Use prefix match for paths that end with a slash and exact match for paths that don't
if path.endswith("/"):
- locations.append(LocationConfig(prefix=path, limit_req=sglang_limits.get(path)))
+ locations.append(LocationConfig(prefix=path, limit_req=router_limits.get(path)))
else:
locations.append(
- LocationConfig(prefix=f"= {path}", limit_req=sglang_limits.get(path))
+ LocationConfig(prefix=f"= {path}", limit_req=router_limits.get(path))
)
# Don't auto-add / location for router-based services (catch-all 403 handles it)
diff --git a/src/dstack/_internal/proxy/lib/const.py b/src/dstack/_internal/proxy/lib/const.py
index 0824a5edb7..2c12512bbd 100644
--- a/src/dstack/_internal/proxy/lib/const.py
+++ b/src/dstack/_internal/proxy/lib/const.py
@@ -5,7 +5,7 @@
DEFAULT_SERVICE_READ_TIMEOUT = 300
# Inference endpoints exposed by the in-replica HTTP router. Applies to both
-# SGLang's router and Dynamo's `dynamo.frontend` — they share the
+# SMG and Dynamo's `dynamo.frontend` — they share the
# OpenAI-compatible endpoint surface.
ROUTER_WHITELISTED_PATHS: tuple[str, ...] = (
"/generate",
diff --git a/src/dstack/_internal/server/background/pipeline_tasks/jobs_running.py b/src/dstack/_internal/server/background/pipeline_tasks/jobs_running.py
index bc551a5390..35d74edc21 100644
--- a/src/dstack/_internal/server/background/pipeline_tasks/jobs_running.py
+++ b/src/dstack/_internal/server/background/pipeline_tasks/jobs_running.py
@@ -414,7 +414,7 @@ class _StartupContext:
"""Dynamo-specific env (e.g. DSTACK_ROUTER_INTERNAL_IP) computed from the
router replica's state. Passed through to RunnerClient.submit_job, which
merges it into a deep-copied job_spec.env so the shared job_spec is not
- mutated. None for SGLang services, non-router runs, and the router
+ mutated. None for SMG services, non-router runs, and the router
replica itself."""
diff --git a/src/dstack/_internal/server/background/pipeline_tasks/service_router_worker_sync.py b/src/dstack/_internal/server/background/pipeline_tasks/service_router_worker_sync.py
index 3d66280bf9..0afe59af01 100644
--- a/src/dstack/_internal/server/background/pipeline_tasks/service_router_worker_sync.py
+++ b/src/dstack/_internal/server/background/pipeline_tasks/service_router_worker_sync.py
@@ -34,7 +34,7 @@
from dstack._internal.server.services.logging import fmt
from dstack._internal.server.services.pipelines import PipelineHinterProtocol
from dstack._internal.server.services.runs.router_worker_sync import (
- run_model_has_sglang_router_replica_group,
+ run_model_has_smg_router_replica_group,
sync_router_workers_for_run_model,
)
from dstack._internal.server.utils import tracing
@@ -220,7 +220,7 @@ async def process(self, item: ServiceRouterWorkerSyncPipelineItem) -> None:
run_model.deleted
or run_model.status.is_finished()
or run_model.status != RunStatus.RUNNING
- or not run_model_has_sglang_router_replica_group(run_model)
+ or not run_model_has_smg_router_replica_group(run_model)
):
early_cleanup_update_map: _SyncRowUpdateMap = {"deleted": True}
set_processed_update_map_fields(early_cleanup_update_map)
diff --git a/src/dstack/_internal/server/services/jobs/configurators/base.py b/src/dstack/_internal/server/services/jobs/configurators/base.py
index 1a1fadf375..9b50b67b3e 100644
--- a/src/dstack/_internal/server/services/jobs/configurators/base.py
+++ b/src/dstack/_internal/server/services/jobs/configurators/base.py
@@ -519,7 +519,7 @@ def _probes(self) -> list[ProbeSpec]:
# workers), and they don't receive traffic directly.
return []
# The router answers chat completions only once it has workers, which it gets
- # regardless of its own readiness. For SGLang routers, dstack registers workers
+ # regardless of its own readiness. For SMG routers, dstack registers workers
# via `ServiceRouterWorkerSyncWorker`, which doesn't check router readiness.
# Dynamo workers register themselves with the router via etcd/NATS.
return [_openai_model_probe_spec(model.name, model.prefix)]
diff --git a/src/dstack/_internal/server/services/runs/replicas.py b/src/dstack/_internal/server/services/runs/replicas.py
index e579335e5c..d917599472 100644
--- a/src/dstack/_internal/server/services/runs/replicas.py
+++ b/src/dstack/_internal/server/services/runs/replicas.py
@@ -233,7 +233,7 @@ def get_router_env_for_job(
router_group = get_router_replica_group(run_spec)
if router_group is None or router_group.name is None:
return None
- # DSTACK_ROUTER_INTERNAL_IP is Dynamo-specific. SGLang workers
+ # DSTACK_ROUTER_INTERNAL_IP is Dynamo-specific. SMG workers
# are registered via the worker-sync pipeline (ServiceRouterWorkerSyncModel)
if router_group.router is None or router_group.router.type != RouterType.DYNAMO:
return None
diff --git a/src/dstack/_internal/server/services/runs/router_worker_sync.py b/src/dstack/_internal/server/services/runs/router_worker_sync.py
index bd40ae9e75..65da86eedd 100644
--- a/src/dstack/_internal/server/services/runs/router_worker_sync.py
+++ b/src/dstack/_internal/server/services/runs/router_worker_sync.py
@@ -1,4 +1,4 @@
-"""Reconcile SGLang router /workers with dstack's ready worker replicas (async, SSH-tunneled)."""
+"""Reconcile SMG /workers with dstack's ready worker replicas (async, SSH-tunneled)."""
import asyncio
import logging
@@ -42,14 +42,14 @@
from dstack._internal.utils.logging import get_logger
from .replicas import job_belongs_to_group
-from .service_router_worker_sync import run_spec_has_sglang_router_replica_group
+from .service_router_worker_sync import run_spec_has_smg_router_replica_group
logger = get_logger(__name__)
-def run_model_has_sglang_router_replica_group(run_model: RunModel) -> bool:
+def run_model_has_smg_router_replica_group(run_model: RunModel) -> bool:
run_spec = get_run_spec(run_model)
- return run_spec_has_sglang_router_replica_group(run_spec)
+ return run_spec_has_smg_router_replica_group(run_spec)
async def sync_router_workers_for_run_model(run_model: RunModel) -> None:
diff --git a/src/dstack/_internal/server/services/runs/service_router_worker_sync.py b/src/dstack/_internal/server/services/runs/service_router_worker_sync.py
index b251e76f92..3a38beebb7 100644
--- a/src/dstack/_internal/server/services/runs/service_router_worker_sync.py
+++ b/src/dstack/_internal/server/services/runs/service_router_worker_sync.py
@@ -32,14 +32,14 @@ def _reactivate_sync_row_update_map(*, now: datetime) -> _SyncRowUpdateMap:
}
-def run_spec_has_sglang_router_replica_group(run_spec: RunSpec) -> bool:
+def run_spec_has_smg_router_replica_group(run_spec: RunSpec) -> bool:
if run_spec.configuration.type != "service":
return False
cfg = run_spec.configuration
if not isinstance(cfg, ServiceConfiguration):
return False
return any(
- g.router is not None and g.router.type == RouterType.SGLANG for g in cfg.replica_groups
+ g.router is not None and g.router.type == RouterType.SMG for g in cfg.replica_groups
)
@@ -48,7 +48,7 @@ async def ensure_service_router_worker_sync_row(
run_model: RunModel,
run_spec: RunSpec,
) -> None:
- if not run_spec_has_sglang_router_replica_group(run_spec):
+ if not run_spec_has_smg_router_replica_group(run_spec):
return
res = await session.execute(
select(ServiceRouterWorkerSyncModel).where(
diff --git a/src/tests/_internal/core/models/test_configurations.py b/src/tests/_internal/core/models/test_configurations.py
index d1949a6edc..19188bdc85 100644
--- a/src/tests/_internal/core/models/test_configurations.py
+++ b/src/tests/_internal/core/models/test_configurations.py
@@ -18,7 +18,7 @@
parse_run_configuration,
)
from dstack._internal.core.models.resources import Range, ResourcesSpec
-from dstack._internal.core.models.routers import ReplicaGroupRouterConfig
+from dstack._internal.core.models.routers import ReplicaGroupRouterConfig, RouterType
class TestParseConfiguration:
@@ -154,8 +154,8 @@ def test_replica_group_router(self):
{
"name": "router",
"count": 1,
- "commands": ["sglang serve"],
- "router": {"type": "sglang"},
+ "commands": ["smg launch"],
+ "router": {"type": "smg"},
},
{"name": "worker", "count": 2, "commands": ["worker"]},
],
@@ -165,7 +165,7 @@ def test_replica_group_router(self):
assert parsed.groups is not None
router_g = next(g for g in parsed.groups if g.name == "router")
assert isinstance(router_g.router, ReplicaGroupRouterConfig)
- assert router_g.router.type == "sglang"
+ assert router_g.router.type == RouterType.SMG
def test_spot_policy_set_at_both_service_and_group_rejected(self):
with pytest.raises(
diff --git a/src/tests/_internal/core/models/test_routers.py b/src/tests/_internal/core/models/test_routers.py
new file mode 100644
index 0000000000..b0bf2a7ea5
--- /dev/null
+++ b/src/tests/_internal/core/models/test_routers.py
@@ -0,0 +1,30 @@
+import pytest
+
+from dstack._internal.core.models.routers import ReplicaGroupRouterConfig, RouterType
+
+
+class TestReplicaGroupRouterConfig:
+ def test_accepts_legacy_smg_value(self):
+ router = ReplicaGroupRouterConfig.model_validate({"type": "sglang"})
+ assert router.type == RouterType.SMG
+
+ def test_accepts_legacy_smg_value_from_json(self):
+ router = ReplicaGroupRouterConfig.model_validate_json('{"type": "sglang"}')
+ assert router.type == RouterType.SMG
+
+ @pytest.mark.parametrize(
+ ("router_type", "expected"),
+ [
+ pytest.param(RouterType.SMG, "sglang", id="smg"),
+ pytest.param(RouterType.DYNAMO, "dynamo", id="dynamo"),
+ ],
+ )
+ def test_serializes_smg_to_legacy_value_in_json(self, router_type: RouterType, expected: str):
+ router = ReplicaGroupRouterConfig(type=router_type)
+ assert router.model_dump(mode="json") == {"type": expected}
+ assert router.model_dump_json() == f'{{"type":"{expected}"}}'
+ assert ReplicaGroupRouterConfig.model_validate_json(router.model_dump_json()) == router
+
+ def test_keeps_enum_in_python_dump(self):
+ router = ReplicaGroupRouterConfig(type=RouterType.SMG)
+ assert router.model_dump() == {"type": RouterType.SMG}
diff --git a/src/tests/_internal/core/models/test_runs.py b/src/tests/_internal/core/models/test_runs.py
index 3ec6f8024c..e911a07125 100644
--- a/src/tests/_internal/core/models/test_runs.py
+++ b/src/tests/_internal/core/models/test_runs.py
@@ -116,7 +116,7 @@ def test_run_get_error_returns_none_for_specific_reasons():
def _service_run_spec_dict(router_type=None, retry=None, top_level_extras=None):
"""Build a minimal RunSpec dict for a service.
- `router_type`: None | "sglang" | "dynamo" — controls whether/how the
+ `router_type`: None | "smg" | "dynamo" — controls whether/how the
second replica group has a router field.
`retry`: optional dict passed as `profile.retry`.
`top_level_extras`: optional dict merged into the service configuration.
@@ -177,12 +177,12 @@ def test_dynamo_router_without_retry_is_accepted(self):
# Should not raise:
RunSpec.model_validate(spec)
- def test_sglang_router_with_retry_is_accepted(self):
+ def test_smg_router_with_retry_is_accepted(self):
spec = _service_run_spec_dict(
- router_type="sglang",
+ router_type="smg",
retry={"on_events": ["error"]},
)
- # SGLang services are unaffected by the validator.
+ # SMG services are unaffected by the validator.
RunSpec.model_validate(spec)
def test_service_without_router_with_retry_is_accepted(self):
diff --git a/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py b/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py
index 6b82e0c3f0..9493cc1104 100644
--- a/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py
+++ b/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py
@@ -2968,7 +2968,7 @@ async def test_registers_router_replica_but_not_worker_replica_in_gateway(
run_spec=get_run_spec(
run_name="test",
repo_id=repo.name,
- configuration=_router_service_configuration("sglang", gateway="test-gateway"),
+ configuration=_router_service_configuration("smg", gateway="test-gateway"),
),
gateway=gateway,
)
@@ -3046,7 +3046,7 @@ async def test_resets_stale_registered_flag_for_non_router_replica(
run_name="test",
repo_id=repo.name,
configuration=_router_service_configuration(
- "sglang",
+ "smg",
probes=[ProbeConfig(type="http", url="/health", ready_after=1)],
),
),
@@ -3645,7 +3645,7 @@ async def test_non_dynamo_loads_only_own_replica(self, test_db, session: AsyncSe
repo = await create_repo(session=session, project_id=project.id)
run_spec = get_run_spec(
repo_id=repo.name,
- configuration=_router_service_configuration("sglang"),
+ configuration=_router_service_configuration("smg"),
)
run = await create_run(
session=session, project=project, repo=repo, user=user, run_spec=run_spec
@@ -3674,7 +3674,7 @@ async def test_non_dynamo_loads_only_own_replica(self, test_db, session: AsyncSe
)
assert {j.replica_num for j in run_model.jobs} == {0}
- async def test_sglang_with_replica_ip_refs_loads_all_replicas(
+ async def test_smg_with_replica_ip_refs_loads_all_replicas(
self, test_db, session: AsyncSession
):
project = await create_project(session=session)
@@ -3683,7 +3683,7 @@ async def test_sglang_with_replica_ip_refs_loads_all_replicas(
run_spec = get_run_spec(
repo_id=repo.name,
configuration=_router_service_configuration(
- "sglang",
+ "smg",
router_commands=["echo ${{ groups[0].replicas[0].IP_ADDRESS }}"],
),
)
@@ -3714,7 +3714,7 @@ async def test_sglang_with_replica_ip_refs_loads_all_replicas(
)
assert {j.replica_num for j in run_model.jobs} == {0, 1}
- async def test_sglang_without_replica_ip_refs_loads_only_own_replica(
+ async def test_smg_without_replica_ip_refs_loads_only_own_replica(
self, test_db, session: AsyncSession
):
project = await create_project(session=session)
@@ -3722,7 +3722,7 @@ async def test_sglang_without_replica_ip_refs_loads_only_own_replica(
repo = await create_repo(session=session, project_id=project.id)
run_spec = get_run_spec(
repo_id=repo.name,
- configuration=_router_service_configuration("sglang"),
+ configuration=_router_service_configuration("smg"),
)
run = await create_run(
session=session, project=project, repo=repo, user=user, run_spec=run_spec
@@ -3751,7 +3751,7 @@ async def test_sglang_without_replica_ip_refs_loads_only_own_replica(
)
assert {j.replica_num for j in run_model.jobs} == {1}
- async def test_sglang_with_replica_ip_refs_drops_terminated_replicas(
+ async def test_smg_with_replica_ip_refs_drops_terminated_replicas(
self, test_db, session: AsyncSession
):
project = await create_project(session=session)
@@ -3760,7 +3760,7 @@ async def test_sglang_with_replica_ip_refs_drops_terminated_replicas(
run_spec = get_run_spec(
repo_id=repo.name,
configuration=_router_service_configuration(
- "sglang",
+ "smg",
router_commands=["echo ${{ groups[0].replicas[0].IP_ADDRESS }}"],
),
)
diff --git a/src/tests/_internal/server/background/pipeline_tasks/test_runs/test_active.py b/src/tests/_internal/server/background/pipeline_tasks/test_runs/test_active.py
index 389f70f458..b2c2971725 100644
--- a/src/tests/_internal/server/background/pipeline_tasks/test_runs/test_active.py
+++ b/src/tests/_internal/server/background/pipeline_tasks/test_runs/test_active.py
@@ -1871,7 +1871,7 @@ def _router_worker_service_configuration() -> ServiceConfiguration:
{"name": "worker", "commands": ["echo worker"], "count": 1},
{
"name": "router",
- "router": {"type": "sglang"},
+ "router": {"type": "smg"},
"commands": ["echo router"],
"count": 1,
},
diff --git a/src/tests/_internal/server/background/pipeline_tasks/test_service_router_worker_sync.py b/src/tests/_internal/server/background/pipeline_tasks/test_service_router_worker_sync.py
index a62d50b01d..04cd5cf0b0 100644
--- a/src/tests/_internal/server/background/pipeline_tasks/test_service_router_worker_sync.py
+++ b/src/tests/_internal/server/background/pipeline_tasks/test_service_router_worker_sync.py
@@ -43,8 +43,8 @@ def _router_service_run_spec(repo_id: str, run_name: str = "test-run"):
{
"name": "router",
"count": 1,
- "commands": ["sglang serve"],
- "router": {"type": "sglang"},
+ "commands": ["smg launch"],
+ "router": {"type": "smg"},
},
{"name": "worker", "count": 2, "commands": ["worker"]},
],
diff --git a/src/tests/_internal/server/services/jobs/configurators/test_service.py b/src/tests/_internal/server/services/jobs/configurators/test_service.py
index 3f0de12890..a13b24701a 100644
--- a/src/tests/_internal/server/services/jobs/configurators/test_service.py
+++ b/src/tests/_internal/server/services/jobs/configurators/test_service.py
@@ -13,7 +13,7 @@
)
from dstack._internal.core.models.profiles import SpotPolicy
from dstack._internal.core.models.resources import Range
-from dstack._internal.core.models.routers import ReplicaGroupRouterConfig
+from dstack._internal.core.models.routers import ReplicaGroupRouterConfig, RouterType
from dstack._internal.core.models.services import OpenAIChatModel
from dstack._internal.server.services.docker import ImageConfig
from dstack._internal.server.services.jobs.configurators.base import get_default_image
@@ -106,7 +106,7 @@ def _router_worker_configuration() -> ServiceConfiguration:
ReplicaGroup(
name="router",
replicas=Range[int](min=1, max=1),
- router=ReplicaGroupRouterConfig(type="sglang"),
+ router=ReplicaGroupRouterConfig(type=RouterType.SMG),
),
ReplicaGroup(
name="worker",
diff --git a/src/tests/_internal/server/services/runs/test_router_worker_sync.py b/src/tests/_internal/server/services/runs/test_router_worker_sync.py
index 0bdd03ffa1..7c411fd829 100644
--- a/src/tests/_internal/server/services/runs/test_router_worker_sync.py
+++ b/src/tests/_internal/server/services/runs/test_router_worker_sync.py
@@ -1122,7 +1122,7 @@ def _service_configuration() -> ServiceConfiguration:
"name": "router",
"replicas": 1,
"commands": ["smg launch"],
- "router": {"type": "sglang"},
+ "router": {"type": "smg"},
},
{"name": "worker", "replicas": 1, "commands": ["worker"]},
],
diff --git a/src/tests/_internal/server/services/runs/test_spec.py b/src/tests/_internal/server/services/runs/test_spec.py
index 419d952201..20ac7bd605 100644
--- a/src/tests/_internal/server/services/runs/test_spec.py
+++ b/src/tests/_internal/server/services/runs/test_spec.py
@@ -482,15 +482,15 @@ def test_false_for_task_node_refs(self):
class TestCheckCanUpdateConfigurationRouterType:
- def test_sglang_to_dynamo_router_type_change_is_rejected(self):
- current = _run_spec(_service_configuration(router_type="sglang"))
+ def test_smg_to_dynamo_router_type_change_is_rejected(self):
+ current = _run_spec(_service_configuration(router_type="smg"))
new = _run_spec(_service_configuration(router_type="dynamo"))
with pytest.raises(ServerClientError, match="router.type"):
check_can_update_run_spec(current, new)
- def test_dynamo_to_sglang_router_type_change_is_rejected(self):
+ def test_dynamo_to_smg_router_type_change_is_rejected(self):
current = _run_spec(_service_configuration(router_type="dynamo"))
- new = _run_spec(_service_configuration(router_type="sglang"))
+ new = _run_spec(_service_configuration(router_type="smg"))
with pytest.raises(ServerClientError, match="router.type"):
check_can_update_run_spec(current, new)
@@ -592,10 +592,10 @@ def test_dynamo_worker_commands_change_is_allowed(self):
class TestCheckCanUpdateConfigurationNonDynamoUnchanged:
- def test_sglang_top_level_image_change_is_allowed(self):
- current = _run_spec(_service_configuration(router_type="sglang", image="img:1"))
- new = _run_spec(_service_configuration(router_type="sglang", image="img:2"))
- # Top-level changes on SGLang services flow through to the existing
+ def test_smg_top_level_image_change_is_allowed(self):
+ current = _run_spec(_service_configuration(router_type="smg", image="img:1"))
+ new = _run_spec(_service_configuration(router_type="smg", image="img:2"))
+ # Top-level changes on SMG services flow through to the existing
# rolling-deployment path; no Dynamo gate fires.
check_can_update_run_spec(current, new)
@@ -609,8 +609,8 @@ class TestCheckCanUpdateConfigurationFieldAllowlist:
"""`_check_can_update_configuration` is also called directly with configs only."""
def test_non_dynamo_image_change_passes_configuration_gate(self):
- current = _service_configuration(router_type="sglang", image="img:1")
- new = _service_configuration(router_type="sglang", image="img:2")
+ current = _service_configuration(router_type="smg", image="img:1")
+ new = _service_configuration(router_type="smg", image="img:2")
_check_can_update_configuration(current, new, ignore_files=True)