SGLang 플러그인 시스템

SGLang 플러그인 시스템 (SGLang Plugin System)

이 문서는 하드웨어 벤더와 개발자가 메인 저장소 코드를 수정하지 않고 SGLang을 확장하는 방법을 설명해요. 프레임워크는 두 가지 플러그인 유형을 제공하며, 둘 다 Python의 표준 setuptools entry_points를 통해 발견돼요.

출처: 문서

본문

플러그인 유형 엔트리 포인트 그룹 목적
하드웨어 플랫폼 플러그인 sglang.srt.platforms
sglang.multimodal\_gen.platforms
커스텀 하드웨어 플랫폼 등록 (디바이스 연산, KV 캐시 풀, 어텐션 백엔드, 그래프 캡처, 컴파일 백엔드 등)
일반 플러그인 sglang.srt.plugins
sglang.multimodal\_gen.plugins
모든 함수/메서드에 훅 삽입 (before/after/around/replace), 또는 전체 클래스 대체

원칙 (Principles)

  • 비간섭적 (Non-intrusive): OOT 플랫폼이 활성화되지 않으면 내장 플랫폼이 폴백으로 유지돼요.
  • 설치 시 발견 (Install-time discovery): 플러그인은 설치 후 Python 엔트리 포인트에서 발견돼요.
  • 환경 변수 제어: SGLANG_PLATFORM은 SRT 플랫폼을 선택하고, SGLANG_DIFFUSION_PLATFORM_OVERRIDE는 diffusion 플랫폼을 선택하며, SGLANG_PLUGINS는 두 훅 그룹 중 하나에서 일반 플러그인을 필터링해요.

현재 범위 (Current scope)

플랫폼 플러그인 시스템은 out-of-tree (OOT) 하드웨어 플랫폼을 대상으로 해요. Diffusion 지원은 실험적이며 아래 문서화된 지점들만 다루고, 모든 디바이스별 분기를 다루지는 않아요.

아키텍처 (Architecture)

런타임별 플랫폼 인터페이스 (Runtime-specific platform interfaces)

SRT와 diffusion은 sglang.srt.platformssglang.multimodal_gen.runtime.platforms에 별도의 플랫폼 베이스 클래스와 플랫폼 정체성 유형을 가져요. 두 런타임을 모두 지원하는 패키지는 별도의 플랫폼 클래스를 정의하고 각 런타임의 PlatformEnum.OOT 값을 사용해야 해요.

각 플랫폼 엔트리 포인트는 인자 없는 활성화 콜백으로 해석돼요:

def activate() -> str | None:
    """이 하드웨어가 사용 가능할 때 플랫폼 클래스 qualname을 반환."""

프로바이더가 실행될 수 있으면 정규화된 클래스 이름을, 그렇지 않으면 None을 반환하세요. 활성화를 import-safe하게 유지하세요: current_platform에 접근하거나 런타임/디바이스 상태를 초기화하지 마세요. 필요한 백엔드 설정은 플랫폼의 init_backend() 메서드에 두고, 플랫폼 인터페이스로 표현할 수 없는 훅만 일반 플러그인에 남겨두세요.

명시적 선택은 엔트리 포인트 메타데이터를 열거하고 선택된 콜백만 임포트해요. 자동 선택은 설치된 플랫폼 콜백을 호출해 어떤 프로바이더가 활성인지 결정해요.

Diffusion은 cpu, cuda, rocm, xpu, mps, npu, musa를 보유해요. 자동 발견은 중복 엔트리 포인트 이름도 거부하며, 명시적 선택은 선택된 이름만 검증하고 관련 없는 프로바이더를 임포트하지 않아요.

SRT 선택 (SRT selection)

current_platformsglang.srt.platformslazy singleton이에요. 첫 접근 시 다음 우선순위 체인으로 활성 플랫폼을 해석해요:

entry_points("sglang.srt.platforms")  → 이름으로 ALL 플러그인 열거 (메타데이터만)
  │
  ├─ SGLANG_PLATFORM 설정 (front-loading 필터):
  │   ├─ 발견된 이름 없음 → RuntimeError
  │   ├─ activate()가 non-None 반환  → 해당 플랫폼 로드
  │   └─ activate()가 None 반환      → RuntimeError (하드웨어 사용 불가)
  │
  └─ SGLANG_PLATFORM 미설정 (자동 발견, 전부 활성화):
      ├─ 0 활성화 + SGLANG_USE_CPU_ENGINE=1 → 폴백 CpuSRTPlatform
      ├─ 0 활성화 + CUDA 사용 가능          → 폴백 CudaSRTPlatform
      ├─ 0 활성화 + ROCm 사용 가능          → 폴백 RocmSRTPlatform
      ├─ 0 활성화 + XPU 사용 가능           → 폴백 XpuSRTPlatform
      ├─ 0 활성화 + 위 모두 아님            → 폴백 기본 SRTPlatform
      ├─ 1 활성화 → 사용
      └─ N 활성화 → RuntimeError (SGLANG_PLATFORM 설정 필요)
Diffusion 선택 (Diffusion selection)

SGLang Diffusion은 다음 순서로 플랫폼을 해석해요:

  1. SGLANG_DIFFUSION_PLATFORM_OVERRIDEcpu, cuda, rocm, mps, npu, musa 중 하나를 가리키면 하드웨어 프로빙 없이 그 내장 플랫폼을 선택해요. XPU는 기존 선택기 동작을 유지하며 자동 전용으로 남아요.
  2. 다른 이름을 포함하면 일치하는 sglang.multimodal_gen.platforms 엔트리 포인트만 로드해요. 알 수 없는 이름이나 None을 반환하는 콜백은 오류예요.
  3. 설정되지 않으면 설치된 diffusion 플랫폼 플러그인을 활성화해요. 활성 플러그인이 없으면 내장 감지로 계속되고, 하나가 선택되면 그 플러그인을 선택하며, 여러 개 활성화되면 선택기를 설정하라는 오류를 발생시켜요. 활성화 콜백이 예외를 던지면 폴백하지 않고 시작을 중단해, 손상된 벤더 런타임이 내장 플랫폼에서 조용히 작업을 실행하지 못하게 해요.
  4. 내장 플랫폼을 순서대로 시도해요: MPS, XPU, ROCm, CUDA, NPU, MUSA, 그 다음 CPU.

따라서 기존 오버라이드 변수는 지원되는 내장 별칭과 OOT 엔트리 포인트 이름을 위한 유일한 명시적 선택기예요.

선택은 프로세스에서 어떤 것이든 current_platform을 처음 건드릴 때 늦게 해석되므로 호출 지점이 필요 없고 모든 프로세스에서 자동으로 일어나요. SGLang Diffusion은 또한 선택한 플랫폼을 제공한 배포(distribution)를 기록하고 다른 모든 설치된 플랫폼 패키지의 훅을 건너뛰어요.

필수 플랫폼 초기화 vs 선택적 훅 (Required platform initialization vs. optional hooks)

두 메커니즘은 실패 의미가 다르며, 어느 것을 사용하는지가 중요해요:

플랫폼 콘트랙트 일반 플러그인
엔트리 포인트 그룹 sglang.multimodal_gen.platforms sglang.multimodal_gen.plugins
전달 Platform 서브클래스의 메서드 대상을 monkey-patch하는 훅
활성화 lazy current_platform + 각 워커의 guarded init_backend() load_plugins()로 등록 후, 프로세스당 명시적 apply_plugin_hooks()
실패 시작 중단 플러그인이 선택된 플랫폼의 배포에 있으면 시작 중단, 그 외에는 로깅

하드웨어가 올바르게 동작하기 위해 필요한 모든 것은 Platform 서브클래스에 있어야 하므로, 손상된 플랫폼이 조용히 서빙할 수 없어요. Platform 인터페이스에 필요한 부분이 없을 때만 일반 플러그인을 사용하고, 그 격차를 보고해 주세요.

선택된 플랫폼 자체의 배포에 포함된 플러그인은 그 플랫폼의 콘트랙트 일부로 취급돼요: 로드 실패, 콜백 실행 실패, 등록한 훅 적용 실패는 플랫폼을 반쯤 초기화한 채로 두지 않고 시작을 중단해요. 다른 설치된 패키지의 플러그인은 최선 노력(best-effort)으로 유지되므로 손상된 타사가 서버를 다운시키지 못해요. 명시적 SGLANG_PLUGINS 허용 목록은 선택된 플랫폼 패키지의 것을 포함해 모든 일반 플러그인을 비활성화할 수 있어요. 따라서 필수 하드웨어 설정은 init_backend()에 있어야 해요.

플러그인 로딩 흐름 (Plugin Loading Flow)

각 런타임에는 프로세스 로컬 훅 레지스트리가 있어요. SRT는 단일 load_plugins() 활성화 단계를 유지해요. Diffusion은 점으로 구분된 훅 대상 해석이 대상의 전체 모듈 그래프를 임포트할 수 있기 때문에 등록(load_plugins())과 대상 해석(apply_plugin_hooks())을 분리해요. 두 런타임 모두 SGLANG_PLUGINS를 존중해요.

로더는 다음 SRT 엔트리 포인트에서 호출돼요:

호출 지점 프로세스 시점
cli/serve.py serve() Main prepare\_server\_args()
launch\_server.py **main** Main prepare\_server\_args()
engine.py \_launch\_subprocesses() Main server\_args.check\_server\_args()
scheduler.py run\_scheduler\_process() Subprocess Scheduler() 생성 전

참고: Diffusion 플러그인 등록과 훅 적용은 각각 프로세스당 한 번 실행돼요. 생성된 서브프로세스는 빈 인터프리터에서 시작해 자신의 훅을 확립해요 — 부모가 패치한 어떤 것도 spawn 경계를 넘지 못해요. SRT의 load_plugins()는 두 단계를 한 번의 호출로 수행해요.

load_plugins()
  ├── _get_excluded_dists()                       → 건너뛸 dists 계산 (SGLANG_PLATFORM 통해)
  ├── load_plugins_by_group("sglang.srt.plugins", → entry_points 발견, SGLANG_PLUGINS로 필터
  │     excluded_dists=...)                          선택되지 않은 플랫폼 패키지의 플러그인 건너뜀
  ├── for each plugin:                            → _current_plugin_source 컨텍스트 var 설정
  │     func()                                      부작용 (셧로 매핑 훅 등록)
  └── HookRegistry.apply_hooks()                  → 대상 monkey-patch

Diffusion 런처는 apply_plugin_hooks()를 호출하며, 필요하면 먼저 등록을 수행한 후 대상을 해석하고 패치해요. 스케줄러 자식은 runtime/managers/worker_bootstrap.py에서 더 엄격한 라이프사이클을 사용해요:

spawn unpickles SchedulerProcessSpec (stdlib 타입 + opaque ServerArgs bytes)
  -> initialize_current_platform()
  -> load_plugins()                 # 콜백 발견 및 훅 등록만
  -> apply_plugin_hooks()           # 여기부터 대상 임포트 허용
  -> materialize ServerArgs
  -> resolve and invoke the patched run_scheduler_process

HTTP 자식도 runtime.launch_server를 임포트하거나 ServerArgs를 materialize하기 전에 플러그인 등록과 훅 적용을 끝내요. 이는 서버 모듈 그래프 임포트 중에 만들어진 클래스나 등록에 클래스 대체와 같은 훅 효과를 소급 적용할 수 없기 때문에 중요해요.

부트스트랩 모듈은 mp.Process 대상이며 모듈 스코프에서 diffusion 런타임 모듈을 임포트하지 않아요. ServerArgsServerArgsPayload 안에서 직렬화되므로 multiprocessing unpickler가 대상 시작 전에 파이프라인 구성 모듈을 임포트할 수 없어요. 워커는 항상 임베딩 애플리케이션의 전역 multiprocessing 설정과 무관한 로컬 spawn 컨텍스트를 사용해요.

이 순서는 백엔드 초기화가 플러그인 콜백 임포트, 훅 대상 해석, ServerArgs materialization, 워커 임포트보다 앞서도록 해요. 플랫폼 활성화 모듈은 필연적으로 자신의 init_backend() 전에 로드되므로 활성화 모듈은 import-safe로 유지되어야 해요. 필수 동작에는 플랫폼 메서드와 레지스트리를 선호하세요.

각 활성화 단계는 프로세스당 한 번만 lock 뒤에서 실행돼요: 플러그인 콜백에서 재진입 호출은 반환하고 바깥 호출이 마치도록 하며, 다른 스레드는 기다리며, 실패는 치명적이에요. 콜백은 활성화를 두 번째 스레드에 넘겨 조인하면 안 돼요 — 교착 상태가 발생해요.

오프라인 스크립트와 spawn 경계 (Offline scripts and the spawn boundary)

spawn은 대상의 인자를 unpickle하기 전에 모든 자식에서 실행 스크립트의 모듈 스코프를 다시 실행하므로, 오프라인 스크립트 자신의 임포트가 그 자식의 플랫폼 초기화보다 앞서 실행되고 ServerArgsPayload가 도움이 될 수 없어요. 지원되는 스크립트 레이아웃은:

from sglang.multimodal_gen import DiffGenerator  # a proxy: imports nothing yet

if __name__ == "__main__":
    generator = DiffGenerator.from_pretrained(model_path="...")

sglang.multimodal_gen 파사드의 DiffGenerator는 lazy proxy이므로 모듈 스코프에서 바인딩해도 diffusion 임포트 비용이 없고 자식은 깨끗한 모듈 테이블로 initialize_current_platform()에 도달해요. 모듈 스코프는 envsruntime.platforms(및 runtime.platforms.plugins)에도 열려 있어요. 이 모듈들은 import-safe로 유지되므로 플러그인이 백엔드 초기화 전에 Platform을 서브클래스화하고 훅을 등록할 수 있어요. 같은 파사드의 SamplingParamsPipelineConfig 같은 다른 모든 것은 if __name__ == "__main__": 가드 안이나 그것을 사용하는 함수 안에 넣어야 해요. 이미 임포트된 런타임 모듈을 발견한 자식은 경고로 그 이름을 알려줘요: 훅 적용이 그 모듈을 패치할 수는 있지만, 임포트 중에 만들어진 클래스와 등록은 이미 도달 범위를 벗어났어요.


플러그인 유형 1: 하드웨어 플랫폼 플러그인 (Hardware Platform Plugin)

설명 (Description)

하드웨어 플랫폼 플러그인은 SRT SRTPlatform 서브클래스, diffusion Platform 서브클래스 또는 둘 다를 등록해요. 선택된 클래스는 해당 런타임에 특정 하드웨어 백엔드와 상호작용하는 방법을 알려줘요.

SRT 퀵스타트 (SRT quick start)

1. 최소 패키지 생성:

my_platform_plugin/
├── pyproject.toml
└── my_platform_plugin/
    ├── __init__.py    # activate() function
    ├── device.py      # MyDeviceMixin
    └── platform.py    # MySRTPlatform

2. pyproject.toml:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "my-platform-plugin"
version = "0.1.0"

[project.entry-points."sglang.srt.platforms"]
my_device = "my_platform_plugin:activate"

3. __init__.py — 활성화 함수:

def activate():
    """활성화할 정규화된 클래스 이름을 반환하거나, 건너뛰려면 None을 반환."""
    if _my_device_is_available():
        return "my_platform_plugin.platform.MySRTPlatform"
    return None

4. device.py — 디바이스 믹스인:

from sglang.srt.platforms.device_mixin import DeviceMixin, PlatformEnum

class MyDeviceMixin(DeviceMixin):
    _enum = PlatformEnum.OOT
    device_name = "my_device"
    device_type = "my_device"   # torch device type

    def set_device(self, device) -> None: ...
    def get_device_name(self, device_id=0) -> str: ...
    def get_device_total_memory(self, device_id=0) -> int: ...
    def get_current_memory_usage(self, device=None) -> float: ...
    def get_device_capability(self, device_id=0): ...
    def get_torch_distributed_backend_str(self) -> str: ...

5. platform.py — SRT 플랫폼:

from sglang.srt.platforms.interface import SRTPlatform
from my_platform_plugin.device import MyDeviceMixin

class MySRTPlatform(SRTPlatform, MyDeviceMixin):
    def get_default_attention_backend(self) -> str: ...
    def support_cuda_graph(self) -> bool: ...
    # ... 필요에 따라 다른 메서드 오버라이드

6. 설치 및 검증:

pip install -e my_platform_plugin/
python -c "from sglang.srt.platforms import current_platform; print(current_platform)"

SRT 플랫폼 인터페이스 참조 (SRT platform interface reference)

정체성 조회 (Identity Queries, from DeviceMixin)

메서드 기본값 설명
is\_cuda() \_enum 기반 NVIDIA CUDA 플랫폼인지 여부
is\_rocm() \_enum 기반 AMD ROCm 플랫폼인지 여부
is\_npu() \_enum 기반 Huawei NPU 플랫폼인지 여부
is\_cpu() \_enum 기반 CPU 전용 플랫폼인지 여부
is\_xpu() \_enum 기반 Intel XPU 플랫폼인지 여부
is\_musa() \_enum 기반 Moore Threads MUSA 플랫폼인지 여부
is\_cuda\_alike() CUDA+ROCM+MUSA 하드웨어가 CUDA 유사 API를 지원하면 True
is\_out\_of\_tree() OOT에 True \_enum = PlatformEnum.OOT 기반 자동 감지

디바이스 연산 (Device Operations, from DeviceMixin)

[Active] 로 표시된 메서드는 current_platform을 통해 SGLang 코어가 호출해요 — OOT 구현이 즉시 적용돼요. [Planned] 로 표시된 메서드는 예약된 인터페이스예요 — SGLang 코어는 여전히 하드코딩된 호출(예: torch.cuda.empty_cache())을 사용해요. OOT 구현은 코어가 향후 PR에서 마이그레이션될 때까지 적용되지 않아요.

메서드 기본값 상태 설명
get\_device(local\_rank) raise NotImplementedError Planned 주어진 로컬 랭크의 torch.device 반환
set\_device(device) raise NotImplementedError Planned 현재 디바이스 설정
get\_device\_name(device\_id) raise NotImplementedError Planned 사람이 읽을 수 있는 디바이스 이름
get\_device\_uuid(device\_id) raise NotImplementedError Planned 고유 디바이스 식별자
get\_device\_capability(device\_id) raise NotImplementedError Planned DeviceCapability(major, minor) 반환. 해당 없으면 None
empty\_cache() pass Planned 캐시된 디바이스 메모리 해제
synchronize() pass Planned 디바이스 연산 동기화
get\_device\_total\_memory(device\_id) raise NotImplementedError Active 바이트 단위 총 디바이스 메모리
get\_available\_memory(device\_id) raise NotImplementedError Planned (free\_bytes, total\_bytes) 반환
get\_current\_memory\_usage(device) raise NotImplementedError Active 바이트 단위 현재 최고 메모리 사용량
is\_pin\_memory\_available(device=None) False Active 대상 디바이스에 pinned 호스트 메모리 사용 가능 여부
get\_torch\_distributed\_backend\_str() raise NotImplementedError Planned 분산 백엔드 문자열 (예: "nccl", "hccl")
get\_communicator\_class() None Planned 플랫폼별 커뮤니케이터 클래스
inference\_mode() torch.inference\_mode(True) Planned 추론 모드 컨텍스트 매니저 반환
seed\_everything(seed) Set random/np/torch seeds Planned 재현성을 위한 난수 시드 설정
verify\_quantization(quant) pass Planned 양자화 방법 지원 검증
get\_cpu\_architecture() Auto-detect x86/arm Planned CPU 아키텍처 감지 (CpuArchEnum)

유형 (Types, from DeviceMixin)

유형 설명
PlatformEnum 플랫폼 유형 열거: CUDA, ROCM, CPU, XPU, MUSA, NPU, TPU, MPS, OOT, UNSPECIFIED
CpuArchEnum CPU 아키텍처: X86, ARM, UNSPECIFIED
DeviceCapability 비교 지원이 있는 NamedTuple(major, minor). 메서드: as\_version\_str(), to\_int()

기능 플래그 (Capability Flags, from SRTPlatform)

메서드 기본값 설명
support\_cuda\_graph() False 디바이스 그래프 캡처 지원 여부 (일반 CUDA graph)
support\_piecewise\_cuda\_graph() False piecewise CUDA graph (torch.compile backend) 지원 여부
supports\_fp8() False FP8 양자화 지원 여부

서브시스템 팩토리 메서드 (Subsystem Factory Methods, from SRTPlatform)

메서드 기본값 설명
get\_default\_attention\_backend() raise NotImplementedError 기본 어텐션 백엔드 이름
get\_graph\_runner\_cls() raise NotImplementedError Graph Runner 클래스
get\_mha\_kv\_pool\_cls() raise NotImplementedError MHA KV 캐시 풀 클래스
get\_mla\_kv\_pool\_cls() raise NotImplementedError MLA KV 캐시 풀 클래스
get\_dsa\_kv\_pool\_cls() raise NotImplementedError DSA KV 캐시 풀 클래스 (DeepSeek V3.2)
get\_paged\_allocator\_cls() raise NotImplementedError Paged 할당기 클래스
get\_quantization\_config(quantization) raise NotImplementedError 특정 양자화 방식의 하드웨어별 양자화 구성 반환, 미지원이면 오류 발생 또는 기본 구성을 쓰려면 None 반환
get\_piecewise\_backend\_cls() raise NotImplementedError Piecewise 컴파일 백엔드 클래스
get\_compile\_backend(mode) "inductor" 컴파일 백엔드 문자열
get\_dispatch\_key\_name() "native" BaseFusedOp (fused-op) 디스패치 키 이름

라이프사이클 훅 (Lifecycle Hooks, from SRTPlatform)

메서드 호출 시점 목적
apply\_server\_args\_defaults(server\_args) ServerArgs 파싱 후, **post\_init**에서 플랫폼별 기본값 설정
init\_backend() 각 워커에서, 모델 생성 전 일회성 백엔드 초기화

플랫폼 및 플러그인 환경 변수 (Platform and plugin environment variables)

변수 설명
SGLANG\_PLATFORM 엔트리_포인트 이름으로 플랫폼 플러그인 선택 (예: kunlun, demo\_cuda). 설정되면 오직 이름이 지정된 플러그인의 activate()만 호출되고(front-loading 필터) 다른 플러그인은 건드리지 않아요. 또한 선택되지 않은 플랫폼 패키지의 일반 플러그인(sglang.srt.plugins)은 의존성 임포트를 피하기 위해 자동으로 건너뛰어요. 여러 플러그인이 활성화될 때 필요해요. 이름을 찾지 못하거나 플러그인의 하드웨어가 사용 불가하면 오류가 발생해요.
SGLANG\_PLUGINS 어느 훅 그룹에서든 로드할 일반 플러그인 이름의 쉼표 구분 허용 목록이에요. SGLANG\_PLATFORM이 설정되지 않았을 때 자동 SRT 플랫폼 발견도 필터링해요. 명시적 플랫폼 선택은 이를 무시해요.

diffusion 지원 추가 (Add diffusion support)

패키지는 SRT, SGLang Diffusion 또는 둘 다 지원할 수 있어요. Diffusion은 인자 없는 Platform 서브클래스와 별도의 플랫폼·훅 엔트리 포인트를 사용해요:

[project.entry-points."sglang.multimodal_gen.platforms"]
my_device = "my_platform_plugin:activate_diffusion"

[project.entry-points."sglang.multimodal_gen.plugins"]
my_device = "my_platform_plugin:register_hooks"

활성화를 import-safe하게 유지하고 백엔드가 사용 가능할 때만 정규화된 클래스 이름을 반환하세요:

def activate_diffusion() -> str | None:
    try:
        import my_device_runtime
    except ImportError:
        return None
    if not my_device_runtime.is_available():
        return None
    return "my_platform_plugin.diffusion_platform.MyDiffusionPlatform"

참조된 클래스는 인자 없이 생성 가능해야 해요:

from sglang.multimodal_gen.runtime.platforms import Platform, PlatformEnum

class MyDiffusionPlatform(Platform):
    _enum = PlatformEnum.OOT
    device_name = "my_device"
    device_type = "my_device"
    dispatch_key = "PrivateUse1"

    def get_dispatch_key_name(self) -> str:
        return "my_device"

dispatch_key를 직접 torch.library 등록에서 사용하는 PyTorch 디스패치 키로 설정하세요. CustomOp 구현을 선택하는 get_dispatch_key_name()과는 분리해 두세요. 나머지 콘트랙트를 의존성 순서대로 구성하세요:

  1. ServerArgs를 구성하기 전에 get_device_name(), get_device_total_memory(), get_available_gpu_memory()를 구현하세요.
  2. 워커 바인딩 전에 get_device()get_local_torch_device()를 구현하세요.
  3. 지원 작업 부하에 대해 분산 초기화, 어텐션 선택, custom-op 구현을 구성하세요.
  4. 엔드투엔드 작업 부하를 실행하고 모델별 커널, 컴파일, 나머지 디바이스 패밀리 분기를 감사하세요.

파이프라인 모듈이 구성되기 전에 각 워커에서 한 번 실행되는 init_backend()에서 custom-op 구현을 등록하세요:

class MyDiffusionPlatform(Platform):
    ...

    def init_backend(self) -> None:
        from my_platform_plugin.ops import rms_norm_forward
        from sglang.multimodal_gen.runtime.layers.custom_op import CustomOp
        from sglang.multimodal_gen.runtime.layers.layernorm import RMSNorm

        CustomOp.register_oot_forward(
            RMSNorm, fn=rms_norm_forward, platform_key="my_device"
        )

init_backend()는 워커 구현이 임포트되기 전에 워커 프로세스당 최대 한 번 실행돼요. 예외가 발생하면 시작이 중단되고 해당 프로세스에서 실패한 초기화가 재시도되지 않아요. 등록과 다른 백엔드 부작용이 부분적으로만 되돌릴 수 있기 때문이에요. custom-op 디스패치는 백엔드 초기화가 완료된 후 각 연산이 구성될 때 해석돼요. 따라서 선택된 콜러블은 첫 번째 컴파일 호출에서 바뀌는 대신 연산이 컴파일될 때 안정적으로 유지돼요.

함수는 정상 인자 전에 연산 인스턴스를 받아요. 등록은 정확한 연산 클래스와 get_dispatch_key_name()이 반환한 값과 일치해요. 등록이 없으면 SGLang은 연산에서 일치하는 forward_<key>() 구현을 찾고, 그 다음 forward_oot()로 폴백하며, 그 기반 구현은 forward_native()을 호출해요. 예를 들어 "cuda"를 반환하면 CUDA 호환 OOT 플랫폼이 CUDA 플랫폼 정체성을 보고하지 않고도 연산별 forward_cuda() 구현을 재사용할 수 있어요.

sglang.multimodal_gen.plugins 엔트리 포인트는 Platform 인터페이스가 다루지 않는 지점을 위해 남아 있어요. 그 콜백은 스케줄러나 모델 구성 전에 런처와 워커에서 실행되므로, 랭크 인지 초기화를 위해 GPUWorker.init_device_and_model()BEFORE 또는 AROUND 훅을 사용하세요. 선택된 것과 다른 플랫폼 배포의 훅은 건너뛰어져요. 그것들이 플랫폼의 배포에 포함되므로 로드, 콜백, 훅 적용 어느 단계의 실패든 init_backend()와 마찬가지로 시작을 중단해요.

Diffusion 플랫폼 콘트랙트 (Diffusion platform contract)

Active 인터페이스는 current_platform을 통해 호출돼요. Compatibility 인터페이스는 유지되는 어댑터예요. 목록에 없는 Platform 메서드는 안정적인 OOT 콘트랙트가 아니에요.

상태 영역 인터페이스
Active 초기화 init_backend()
Active 구성 apply_server_args_defaults()
Active 디바이스와 메모리 get_device(), set_device(), get_local_torch_device(), get_device_name(), get_device_uuid(), get_device_total_memory(), get_available_gpu_memory(), get_device_capability()
Active 분산 get_torch_distributed_backend_str(), supports_distributed_device_id(), get_all_to_all_communicator_cls(), get_cpu_architecture()
Active 디스패치와 모델 dispatch_key (get_torch_library_dispatch_key()가 읽음), get_dispatch_key_name(), get_attn_backend_cls_str(), verify_model_arch(), optimize_vae()
Active 실행 get_compile_backend(), get_compile_options(), inference_mode(), seed_everything(), enable_dit_layerwise_offload_by_default()
Compatibility 커뮤니케이터 get_device_communicator_cls()는 기존 플랫폼 서브클래스를 위해 get_all_to_all_communicator_cls()가 사용하는 폴백으로 유지돼요. 새 OOT 플랫폼은 후자를 오버라이드해야 해요.

패키지를 설치하고, 엔트리 포인트 메타데이터를 새로고침하기 위해 Python을 재시작하고, 명시적 선택을 검증하세요:

pip install -e my_platform_plugin/
SGLANG_DIFFUSION_PLATFORM_OVERRIDE=my_device python -c \
  "from sglang.multimodal_gen.runtime.platforms import current_platform; print(current_platform)"

Diffusion 플랫폼 제한 사항 (Diffusion platform limitations)

  • SRT와 diffusion은 별도의 플랫폼 클래스가 필요해요.
  • 프로세스당 외부 플랫폼 하나만 활성화될 수 있어요. 여러 콜백이 활성화되면 런타임 선택기를 설정하세요.
  • get_all_to_all_communicator_cls()all_to_all_4D()만 제어하며, 모든 collective, 그래프 캡처, 동기화 경로는 제어하지 않아요. get_device_communicator_cls()는 호환성을 위해 유지돼요.
  • 컴파일 설정은 build_torch_compile_kwargs() 호출자에게만 영향을 줘요. 정적 @torch.compile 데코레이터와 다른 직접 컴파일 경로를 감사하세요.
  • 명시적 어텐션 선택기는 AttentionBackendEnum 이름만 받아요. 커스텀 백엔드 반환은 아무것도 선택되지 않았을 때 동작하며, 이름으로 선택하려면 훅이나 다운스트림 패치가 필요해요.
  • 디바이스 패밀리 분기는 인터페이스 밖에 있어요. 각 지원 작업 부하에는 네이티브 폴백 또는 실행 가능한 미지원 기능 오류가 필요해요.
  • diffusion PlatformEnum.OOT은 외부 프로바이더를 식별해요. 내장 정체성 술어는 활성화된 모든 분기를 감사한 후에만 오버라이드하세요.

플러그인 유형 2: 일반 플러그인 (General Plugin)

설명 (Description)

일반 함수 플러그인은 커스텀 플랫폼 없이 SRT 또는 diffusion에 동작을 주입해요. 두 런타임은 별도의 엔트리 포인트 그룹과 훅 레지스트리를 가져요:

# SRT 플러그인
from sglang.srt.plugins.hook_registry import HookType, plugin_hook

# Diffusion 플러그인
from sglang.multimodal_gen.runtime.platforms.plugins import HookType, plugin_hook

SRT 플러그인은 sglang.srt.plugins 아래에, diffusion 플러그인은 sglang.multimodal_gen.plugins 아래에 등록하고, 위에서 일치하는 모듈에서 훅 API를 임포트하세요. 각 런타임은 자신의 레지스트리만 적용하므로 다른 런타임의 API로 등록된 훅은 절대 실행되지 않아요. 아래 예시는 SRT를 사용해요.

diffusion 훅 API는 플랫폼 발견과 함께 sglang.multimodal_gen.runtime.platforms.plugins에 있어요. sglang.multimodal_gen.plugins는 그 엔트리 포인트 그룹이며 Python 모듈 경로가 아니에요.

사용 사례:

  • 관측성 (Observability): 모든 함수에 로깅, 메트릭, 트레이싱 추가
  • 동작 수정 (Behavior modification): 함수 인자 또는 반환 값 수정
  • 성능 프로파일링 (Performance profiling): 중요 함수에 타이밍 추가
  • A/B 테스트: 런타임 시 구현 교체

퀵스타트 (Quick Start)

1. 최소 패키지 생성:

my_general_plugin/
├── pyproject.toml
└── my_general_plugin/
    └── __init__.py    # register() function

2. pyproject.toml:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "my-general-plugin"
version = "0.1.0"

[project.entry-points."sglang.srt.plugins"]
my_plugin = "my_general_plugin:register"

3. __init__.py — 훅 등록:

from sglang.srt.plugins.hook_registry import HookRegistry, HookType

def register():
    """load_plugins()가 호출하는 엔트리 포인트."""
    HookRegistry.register(
        "sglang.srt.managers.scheduler.Scheduler.__init__",
        my_hook,
        HookType.AROUND,
    )

def my_hook(original_fn, self, *args, **kwargs):
    result = original_fn(self, *args, **kwargs)
    print(f"Scheduler initialized! gpu_id={self.gpu_id}")
    return result

4. 설치 및 실행:

pip install -e my_general_plugin/
sglang serve --model-path <model> [options]
# 로그에서 "Scheduler initialized!" 찾기

훅 유형 (Hook Types)

HookRegistry는 네 가지 훅 유형을 지원해요:

훅 유형 시그니처 설명
BEFORE fn(\*args, \*\*kwargs) -> (args, kwargs) | None 원본 전에 실행. 인자를 그대로 두려면 None 반환, 수정하려면 (args, kwargs) 반환.
AFTER fn(result, \*args, \*\*kwargs) -> new\_result | None 원본 후에 실행. 결과를 유지하려면 None, 교체하려면 새 값 반환.
AROUND fn(original\_fn, \*args, \*\*kwargs) -> result 원본을 감싸요. original\_fn을 직접 호출해야 해요. 실행에 대한 완전한 제어.
REPLACE fn(\*args, \*\*kwargs) -> result 또는 class 원본 함수나 클래스를 완전히 대체. 클래스 대상은 교체 클래스를 직접 전달 — setattr로 치환되어 isinstance()/issubclass() 의미를 보존.

참고: 훅으로 클래스를 받는 것은 REPLACE만 가능해요. BEFORE/AFTER/AROUND에 클래스를 전달하면 등록 시 TypeError가 발생해요.

등록 API (Registration API)

훅은 명령형 API 또는 데코레이터 API로 등록할 수 있어요:

# --- Imperative API ---
from sglang.srt.plugins.hook_registry import HookRegistry, HookType

def my_timer(original_fn, *args, **kwargs):
    start = time.perf_counter()
    result = original_fn(*args, **kwargs)
    print(f"Elapsed: {time.perf_counter() - start:.3f}s")
    return result

HookRegistry.register(
    "sglang.srt.managers.scheduler.Scheduler.get_next_batch_to_run",
    my_timer,
    HookType.AROUND,
)

# --- Decorator API ---
from sglang.srt.plugins.hook_registry import plugin_hook, HookType

@plugin_hook(
    "sglang.srt.managers.scheduler.Scheduler.get_next_batch_to_run",
    type=HookType.AROUND,
)
def my_timer(original_fn, *args, **kwargs):
    start = time.perf_counter()
    result = original_fn(*args, **kwargs)
    print(f"Elapsed: {time.perf_counter() - start:.3f}s")
    return result

# --- Class replacement (REPLACE) ---
from sglang.srt.plugins.hook_registry import plugin_hook, HookType
from sglang.srt.managers.scheduler import Scheduler

@plugin_hook(
    "sglang.srt.managers.scheduler.Scheduler",
    type=HookType.REPLACE,
)
class MyScheduler(Scheduler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("Enhanced scheduler initialized!")

훅 대상 해석 (Hook Target Resolution)

대상 경로는 정규화된 점 표기법을 사용해요. 두 형식 모두 지원돼요:

  • 점 표기 (Dotted): sglang.srt.managers.scheduler.Scheduler.__init__
  • 엔트리 포인트 스타일: sglang.srt.managers.scheduler:Scheduler.__init__ (콜론을 점으로 처리)

일반 SRT 훅 대상 (Common SRT Hook Targets)

대상 설명
sglang.srt.server\_args.ServerArgs.add\_cli\_args 커스텀 CLI 인자 추가
sglang.srt.server\_args.ServerArgs.**post\_init** 파싱 후 ServerArgs 수정
sglang.srt.server\_args.ServerArgs.check\_server\_args 검증 추가/완화
sglang.srt.managers.scheduler.Scheduler.**init** 커스텀 스케줄러 상태
sglang.srt.managers.scheduler.Scheduler.get\_next\_batch\_to\_run 커스텀 스케줄링 정책
sglang.srt.managers.scheduler.Scheduler.run\_batch 프로파일링 / 검사
sglang.srt.managers.scheduler.Scheduler.process\_batch\_result 커스텀 메트릭
sglang.srt.managers.tp\_worker.TpModelWorker.**init** 커스텀 워커 상태
sglang.srt.managers.tp\_worker.TpModelWorker.forward\_batch\_generation 포워드 패스 래핑

파일 참조 (File Reference)

파일 설명
sglang/srt/platforms/device\_mixin.py DeviceMixin 베이스 클래스와 SRT 플랫폼 정체성 유형
sglang/srt/platforms/interface.py SRTPlatform 베이스 클래스 (DeviceMixin 확장)
sglang/srt/platforms/\_\_init\_\_.py current\_platform lazy singleton + 발견 로직
sglang/multimodal\_gen/runtime/platforms/interface.py Diffusion Platform 베이스 클래스
sglang/multimodal\_gen/runtime/platforms/\_\_init\_\_.py Diffusion current\_platform lazy singleton과 내장 폴백 순서
sglang/multimodal\_gen/runtime/platforms/plugins.py Diffusion 플러그인 등록, 명시적 훅 적용 단계, 훅 레지스트리
sglang/multimodal\_gen/runtime/managers/worker\_bootstrap.py 런타임 훅 해석 전에 백엔드를 초기화하는 import-neutral 프로세스 사양과 spawn 대상
sglang/srt/plugins/\_\_init\_\_.py load\_plugins() + load\_plugins\_by\_group()
sglang/srt/plugins/hook\_registry.py HookRegistry, HookType, plugin\_hook 데코레이터

더 알아보기 (Learn more)