SGLang 기여 가이드

SGLang 기여 가이드

SGLang에 관심을 가져 주셔서 감사해요. 이 가이드는 개발 환경을 세팅하고, 테스트를 돌리고, 문서를 만들고, Pull Request(PR)를 올리는 전체 흐름을 한눈에 정리한 내용이에요. 작은 버그 수정이든 큰 기능 개발이든, 아래 순서를 따라가면 매끄럽게 기여하실 수 있어요.

출처: SGLang 기여 가이드

소스에서 SGLang 설치하기

저장소를 포크하고 클론하기

참고: 새로 참여하시는 분은 공식 SGLang 저장소에 바로 push할 권한이 없어요. 자기 GitHub 계정으로 저장소를 포크한 뒤, 그 포크를 로컬에 클론해서 작업하세요.

git clone https://github.com/<your_user_name>/sglang.git

소스에서 빌드하기

소스에서 SGLang 설치하기 문서를 참고하세요.

pre-commit으로 코드 포맷하기

일관된 코드 스타일을 유지하기 위해 pre-commit을 사용해요. 변경 사항을 push하기 전에 반드시 아래 명령을 실행하세요.

pip3 install pre-commit
pre-commit install
pre-commit run --all-files
  • pre-commit run --all-files 은 설정된 모든 검사를 직접 실행하며, 가능하면 자동으로 수정까지 해줘요. 첫 실행에서 실패해도 다시 실행하면 린트 오류가 모두 해결된 상태인지 확인할 수 있어요. Pull Request를 만들기 전에 모든 검사를 통과시켜 두어야 해요.
  • main 브랜치에 직접 커밋하지 마세요. 항상 새 브랜치(예: feature/my-new-feature)를 만들고, 그 브랜치에서 변경을 push한 뒤 PR을 열어야 해요.
  • 문서의 링크와 앵커는 CI에서 Mintlify로 검사해요. 같은 버전을 로컬에서 돌리려면 npm install -g [email protected]로 설치한 뒤 cd docs && mint broken-links --check-anchors --check-redirects를 실행하면 돼요.
  • 수동 lychee pre-commit 훅은 저장소 최상위 README.md의 링크를 검사해요. pre-commit run --hook-stage manual lychee --all-files로 실행할 수 있어요.

단위 테스트 추가하고 실행하기

기능을 추가하거나 버그를 고칠 때, 그 테스트가 실제 동작·불변 조건·북기핑 계약을 보호한다면 집중된 회귀 테스트를 추가해 주세요.

단위 테스트 (서버 불필요)

단위 테스트는 test/registered/unit/ 아래에 있고, python/sglang/srt/ 소스 트리를 그대로 반영한 구조를 가져요. 이 테스트들은 서버를 띄우지 않고 실제 모델 가중치를 로드하지 않은 채 컴포넌트 로직을 검증해요. SGLang은 Python 내장 unittestpytest를 모두 지원해요. 등록된 CI 테스트는 raw unittest.TestCase 대신 CustomTestCase를 쓰고, register_*_ci(...)로 자기자신을 등록하며, 표준 __main__ 진입점을 포함해야 해요. CI는 test/run_suite.py로 등록 정보를 찾아 각 테스트 파일을 fail-fast로 직접 실행해요.

단위 테스트를 추가해야 할 때: python/sglang/srt/ 아래 파일을 수정했다면, test/registered/unit/에 대응하는 테스트가 있는지 확인하고 내 변경을 커버하도록 추가해 주세요. 예:

srt/mem_cache/radix_cache.py   →  unit/mem_cache/test_radix_cache_unit.py
srt/sampling/sampling_params.py →  unit/sampling/test_sampling_params.py

로컬에서 테스트 실행하기:

# CI가 개별 등록 테스트 파일을 실행하는 방식과 가장 비슷한 실행
python3 test/registered/unit/mem_cache/test_radix_cache_unit.py

# 등록된 CI 스위트 실행
python3 test/run_suite.py --hw cpu --suite base-a-test-cpu

# 로컬에서 디렉토리 단위 발견에는 pytest도 유용
pytest test/registered/unit/mem_cache/ -v

커버리지와 함께 실행하기:

pytest test/registered/unit/ --cov --cov-config=.coveragerc -v

CI 등록 규칙, 테스트 구조, 예시에 대한 자세한 내용은 test/registered/unit/README.md를 보세요.

E2E 테스트 (서버 필요)

서버를 띄워야 하는 테스트는 test/registered/README.md를 보고 어디에 둘지 정하세요.

테스트 실행과 CI 통합에 대한 자세한 안내는 test/README.md를 참고하세요.

문서 작성하기

문서는 새로 참여하는 분들이 SGLang 코드베이스를 익히기에 아주 좋은 방법이에요. 현재 Mintlify 구성과 검증 명령은 docs/README.md를 보세요.

정확도 테스트하기

내 코드가 모델 출력을 바꾼다면, 영향받은 모델과 기능에 맞는 정확도 평가를 돌려야 해요. 예를 들어 서버를 띄운 뒤 통합 GSM8K 평가기를 실행해 보세요.

# 서버 실행
sglang serve --model-path Qwen/Qwen3-8B

# 평가 실행
python3 -m sglang.test.run_eval \
  --eval-name gsm8k \
  --port 30000 \
  --num-examples 200

위 스크립트는 기본적으로 스모크 체크용이라는 점을 기억하세요. 엄밀한 정확도나 속도 테스트가 아니에요. 배칭과 추론 엔진의 비결정성 때문에 정확도가 1%~5% 정도 변동할 수 있고요. 이 스크립트가 출력하는 "Latency/Output throughput"도 제대로 된 속도 테스트가 아니니 참고하지 마세요.

GSM8K는 최신 모델들에겐 너무 쉬운 문제라, 가능하면 더 어렵거나 기능에 특화된 평가를 쓰는 게 좋아요. 현재 MMLU·GSM8K·HellaSwag·GPQA·HumanEval·MMMU 명령은 SGLang으로 새 모델 평가하기 문서를 보세요. 추가 예시는 test/manual/eval에 있어요.

속도 벤치마크하기

벤치마크와 프로파일링 문서를 참고하세요.

머지 리뷰 요청하기

Pull Request 머지 절차는 MAINTAINER.md에 설명돼 있어요. Merge Oncall, Codeowner, 그리고 다른 리뷰어들과 협력해 승인을 받아야 하고, 그 다음에야 PR이 머지될 수 있어요.

CI 테스트 트리거하는 법

오픈 PR은 많지만 CI 머신은 한정돼 있어서, 상위·신뢰 기여자에게만 CI 테스트를 트리거할 권한을 드려요. 권한이 있는 사용자 목록은 CI_PERMISSIONS.json에서 확인할 수 있어요.

PR 작성자CI_PERMISSIONS.json에 없어도 자기 PR에서 /rerun-failed-ci를 쓸 수 있어요. 선택적 재실행은 self-hosted 러너에서 PR 코드를 실행하기 때문에 추가 규칙이 있어요. 아래 권한 표를 확인하세요.

PR에서 CI가 돌려면 "run-ci" 라벨이 붙어 있어야 해요. 권한이 있는 사용자가 PR에 댓글로 아래 명령 중 하나를 남기면 라벨을 추가하거나 실패한 테스트를 재실행해 줘요.

  • /tag-run-ci-label: "run-ci" 라벨을 붙여요. 이후의 커밋만 CI를 트리거하고, 현재 커밋에는 영향이 없어요. extra 인자를 붙이면(/tag-run-ci-label extra) "run-ci-extra" 라벨도 함께 적용해서 PR을 추가 테스트 워크플로(pr-test-extra.yml)에 넣어요.
  • /rerun-failed-ci: 가장 최근 커밋의 워크플로 중 결론이 failed, skipped, cancelled, timed out인 것만 재실행해요.
  • /tag-and-rerun-ci: 두 가지를 모두 실행해요. 새 PR에서 현재 커밋에 CI를 걸려면 /tag-run-ci-label만으로는 부족하니까 이 명령을 쓰세요. extra 인자도 동일하게 받아요(/tag-and-rerun-ci extra).
  • /run-full-ci: 기본 그리고 추가 CI를 모두 돌려요. /tag-and-rerun-ci extra의 짧은 형태예요 — 두 라벨을 모두 붙인 뒤, 현재 커밋에서 아직 성공하지 못한 워크플로를 전부 재실행해요.
  • /run-extra-ci: 추가 CI 돌려요. 두 라벨을 모두 붙인 뒤 PR Test Extra만 재실행하고 기본 CI는 건드리지 않아요. 기본 CI가 이미 초록이고 추가 등급만 필요할 때 쓰면 돼요.
  • /rerun-test <test-spec> [<test-spec> ...]: 특정 테스트 하나 이상을 직접 재실행해요. 스펙은 <file>::<TestClass>[.<test_method>] 형식으로 파일·클래스·메서드를 고를 수 있어요. 여러 스펙과 파일 글롭을 지원해요. --changed(짧은 형태 -c)는 PR이 추가하거나 수정한 모든 테스트 파일(test/registered/ 아래 또는 멀티모달 테스트 디렉토리)을 포함해요. PR이 여러 테스트를 건드렸다면 일일이 나열하지 않고 전부 재실행할 수 있죠. 예: /rerun-test test_srt_endpoint.py, /rerun-test registered/core/test_srt_endpoint.py::TestSRTEndpoint.test_simple_decode, /rerun-test test_a.py test_b.py, /rerun-test test_*backend*.py, /rerun-test --changed, /rerun-test -c.
  • /rerun-group <group> [<group> ...]: 등록된 테스트 그룹 하나 이상을 펼쳐서(예: /rerun-group rust-server) 그 테스트들을 같은 선택적 재실행 워크플로로 보내요. 그룹 정의는 scripts/ci/rerun_test_groups.json에 있어요.

재실행 명령의 권한 규칙은 다음과 같아요.

명령 PR 작성자 다른 사용자 포크 PR의 추가 규칙
/rerun-failed-ci 자기 PR에서는 항상 허용 CI_PERMISSIONS.jsoncan_rerun_failed_ci가 있어야 함 없음
/run-full-ci, /run-extra-ci 자동 허용 아님 — 이 명령은 라벨을 적용하며, PR 작성자 허용 범위에 포함되지 않음 can_tag_run_ci_labelcan_rerun_failed_ci가 모두 있어야 함(모든 등록 사용자는 둘 다 보유) 없음
/rerun-test, /rerun-group 특별 허용 없음 — 다른 댓글 작성자와 동일하게 판정 CI_PERMISSIONS.jsoncooldown_interval_minutes: 0이 있거나, write/admin 저장소 권한 필요 없음 — PR이 어디서 왔든 동일한 규칙 적용
선택적 재실행은 PR 전용 `sglang-kernel` 휠을 빌드하거나 설치하지 않아요. PR이 `python/sglang/kernels/aot/`나 그 AOT 변경에 의존하는 코드를 수정한다면, 일반/전체 PR CI 워크플로를 사용하세요. `/rerun-test` 또는 `/rerun-group` 결과는 고정된 릴리스 커널을 사용할 수 있어서 결합 변경을 검증하지 못해요.

권한이 있다면 Slash Command Handler가 명령을 실행하고 댓글에 👍로 반응해요. 반응이 나타나기까지 몇 분 걸릴 수 있어요. 사용 예시를 참고하세요.

명령을 인식하지 못하면 헨들러가 대신 😕로 반응하고, 가장 비슷한 명령이 있으면 그걸 답글로 알려줘요. 반응이 전혀 없다면 헨들러가 댓글을 보지 못한 거예요 — 명령이 위 목록대로 정확히 적었는지 확인하세요.

PR에 /rerun-failed-ci 댓글이 너무 많이 쌓이지 않게, 기존 댓글을 수정하고 접미사(예: /rerun-failed-ci try again)를 붙여서 실행할 수도 있어요.

권한이 없고 PR 작성자도 아니라면, 관리자에게 CI 트리거를 요청하세요.

CI 레이트 리밋

CI 스케줄링과 한정된 리소스 때문에 우선순위가 높은 PR이 실행 중인 잡을 선점할 수 있어요. 그러면 테스트를 다시 실행해야 할 수 있어요. 우리는 남용을 막고 CI 리소스를 공정하게 쓰기 위해 CI 레이트 리밋을 적용해요.

각 CI 워크플로마다 워크플로 설정 파일에 기본 한도가 정의돼 있어요. 예를 들어 pr-gate.yml에서는 기본 쿨다운이 120분이고, 각 워크플로가 cool-down-minutes 입력 파라미터로 이를 재정의할 수 있어요.

cool-down-minutes:
  description: "Cooldown period in minutes for low-permission users; 0 disables rate limiting"
  type: number
  default: 120

CI_PERMISSIONS.json에 등록된 사용자는 사용자별 쿨다운 간격이 있을 수 있어요. 실제로는 워크플로 기본 창과 사용자별 간격 중 더 작은 값을 사용해요.

코드 스타일 가이드

  • 코드 중복을 피하세요. 같은 코드 조각(다섯 줄 이상)이 여러 번 나오면 공유 함수로 추출하세요.
  • 디바이스 동기화를 최소화하세요. tensor.item()이나 tensor.cpu() 같은 값비싼 CPU-GPU 동기화 연산은 최대한 줄이고, 벡터화된 코드를 쓰세요.
  • 극단적인 효율을 우선하세요. SGLang은 런타임이라 내 코드 대부분이 모든 요청의 critical path에서 돌아요. 특히 모델 forward 코드에서는 모든 사소한 오버헤드를 최대한 최적화하세요.
    • 흔한 패턴으로 모델 forward 패스의 런타임 체크(예: 여기)가 있는데, 이 체크는 거의 모든 레이어에서 동일할 가능성이 높아요. 가능하면 __init__에서 결과를 단일 불리언 값으로 캐시하세요.
  • 함수를 최대한 순수하게 유지하세요. 인자를 in-place로 수정하지 마세요.
  • 불변 데이터를 선호하고, 입력이 바뀔 수 없을 때는 구성에서 파생된 값을 초기화 중에 한 번만 계산하세요.
  • 함수는 대략 100줄 이하로 유지하고, 오케스트레이션 함수는 세부 사항을 집중된 헬퍼로 추출해서 마치 고수준 의사코드처럼 읽히게 만드세요.
  • 파일을 간결하게 유지하세요. 파일이 코드 2,000줄을 넘으면 응집력 있는 작은 모듈로 나누세요.
  • mixin 추가는 피하고, 컴포지션 또는 일반 함수를 선호하세요. 새 데이터 컨테이너에는 dataclasses.dataclassattrs 대신 msgspec.Struct를 쓰세요.
  • 인자가 두 개 이상인 호출에는 키워드 인자를 선호하고, 큰 상태 저장 객체 대신 호출 대상이 실제로 필요한 특정 값을 넘기세요.
  • 파일에서 핵심 데이터 구조는 파일 상단에, 유틸리티 함수는 하단에 두세요.
  • 테스트가 빨리 돌게 유지하세요.
    • 단일 테스트 파일이 500초보다 오래 걸리면 여러 작은 파일로 나누세요(예: test_eagle_infer_a.py, test_eagle_infer_b.py).
    • GitHub Actions 워크플로의 단일 잡이 30분보다 오래 걸리면 더 작은 잡이나 스텝으로 나누세요.
    • E2E 테스트 파일에서는 테스트 메서드 간에 서버 실행을 재사용하세요.
  • 신뢰할 수 없거나 네트워크에서 받은 데이터를 역직렬화할 때 pickle.loads(), pickle.load(), recv_pyobj()를 절대 쓰지 마세요. Python의 pickle 모듈은 안전하지 않아요 — 역직렬화 중 임의 코드를 실행할 수 있어요. msgpack이나 JSON 같은 안전한 직렬화 포맷을 쓰세요.
  • 새 하드웨어나 기능을 지원할 때는 다음 지침을 따르세요.
    • 기존 코드를 크게 바꾸지 마세요.
    • 새 하드웨어 전용 컴포넌트는 항상 새 파일로 도입하는 걸 선호하세요(예: allocator_ascend.py).
    • 새 기능에 if/else 블록을 여러 개 쓴다면, 공통 경로(예: NVIDIA 하드웨어 또는 기존 코드 경로)가 첫 번째 분기가 되게 하세요.

SGLang에서 커널 업데이트하는 법

커널의 의존성에 따라 구현과 릴리스 경로를 선택하세요. CUTLASS나 다른 큰 C++ 프로젝트에 의존하지 않는 가벼운 커널은 in-tree JIT 커널 경로를 선호하세요. 무거운 커널, 큰 C++ 의존성, 또는 휠 패키징과 Torch 연산자 등록이 필요한 작업에는 AOT sglang-kernel 경로를 쓰세요. FlashInfer 기반 커널은 예외로 JIT 경로를 계속 쓸 수 있어요. SGLang은 별도 릴리스되는 DeepGEMM·DeepEP 커스텀 빌드도 소비하므로, 아래 설명처럼 원본 저장소에서 업데이트하세요.

sglang-kernel 업데이트

sglang-kernel 배포판(이전 이름 sgl-kernel)은 별도 Python 패키지지만, 소스는 이제 이 저장소의 python/sglang/kernels/aot/ 아래에 있어요. 일반 PR CI는 PR 전용 sglang-kernel 휠을 빌드·설치하므로 AOT 커널과 호출자를 함께 테스트할 수 있어요. 하지만 이것이 머지 후 호환성을 보장하진 않아요: 설치된 SGLang과 스케줄된 CI는 python/pyproject.toml에 고정된 릴리스 버전을 사용해요. 호출자가 새 연산자나 변경된 커널 계약을 무조건 요구한다면, 먼저 커널을 머지·릴리스하고 고정된 sglang-kernel 버전을 올린 뒤 호출자를 반영하세요. 지원되는 모든 경로가 고정 휠로도 올바른 경우에만 결합 PR이 허용돼요 — 예를 들어 호출자가 연산자 가용성을 확인하고 릴리스 휠에 변경이 포함될 때까지 의미상 동등한 폴백을 유지하는 경우예요. 선택적 /rerun-test, /rerun-group 워크플로는 PR 전용 휠을 설치하지 않으므로 결합 AOT 경로를 검증할 수 없어요.

AOT 커널 변경의 경우:

  1. python/sglang/kernels/aot/csrc/ 아래에 커널을 구현하고, 선언·Torch 등록·CMake 소스 목록을 갱신하세요.
  2. python/sglang/kernels/aot/python/sgl_kernel/ 아래에 Python API를 노출하세요.
  3. python/sglang/kernels/aot/tests/ 아래에 정확성 테스트를, 해당되면 python/sglang/kernels/aot/benchmark/ 아래에 벤치마크를 추가하세요.
  4. python/sglang/kernels/aot/에서 README를 따라 빌드·테스트하세요. PR 전용 커널 변경을 테스트하려고 python/pyproject.toml의 고정 sglang-kernel 버전을 올리지는 마세요.

sgl-deep-gemm 업데이트

SGLang의 맞춤 DeepGEMM 패키지는 sgl-project/DeepGEMMdev 브랜치에서 개발해요. 들어오는 변경을 dev에 리베이스하고, 새·수정된 패키지 테스트는 sgl_deep_gemm/tests/ 아래에 두며, sgl-deep-gemm README를 따라 로컬 휠을 빌드·설치하세요. 구현이 머지된 뒤에는 SGLang 팀에 sgl-deep-gemm 릴리스 워크플로를 새 버전·CUDA 타깃·DeepGEMM 브랜치로 실행해 달라고 요청하세요. 필요한 휠이 모두 게시·검증되면, 새 동작이 필요한 SGLang 코드를 반영하기 전에 python/pyproject.tomlsgl-deep-gemm 고정 버전을 올리세요.

sgl-deep-ep 업데이트

sgl-deep-epsgl-project/DeepEP에서 개발해요. CUDA 13(x86_64 또는 aarch64)용 구현 브랜치인 sgl-deepep을 사용하세요. 패키징 변경은 sgl-deepep-packaging에 머지하세요. sgl-deep-ep README에 플랫폼 사전 조건과 릴리스 매트릭스가 설명돼 있어요.

로컬 검증을 위해 선택한 구현 브랜치를 DeepEP-source로, 패키징 브랜치를 DeepEP-packaging으로 체크아웃하세요. 먼저 필요한 빌드 의존성을 설치해요. 아래 예시는 호스트 아키텍처용 휠을 빌드하고, 그 정확한 휠을 설치한 뒤 가드된 패키지 import가 성공하는지 확인해요.

DEEPEP_OUTPUT_DIR="$(mktemp -d)"
bash DeepEP-packaging/sgl_deep_ep/build_sgl_deep_ep.sh \
  DeepEP-source \
  DeepEP-packaging/sgl_deep_ep \
  "${DEEPEP_OUTPUT_DIR}" \
  13.0 \
  "$(uname -m)"
python3 -m pip install --force-reinstall --no-deps \
  "${DEEPEP_OUTPUT_DIR}"/sgl_deep_ep-*.whl
python3 -c "import deep_ep; print(deep_ep.__file__)"

import 확인은 패키징과 바이너리 로딩을 검증할 뿐 통신 정확성은 검증하지 않아요. 멀티 GPU가 구성된 호스트에서는 아래도 실행하세요.

python3 DeepEP-source/tests/elastic/test_ep.py --num-processes 8

--num-processes는 가용 GPU에 맞추고, 해당 전송이 바뀌었다면 inter-node 또는 저지연 테스트도 실행하세요. 로컬 검증 후 SGLang 팀에 sgl-deep-ep 릴리스 워크플로를 새 버전·패키징 ref로 실행해 달라고 요청하세요. 지원되는 Python 버전·아키텍처의 휠이 게시·검증된 것을 확인한 뒤, 의존하는 SGLang 변경을 반영하기 전에 python/pyproject.tomlsgl-deep-ep 고정 버전을 올리세요.

새로 오신 분을 위한 팁

기여하고 싶은데 구체적인 아이디어가 없다면 "good first issue" 또는 "help wanted" 라벨이 붙은 이슈를 골라보세요. 이 작업들은 보통 복잡도가 낮아서 코드베이스를 익히기에 아주 좋아요.

시작 가이드로 아래 자료도 함께 보세요.

  • Mini-SGLang — sglang 구조를 빠르게 훑어볼 수 있어요.
  • Code Walk-through — SGLang 워크플로를 더 깊게 볼 수 있어요.
  • GTC-2026 Training Lab — 실행 중인 SGLang 인스턴스에서 최적화·벤치마킹·프로파일링을 직접 해보는 실습이에요.

질문이 있거나 토론을 시작하고 싶다면 Slack 채널에서 편하게 물어보세요.

SGLang에 관심을 가져 주셔서 감사해요. 해피 코딩!

더 알아보기 (Learn more)