[router] Allow empty worker list for sglang.launch_router (#2979)
This commit is contained in:
4
.github/workflows/pr-test-rust.yml
vendored
4
.github/workflows/pr-test-rust.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
cd sgl-router/
|
||||
cargo test
|
||||
|
||||
e2e-rust:
|
||||
e2e-python:
|
||||
if: github.repository == 'sgl-project/sglang' || github.event_name == 'pull_request'
|
||||
runs-on: 2-gpu-runner
|
||||
steps:
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
python3 run_suite.py
|
||||
|
||||
finish:
|
||||
needs: [unit-test-rust, e2e-rust]
|
||||
needs: [unit-test-rust, e2e-python]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Finish
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -euxo pipefail
|
||||
|
||||
# these are required for actix
|
||||
apt-get update
|
||||
apt-get install -y libssl-dev pkg-config
|
||||
# Check if sudo is available
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libssl-dev pkg-config
|
||||
else
|
||||
apt-get update
|
||||
apt-get install -y libssl-dev pkg-config
|
||||
fi
|
||||
|
||||
# Install rustup (Rust installer and version manager)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
@@ -67,6 +67,16 @@ $ pip install -e .
|
||||
|
||||
**Note:** When modifying Rust code, you must rebuild the wheel for changes to take effect.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
1. If rust analyzer is not working in VSCode, set `rust-analyzer.linkedProjects` to the absolute path of `Cargo.toml` in your repo. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"rust-analyzer.linkedProjects": ["/workspaces/sglang/sgl-router/Cargo.toml"]
|
||||
}
|
||||
```
|
||||
|
||||
### CI/CD Setup
|
||||
|
||||
The continuous integration pipeline consists of three main steps:
|
||||
|
||||
@@ -27,7 +27,7 @@ def setup_logger():
|
||||
@dataclasses.dataclass
|
||||
class RouterArgs:
|
||||
# Worker configuration
|
||||
worker_urls: List[str]
|
||||
worker_urls: List[str] = dataclasses.field(default_factory=list)
|
||||
host: str = "127.0.0.1"
|
||||
port: int = 30000
|
||||
|
||||
@@ -141,8 +141,9 @@ class RouterArgs:
|
||||
use_router_prefix: If True, look for arguments with 'router-' prefix
|
||||
"""
|
||||
prefix = "router_" if use_router_prefix else ""
|
||||
worker_urls = args.worker_urls if args.worker_urls is not None else []
|
||||
return cls(
|
||||
worker_urls=args.worker_urls,
|
||||
worker_urls=worker_urls,
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
policy=getattr(args, f"{prefix}policy"),
|
||||
@@ -237,7 +238,6 @@ Examples:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logger = setup_logger()
|
||||
router_args = parse_router_args(sys.argv[1:])
|
||||
router = launch_router(router_args)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ def setup_logger():
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
formatter = logging.Formatter(
|
||||
"[Router (Python)] %(asctime)s - %(levelname)s - %(message)s",
|
||||
"[Router (Python)] %(asctime)s - %(levelname)s - %(message)s - %(filename)s:%(lineno)d",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.1"
|
||||
__version__ = "0.1.2"
|
||||
|
||||
@@ -22,11 +22,9 @@ def terminate_process(process: multiprocessing.Process, timeout: float = 1.0) ->
|
||||
|
||||
|
||||
class TestLaunchRouter(unittest.TestCase):
|
||||
def test_launch_router_no_exception(self):
|
||||
|
||||
# Create SimpleNamespace with default arguments
|
||||
args = SimpleNamespace(
|
||||
worker_urls=["http://localhost:8000"],
|
||||
def setUp(self):
|
||||
"""Set up default arguments for router tests."""
|
||||
self.default_args = SimpleNamespace(
|
||||
host="127.0.0.1",
|
||||
port=30000,
|
||||
policy="cache_aware",
|
||||
@@ -39,6 +37,15 @@ class TestLaunchRouter(unittest.TestCase):
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
def create_router_args(self, **kwargs):
|
||||
"""Create router arguments by updating default args with provided kwargs."""
|
||||
args_dict = vars(self.default_args).copy()
|
||||
args_dict.update(kwargs)
|
||||
return SimpleNamespace(**args_dict)
|
||||
|
||||
def run_router_process(self, args):
|
||||
"""Run router in a separate process and verify it starts successfully."""
|
||||
|
||||
def run_router():
|
||||
try:
|
||||
from sglang_router.launch_router import launch_router
|
||||
@@ -51,7 +58,6 @@ class TestLaunchRouter(unittest.TestCase):
|
||||
print(e)
|
||||
return 1
|
||||
|
||||
# Start router in separate process
|
||||
process = multiprocessing.Process(target=run_router)
|
||||
try:
|
||||
process.start()
|
||||
@@ -62,6 +68,14 @@ class TestLaunchRouter(unittest.TestCase):
|
||||
finally:
|
||||
terminate_process(process)
|
||||
|
||||
def test_launch_router_common(self):
|
||||
args = self.create_router_args(worker_urls=["http://localhost:8000"])
|
||||
self.run_router_process(args)
|
||||
|
||||
def test_launch_router_with_empty_worker_urls(self):
|
||||
args = self.create_router_args(worker_urls=[])
|
||||
self.run_router_process(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "sglang-router"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
description = "SGLang router is a standalone module implemented in Rust to achieve data parallelism across SGLang instances."
|
||||
authors = [{name = "Byron Hsu", email = "byronhsu1230@gmail.com"}]
|
||||
requires-python = ">=3.8"
|
||||
|
||||
Reference in New Issue
Block a user