feat: add native gRPC sidecar module launcher (#31076)
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com> Signed-off-by: Connor Carpenter <connorc@nvidia.com> Co-authored-by: Connor Carpenter <connorc@nvidia.com>
This commit is contained in:
co-authored by
Connor Carpenter
parent
74338e94f1
commit
21065bc862
@@ -9,6 +9,13 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import sglang.srt.server_args as server_args_module
|
||||
from sglang.srt.arg_groups.speculative_hook import handle_speculative_decoding
|
||||
from sglang.srt.entrypoints.sidecar import (
|
||||
SGLANG_GRPC_ENDPOINT_ENV,
|
||||
Sidecar,
|
||||
_run_sidecar,
|
||||
build_sidecar_endpoint,
|
||||
start_sidecar,
|
||||
)
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.layers.cp.base import is_cp_enabled, is_interleave
|
||||
from sglang.srt.model_executor.cuda_graph_config import (
|
||||
@@ -1536,6 +1543,133 @@ class TestGrpcServerArgs(CustomTestCase):
|
||||
sa._handle_deprecated_args()
|
||||
self.assertEqual(sa.grpc_port, 45000)
|
||||
|
||||
@staticmethod
|
||||
def _sidecar_parser():
|
||||
parser = server_args_module.argparse.ArgumentParser()
|
||||
ServerArgs.add_cli_args(parser)
|
||||
return parser
|
||||
|
||||
def test_sidecar_builds_loopback_grpc_endpoints(self):
|
||||
self.assertEqual(
|
||||
build_sidecar_endpoint(SimpleNamespace(host="0.0.0.0", grpc_port=50051)),
|
||||
"http://127.0.0.1:50051",
|
||||
)
|
||||
self.assertEqual(
|
||||
build_sidecar_endpoint(SimpleNamespace(host="::", grpc_port=50051)),
|
||||
"http://[::1]:50051",
|
||||
)
|
||||
self.assertEqual(
|
||||
build_sidecar_endpoint(SimpleNamespace(host="[::]", grpc_port=50051)),
|
||||
"http://[::1]:50051",
|
||||
)
|
||||
|
||||
def test_sidecar_args_parse_as_exact_json_argv(self):
|
||||
argv = ["--flag", "value"]
|
||||
parsed = self._sidecar_parser().parse_args(
|
||||
["--model-path", "dummy", "--sidecar-args", json.dumps(argv)]
|
||||
)
|
||||
self.assertEqual(parsed.sidecar_args, argv)
|
||||
|
||||
def test_start_sidecar_passes_endpoint_and_provider_argv_separately(self):
|
||||
server_args = SimpleNamespace(
|
||||
sidecar="example.sidecar",
|
||||
sidecar_args=[
|
||||
"--sidecar-shutdown-timeout",
|
||||
"42",
|
||||
"--grpc-connections",
|
||||
"2",
|
||||
],
|
||||
host="127.0.0.1",
|
||||
grpc_port=50051,
|
||||
)
|
||||
with (
|
||||
patch("sglang.srt.entrypoints.sidecar.mp.get_context") as get_context,
|
||||
patch("sglang.srt.entrypoints.sidecar.Sidecar") as sidecar_class,
|
||||
):
|
||||
start_sidecar(server_args)
|
||||
|
||||
process_kwargs = get_context.return_value.Process.call_args.kwargs
|
||||
self.assertEqual(process_kwargs["name"], "sglang_sidecar_example.sidecar")
|
||||
self.assertEqual(process_kwargs["target"], _run_sidecar)
|
||||
self.assertEqual(
|
||||
process_kwargs["args"],
|
||||
(
|
||||
"example.sidecar",
|
||||
["--grpc-connections", "2"],
|
||||
"http://127.0.0.1:50051",
|
||||
),
|
||||
)
|
||||
sidecar_class.assert_called_once_with(
|
||||
get_context.return_value.Process.return_value,
|
||||
"example.sidecar",
|
||||
shutdown_timeout=42.0,
|
||||
)
|
||||
|
||||
def test_sidecar_requires_native_grpc(self):
|
||||
sa = self._args(sidecar="example.sidecar")
|
||||
with self.assertRaisesRegex(ValueError, "requires --grpc-port"):
|
||||
sa._handle_deprecated_args()
|
||||
|
||||
def test_sidecar_rejects_legacy_grpc(self):
|
||||
sa = self._args(sidecar="example.sidecar", smg_grpc_mode=True)
|
||||
with self.assertRaisesRegex(ValueError, "native gRPC server"):
|
||||
sa._handle_deprecated_args()
|
||||
|
||||
def test_sidecar_rejects_empty_value(self):
|
||||
sa = self._args(sidecar="", grpc_port=50051)
|
||||
with self.assertRaisesRegex(ValueError, "must not be empty"):
|
||||
sa._handle_deprecated_args()
|
||||
|
||||
def test_sidecar_sets_endpoint_env_before_import_and_calls_main(self):
|
||||
main = MagicMock()
|
||||
|
||||
def import_module(module_name):
|
||||
self.assertEqual(module_name, "example.sidecar")
|
||||
self.assertEqual(
|
||||
os.environ[SGLANG_GRPC_ENDPOINT_ENV],
|
||||
"http://127.0.0.1:50051",
|
||||
)
|
||||
self.assertEqual(os.environ["DYN_NAMESPACE"], "pluh")
|
||||
return SimpleNamespace(main=main)
|
||||
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
SGLANG_GRPC_ENDPOINT_ENV: "http://stale.example:1",
|
||||
"DYN_NAMESPACE": "pluh",
|
||||
},
|
||||
),
|
||||
patch("sglang.srt.entrypoints.sidecar.kill_itself_when_parent_died"),
|
||||
patch(
|
||||
"sglang.srt.entrypoints.sidecar.importlib.import_module",
|
||||
side_effect=import_module,
|
||||
),
|
||||
):
|
||||
_run_sidecar(
|
||||
"example.sidecar",
|
||||
["--provider-flag", "value"],
|
||||
"http://127.0.0.1:50051",
|
||||
)
|
||||
|
||||
main.assert_called_once_with(["--provider-flag", "value"])
|
||||
|
||||
def test_sidecar_stop_uses_configured_shutdown_timeout(self):
|
||||
proc = MagicMock(pid=1234)
|
||||
proc.is_alive.side_effect = [True, True]
|
||||
sidecar = Sidecar(
|
||||
proc,
|
||||
"example.sidecar",
|
||||
shutdown_timeout=42.0,
|
||||
)
|
||||
|
||||
with patch("sglang.srt.entrypoints.sidecar.kill_process_tree") as kill_tree:
|
||||
sidecar.stop()
|
||||
|
||||
proc.terminate.assert_called_once_with()
|
||||
proc.join.assert_called_once_with(timeout=42.0)
|
||||
kill_tree.assert_called_once_with(1234, wait_timeout=42.0)
|
||||
|
||||
def test_legacy_smg_derives_grpc_port_from_http_port(self):
|
||||
sa = self._args(port=30000, smg_grpc_mode=True)
|
||||
sa._handle_deprecated_args()
|
||||
|
||||
Reference in New Issue
Block a user