| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- // SGLang serve command tests. Moved out of `docker/tests.rs` so the parent
- // module stays under the ≤500-line gate. Helpers (`tcls`, `make_dm_ctx`,
- // `squash`, ...) are in `super`.
- use super::*;
- fn sglang_recipe() -> Recipe {
- Recipe {
- build_profile: None,
- parent_model: None,
- archived: false,
- engine: "sglang".into(),
- name: "S".into(),
- docker_image: "sglang-img".into(),
- model: "org/sgl-model".into(),
- port: 8000,
- required_nodes: Some(4),
- pipeline_parallel_size: None,
- tensor_parallel_size: 4,
- args: RecipeArgs::List(vec!["--mem-fraction-static".into(), "0.8".into()]),
- env: HashMap::from([("TORCH_NCCL_ASYNC_ERROR_HANDLING".into(), "1".into())]),
- cluster: None,
- entrypoint: vec!["sglang".into(), "serve".into()],
- distributed_backend: Some(DistributedBackend::Mp),
- before_serve: vec![],
- after_stop: vec![],
- solo: None,
- deploy: DeployTarget::Docker,
- venv: None,
- venv_setup: VenvSetup::default(),
- venv_setup_script: None,
- mods: Default::default(),
- shm_size: String::new(),
- devices: vec![],
- memory_limit: None,
- cpus: None,
- sync_image: true,
- docker_image_dir: None,
- extra_mounts: vec![],
- extra_mounts_manifest: None,
- build: Default::default(),
- composed_model: None,
- staging: vec![],
- }
- }
- fn srun(recipe: &Recipe, node: &Node, head: bool) -> String {
- let tp = if recipe.required_nodes == Some(recipe.tensor_parallel_size) {
- 4
- } else {
- recipe.tensor_parallel_size
- };
- let mut ctx = make_dm_ctx(recipe, 4, 4, tp, false);
- ctx.recipe_name = "sg".to_string();
- squash(&build_docker_command(&ctx, node, head).unwrap().join(" "))
- }
- #[test]
- fn sglang_host_ip() {
- let cmd = srun(&sglang_recipe(), &tcls().nodes[0], true);
- assert!(cmd.contains("-e SGLANG_WORKER_IP=1"));
- assert!(!cmd.contains("VLLM_HOST_IP"));
- }
- #[test]
- fn sglang_head() {
- let cmd = srun(&sglang_recipe(), &tcls().nodes[0], true);
- assert!(cmd.contains("--model-path"));
- assert!(cmd.contains("org/sgl-model"));
- assert!(cmd.contains("--tp 4"));
- assert!(cmd.contains("--nnodes 4"));
- assert!(cmd.contains("--node-rank 0"));
- assert!(cmd.contains("--dist-init-addr 1:25000"));
- assert!(!cmd.contains("--master-addr"));
- assert!(!cmd.contains("--tensor-parallel-size"));
- assert!(!cmd.contains("--pipeline-parallel-size"));
- assert!(!cmd.contains("--distributed-executor-backend"));
- assert!(cmd.contains("-e TORCH_NCCL_ASYNC_ERROR_HANDLING=1"));
- assert!(
- !cmd.contains("-p "),
- "host networking: no port mapping expected"
- );
- assert!(cmd.contains("--port 8000"));
- assert!(cmd.contains("-e HF_HOME=/root/.cache/huggingface"));
- assert!(cmd.contains("-v /home/user/.cache/huggingface:/root/.cache/huggingface"));
- assert!(
- cmd.contains("--rm"),
- "SGLang default --rm should be present"
- );
- }
- #[test]
- fn sglang_worker() {
- let cmd = srun(&sglang_recipe(), &tcls().nodes[1], false);
- assert!(!cmd.contains("-p 8000:8000"));
- assert!(cmd.contains("--node-rank 1"));
- assert!(cmd.contains("--port 8000"));
- }
- #[test]
- fn sglang_entrypoint_model_path_order() {
- let cmd = srun(&sglang_recipe(), &tcls().nodes[0], true);
- let ep = cmd.find("--entrypoint sglang").unwrap();
- let ip = cmd.find("img").unwrap();
- let mp = cmd.find("--model-path").unwrap();
- let mdl = cmd.find("org/sgl-model").unwrap();
- let tp = cmd.find("--tp 4").unwrap();
- assert!(
- ep < ip && ip < mp && mp < mdl,
- "entrypoint flag, then image, then model-path, then model"
- );
- assert!(mdl < tp, "model before --tp");
- let port = cmd.find("--port 8000").unwrap();
- assert!(tp < port, "--tp before --port");
- }
|