chore: vendor sglang v0.5.10 snapshot
This commit is contained in:
372
third_party/sglang/docs/advanced_features/attention_backend.md
vendored
Normal file
372
third_party/sglang/docs/advanced_features/attention_backend.md
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
# Attention Backend
|
||||
|
||||
SGLang supports a large variety of attention backends. Each of them has different pros and cons.
|
||||
You can test them according to your needs.
|
||||
|
||||
```{important}
|
||||
Selecting an optimal attention backend is crucial for maximizing your performance. Different backends excel in various scenarios, so choose based on your model, hardware, and use case. Not all backends are supported on all platforms and model architectures.
|
||||
|
||||
If you don't specify `--attention-backend`, SGLang makes a best effort to automatically select the most performant backend based on your hardware and model architecture.
|
||||
```
|
||||
|
||||
## Support Matrix
|
||||
|
||||
The support matrix is split into two parts: MHA (standard attention) and MLA (multi-head latent attention). For an explanation of the key differences between MHA and MLA, please see the [SGLang documentation on DeepSeek MLA](../basic_usage/deepseek_v3.md#multi-head-latent-attention-mla-throughput-optimizations) and the original [DeepSeek MLA paper](https://arxiv.org/pdf/2405.04434).
|
||||
|
||||
### MHA Backends
|
||||
|
||||
| **Backend** | **Page Size > 1 (native)** | **FP8 KV Cache** | **FP4 KV Cache** | **Spec topk=1** | **Spec topk>1** | **Sliding Window** | **MultiModal** |
|
||||
|---------------------------------|-----------------------------|------------------|-----------------|-----------------|-----------------|--------------------|----------------|
|
||||
| **FlashInfer** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
|
||||
| **FA3 (FlashAttention 3)** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **FA4 (FlashAttention 4)** | 128 | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| **Triton** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **Torch Native (SDPA)** | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
||||
| **FlexAttention (PyTorch)** | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **TRTLLM MHA** | 16, 32 or 64 | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
|
||||
| **Dual Chunk FlashAttention** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **AITER (ROCm)** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **Wave (ROCm)** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Ascend (NPU)** | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
|
||||
| **Intel XPU** | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
||||
| **Intel AMX (CPU)** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
### MLA Backends
|
||||
|
||||
| **Backend** | **Native Page Sizes** | **FP8 KV Cache** | **FP4 KV Cache** | **Chunked Prefix Cache** | **Spec topk=1** | **Spec topk>1** |
|
||||
|----------------------------|---------------------------|------------------|------------------|--------------------------|-----------------|-----------------|
|
||||
| **FlashInfer MLA** | 1 | ❌ | ✅ | ✅ | ✅ | ❌ |
|
||||
| **FlashMLA** | 64 | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| **Cutlass MLA** | 128 | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| **TRTLLM MLA (Blackwell)** | 32 or 64 | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| **FA3 (FlashAttention 3)** | n/a | ❌ | ❌ | ✅ | ✅ | ⚠️ (page_size=1 only) |
|
||||
| **Triton** | n/a | ❌ | ❌ | ❌ | ✅ | ⚠️ (page_size=1 only) |
|
||||
| **FA4** | 1 | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
| **Ascend MLA (NPU)** | 128 | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
```{note}
|
||||
Multimodal attention is selected by `--mm-attention-backend`. The "MultiModal" column indicates whether a corresponding multimodal implementation exists for that backend family.
|
||||
```
|
||||
|
||||
```{note}
|
||||
- FlashAttention 4 supports both prefill and decode on SM90 (Hopper) and SM100 (Blackwell). FA4 MLA supports `page_size = 1`; FA4 MHA requires `page_size = 128`. On SM100, this is auto-enforced by the server; on SM90, users must set `--page-size 128` manually.
|
||||
- NSA is specifically designed for [DeepSeek V3.2 DSA](https://lmsys.org/blog/2025-09-29-deepseek-V32/). See the [DSA Attention Backend (NSA)](#dsa-attention-backend-nsa) section and [DeepSeek V3.2 deployment guide](../basic_usage/deepseek_v32.md) for details.
|
||||
```
|
||||
|
||||
```{warning}
|
||||
**FA4 on Hopper (SM90):** FA4 decode speed decreases as sequence length grows due to lack of SplitKV support. At batch=1 compared to FA3 on H100: ~-10% at 2K tokens, ~-18% at 4K, ~-31% at 8K, ~-49% at 16K. Larger batch sizes reduce the gap (e.g., batch=8: ~-2% at 2K, ~-8% at 4K). Blackwell (SM100) is not affected.
|
||||
```
|
||||
|
||||
```{note}
|
||||
For the KV4 FA4 scenario, FA4 requires using a different --decode-attention-backend to run. Except for trtllm_mha being incompatible with FA4, all other decode backends behave as shown in the table.
|
||||
```
|
||||
|
||||
```{tip}
|
||||
Speculative decoding topk: `topk` is the number of draft tokens sampled per step from the draft model. `topk = 1` follows classic EAGLE; `topk > 1` explores multiple branches and requires backend support in both draft and verification paths.
|
||||
```
|
||||
|
||||
```{note}
|
||||
**Speculative Decoding V2 (Spec V2):** Spec V2 uses overlap scheduling (`SGLANG_ENABLE_SPEC_V2=True`) that benefits various attention backends. Requires `--speculative-eagle-topk 1` and currently applies to EAGLE and EAGLE3.
|
||||
|
||||
**Verified backends:** TRTLLM MLA, TRTLLM MHA, FA3, Ascend (NPU), Triton.
|
||||
|
||||
**Limited support:** FlashInfer can run under Spec V2, but its plan stream (used for split-KV optimization) introduces a synchronization point that limits overlap benefits.
|
||||
```
|
||||
|
||||
```{tip}
|
||||
Page size controls how many tokens are grouped into a KV cache block. For the prefix cache to take effect, the number of tokens must fill at least one complete page. For example, if your prompt is only 32 tokens and `page_size = 64`, it won't fill a complete page and cannot be matched in the prefix cache (pages cannot be padded). With 65 tokens and `page_size = 64`, only the first page of 64 tokens will be cached and matched; the remaining 1 token is discarded. Use `page_size = 1` for maximum prefix reuse (token-level matching). Note that higher page sizes generally improve attention kernel performance, so prefer `page_size > 1` when prefix cache reuse is not critical.
|
||||
```
|
||||
|
||||
Many backends that do not natively operate on pages can emulate `page_size > 1` at the wrapper layer by expanding page tables to per-token indices. The "Page Size > 1 (native)" column indicates true in-kernel paging. Some backends require fixed native page sizes and cannot be reduced/emulated differently: TRTLLM MHA (16/32/64), TRTLLM MLA (32/64), FlashMLA (64), Cutlass MLA (128), Ascend (128).
|
||||
|
||||
MLA page-size constraints:
|
||||
- FlashInfer MLA: page_size = 1.
|
||||
- FlashMLA: page_size = 64.
|
||||
- Cutlass MLA: page_size = 128.
|
||||
- TRTLLM MLA: page_size ∈ {32, 64}.
|
||||
|
||||
### GDN Attention Backends
|
||||
|
||||
GDN (Gated Delta Network) is a linear attention mechanism with O(n) complexity, used in hybrid models that alternate GDN linear attention layers with standard full attention layers. GDN is **not** selected via `--attention-backend`; it is automatically activated when the model architecture requires it (e.g., Qwen 3.5, Qwen 3 Next, Jet Nemotron, Jet VLM).
|
||||
|
||||
The GDN linear attention layers have their own kernel backends, selected via `--linear-attn-backend` (default: `triton`). You can override the kernel per phase with `--linear-attn-decode-backend` and `--linear-attn-prefill-backend`.
|
||||
|
||||
| **Backend** | **Decode** | **Prefill / Extend** | **Spec Decoding (Target Verify)** |
|
||||
|--------------------------|------------|----------------------|-----------------------------------|
|
||||
| **Triton (CUDA)** | ✅ | ✅ | ✅ |
|
||||
| **Triton (AMD/ROCm)** | ✅ | ✅ | ✅ |
|
||||
| **Triton (NPU)** | ✅ | ✅ | ❌ |
|
||||
| **Triton (CPU)** | ✅ | ✅ | ❌ |
|
||||
| **CuTe DSL (CUDA only)**| ✅ | ❌ | ❌ |
|
||||
|
||||
```{important}
|
||||
GDN models are hybrid: the full-attention layers still require a standard `--attention-backend`. Platform constraints for the full-attention backend on hybrid GDN models:
|
||||
- **Blackwell (e.g., B200)**: `triton`, `trtllm_mha`, or `fa4` only.
|
||||
- **NPU (Ascend)**: `ascend` only.
|
||||
- **AMD (ROCm)**: `triton` recommended.
|
||||
- **Other CUDA (Hopper, Ampere, etc.)**: auto-selection works; no special constraints.
|
||||
```
|
||||
|
||||
### DSA Attention Backend (NSA)
|
||||
|
||||
DSA (Deepseek Sparse Attention) is a native sparse attention mechanism used by [DeepSeek V3.2](https://lmsys.org/blog/2025-09-29-deepseek-V32/). It is activated automatically when the model architecture requires it and is selected via `--attention-backend nsa`.
|
||||
|
||||
Internally, the NSA backend dispatches to different sub-backends for prefill and decode phases. You can override these with `--nsa-prefill-backend` and `--nsa-decode-backend`:
|
||||
|
||||
| **Sub-backend** | **Prefill** | **Decode** | **Notes** |
|
||||
|-----------------------|-------------|------------|-----------------------------------------------|
|
||||
| **flashmla_sparse** | ✅ | ✅ | Default prefill on Hopper and Blackwell (bf16) |
|
||||
| **flashmla_kv** | ✅ | ✅ | Default decode for FP8 on Blackwell with DP |
|
||||
| **flashmla_auto** | ✅ | ❌ | Auto-selects flashmla_sparse or flashmla_kv based on kv_cache_dtype |
|
||||
| **fa3** | ✅ | ✅ | Default decode on Hopper (bf16) |
|
||||
| **trtllm** | ✅ | ✅ | Default decode on Blackwell (bf16); default for both on Blackwell without DP |
|
||||
| **tilelang** | ✅ | ✅ | Default on AMD (ROCm) |
|
||||
| **aiter** | ✅ | ✅ | AMD-specific kernel library (requires aiter package) |
|
||||
|
||||
For deployment examples, see the [DeepSeek V3.2 deployment guide](../basic_usage/deepseek_v32.md).
|
||||
|
||||
### Hybrid attention (different backends for prefill vs decode) (Experimental)
|
||||
|
||||
```{warning}
|
||||
Hybrid attention is an experimental feature.
|
||||
```
|
||||
|
||||
You can mix-and-match attention backends for prefill and decode. This is useful when one backend excels at prefill and another excels at decode. For the implementation details, please see `python/sglang/srt/layers/attention/hybrid_attn_backend.py`.
|
||||
|
||||
```bash
|
||||
# Example: Prefill with FA4, Decode with TRTLLM MLA (Blackwell)
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path nvidia/DeepSeek-R1-FP4 \
|
||||
--tp 8 \
|
||||
--attention-backend trtllm_mla \
|
||||
--moe-runner-backend flashinfer_trtllm \
|
||||
--quantization modelopt_fp4 \
|
||||
--prefill-attention-backend fa4
|
||||
```
|
||||
|
||||
#### Speculative decoding with hybrid attention
|
||||
|
||||
Hybrid attention also works with speculative decoding. The backend used for draft decoding and target verification depends on `--speculative-attention-mode`:
|
||||
|
||||
- `--speculative-attention-mode decode` (recommended): draft/verify use the decode backend.
|
||||
- `--speculative-attention-mode prefill` (default): draft/verify use the prefill backend.
|
||||
|
||||
Constraints when combining hybrid attention with speculative decoding:
|
||||
|
||||
- If any attention backend is `trtllm_mha`, speculative decoding supports only `--speculative-eagle-topk 1`.
|
||||
- For paged MHA backends with `--page-size > 1` and `--speculative-eagle-topk > 1`, only `flashinfer` is supported.
|
||||
- CUDA Graph: the decode backend is always captured; the prefill backend is captured only when `--speculative-attention-mode prefill`.
|
||||
|
||||
|
||||
```{tip}
|
||||
If you set only one of `--prefill-attention-backend` or `--decode-attention-backend`, the unspecified phase inherits `--attention-backend`.
|
||||
If both are specified and differ, SGLang automatically enables a hybrid wrapper to dispatch to the chosen backend per phase.
|
||||
```
|
||||
|
||||
## Attention Backend Selection Guide (CUDA)
|
||||
|
||||
If the `--attention-backend` argument is not specified, SGLang automatically selects the best backend based on the hardware (CUDA) and model architecture.
|
||||
|
||||
### Automatic Selection Logic
|
||||
|
||||
**1. MHA Models (e.g., Llama, Qwen)**
|
||||
- **Hopper (e.g., H100, H200)**: Defaults to `fa3` if using CUDA 12.3+ and the model configuration is supported.
|
||||
- **Blackwell (e.g., B200)**: Defaults to `trtllm_mha`, unless using speculative decoding with `topk > 1`.
|
||||
- **Other Architectures (Ampere, Ada, etc.)**: Defaults to `flashinfer` if available; otherwise falls back to `triton`.
|
||||
|
||||
**2. MLA Models (e.g., DeepSeek V3)**
|
||||
- **Hopper**: Defaults to `fa3` (requires CUDA 12.3+).
|
||||
- **Blackwell**: Defaults to `flashinfer`; `trtllm_mla` is auto-selected for DeepSeek V3 models specifically.
|
||||
- **Other Architectures**: Defaults to `triton`.
|
||||
|
||||
|
||||
## User Guide
|
||||
|
||||
### Launch Command for Different Attention Backends
|
||||
|
||||
- FlashInfer (Default for Non-Hopper Machines, e.g., A100, A40)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend flashinfer
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-V3 \
|
||||
--attention-backend flashinfer \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- FlashAttention 3 (Default for Hopper Machines, e.g., H100, H200, H20)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend fa3
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-V3 \
|
||||
--trust-remote-code \
|
||||
--attention-backend fa3
|
||||
```
|
||||
|
||||
- Triton
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend triton
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-V3 \
|
||||
--attention-backend triton \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- FlashMLA
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--attention-backend flashmla \
|
||||
--trust-remote-code
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--attention-backend flashmla \
|
||||
--kv-cache-dtype fp8_e4m3 \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- TRTLLM MLA (Optimized for Blackwell Architecture, e.g., B200)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--attention-backend trtllm_mla \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- TRTLLM MLA with FP8 KV Cache (Higher concurrency, lower memory footprint)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--attention-backend trtllm_mla \
|
||||
--kv-cache-dtype fp8_e4m3 \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- TRTLLM MHA (Optimized for Blackwell Architecture, e.g., B200)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 4 \
|
||||
--model Qwen/Qwen3.5-35B-A3B-FP8 \
|
||||
--attention-backend trtllm_mha \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- TRTLLM MHA (XQA backend) (Optimized for SM90 and SM120, e.g., H20, H200, 5090)
|
||||
Note that TRTLLM XQA backend only works well for pagesize 64.
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 4 \
|
||||
--model Qwen/Qwen3.5-35B-A3B-FP8 \
|
||||
--decode-attention-backend trtllm_mha \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- FlashAttention 4 (MHA & MLA)
|
||||
```bash
|
||||
# FA4 for both prefill and decode on SM90/SM100
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-30B-A3B-Instruct-2507-FP8 \
|
||||
--attention-backend fa4 \
|
||||
--page-size 128 \
|
||||
--trust-remote-code
|
||||
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--prefill-attention-backend fa4 \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- Cutlass MLA
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--tp 8 \
|
||||
--model deepseek-ai/DeepSeek-R1 \
|
||||
--attention-backend cutlass_mla \
|
||||
--trust-remote-code
|
||||
```
|
||||
|
||||
- Ascend
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend ascend
|
||||
```
|
||||
|
||||
- Intel XPU
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend intel_xpu
|
||||
```
|
||||
|
||||
- Wave
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend wave
|
||||
```
|
||||
|
||||
- FlexAttention
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend flex_attention
|
||||
```
|
||||
|
||||
- Dual Chunk FlashAttention
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen2.5-14B-Instruct-1M \
|
||||
--attention-backend dual_chunk_flash_attn
|
||||
```
|
||||
|
||||
- Torch Native
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--attention-backend torch_native
|
||||
```
|
||||
|
||||
## Steps to add a new attention backend
|
||||
To add a new attention backend, you can learn from the existing backends
|
||||
(`python/sglang/srt/layers/attention/triton_backend.py`, `python/sglang/srt/layers/attention/flashattention_backend.py`)
|
||||
and follow the steps below.
|
||||
|
||||
```{note}
|
||||
Linear attention kernel backends (GDN, KDA) follow a different pattern. They implement `LinearAttnKernelBase` in `python/sglang/srt/layers/attention/linear/kernels/` and are dispatched by `GDNKernelDispatcher` / `KDAKernelDispatcher` rather than registered via `@register_attention_backend`.
|
||||
```
|
||||
|
||||
1. Run without cuda graph. Support the two forward functions
|
||||
- forward_extend
|
||||
- Will be used for prefill, prefill with KV cache, and target verification
|
||||
- It will be called once per layer
|
||||
- forward_decode
|
||||
- Will be used for normal decode, and draft decode
|
||||
- It will be called once per layer
|
||||
- init_forward_metadata
|
||||
- Initialize the class and common metadata shared by all layers
|
||||
- Call the plan function for optimizations like split_kv
|
||||
- It will be called once per forward
|
||||
2. Run with cuda graph. It has two phases (capture and replay) and you need to implement three functions
|
||||
- init_cuda_graph_state
|
||||
- It will be called once during life time
|
||||
- Create all common shared buffers
|
||||
- init_forward_metadata_capture_cuda_graph
|
||||
- It will be called before capturing a cuda graph
|
||||
- It is similar to init_forward_metadata but write the medatada to some pre-defined buffers
|
||||
- init_forward_metadata_replay_cuda_graph
|
||||
- It will be called before replaying a cuda graph
|
||||
- This function is in the critical path and needs to be fast
|
||||
254
third_party/sglang/docs/advanced_features/checkpoint_engine.md
vendored
Normal file
254
third_party/sglang/docs/advanced_features/checkpoint_engine.md
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
# Checkpoint Engine Integration
|
||||
|
||||
The SGLang checkpoint engine integration provides an efficient way to load model weights using a distributed checkpoint loading system. This feature significantly reduces model loading time, especially for large models and multi-node setups, by parallelizing the weight loading process across multiple processes and nodes.
|
||||
|
||||
## Overview
|
||||
|
||||
The checkpoint engine integration allows SGLang to:
|
||||
- Load model weights in parallel using multiple processes
|
||||
- Distribute weight loading across multiple nodes to increase effective disk bandwidth
|
||||
- Overlap weight loading with other initialization tasks like CUDA graph capture
|
||||
- Support both single-node and multi-node deployments
|
||||
|
||||
## Installation
|
||||
|
||||
First, install the checkpoint engine package:
|
||||
|
||||
```bash
|
||||
pip install 'checkpoint-engine[p2p]'
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The system consists of two main components:
|
||||
|
||||
1. **SGLang Server**: Runs with `--wait-for-initial-weights` flag to wait for weights before becoming ready
|
||||
2. **Checkpoint Engine Workers**: Separate processes (managed by torchrun) that load and distribute model weights
|
||||
|
||||
The checkpoint engine uses a parameter server architecture with support for:
|
||||
- **Broadcast mode**: Weights are broadcast from loading processes to inference processes
|
||||
- **P2P mode**: Direct peer-to-peer weight transfer between processes
|
||||
- **All mode**: Combination of both broadcast and P2P methods
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Single Node Setup
|
||||
|
||||
**Terminal 1 - Launch SGLang Server:**
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--tp 8 \
|
||||
--load-format dummy \
|
||||
--wait-for-initial-weights
|
||||
```
|
||||
|
||||
**Terminal 2 - Run Checkpoint Engine:**
|
||||
|
||||
Using sglang entrypoint:
|
||||
```bash
|
||||
python -m sglang.srt.checkpoint_engine.update \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
Using torchrun directly:
|
||||
```bash
|
||||
torchrun --nproc-per-node 8 \
|
||||
examples/checkpoint_engine/update.py \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
### Multi-Node Setup (2 Nodes)
|
||||
|
||||
**Node 0:**
|
||||
|
||||
Launch SGLang server:
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--tp 8 \
|
||||
--load-format dummy \
|
||||
--wait-for-initial-weights \
|
||||
--host [IP]
|
||||
```
|
||||
|
||||
Run checkpoint engine:
|
||||
|
||||
Using sglang entrypoint (recommended):
|
||||
```bash
|
||||
python -m sglang.srt.checkpoint_engine.update \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
Using torchrun directly:
|
||||
```bash
|
||||
torchrun --nproc-per-node 8 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--master-addr [IP] \
|
||||
--master-port 29500 \
|
||||
examples/checkpoint_engine/update.py \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
**Node 1:**
|
||||
|
||||
Launch SGLang server:
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--tp 8 \
|
||||
--load-format dummy \
|
||||
--wait-for-initial-weights \
|
||||
--host [IP]
|
||||
```
|
||||
|
||||
Run checkpoint engine:
|
||||
|
||||
Using sglang entrypoint (recommended):
|
||||
```bash
|
||||
python -m sglang.srt.checkpoint_engine.update \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
Using torchrun directly:
|
||||
```bash
|
||||
torchrun --nproc-per-node 8 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--master-addr [IP] \
|
||||
--master-port 29500 \
|
||||
examples/checkpoint_engine/update.py \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 8
|
||||
```
|
||||
|
||||
### Multi-Node Setup with Tensor Parallelism (TP=16)
|
||||
|
||||
**Node 0:**
|
||||
|
||||
Launch SGLang server:
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--tp 8 \
|
||||
--load-format dummy \
|
||||
--wait-for-initial-weights \
|
||||
--host [IP] \
|
||||
--dist-init-addr [IP]:9120 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0
|
||||
```
|
||||
|
||||
Run checkpoint engine:
|
||||
|
||||
Using sglang entrypoint (recommended):
|
||||
```bash
|
||||
python -m sglang.srt.checkpoint_engine.update \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 16
|
||||
```
|
||||
|
||||
Using torchrun directly:
|
||||
```bash
|
||||
torchrun --nproc-per-node 8 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--master-addr [IP] \
|
||||
--master-port 29500 \
|
||||
examples/checkpoint_engine/update.py \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 16
|
||||
```
|
||||
|
||||
**Node 1:**
|
||||
|
||||
Launch SGLang server:
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--tp 8 \
|
||||
--load-format dummy \
|
||||
--wait-for-initial-weights \
|
||||
--host [IP] \
|
||||
--dist-init-addr [IP]:9120 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1
|
||||
```
|
||||
|
||||
Run checkpoint engine:
|
||||
|
||||
Using sglang entrypoint (recommended):
|
||||
```bash
|
||||
python -m sglang.srt.checkpoint_engine.update \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 16
|
||||
```
|
||||
|
||||
Using torchrun directly:
|
||||
```bash
|
||||
torchrun --nproc-per-node 8 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--master-addr [IP] \
|
||||
--master-port 29500 \
|
||||
examples/checkpoint_engine/update.py \
|
||||
--update-method broadcast \
|
||||
--checkpoint-path /path/to/Qwen/Qwen3-8B/ \
|
||||
--inference-parallel-size 16
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### SGLang Server Options
|
||||
|
||||
- `--load-format dummy`: Use dummy format for initial loading (allows overlapping with other tasks)
|
||||
- `--wait-for-initial-weights`: Wait for checkpoint engine to provide weights before becoming ready
|
||||
- `--host`: Host address for multi-node setups
|
||||
- `--dist-init-addr`: Distributed initialization address for tensor parallelism
|
||||
|
||||
### Checkpoint Engine Options
|
||||
|
||||
- `--update-method`: Weight update method (`broadcast`, `p2p`, or `all`)
|
||||
- `--checkpoint-path`: Path to model checkpoint directory
|
||||
- `--inference-parallel-size`: Number of inference parallel processes
|
||||
- `--endpoint`: SGLang server endpoint (default: `http://localhost:19730`)
|
||||
- `--checkpoint-name`: Name for the checkpoint (default: `my-checkpoint-iter-0`)
|
||||
- `--save-metas-file`: File to save checkpoint metadata
|
||||
- `--load-metas-file`: File to load checkpoint metadata from
|
||||
- `--uds`: Unix domain socket path for communication
|
||||
- `--weight-version`: Version identifier for weights
|
||||
|
||||
## Performance Benefits
|
||||
|
||||
The checkpoint engine provides significant time savings in two main aspects:
|
||||
|
||||
1. **Multi-node Loading**: Each node only loads a portion of weights from disk, effectively increasing disk bandwidth. More participating nodes provide greater acceleration. Preliminary tests show 20-second acceleration when loading DeepSeek-R1 on H20-3e with two nodes.
|
||||
|
||||
2. **Single Process Optimization**: Using dummy format allows overlapping disk-to-CPU transfer with CUDA graph capture and other initialization tasks, providing additional time savings.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Ensure checkpoint engine package is installed: `pip install 'checkpoint-engine[p2p]'`
|
||||
- Verify network connectivity between nodes in multi-node setups
|
||||
- Check that the checkpoint path contains valid model files
|
||||
- Monitor logs for connection errors between SGLang server and checkpoint engine
|
||||
- Use `--sleep-time` parameter to add delays if needed for debugging
|
||||
|
||||
## References
|
||||
|
||||
- [Checkpoint Engine Repository](https://github.com/MoonshotAI/checkpoint-engine)
|
||||
73
third_party/sglang/docs/advanced_features/cuda_graph_for_multi_modal_encoder.md
vendored
Normal file
73
third_party/sglang/docs/advanced_features/cuda_graph_for_multi_modal_encoder.md
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# Cuda Graph for Multi-Modal Encoder in SGLang
|
||||
|
||||
## Motivation
|
||||
|
||||
In multimodal reasoning services, the visual encoder (ViT / Vision Transformer) typically has a few characteristic traits:
|
||||
|
||||
Many layers, fragmented operators: Each layer includes LN, QKV projections, attention, MLP, residual connections, etc., resulting in extremely frequent kernel launches.
|
||||
|
||||
Server-side “small batch / low latency” is common: The batch size is very small (sometimes it looks like 1 after “flattening” the batch), so kernel launch overhead accounts for a large portion of end-to-end latency.
|
||||
|
||||
Input token count (number of patches) varies frequently: Different image/video resolutions and different batch composition lead to different sequence lengths
|
||||
S — and this is precisely the biggest obstacle for CUDA Graph (unstable shapes).
|
||||
|
||||
The value of CUDA Graph: It captures a long sequence of GPU kernels with fixed shapes and fixed memory addresses into a graph; later, for the same shapes, it can replay the graph directly, dramatically reducing launch overhead and making GPU scheduling more compact.
|
||||
|
||||
This led us to seek a CUDA Graph enabled feature for ViT in order to improve ViT performance.
|
||||
|
||||
## Design and Restrictions
|
||||
|
||||
The new CUDA Graph enabled ViT logic is built on ViTCudaGraphRunner. This runner captures the "blocks + merger + deepstack merger (optional)" part of a vision transformer into a CUDA graph and replays it for identical shapes. See the following design consideration and restrictions for more details.
|
||||
|
||||
### Dynamic inputs to fit static constraints of CUDA Graph
|
||||
|
||||
Variable sequence length S is very common in ViT. While CUDA Graph requires fixed shapes. The solution is to build a graph cache by S(e.g., graph_key = S). The first time create a new S, and then capture a graph; afterwards, replay it.
|
||||
|
||||
If there are many distinct S values, we need to increase VRAM usage which is graph-private memory pools for many graphs.
|
||||
|
||||
### Stable addresses
|
||||
|
||||
Everything "parameter-like" becomes a static buffer:
|
||||
|
||||
- block_input / block_ws / block_output
|
||||
- cu_full_len / cu_window_len and their kk variants
|
||||
- sin_cos_ws
|
||||
|
||||
In this way to solve the underlying requirement: during replay, not allowed to swap tensors, can only modify tensor contents.
|
||||
|
||||
### Attention backend arguments
|
||||
Attention backend arguments are fixed inside the graph:
|
||||
|
||||
TritonAttn expects [cu_seqlens, cu_seqlens_kk, max_len]
|
||||
FA3 expects [cu_seqlens, max_len]
|
||||
|
||||
max_len is frozen as an int constant.
|
||||
cu_seqlens is cached into a dict during create_graph(), and its contents are not updated during subsequent replays.
|
||||
|
||||
For the same graph_key = S, you not only require the input shape to match, but also require the segmentation pattern in cu_seqlens (and window seqlens) to be identical. Otherwise, attention will segment the sequence incorrectly.
|
||||
|
||||
### Rotary buffer management
|
||||
The feature reallocates a larger sin_cos_ws when seq_len increases.
|
||||
The max_content_len is used to make sure the maximum size of the allocated rotary buffer.
|
||||
|
||||
|
||||
## Command Example
|
||||
You can enable CUDA Graph for ViT by setting env variable `SGLANG_VIT_ENABLE_CUDA_GRAPH=1`, for example:
|
||||
```
|
||||
SGLANG_VIT_ENABLE_CUDA_GRAPH=1 \
|
||||
python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen3-VL-8B-Instruct
|
||||
```
|
||||
Or you can run CUDA Graph for ViT together with Piecewise CUDA Graph feature by both setting env variable `SGLANG_VIT_ENABLE_CUDA_GRAPH=1` and setting `--enable-piecewise-cuda-graph`, for example:
|
||||
```
|
||||
SGLANG_VIT_ENABLE_CUDA_GRAPH=1 \
|
||||
python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen3-VL-8B-Instruct \
|
||||
--piecewise-cuda-graph-max-tokens 4096 \
|
||||
--enable-piecewise-cuda-graph \
|
||||
--piecewise-cuda-graph-compiler eager
|
||||
```
|
||||
|
||||
## Known supported models
|
||||
- Qwen2.5-VL (https://github.com/sgl-project/sglang/pull/14422)
|
||||
- Qwen3-VL (https://github.com/sgl-project/sglang/pull/15320)
|
||||
154
third_party/sglang/docs/advanced_features/deterministic_inference.md
vendored
Normal file
154
third_party/sglang/docs/advanced_features/deterministic_inference.md
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
# Deterministic Inference
|
||||
|
||||
## Why Deterministic Inference Matters
|
||||
|
||||
Deterministic inference ensures consistent LLM outputs across runs, which is critical for:
|
||||
- **Reinforcement Learning**: Ensures consistent logprobs across runs, reducing stochastic noise and making RL training more stable, reproducible, and debuggable.
|
||||
- **Testing & Debugging**: Enables reproducible validation
|
||||
- **Production**: Improves reliability and user experience
|
||||
|
||||
Even with `temperature=0`, standard LLM inference can produce different outputs due to dynamic batching and varying reduction orders in GPU kernels.
|
||||
|
||||
## The Root Cause of Non-Determinism
|
||||
|
||||
The main source is **varying batch sizes**. Different batch sizes cause GPU kernels to split reduction operations differently, leading to different addition orders. Due to floating-point non-associativity (`(a + b) + c ≠ a + (b + c)`), this produces different results even for identical inputs.
|
||||
|
||||
|
||||
## SGLang's Solution
|
||||
|
||||
Building on [Thinking Machines Lab's batch-invariant operators](https://github.com/thinking-machines-lab/batch_invariant_ops), SGLang achieves fully deterministic inference while maintaining compatibility with chunked prefill, CUDA graphs, radix cache, and non-greedy sampling. The development roadmap for deterministic inference features can be found in this [issue](https://github.com/sgl-project/sglang/issues/10278).
|
||||
|
||||
### Supported Backends
|
||||
|
||||
Deterministic inference is only supported with the following three attention backends: **FlashInfer**, **FlashAttention 3 (FA3)**, and **Triton**.
|
||||
|
||||
The following table shows feature compatibility for deterministic inference across different attention backends:
|
||||
|
||||
| Attention Backend | CUDA Graph | Chunked Prefill | Radix Cache | Non-greedy Sampling (Temp > 0) |
|
||||
|-------------------|------------|-----------------|-------------|---------------------|
|
||||
| **FlashInfer** | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
|
||||
| **FlashAttention 3 (FA3)** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
||||
| **Triton** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Enable deterministic inference by adding the `--enable-deterministic-inference` flag:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--attention-backend fa3 \
|
||||
--enable-deterministic-inference
|
||||
```
|
||||
|
||||
### Server Arguments
|
||||
|
||||
| Argument | Type/Default | Description |
|
||||
|----------|--------------|-------------|
|
||||
| `--enable-deterministic-inference` | flag; default: disabled | Enable deterministic inference with batch-invariant operations |
|
||||
| `--attention-backend` | string; default: fa3 | Choose attention backend (flashinfer, fa3, or triton) |
|
||||
|
||||
### Example Configurations
|
||||
|
||||
#### Qwen3-8B
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-8B \
|
||||
--attention-backend flashinfer \
|
||||
--enable-deterministic-inference
|
||||
```
|
||||
|
||||
#### Llama Models
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--attention-backend fa3 \
|
||||
--enable-deterministic-inference
|
||||
```
|
||||
|
||||
#### Qwen3-30B-A3B (MoE Model)
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-30B-A3B \
|
||||
--attention-backend fa3 \
|
||||
--enable-deterministic-inference
|
||||
```
|
||||
|
||||
### Deterministic Inference with Non-Greedy Sampling (Temperature > 0)
|
||||
|
||||
SGLang supports deterministic inference even with non-greedy sampling by using sampling seeds. This is particularly useful for reinforcement learning scenarios like GRPO (Group Relative Policy Optimization) where you need multiple diverse but reproducible responses.
|
||||
|
||||
#### Default Behavior
|
||||
|
||||
By default, SGLang uses a sampling seed of `42` for reproducible sampling:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.post(
|
||||
"http://localhost:30000/generate",
|
||||
json={
|
||||
"text": "Tell me a joke",
|
||||
"sampling_params": {
|
||||
"temperature": 0.8, # Non-greedy sampling
|
||||
"max_new_tokens": 128,
|
||||
},
|
||||
},
|
||||
)
|
||||
print(response.json())
|
||||
# This will always produce the same response across runs
|
||||
```
|
||||
|
||||
#### Generating Multiple Reproducible Responses
|
||||
|
||||
To sample different responses from the same prompt while maintaining reproducibility (e.g., for GRPO training), provide different sampling seeds in your requests:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# Prepare a list of sampling seeds for different responses
|
||||
sampling_seeds = [42, 43, 44, 45, 46]
|
||||
|
||||
responses = []
|
||||
for seed in sampling_seeds:
|
||||
response = requests.post(
|
||||
"http://localhost:30000/generate",
|
||||
json={
|
||||
"text": "Tell me a joke",
|
||||
"sampling_params": {
|
||||
"temperature": 0.8,
|
||||
"max_new_tokens": 128,
|
||||
"sampling_seed": seed, # Specify sampling seed
|
||||
},
|
||||
},
|
||||
)
|
||||
responses.append(response.json())
|
||||
|
||||
# Each seed will produce a different but reproducible response
|
||||
# Using the same seed will always produce the same response
|
||||
```
|
||||
|
||||
This approach ensures that:
|
||||
- Different seeds produce diverse responses
|
||||
- The same seed always produces the same response across different runs
|
||||
- Results are reproducible for debugging and evaluation
|
||||
|
||||
|
||||
## Verification
|
||||
|
||||
Run deterministic tests to verify consistent outputs:
|
||||
|
||||
```bash
|
||||
# Single test: same prompt, varying batch sizes
|
||||
python3 -m sglang.test.test_deterministic --test-mode single --n-trials 50
|
||||
|
||||
# Prefix test: prompts with different prefix lengths
|
||||
python3 -m sglang.test.test_deterministic --test-mode prefix --n-trials 50
|
||||
|
||||
# Radix Cache Consistency mode: test radix cache determinism (cached vs uncached prefill)
|
||||
python3 -m sglang.test.test_deterministic --test-mode radix_cache
|
||||
```
|
||||
|
||||
Expected result: All tests should show `Unique samples: 1` (perfectly deterministic).
|
||||
373
third_party/sglang/docs/advanced_features/dp_dpa_smg_guide.md
vendored
Normal file
373
third_party/sglang/docs/advanced_features/dp_dpa_smg_guide.md
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
# DP, DPA and SGLang DP Router
|
||||
|
||||
This guide explains the difference between Data Parallelism (DP) and Data Parallelism Attention (DPA), how to enable each mode correctly, and how to use the SGLang Model Gateway (SMG) for production-grade DP deployments.
|
||||
|
||||
## Data Parallelism (DP)
|
||||
|
||||
**Data Parallelism (DP)** is the most common parallelism strategy that replicates the entire model across multiple GPU sets and processes different batches of requests in parallel. Each GPU set handles independent requests. With dedicated routing strategies, as we will introduce later, with those proper routing algorithms in SGLang Model Gateway, the throughput of your serving system could be multiplied nearly linearly.
|
||||
|
||||
### Key characteristics
|
||||
|
||||
- Each replica has a full copy of the model
|
||||
- Requests are distributed/scattered across replicas
|
||||
- No inter-replica communication during one request's inference (for simple DP)
|
||||
|
||||
## Data Parallelism Attention (DPA)
|
||||
|
||||
**Data Parallelism Attention (DPA)**, also known as DP Attention, is an advanced parallelism strategy. While DPA provides the most significant benefits for **Multi-Head Latent Attention (MLA)** models (such as DeepSeek, MiniMax, Kimi-K2), it also supports **standard attention models** like Qwen.
|
||||
|
||||
### The Problem with Tensor Parallelism for MLA Models
|
||||
|
||||
The most common parallelism strategy for inference is **Tensor Parallelism (TP)**. However, TP might not be the most efficient strategy for certain models. For example, DeepSeek models use MLA and only have **one KV head**. If we use tensor parallelism on 8 GPUs, it will lead to:
|
||||
|
||||
- **Duplicated KV cache** across all GPUs
|
||||
- **Unwanted memory usage** that limits batch size
|
||||
- **Reduced throughput** due to memory constraints
|
||||
|
||||
### How DPA Works
|
||||
|
||||
DPA addresses these limitations by applying **data parallelism specifically to the attention component**.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<img src="../_static/image/dpa.png" alt="DPA + EP Architecture" width="100%">
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Each DP replica:**
|
||||
|
||||
- Processes different batches independently (can be in different forward modes: prefill, decode, or idle)
|
||||
- Maintains its own KV cache (no duplication)
|
||||
- Enables significantly larger batch sizes due to memory savings
|
||||
|
||||
**Communication patterns in DPA + EP:**
|
||||
-
|
||||
- **All2All (Dispatch)**: Routes tokens to expert sub-groups based on gating decisions
|
||||
- **All2All (Combine)**: Gathers computed results from experts back to original token positions
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Key benefits of DPA
|
||||
|
||||
1. **Significantly reduced KV cache memory**: Each DP replica only stores KV cache for its own batches
|
||||
2. **Larger batch sizes**: Memory savings enable larger batch sizes
|
||||
3. **Improved decoding throughput**: Significant throughput gains for MLA-based models
|
||||
4. **Independent forward modes**: Each DP replica can be in different forward modes (prefill, decode, or idle) and handles its assigned batches independently during attention computation
|
||||
|
||||
### DPA with Expert Parallelism for MoE
|
||||
|
||||
For MoE models like DeepSeek, DPA is **often** paired with Expert Parallelism (EP) for best throughput at scale. However, **DPA does not require EP**: you can enable DPA without EP if your deployment does not need expert sharding.
|
||||
|
||||
- Distribute 256+ expert weights across GPUs (cannot fit on a single GPU)
|
||||
- Enable efficient all-to-all token routing via DeepEP
|
||||
- Scale to large clusters (up to 5x throughput improvement over vanilla TP)
|
||||
|
||||
### Recommended setup for DeepSeek
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3 \
|
||||
--tp 8 \
|
||||
--dp-size 8 \
|
||||
--ep 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--moe-runner-backend deep_gemm
|
||||
```
|
||||
|
||||
> **Note**: `--dp-size` must be explicitly set when using `--enable-dp-attention`. If `dp_size` is 1 (default), DPA will be disabled.
|
||||
|
||||
For detailed EP configuration (DeepEP, Two-Batch Overlap, EPLB), see [Expert Parallelism](expert_parallelism.md).
|
||||
|
||||
### Target Models
|
||||
|
||||
DPA supports the following model architectures:
|
||||
|
||||
- **MLA (Multi-Head Latent Attention) models** - where DPA provides the most significant benefits:
|
||||
- DeepSeek family (DeepSeek-V2, DeepSeek-V3, DeepSeek-R1)
|
||||
- MiniMax models
|
||||
- Kimi-K2
|
||||
- Other models using MLA architecture
|
||||
|
||||
- **Standard attention models** - also supported:
|
||||
- Qwen models (see [PR #6121](https://github.com/sgl-project/sglang/pull/6121))
|
||||
|
||||
For models like Llama, with standard GQA, standard DP, or TP is typically recommended.
|
||||
|
||||
To enable DPA, add `--enable-dp-attention` to your server launch command.
|
||||
|
||||
### Activation Logic
|
||||
|
||||
DPA is enabled explicitly via server arguments (CLI or config). You must set both `--dp-size` and `--enable-dp-attention`:
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3 \
|
||||
--tp 8 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention
|
||||
```
|
||||
|
||||
**Important**: `--dp-size` must be greater than 1 for DPA to work. When `dp_size == 1` (default), `--enable-dp-attention` is automatically disabled. The constraint `tp_size % dp_size == 0` must also be satisfied.
|
||||
|
||||
### Standard DP for MLA models
|
||||
|
||||
Note that MLA models, of course, also support DP. Suppose you want to enable standard DP for MLA models. First, launch each MLA model's replica independently. You may launch these replicas one by one with DPA enabled. After launching each MLA model's replica, launch an SMG and connect all the replicas to the SMG. A detailed explanation of SMG is as follows.
|
||||
|
||||
## Modern Data Parallelism SGLang Model Gateway (SMG)
|
||||
|
||||
### Native DP Mode
|
||||
|
||||
Native DP (built-in Data Parallelism) in SGLang creates multiple worker processes within a single SGLang instance, under the control of `DataParallelController` with the launching parameter of `dp-size`.
|
||||
|
||||
|
||||
```bash
|
||||
# Native DP mode
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--dp-size 4
|
||||
```
|
||||
|
||||
**Limitations:**
|
||||
|
||||
- Built-in in-process load balancing only (e.g., `round_robin`, `total_requests`, `total_tokens`)
|
||||
- No cache-aware routing
|
||||
- Limited observability and metrics
|
||||
- No fault tolerance or circuit breakers
|
||||
- Not suitable for production workloads
|
||||
|
||||
⚠️ Native DP is **highly not recommended for use right now**. It is only used in some ancient/outdated RL frameworks. You can use SGLang Model Gateway (SMG) to power up your data parallelism in any use case.
|
||||
|
||||
### SMG-Based DP (Recommended)
|
||||
|
||||
Starting from September 2024, SGLang Model Gateway, i.e., SMG, formerly named as SGLang DP Router, was built especially as a production-ready DP routing system with Rust. It starts from DP routing, but later we further expanded its scope to coordinate RL, PD Disaggregation, and other scenarios. This doc only discusses SMG's usage in DP routing. For other usage, please refer to [SGLang Model Gateway Documentation](sgl_model_gateway.md).
|
||||
|
||||
> To achieve the best production-level routing performance and reduce the overhead to an extreme extent, we use Rust to build SMG, but not Python, since Python is never FAST enough.
|
||||
|
||||
**We strongly recommend using the SGLang Model Gateway (SMG) for production-grade Data Parallelism.** SMG provides significant advantages over native DP mode.
|
||||
|
||||
```bash
|
||||
# SMG-based DP mode (Recommended)
|
||||
python -m sglang_router.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--dp-size 4
|
||||
```
|
||||
|
||||
⚠️ Note that **SMG and Naive DP share the same launching parameter, `--dp-size`**. But the entrypoint of Naive DP is `python -m sglang.launch_server`, and SMG's entrypoint is `python -m sglang_router.launch_server`.
|
||||
|
||||
**Advantages of SMG-Based DP:**
|
||||
|
||||
| Feature | Native DP | SMG-Based DP |
|
||||
|---------|-----------|--------------|
|
||||
| **Load Balancing** | Built-in in-process methods | Advanced policies (cache-aware, power-of-two, etc.) |
|
||||
| **Cache Awareness** | ❌ No | ✅ Yes - significantly higher cache hit rate |
|
||||
| **Throughput** | Baseline | Significant improvement |
|
||||
| **Multi-Node Support** | Limited | ✅ Full support |
|
||||
| **Worker Health Monitoring** | Basic | ✅ Circuit breakers, health checks |
|
||||
| **Reliability** | Basic | ✅ Retries, rate limiting, queuing |
|
||||
| **Observability** | Basic metrics | ✅ 40+ Prometheus metrics, OpenTelemetry |
|
||||
| **Hot Worker Add/Remove** | ❌ No | ✅ Yes |
|
||||
|
||||
### SMG's Performance
|
||||
|
||||
The cache-aware routing policy in SMG significantly improves performance for workloads with shared prefixes:
|
||||
|
||||
| Metric | Without Cache-Aware | With Cache-Aware SMG |
|
||||
|--------|---------------------|----------------------|
|
||||
| Throughput (token/s) | 82,665 | 158,596 (+92%) |
|
||||
| Cache Hit Rate | 20% | 75% (+275%) |
|
||||
|
||||
*Benchmark from [SGLang v0.4 blog](https://lmsys.org/blog/2024-12-04-sglang-v0-4/), workload with multiple long prefix groups, 8x A100 80GB GPUs, dp-size=8*
|
||||
|
||||
### When to Use Each
|
||||
|
||||
**Use Native DP when:**
|
||||
|
||||
- ~Never use Native/Naive DP~
|
||||
- Learning material of DP routing
|
||||
|
||||
**Use SMG-Based DP when:**
|
||||
|
||||
- In any case, when you think DP is needed
|
||||
- Production deployments
|
||||
- Multi-node distributed setups
|
||||
- Workloads with shared prefixes (high cache reuse potential)
|
||||
- You need high availability and reliability features
|
||||
- You require detailed observability and metrics
|
||||
- You want to have highly efficient RL rollout systems
|
||||
|
||||
Note that for RL rollout systems, **there are four crucial reasons that SMG-Based DP is far better than naive DP routing**. Details can be found at [Load Balancing Router in RL](./sglang_for_rl.md#load-balancing-router).
|
||||
|
||||
### Quick Start For SMG
|
||||
|
||||
**Installation**
|
||||
|
||||
```bash
|
||||
pip install sglang-router
|
||||
# or
|
||||
pip install "sglang[all]"
|
||||
```
|
||||
|
||||
**Option A: Co-launch Workers and SMG (Simplest)**
|
||||
|
||||
This is the easiest way to get started - SMG and workers are launched together:
|
||||
|
||||
```bash
|
||||
python -m sglang_router.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--dp-size 4 \
|
||||
--host 0.0.0.0 \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
**Option B: Separate Launch (Multi-Node)**
|
||||
|
||||
For distributed deployments across multiple machines:
|
||||
|
||||
1. Launch workers on each node
|
||||
|
||||
```bash
|
||||
# Node 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--port 8000
|
||||
|
||||
# Node 2
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--port 8000
|
||||
```
|
||||
|
||||
2. Launch SMG pointing to workers
|
||||
|
||||
```bash
|
||||
python -m sglang_router.launch_router \
|
||||
--worker-urls http://node1:8000 http://node2:8000 \
|
||||
--policy cache_aware \
|
||||
--host 0.0.0.0 \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
**Option C: Dynamic Worker Registration**
|
||||
|
||||
For elastic deployments where workers can be added/removed dynamically:
|
||||
|
||||
```bash
|
||||
# Launch SMG first
|
||||
python -m sglang_router.launch_router \
|
||||
--policy cache_aware \
|
||||
--host 0.0.0.0 \
|
||||
--port 30000
|
||||
|
||||
# Register workers dynamically
|
||||
curl -X POST http://localhost:30000/workers \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"url": "http://worker1:8000"}'
|
||||
|
||||
curl -X POST http://localhost:30000/workers \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"url": "http://worker2:8000"}'
|
||||
```
|
||||
|
||||
### Load Balancing Policies
|
||||
|
||||
SMG supports multiple load balancing policies:
|
||||
|
||||
| Policy | Description | Best For |
|
||||
|--------|-------------|----------|
|
||||
| `cache_aware` | Combines cache locality with load balancing | **Recommended for most workloads** |
|
||||
| `round_robin` | Cycles through workers in order | Simple, predictable distribution |
|
||||
| `random` | Random worker selection | Baseline, testing |
|
||||
| `power_of_two` | Samples two workers, picks lighter one | Low latency requirements |
|
||||
|
||||
**Cache-Aware Policy (Default, Recommended)**
|
||||
|
||||
The cache-aware policy provides the best performance for most workloads:
|
||||
|
||||
```bash
|
||||
python -m sglang_router.launch_router \
|
||||
--worker-urls http://worker1:8000 http://worker2:8000 \
|
||||
--policy cache_aware \
|
||||
--cache-threshold 0.5 \
|
||||
--balance-abs-threshold 32 \
|
||||
--balance-rel-threshold 1.5 \
|
||||
--eviction-interval-secs 120 \
|
||||
--max-tree-size 67108864
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. Maintains an approximate radix tree for each worker based on request history
|
||||
2. Routes requests to workers with the highest prefix match (cache hit)
|
||||
3. Falls back to shortest-queue routing when load is imbalanced
|
||||
4. Automatically evicts old entries to prevent memory overflow
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Start with `cache_aware` policy** - It provides the best balance between cache locality and load distribution for most workloads
|
||||
2. **Use SMG for production** - Prefer `sglang_router.launch_server` over `sglang.launch_server` for better reliability and observability
|
||||
3. **Enable health checks** - Configure `--router-health-check-interval-secs` to detect and remove unhealthy workers automatically
|
||||
|
||||
**Recommended command with best practices applied:**
|
||||
|
||||
```bash
|
||||
python -m sglang_router.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--dp-size 4 \
|
||||
--router-policy cache_aware \
|
||||
--router-health-check-interval-secs 30 \
|
||||
--router-prometheus-port 10001 \
|
||||
--host 0.0.0.0 \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
For advanced configuration (circuit breakers, retries, Prometheus metrics, K8s integration), see [SGLang Model Gateway Documentation](sgl_model_gateway.md).
|
||||
|
||||
### Verifying Traffic Distribution
|
||||
|
||||
After launching SMG, verify that traffic is being distributed correctly:
|
||||
|
||||
**1. Check worker status:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:30000/workers
|
||||
```
|
||||
|
||||
**2. Check load distribution:**
|
||||
|
||||
```bash
|
||||
curl http://localhost:30000/get_loads
|
||||
```
|
||||
|
||||
**3. Monitor metrics (if Prometheus enabled):**
|
||||
|
||||
```bash
|
||||
# Key metrics to check
|
||||
smg_router_requests_total{model="..."}
|
||||
smg_worker_requests_active{worker="..."}
|
||||
sglang_cache_hit_rate{source="..."}
|
||||
```
|
||||
|
||||
For detailed metrics and monitoring setup, see [SGLang Model Gateway Documentation](sgl_model_gateway.md).
|
||||
|
||||
## Reference
|
||||
|
||||
| Strategy | Use Case | Key Benefit |
|
||||
|----------|----------|-------------|
|
||||
| **Native DP** (`--dp-size`) | Never | Easy to understand, not rust based |
|
||||
| **SMG-Based DP** | **Production (recommended)** | Cache-aware routing, high availability |
|
||||
| **DPA** (`--dp-size N --enable-dp-attention`) | DeepSeek/MLA models | Eliminates KV cache duplication, improved throughput |
|
||||
| **DPA + EP** | DeepSeek MoE models | Significant throughput improvement vs vanilla TP |
|
||||
|
||||
**Recommended production setup for DeepSeek:**
|
||||
1. Enable **DPA** for attention layers (`--dp-size 8 --enable-dp-attention`)
|
||||
2. Enable **EP** for MoE layers (`--ep 8 --moe-a2a-backend deepep`)
|
||||
3. Use **SMG** with **cache_aware** policy
|
||||
|
||||
**Related documentation:**
|
||||
- [Expert Parallelism](expert_parallelism.md) - DeepEP, Two-Batch Overlap, EPLB
|
||||
- [SGLang Model Gateway Documentation](sgl_model_gateway.md) - SMG configuration & troubleshooting
|
||||
- [Large-Scale EP Blog](https://lmsys.org/blog/2025-05-05-large-scale-ep/) - 96 GPU deployment guide
|
||||
30
third_party/sglang/docs/advanced_features/dp_for_multi_modal_encoder.md
vendored
Normal file
30
third_party/sglang/docs/advanced_features/dp_for_multi_modal_encoder.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# DP for Multi-Modal Encoder in SGLang
|
||||
|
||||
A typical VLM architecture involves two main components: an multi-modal encoder and a text decoder.
|
||||
|
||||
Most VLMs utilize a Vision Transformer (ViT) as their multi-modal encoder, it is responsible for processing visual data, extracting features (objects, colors, textures, etc.), and transforming them into a format that can be understood by the model.
|
||||
|
||||
The text decoder is based on LLM. It processes textual data and generates output based on the encoded visual features.
|
||||
|
||||
However, since the size of ViT is very small compared to language decoders,
|
||||
there is relatively little gain from TP. On the other hand, TP incurs significant communication
|
||||
overhead because of all-reduce being performed after every layer.
|
||||
|
||||
Placing the ViT in data parallel while keeping the LLM in tensor parallel consistently lowers TTFT and boosts end-to-end throughput. In this hybrid layout, the vision front-end becomes parallel and lightweight, while scarce interconnect bandwidth and collective ops are reserved for the LLM.
|
||||
|
||||
Data parallelism replicates the entire model across multiple GPU sets and processes different batches of requests in parallel.
|
||||
|
||||
## Command Example
|
||||
You can enable batch-level DP by setting `mm-enable-dp-encoder`, for example:
|
||||
```
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--tp 2 \
|
||||
--mm-enable-dp-encoder
|
||||
```
|
||||
|
||||
## Known supported models
|
||||
- Qwen2.5-VL (<https://github.com/sgl-project/sglang/pull/13126>)
|
||||
- Qwen3-VL (<https://github.com/sgl-project/sglang/pull/13724>)
|
||||
- InternVL (<https://github.com/sgl-project/sglang/pull/13925>)
|
||||
- GLM-4.5V & GLM-4.6V (<https://github.com/sgl-project/sglang/pull/14097>)
|
||||
194
third_party/sglang/docs/advanced_features/epd_disaggregation.md
vendored
Normal file
194
third_party/sglang/docs/advanced_features/epd_disaggregation.md
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
# EPD Disaggregation
|
||||
|
||||
## Why and What is EPD Disaggregation?
|
||||
|
||||
In modern Vision-Language Model (VLM) inference, request execution naturally decomposes into three distinct stages: Encoder, Prefill, and Decode.
|
||||
The Encoder stage performs vision preprocessing and ViT-based image encoding, which is highly compute-intensive but only required during request initialization. The Prefill stage processes the full multimodal input sequence to initialize the language model’s Key-Value (KV) cache, while the Decode stage is dominated by memory bandwidth and KV cache access for autoregressive token generation.
|
||||
|
||||
Existing deployments typically colocate these stages within a unified execution engine, or at best apply Prefill–Decode (PD) disaggregation. However, such designs still tightly couple vision encoding with language prefill, leading to inefficient resource utilization, limited scalability for image-heavy workloads, and suboptimal scheduling under load.
|
||||
|
||||
To address these challenges, we introduce Encoder–Prefill–Decode (EPD) Disaggregation in SGLang. EPD further separates vision encoding from language processing, enabling independent horizontal scaling of encoder servers, improved load balancing for multimodal requests, and seamless integration with existing PD disaggregation to form a fully decoupled three-tier inference architecture.
|
||||
|
||||
### Usage
|
||||
|
||||
You can launch a language-only model using `--language-only`, or an encoder-only model using `--encoder-only`.
|
||||
When launching a language-only model, you must additionally specify the encoder service endpoints via `--encoder-urls`.
|
||||
|
||||
We support multiple encoder transfer backends, including zmq_to_scheduler, zmq_to_tokenizer, and mooncake (the default is zmq_to_scheduler). The backend can be selected using `--encoder-transfer-backend`.
|
||||
|
||||
### Encoder transfer with Mooncake
|
||||
|
||||
`--encoder-transfer-backend mooncake` controls **how encoder outputs are transferred** between encoder and language/prefill services. It is an encoder transfer option and can be used independently of the global multimodal embedding cache.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
# encoder
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--encoder-transfer-backend mooncake \
|
||||
--port 30000
|
||||
|
||||
# language-only server
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--language-only \
|
||||
--encoder-urls http://127.0.0.1:30000 \
|
||||
--encoder-transfer-backend mooncake \
|
||||
--port 30002
|
||||
```
|
||||
|
||||
### Global multimodal embedding cache with Mooncake
|
||||
|
||||
SGLang also supports a Mooncake-backed **global multimodal embedding cache** for EPD workloads. When enabled on encoder servers, repeated image inputs can reuse previously computed ViT embeddings across instances instead of running the vision encoder again.
|
||||
|
||||
This feature is useful when:
|
||||
|
||||
- the deployment serves repeated or overlapping image inputs,
|
||||
- encoder compute is the bottleneck, and
|
||||
- Mooncake is already available in the cluster.
|
||||
|
||||
At a high level, the encoder checks whether the image embedding already exists in Mooncake. Cache hits are prefetched from the global store, while misses are encoded normally and inserted into the cache in the background.
|
||||
|
||||
To enable it:
|
||||
|
||||
- install and configure Mooncake in the same way as other SGLang Mooncake integrations,
|
||||
- add `--enable-mm-global-cache` on the encoder server.
|
||||
|
||||
`--enable-mm-global-cache` controls **whether multimodal embeddings are looked up and stored in the global Mooncake cache**. It is separate from `--encoder-transfer-backend`, which only controls encoder output transport.
|
||||
|
||||
For Mooncake deployment and configuration details, see [HiCache best practices](hicache_best_practices.md#deployment-with-mooncake) and the [Mooncake backend README](../../python/sglang/srt/mem_cache/storage/mooncake_store/README.md).
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
# Shared Mooncake configuration
|
||||
export MOONCAKE_TE_META_DATA_SERVER="http://127.0.0.1:8080/metadata"
|
||||
export MOONCAKE_MASTER="127.0.0.1:50051"
|
||||
export MOONCAKE_PROTOCOL="rdma"
|
||||
export MOONCAKE_GLOBAL_SEGMENT_SIZE="4gb"
|
||||
|
||||
# encoder with global multimodal cache enabled
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--enable-mm-global-cache \
|
||||
--port 30000
|
||||
|
||||
# language-only server
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--language-only \
|
||||
--encoder-urls http://127.0.0.1:30000 \
|
||||
--port 30002
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- This cache is for **multimodal encoder embeddings**, not the language model KV cache.
|
||||
- The feature currently uses Mooncake as the shared backing store.
|
||||
- It can be enabled regardless of which `--encoder-transfer-backend` you use.
|
||||
- It is most relevant for EPD or encoder-disaggregated VLM deployments where the same images are likely to appear across requests or instances.
|
||||
|
||||
#### Qwen VL
|
||||
|
||||
- EP Disaggregation
|
||||
|
||||
```bash
|
||||
# encoder 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30000
|
||||
# encoder 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30001
|
||||
# language-only server
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--language-only \
|
||||
--encoder-urls http://127.0.0.1:30000 http://127.0.0.1:30001 \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30002
|
||||
```
|
||||
|
||||
- EPD Disaggregation
|
||||
|
||||
```bash
|
||||
# encoder 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30000
|
||||
# encoder 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30001
|
||||
# prefill 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--language-only \
|
||||
--encoder-urls http://127.0.0.1:30000 http://127.0.0.1:30001 \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30002
|
||||
# decode 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--disaggregation-mode decode \
|
||||
--port 30003
|
||||
# router
|
||||
python -m sglang_router.launch_router \
|
||||
--pd-disaggregation \
|
||||
--prefill http://$PREFILL_HOST:30002 \
|
||||
--decode http://$DECODE_HOST:30003 \
|
||||
--port 8000
|
||||
|
||||
```
|
||||
|
||||
#### gRPC Encoder (EPD)
|
||||
|
||||
You can run the encoder as a gRPC server while keeping prefill/decode as HTTP.
|
||||
When using gRPC encoders, set `SGLANG_ENCODER_MM_RECEIVER_MODE=grpc` for the
|
||||
prefill process so it uses the gRPC receiver.
|
||||
|
||||
```bash
|
||||
# gRPC encoder
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--encoder-only \
|
||||
--grpc-mode \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30000
|
||||
|
||||
# prefill (HTTP) - tell it to use gRPC receiver
|
||||
SGLANG_ENCODER_MM_RECEIVER_MODE=grpc \
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--language-only \
|
||||
--encoder-urls grpc://127.0.0.1:30000 \
|
||||
--encoder-transfer-backend zmq_to_scheduler \
|
||||
--port 30002
|
||||
|
||||
# decode (HTTP)
|
||||
python -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-VL-8B-Instruct \
|
||||
--disaggregation-mode decode \
|
||||
--port 30003
|
||||
|
||||
# router
|
||||
python -m sglang_router.launch_router \
|
||||
--pd-disaggregation \
|
||||
--prefill http://$PREFILL_HOST:30002 \
|
||||
--decode http://$DECODE_HOST:30003 \
|
||||
--port 8000
|
||||
```
|
||||
200
third_party/sglang/docs/advanced_features/expert_parallelism.md
vendored
Normal file
200
third_party/sglang/docs/advanced_features/expert_parallelism.md
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
# Expert Parallelism
|
||||
|
||||
Expert Parallelism (EP) in SGLang distributes expert weights across multiple devices in Mixture-of-Experts (MoE) models, addressing memory bottlenecks and enabling efficient scaling for high-performance inference. It is particularly vital for serving large-scale MoE models where tokens are dynamically routed to specialized experts across GPUs. By leveraging optimized all-to-all communication and grouped matrix multiplications (GEMMs), EP reduces latency, boosts throughput, and minimizes idle GPU time. SGLang's EP offers strong extensibility through its modular framework, allowing seamless integration of custom kernels, backends, and optimizations without refactoring core logic, supporting diverse hardware and quantization schemes.
|
||||
|
||||
## Supported Backends and Selection Guidance
|
||||
|
||||
SGLang's EP integrates diverse, highly efficient backends for different use cases, allowing fine-grained control over performance trade-offs. Users specify backends via command-line flags:
|
||||
- `--moe-a2a-backend`: Selects the backend for all-to-all communication.
|
||||
- `--moe-runner-backend`: Selects the backend for MoE computation.
|
||||
|
||||
### Backends for All-to-All Communication
|
||||
|
||||
| Backend | Description | Use Cases |
|
||||
|--------------|-----------------------------------------------------------------------------|------------------------------------|
|
||||
| **`none` (default)** | Disables all-to-all for EP. Uses All-Reduce or All-Gather for token dispatch. | Hybrid EP and TP setups. |
|
||||
| `deepep` | DeepEP, a communication library for efficient token shuffling in MoE models. | Large-scale EP deployments. |
|
||||
| `mooncake` | An extension of DeepEP for elastic inference, leveraging RDMA for high-performance data transfers. | Elastic EP serving. |
|
||||
| `nixl` | [NIXL-EP](https://github.com/ai-dynamo/nixl/tree/main/examples/device/ep), an elastic EP communication library built on NVIDIA's [NIXL](https://github.com/ai-dynamo/nixl) framework with native RDMA and NVLink support. | Elastic EP serving with fault tolerance and dynamic scaling. |
|
||||
| `mori` | MORI-EP, AMD's native all-to-all communication implementation optimized for ROCm. | AMD GPU deployments. |
|
||||
| `flashinfer` | Flashinfer implementation of all-to-all. | Large-scale EP deployments. |
|
||||
| `ascend_fuseep` | Ascend NPU native fused all-to-all communication. | Ascend NPU deployments. |
|
||||
|
||||
DeepEP and Mooncake backends support two modes for token dispatch: `normal` mode (optimized for prefill workloads with high throughput) and `low_latency` mode (optimized for decode workloads with low latency and CUDA Graph compatibility). MORI backend only supports `normal` mode now. NIXL-EP currently operates in low-latency mode with CUDA Graph support. Users are recommended to set `--deepep-mode auto` to enable automatic dispatch mode switching during runtime. Setting `--deepep-mode normal` or `--deepep-mode low_latency` is useful for debugging or development purposes.
|
||||
|
||||
Currently, DeepEP, Mooncake, NIXL-EP, `ascend_fuseep` and MORI only support cases where `ep_size = tp_size`. For hybrid EP and TP (i.e., `ep_size < tp_size`), only the `none` backend (All-Reduce or All-Gather-based dispatching) is supported.
|
||||
|
||||
### Backends for MoE Computation
|
||||
|
||||
| Backend | Description | Use Cases |
|
||||
|--------------------------|-----------------------------------------------------------------------------|------------------------------------|
|
||||
| **`auto` (default)** | Automatically selects the optimal backend based on model architecture, hardware (e.g., NVIDIA architecture like Ampere, Hopper, Blackwell), quantization scheme (e.g., FP8, FP4), and runtime conditions. | General-purpose deployments; ensures compatibility and performance without user intervention. |
|
||||
| `triton` | Triton-based implementation for grouped GEMMs. To achieve higher performance, it's highly recommended to create [tuned configurations](https://github.com/sgl-project/sglang/blob/main/benchmark/kernels/fused_moe_triton/README.md). | Custom kernel development or scenarios requiring high extensibility with Torch compilation support. |
|
||||
| `deep_gemm` | DeepGEMM backend optimized for MoE matrix multiplications, supporting contiguous layouts for prefill and masked layouts for decode; often JIT-compiled for performance. | Large-scale EP deployments with FP8 block-wise quantization. |
|
||||
| `cutlass` | CUTLASS-based backend for efficient GEMMs. | NVIDIA architectures with CUTLASS support. |
|
||||
| `flashinfer_trtllm` | FlashInfer integrated with TensorRT-LLM for accelerated MoE computations, supporting FP4 communication operators and high-performance GEMMs. | Blackwell with TRT-LLM. |
|
||||
| `flashinfer_trtllm_routed` | FlashInfer integrated with TensorRT-LLM for accelerated routed MoE computations, consuming SGLang-computed top-k expert assignments and weights. | Blackwell with TRT-LLM. |
|
||||
| `flashinfer_cutlass` | FlashInfer combined with CUTLASS for high-performance grouped GEMMs in MoE layers, handling FP4/FP8 quantization efficiently. | Blackwell with FP4/FP8 models. |
|
||||
| `flashinfer_mxfp4` | FlashInfer variant optimized for MXFP4 (mixed FP4) quantization in MoE runners, focusing on memory-efficient low-precision inference. | Low-precision models with MXFP4. |
|
||||
| `flashinfer_cutedsl` | FlashInfer with a custom DSL for flexible and efficient MoE kernel generation, integrated with ModelOpt FP4 quantization. | Low-precision models with NVFP4. |
|
||||
|
||||
### Examples
|
||||
|
||||
Launch with DeepEP and DeepGEMM for DeepSeek-V3:
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server --model-path deepseek-ai/DeepSeek-V3 --moe-a2a-backend deepep --moe-runner-backend deep_gemm --tp 8 --ep 8
|
||||
```
|
||||
|
||||
## Extensible EP Framework
|
||||
|
||||
SGLang's EP framework provides modular abstractions for easy integration of custom kernels, backends, and optimizations. It decouples the MoE forward pass into stages (dispatch → pre-permute → core runner → post-permute → combine), enabling seamless extensions without refactoring core logic.
|
||||
|
||||
### Framework Overview
|
||||
|
||||
The framework centers on `FusedMoE` as the unified entry point for a single, extensible structure. Key components include:
|
||||
- **Dispatcher**: Manages dispatch/combine for backends like DeepEP (implements `BaseDispatcher` subclasses).
|
||||
- **MoeRunner**: Orchestrates grouped-GEMM execution via `MoeRunnerCore` implementations (e.g., `TritonRunnerCore`).
|
||||
- **PermuteMethodPool**: Auto-registers layout conversions (e.g., pre/post-permute via `register_pre_permute` and `register_post_permute` for dynamic modes, or `register_fused_func` for static, torch.compile-compatible fused operations).
|
||||
- **TopK Router**: Backend-agnostic expert selection.
|
||||
|
||||
This design supports multiple backends via `--moe-a2a-backend` and `--moe-runner-backend`, with quantization integrated through a standardized `apply()` method. The computation flow ensures modularity:
|
||||
|
||||
```
|
||||
[input_hidden_states]
|
||||
|
|
||||
v
|
||||
TopK.forward -> select_experts / triton_kernels.routing / bypass
|
||||
|
|
||||
v
|
||||
[TopKOutput]
|
||||
|
|
||||
v
|
||||
FusedMoE.forward -> Dispatcher.dispatch -> DeepEP / bypass
|
||||
| |
|
||||
| v
|
||||
| [DispatchOutput]
|
||||
| |
|
||||
| v
|
||||
| quant_method.apply -> MoeRunner.forward
|
||||
| | |
|
||||
| | v
|
||||
| | pre-permute + grouped_gemm + post-permute
|
||||
| | |
|
||||
| |--------------
|
||||
| v
|
||||
| [CombineInput]
|
||||
| |
|
||||
| v
|
||||
| Dispatcher.combine -> DeepEP / bypass
|
||||
| |
|
||||
|---------------------
|
||||
v
|
||||
[final_hidden_states]
|
||||
```
|
||||
|
||||
For details, see the [MoE Refactor Roadmap](https://github.com/sgl-project/sglang/issues/8715).
|
||||
|
||||
### Implementing New Backends
|
||||
|
||||
To add a new backend:
|
||||
1. For a new all-to-all dispatcher, implement a `BaseDispatcher` subclass with `dispatch` and `combine` methods.
|
||||
2. For a new MoE runner backend, define a `MoeRunnerCore` subclass for core operations (e.g., grouped GEMMs).
|
||||
3. Define new input/output formats for the dispatcher or model runner (e.g., `RunnerInput`, `RunnerOutput`).
|
||||
4. Register permute/unpermute methods to ensure compatibility:
|
||||
- **Fused Mode** (static, torch.compile-compatible): Use `register_fused_func` for end-to-end operations.
|
||||
- **Permute Mode** (dynamic): Register `register_pre_permute` and `register_post_permute` for flexible layouts.
|
||||
|
||||
See the [MoE Refactor Implementation PR](https://github.com/sgl-project/sglang/pull/9269) for full changes, including type hints and config expansions.
|
||||
|
||||
### Examples
|
||||
|
||||
For an example implementation, see [moe_runner/triton.py](https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/layers/moe/moe_runner/triton.py), which demonstrates Triton-based grouped GEMMs with registered fused and permutation functions.
|
||||
|
||||
## Computation and Communication Overlap
|
||||
|
||||
SGLang's EP employs advanced overlap techniques to hide communication latency behind computation, maximizing GPU utilization in MoE layers.
|
||||
|
||||
### Two-Batch Overlap (TBO)
|
||||
|
||||
TBO splits requests into micro-batches, interleaving attention computation with dispatch/combine operations. Yield points in the execution graph allow pausing for overlaps, increasing overall throughput without peak memory spikes:
|
||||
|
||||
```python
|
||||
operations = [
|
||||
self._forward_attn,
|
||||
YieldOperation(), # Overlap with dispatch of prior micro-batch
|
||||
self._forward_dispatch,
|
||||
self._forward_mlp,
|
||||
YieldOperation(), # Overlap with combine
|
||||
self._forward_combine,
|
||||
]
|
||||
```
|
||||
|
||||
Users need to specify `--enable-two-batch-overlap` to unlock up to 2x throughput. For details, see the [Large-Scale EP Blog](https://lmsys.org/blog/2025-05-05-large-scale-ep/#two-batch-overlap).
|
||||
|
||||
### Single-Batch Overlap (SBO)
|
||||
|
||||
SGLang introduces a dispatcher-hook system for Single-Batch Overlap (SBO), enabling the overlap of operations within a single batch—such as shared experts computation with communication—while decentralizing logic to enhance modularity. These hooks execute before and after the `dispatch` and `combine` operations without modifying core MoE modules. This design simplifies interfaces, reduces coupling, and improves extensibility. For implementation details and an example of overlapping shared experts with DeepEP's combine operation, refer to [PR #13327](https://github.com/sgl-project/sglang/pull/13327). Users can set `--enable-single-batch-overlap` to enable this feature.
|
||||
|
||||
|
||||
## Workload Balancer
|
||||
|
||||
SGLang integrates the [Expert Parallelism Load Balancer (EPLB)](https://github.com/deepseek-ai/EPLB) from DeepSeek to address routing imbalances in MoE models. By analyzing expert activation statistics, EPLB computes an optimal expert arrangement, strategically placing or replicating experts to minimize GPU utilization variance, reduce idle cycles, and enhance scalability.
|
||||
|
||||
To enable EPLB, use the flags `--enable-eplb`. For optimal performance, increase batch sizes to stabilize activation statistics and configure periodic rebalancing (e.g., every 1000 requests) to adapt to evolving workloads. Simulations demonstrate significant improvements in load balancedness (ratio of mean to max computation time), correlating strongly with throughput gains.
|
||||
|
||||
For more details, refer to the [EPLB Section in the Large-Scale EP Blog](https://lmsys.org/blog/2025-05-05-large-scale-ep/#expert-parallelism-load-balancer) and the [EPLB Repository](https://github.com/deepseek-ai/eplb).
|
||||
|
||||
|
||||
## EP with Spectulative Decoding
|
||||
|
||||
|
||||
When utilizing speculative decoding with MTP on MoE architectures, use the `--speculative-moe-runner-backend` and `--speculative-moe-a2a-backend` arguments to customize the MoE layer behavior for the draft model. While they default to the target model’s settings, users can differentiate them for varying precisions between target and draft models.
|
||||
|
||||
For model like `nvidia/DeepSeek-R1-0528-NVFP4-v2`, the target model uses NVFP4 precision while the draft model uses BF16. To apply `flashinfer_trtllm` kernel for target MoE layer while falling back to triton fused MoE kernel for draft MoE layer, users can set the arguments as follows:
|
||||
```
|
||||
...
|
||||
--moe-runner-backend flashinfer_trtllm \
|
||||
--speculative-moe-runner-backend triton \
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Ascend NPU Guidance
|
||||
|
||||
|
||||
### Guidance on SGLang configuration in Ascend NPU
|
||||
- `--moe-a2a-backend` only supports `deepep` and `ascend_fuseep` backends,
|
||||
- `deepep`: The mechanism is consistent with the above description.
|
||||
- `ascend_fuseep`: Offer a large fused operator which integrates all operations between dispatch and combine to boost MoE computation. Only used for decode stage in PD Disaggregation Mode.
|
||||
- `--moe-runner-backend` parameter does not need to be configured.
|
||||
- `--deepep-mode`:
|
||||
- In PD mixed mode, please set `--deepep-mode auto`.
|
||||
- In PD Disaggregation Mode, prefill instance sets `--deepep-mode normal`, and decode instance sets `--deepep-mode low_latency`.
|
||||
|
||||
|
||||
### DeepEP Ascend Introduction
|
||||
|
||||
DeepEP Ascend is the adapted version of the DeepEP communication library for Huawei Ascend NPUs, specifically designed for Mixture-of-Experts (MoE) model Expert Parallelism (EP).
|
||||
It supports the Ant-moving Function (Split the sequence length into rounds for streaming batch transmission) to optimize the buffer size occupied during collective communication in prefill stage, especially for long sequences.
|
||||
|
||||
Ant-moving Function can be enabled for both the dispatch and combine phases via the following environment variables:
|
||||
- `DEEPEP_NORMAL_LONG_SEQ_PER_ROUND_TOKENS`: Enable ant-moving function in dispatch stage. Indicates the number of tokens transmitted per round on each rank, default 8192.
|
||||
- `DEEPEP_NORMAL_LONG_SEQ_ROUND`: Enable ant-moving function in dispatch stage. Indicates the number of rounds transmitted on each rank, default 1.
|
||||
- `DEEPEP_NORMAL_COMBINE_ENABLE_LONG_SEQ`: Enable ant-moving function in combine stage, default 0 (means disabled).
|
||||
|
||||
`DEEPEP_NORMAL_LONG_SEQ_PER_ROUND_TOKENS * DEEPEP_NORMAL_LONG_SEQ_ROUND` means input sequence length. When the input sequence length exceeds 8192, it is recommended to enable the ant-moving function in both dispatch and combine phase.
|
||||
|
||||
The environment variable `HCCL_BUFFSIZE` is used to configure the buffer size (MB) actually allocated. Its calculation formula is as follows:
|
||||
```angular2html
|
||||
# Enable Ant-moving Function
|
||||
HCCL_BUFFSIZE >= 2 * (102MB + 4MB + DEEPEP_NORMAL_LONG_SEQ_PER_ROUND_TOKENS * (hidden_size + hidden_size + hidden_size) * topk) + PADDING_BUFFSIZE
|
||||
|
||||
# Disable Ant-moving Function
|
||||
HCCL_BUFFSIZE >= 2 * (102MB + 4MB + TOTAL_SEQ_LEN * (hidden_size + hidden_size) * topk) + PADDING_BUFFSIZE
|
||||
```
|
||||
Wherein the parameters are described as follows:
|
||||
- `hidden_size`: hidden size in model config.
|
||||
- `topk`: The number of selected routing experts.
|
||||
- `TOTAL_SEQ_LEN`: input sequence length.
|
||||
- `PADDING_BUFFSIZE`: A value of 20 or greater is recommended.
|
||||
297
third_party/sglang/docs/advanced_features/forward_hooks.md
vendored
Normal file
297
third_party/sglang/docs/advanced_features/forward_hooks.md
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
## Model Hooks
|
||||
|
||||
SGLang supports attaching PyTorch forward hooks to specific submodules in the loaded model, configured entirely via `server_args` JSON.
|
||||
|
||||
This is useful for:
|
||||
|
||||
* Logging intermediate activations
|
||||
* Debugging model internals
|
||||
* Exporting hidden states to external tooling
|
||||
|
||||
Hooks are attached once during `ModelRunner.initialize` and run on every forward pass.
|
||||
|
||||
---
|
||||
|
||||
### Configuration overview
|
||||
|
||||
Hooks are configured via a `ServerArgs` field:
|
||||
|
||||
```python
|
||||
class ServerArgs:
|
||||
...
|
||||
# For forward hooks
|
||||
forward_hooks: Optional[List[dict[str, Any]]] = None
|
||||
````
|
||||
|
||||
In JSON form, a minimal configuration looks like:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"forward_hooks": [
|
||||
{
|
||||
"name": "outer_linear_hooks",
|
||||
"target_modules": ["outer.0", "outer.1"],
|
||||
"hook_factory": "my_project.hooks:dummy_hook_factory",
|
||||
"config": {
|
||||
"tag": "outer-layer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Top-level fields
|
||||
|
||||
* `forward_hooks` (optional list of objects)
|
||||
Each element is a hook spec describing:
|
||||
|
||||
* Which modules to target
|
||||
* Which Python factory to call
|
||||
* What configuration to pass into that factory
|
||||
|
||||
---
|
||||
|
||||
### Hook spec schema
|
||||
|
||||
Each entry in `forward_hooks` is a JSON object with the following shape:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "optional-descriptive-name",
|
||||
"target_modules": ["pattern1", "pattern2", "..."],
|
||||
"hook_factory": "module.submodule:factory_name",
|
||||
"config": {
|
||||
"...": "arbitrary JSON"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `name` (optional)
|
||||
|
||||
* Human-readable name for logging.
|
||||
* Used only in log messages such as:
|
||||
|
||||
```text
|
||||
Registered forward hook 'outer_linear_hooks' on outer.0
|
||||
```
|
||||
|
||||
#### `target_modules` (required)
|
||||
|
||||
* List of **module name patterns** used to match entries in `model.named_modules()`.
|
||||
* Patterns are matched using `fnmatch.fnmatch`, so:
|
||||
|
||||
* `"outer.0"` matches exactly `"outer.0"`.
|
||||
* `"outer.*"` matches `"outer.0"`, `"outer.1"`, `"outer.inner"`, etc.
|
||||
* `"outer.inner.*"` matches children under `outer.inner`.
|
||||
|
||||
> If no modules match the given patterns, hook registration does **not** fail.
|
||||
> Instead, SGLang logs a warning and continues:
|
||||
>
|
||||
> ```text
|
||||
> No modules matched hook spec 'name' patterns=['...']
|
||||
> ```
|
||||
|
||||
#### `hook_factory` (required)
|
||||
|
||||
* String path to the Python factory function that creates the hook.
|
||||
* Supported formats:
|
||||
|
||||
* `"package.module:factory_name"`
|
||||
* `"package.module.submodule.factory_name"`
|
||||
|
||||
The path is resolved via:
|
||||
|
||||
```python
|
||||
def resolve_callable(path: Optional[str]) -> Optional[Callable]:
|
||||
if path is None:
|
||||
return None
|
||||
|
||||
if ":" in path:
|
||||
module_name, fn_name = path.split(":", 1)
|
||||
else:
|
||||
parts = path.split(".")
|
||||
if len(parts) < 2:
|
||||
raise ValueError(
|
||||
f"Invalid hook callable path '{path}'. "
|
||||
"Expected 'module.submodule:factory' or 'module.submodule.factory'."
|
||||
)
|
||||
*mod_parts, fn_name = parts
|
||||
module_name = ".".join(mod_parts)
|
||||
|
||||
module = importlib.import_module(module_name)
|
||||
try:
|
||||
return getattr(module, fn_name)
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"Module '{module_name}' has no attribute '{fn_name}' "
|
||||
f"(from hook path '{path}')"
|
||||
) from e
|
||||
```
|
||||
|
||||
**Failure modes**:
|
||||
|
||||
* If the path is malformed (not enough dots and no `:`), a `ValueError` is raised at startup.
|
||||
* If the module imports but the attribute is missing, an `AttributeError` is raised with a clear error message.
|
||||
* If the hook factory returns `None`, a warning is logged and no hook is registered for that spec (initialization continues).
|
||||
|
||||
The first two cause initialization to fail fast with a descriptive error; the last one is non-fatal.
|
||||
|
||||
#### `config` (optional)
|
||||
|
||||
* Arbitrary JSON object.
|
||||
* Passed directly to the hook factory as a Python `dict`.
|
||||
* This lets you parameterize hook behavior from config (e.g. tags, log levels, sampling rates, etc.).
|
||||
|
||||
---
|
||||
|
||||
### Hook lifecycle and behavior
|
||||
|
||||
Hooks are registered in `ModelRunner.initialize()`:
|
||||
|
||||
```python
|
||||
if server_args.forward_hooks:
|
||||
register_forward_hooks(self.model, server_args.forward_hooks)
|
||||
```
|
||||
|
||||
The actual registration logic is implemented by `register_forward_hooks`:
|
||||
|
||||
```python
|
||||
def register_forward_hooks(model: nn.Module, hook_specs: List[dict[str, Any]]) -> None:
|
||||
"""
|
||||
hook_specs is a list of dicts from server_args.forward_hooks.
|
||||
Attaches forward hooks to the matching modules.
|
||||
"""
|
||||
name_to_module = dict(model.named_modules())
|
||||
|
||||
for spec in hook_specs:
|
||||
spec_name = spec.get("name", "")
|
||||
target_patterns = spec.get("target_modules", [])
|
||||
if not target_patterns:
|
||||
logger.warning(
|
||||
f"Hook spec '{spec_name}' has no 'target_modules', skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
hook_factory_path = spec.get("hook_factory")
|
||||
if not hook_factory_path:
|
||||
logger.warning(
|
||||
f"Hook spec '{spec_name}' has no 'hook_factory', skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
config = spec.get("config") or {}
|
||||
hook_factory = resolve_callable(hook_factory_path)
|
||||
|
||||
hook = hook_factory(config) if hook_factory else None
|
||||
if hook is None:
|
||||
logger.warning(
|
||||
f"Hook factory '{hook_factory_path}' for spec '{spec_name}' "
|
||||
"returned None, not registering any hook"
|
||||
)
|
||||
continue
|
||||
|
||||
# Resolve patterns like "model.layers.*.mlp"
|
||||
matched = []
|
||||
for name, module in name_to_module.items():
|
||||
if any(fnmatch.fnmatch(name, pattern) for pattern in target_patterns):
|
||||
matched.append((name, module))
|
||||
|
||||
if not matched:
|
||||
logger.warning(
|
||||
f"No modules matched hook spec '{spec_name}' "
|
||||
f"patterns={target_patterns}"
|
||||
)
|
||||
continue
|
||||
|
||||
for module_name, module in matched:
|
||||
if hook:
|
||||
_ = module.register_forward_hook(hook)
|
||||
logger.info(
|
||||
f"Registered forward hook '{spec_name}' "
|
||||
f"on {module_name}"
|
||||
)
|
||||
```
|
||||
|
||||
Key points:
|
||||
|
||||
* Hooks are **forward hooks only** (via `module.register_forward_hook`).
|
||||
* They are attached once at initialization.
|
||||
* Hook handles are currently not stored on `ModelRunner` (they cannot be removed later via this API).
|
||||
* Failure to match any modules is non-fatal; a warning is logged instead.
|
||||
* If a hook factory returns `None`, a warning is logged and that spec is skipped.
|
||||
|
||||
---
|
||||
|
||||
### Writing a hook factory
|
||||
|
||||
A hook factory is a regular Python function:
|
||||
|
||||
* Takes a `config: dict` (from JSON)
|
||||
* Returns a forward hook function with signature `(module, inputs, output)`
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
HOOK_CALLS = []
|
||||
|
||||
def dummy_hook_factory(config):
|
||||
"""Factory that returns a forward hook capturing a tag from config."""
|
||||
tag = config.get("tag", "default")
|
||||
|
||||
def hook(module, inputs, output):
|
||||
HOOK_CALLS.append(
|
||||
{
|
||||
"module_type": type(module).__name__,
|
||||
"tag": tag,
|
||||
"shape": tuple(output.shape),
|
||||
}
|
||||
)
|
||||
return output # must return output if you don’t want to modify the tensor
|
||||
|
||||
return hook
|
||||
```
|
||||
|
||||
In JSON:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"forward_hooks": [
|
||||
{
|
||||
"name": "capture_outer",
|
||||
"target_modules": ["outer.0", "outer.1"],
|
||||
"hook_factory": "my_project.hooks:dummy_hook_factory",
|
||||
"config": {
|
||||
"tag": "outer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
* Resolve `my_project.hooks:dummy_hook_factory` to a Python callable.
|
||||
* Call it with `config = {"tag": "outer"}`.
|
||||
* Use the returned hook for all modules matching `outer.0` and `outer.1`.
|
||||
* Append metadata about each call to `HOOK_CALLS`.
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
* Define `forward_hooks` as a list of specs in `ServerArgs` to turn on the feature.
|
||||
|
||||
* Each spec:
|
||||
|
||||
* selects modules via `target_modules` (glob patterns over `model.named_modules()`),
|
||||
* points to a hook factory via `hook_factory`,
|
||||
* passes arbitrary `config` into that factory.
|
||||
|
||||
* Hook factories are resolved via `resolve_callable`, which supports `module:factory` and `module.submodule.factory`.
|
||||
|
||||
* Hooks are standard PyTorch forward hooks, attached once at startup and invoked on every forward pass.
|
||||
|
||||
* Misconfiguration is either:
|
||||
|
||||
* **fatal and explicit** (bad path / missing attribute), or
|
||||
* **non-fatal with clear warnings** (no targets matched, or factory returned `None`).
|
||||
9
third_party/sglang/docs/advanced_features/hicache.rst
vendored
Normal file
9
third_party/sglang/docs/advanced_features/hicache.rst
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Hierarchical KV Caching (HiCache)
|
||||
=================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
hicache_best_practices.md
|
||||
hicache_design.md
|
||||
hicache_storage_runtime_attach_detach.md
|
||||
217
third_party/sglang/docs/advanced_features/hicache_best_practices.md
vendored
Normal file
217
third_party/sglang/docs/advanced_features/hicache_best_practices.md
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
# SGLang HiCache Best Practices
|
||||
|
||||
## Why HiCache Matters
|
||||
|
||||
SGLang HiCache extends the traditional RadixAttention with a three-tier hierarchical KV caching system that dramatically improves performance for long-context and multi-turn conversation scenarios. By intelligently managing KV caches across GPU memory, host memory, and external storage backends, HiCache addresses the fundamental capacity bottleneck that limits cache hit rates in conventional systems.
|
||||
|
||||
## Configuration Guidelines
|
||||
|
||||
## Core HiCache Parameters
|
||||
|
||||
```bash
|
||||
# Essential HiCache flags
|
||||
--page-size 64 # Page size for cache management
|
||||
--enable-hierarchical-cache # Enable HiCache
|
||||
--hicache-ratio 2 # Host memory ratio (2x GPU memory)
|
||||
--hicache-size 100 # Host memory size in GBs, will override the above ratio
|
||||
--hicache-io-backend kernel # The I/O backend of moving data between CPU and GPU
|
||||
--hicache-write-policy write_through # Cache write policy from GPU to CPU
|
||||
--hicache-storage-backend # Optional storage backend (e.g., hf3fs, mooncake, etc.)
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Besides configuring `--hicache-storage-backend` at startup, SGLang also supports **runtime attach/detach** of the HiCache storage backend (no restart required) via HTTP admin endpoints. See [Runtime Attach/Detach HiCache Storage Backend](hicache_storage_runtime_attach_detach.md).
|
||||
|
||||
## Key Configurations with Storage Backends Enabled
|
||||
|
||||
### Memory Layout Optimization
|
||||
|
||||
```bash
|
||||
# Page-first: Optimized for I/O efficiency with zero-copy (recommended with kernel backend)
|
||||
--hicache-mem-layout page_first
|
||||
# Page-first-direct: Optimized for direct I/O operations (Compatible with fa3 and same zero-copy performance as page_first)
|
||||
--hicache-mem-layout page_first_direct
|
||||
# Layer-first
|
||||
--hicache-mem-layout layer_first
|
||||
```
|
||||
**Layout Compatibility:**
|
||||
- `page_first`: Only compatible with `kernel` I/O backend, automatically switches to `layer_first` with `direct` backend
|
||||
- `page_first_direct`: Specifically designed for `direct` I/O backend with optimized memory organization
|
||||
|
||||
### Heterogeneous TP Support (GQA/MHA models)
|
||||
|
||||
HiCache storage supports cross-cluster KV reuse when different deployments use different TP sizes (for example, `tp=4` and `tp=8`) and share the same storage backend namespace.
|
||||
|
||||
Use `tp_lcm_size` in `--hicache-storage-backend-extra-config`:
|
||||
|
||||
```bash
|
||||
# Example: heterogeneous TP = {4, 8}, so lcm = 8
|
||||
--hicache-storage-backend-extra-config '{"tp_lcm_size": 8}'
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Set `tp_lcm_size` to the least common multiple (LCM) of all TP sizes that will share the same HiCache storage.
|
||||
- For MHA models with Mooncake and `page_head` layout, HiCache will split head shards based on `tp_lcm_size` to make keys reusable across heterogeneous TP deployments.
|
||||
- If all clusters use the same TP size, this option is not needed.
|
||||
|
||||
### Prefetch Policies
|
||||
|
||||
```bash
|
||||
# Best-effort: Terminate prefetch when needed
|
||||
--hicache-storage-prefetch-policy best_effort
|
||||
# Wait-complete: Ensure complete prefetch, higher cache reuse
|
||||
--hicache-storage-prefetch-policy wait_complete
|
||||
# Timeout: Balance between completion and best-effort
|
||||
--hicache-storage-prefetch-policy timeout
|
||||
```
|
||||
|
||||
### Integration with PD Disaggregation
|
||||
|
||||
HiCache works seamlessly with PD Disaggregation. You can choose between two configurations:
|
||||
|
||||
1. **Prefill-only HiCache**: Enable HiCache only on Prefill nodes, allowing KV cache sharing among Prefill instances
|
||||
2. **Full HiCache with async offloading**: Enable HiCache on Prefill nodes and async KV cache offloading on Decode nodes, allowing Prefill nodes to reuse KV caches from Decode nodes in multi-turn dialogue scenarios
|
||||
|
||||
```bash
|
||||
# Prefill node with HiCache enabled for cross-prefill sharing (ideal for SystemPrompt scenarios)
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path /xxx/DeepSeek-R1/ \
|
||||
--tp 8 \
|
||||
--host 0.0.0.0 \
|
||||
--port 10000 \
|
||||
--enable-metrics \
|
||||
--enable-cache-report \
|
||||
--mem-fraction-static 0.85 \
|
||||
--page-size 64 \
|
||||
--enable-hierarchical-cache \
|
||||
--hicache-ratio 2 \
|
||||
--hicache-size 0 \
|
||||
--hicache-mem-layout page_first_direct \
|
||||
--hicache-io-backend direct \
|
||||
--hicache-write-policy write_through \
|
||||
--hicache-storage-backend hf3fs \
|
||||
--hicache-storage-prefetch-policy wait_complete \
|
||||
--disaggregation-ib-device mlx5_0 \
|
||||
--disaggregation-mode prefill \
|
||||
--disaggregation-transfer-backend mooncake
|
||||
|
||||
# Decode node with async offloading enabled for KV cache reuse by Prefill (ideal for multi-turn conversations)
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path /xxx/DeepSeek-R1/ \
|
||||
--tp 8 \
|
||||
--host 0.0.0.0 \
|
||||
--port 10000 \
|
||||
--enable-metrics \
|
||||
--enable-cache-report \
|
||||
--page-size 64 \
|
||||
--hicache-ratio 2 \
|
||||
--hicache-size 0 \
|
||||
--hicache-mem-layout page_first_direct \
|
||||
--hicache-io-backend direct \
|
||||
--hicache-write-policy write_through \
|
||||
--hicache-storage-backend hf3fs \
|
||||
--hicache-storage-prefetch-policy wait_complete \
|
||||
--disaggregation-decode-enable-offload-kvcache \ # Enable async KV cache offloading in decode node
|
||||
--disaggregation-ib-device mlx5_0 \
|
||||
--disaggregation-mode decode \
|
||||
--disaggregation-transfer-backend mooncake
|
||||
```
|
||||
|
||||
|
||||
### Deployment with HF3FS
|
||||
|
||||
Here is an example of deploying DeepSeek-R1 with HiCache-HF3FS. For more details, see the [HF3FS Documentation](../../python/sglang/srt/mem_cache/storage/hf3fs/docs/README.md).
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path /xxx/DeepSeek-R1/ \
|
||||
--log-level info \
|
||||
--tp 8 \
|
||||
--host 0.0.0.0 \
|
||||
--port 10000 \
|
||||
--enable-metrics \
|
||||
--enable-cache-report \
|
||||
--page-size 64 \
|
||||
--mem-fraction-static 0.85 \
|
||||
--enable-hierarchical-cache \
|
||||
--hicache-ratio 2 \
|
||||
--hicache-size 0 \
|
||||
--hicache-mem-layout page_first_direct \
|
||||
--hicache-io-backend direct \
|
||||
--hicache-write-policy write_through \
|
||||
--hicache-storage-backend hf3fs \
|
||||
--hicache-storage-prefetch-policy wait_complete \
|
||||
```
|
||||
|
||||
### Deployment with Mooncake
|
||||
|
||||
Here is an example of deploying Qwen3-235B-A22B-Instruct-2507 with Mooncake. For more details, see the [Mooncake Documentation](../../python/sglang/srt/mem_cache/storage/mooncake_store/README.md).
|
||||
|
||||
```bash
|
||||
# Set Mooncake environment variables
|
||||
export MOONCAKE_TE_META_DATA_SERVER="http://127.0.0.1:8080/metadata"
|
||||
export MOONCAKE_GLOBAL_SEGMENT_SIZE=816043786240
|
||||
export MOONCAKE_PROTOCOL="rdma"
|
||||
export MOONCAKE_DEVICE="$DEVICE_LIST"
|
||||
export MOONCAKE_MASTER=127.0.0.1:50051
|
||||
|
||||
# Launch SGLang server with Mooncake backend
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path $MODEL_PATH \
|
||||
--tp 8 \
|
||||
--page-size 64 \
|
||||
--enable-hierarchical-cache \
|
||||
--hicache-ratio 2 \
|
||||
--hicache-mem-layout page_first_direct \
|
||||
--hicache-io-backend direct \
|
||||
--hicache-storage-backend mooncake \
|
||||
--hicache-write-policy write_through \
|
||||
--hicache-storage-prefetch-policy timeout
|
||||
```
|
||||
|
||||
|
||||
## Custom Storage Backend Integration
|
||||
|
||||
To integrate a new storage backend:
|
||||
|
||||
1. **Implement three core methods:**
|
||||
- `get(key)`: Retrieve value by key
|
||||
- `exists(key)`: Check key existence
|
||||
- `set(key, value)`: Store key-value pair
|
||||
|
||||
2. **Register your backend:** Add your storage backend to the HiCache [BackendFactory](../../python/sglang/srt/mem_cache/storage/backend_factory.py#L188)
|
||||
|
||||
The HiCache controller handles all scheduling and synchronization automatically.
|
||||
|
||||
### Dynamic Backend Loading
|
||||
|
||||
Alternatively, you can use dynamic loading to avoid hard-coding your backend in the repository:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path your-model \
|
||||
--enable-hierarchical-cache \
|
||||
--hicache-storage-backend dynamic \
|
||||
--hicache-storage-backend-extra-config '{"backend_name":"custom_backend_name", "module_path": "your_module_path", "class_name": "YourHiCacheClassName"}'
|
||||
```
|
||||
|
||||
**Configuration Parameters:**
|
||||
- `--hicache-storage-backend`: Set to `dynamic`
|
||||
- `--hicache-storage-backend-extra-config`: JSON configuration with:
|
||||
- `backend_name`: Custom backend identifier
|
||||
- `module_path`: Python module path to your implementation
|
||||
- `class_name`: Your HiCache implementation class name
|
||||
- `interface_v1`: 0 (disable) or 1 (enable) to control usage of batch_get_v1 and batch_set_v1 methods
|
||||
|
||||
|
||||
## Community and Support
|
||||
|
||||
- **GitHub Issues**: Report bugs and feature requests
|
||||
- **Slack Channel**: Join community discussions in #sgl-kv-cache-store
|
||||
- **Documentation**: Refer to storage backend-specific guides
|
||||
|
||||
---
|
||||
|
||||
*This document will be continuously updated based on community feedback and new features. Contributions and suggestions are welcome!*
|
||||
157
third_party/sglang/docs/advanced_features/hicache_design.md
vendored
Normal file
157
third_party/sglang/docs/advanced_features/hicache_design.md
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
# HiCache System Design and Optimization
|
||||
|
||||
This document provides a comprehensive overview of SGLang HiCache, covering its system architecture, workflow and key components. It also details configuration parameters, optimization techniques, and integration with various L3 storage backends, serving as a complete reference for users and developers to understand and tune HiCache for efficient LLM inference.
|
||||
|
||||
## Why and What is HiCache?
|
||||
|
||||
In large language model inference, the prefill phase is often time-consuming: input sequences need to be first converted into Key-Value cache (KV cache) for subsequent decoding. When multiple requests share the same prefix, the KV cache for that prefix is identical. By caching and reusing these shared KV caches, redundant computation can be avoided. To address this, SGLang introduced RadixAttention, which leverages idle GPU memory to cache and reuse prefix KV caches, and **HiCache**, which extends this idea to host memory and distributed storage.
|
||||
|
||||
Inspired by the classic three-level cache design of modern CPUs, HiCache organizes GPU memory as L1, host memory as L2, and distributed storage as L3. This hierarchy enables HiCache to fully exploit the "idle" storage space of GPUs and CPUs, while integrating distributed cache systems such as Mooncake, 3FS, NIXL, and AIBrix KVCache for global KV cache storage and scheduling. As a result, HiCache significantly expands KV cache capacity while maintaining strong read performance—especially in workloads such as multi-QA and long-context inference, where KV cache reuse is frequent. For detailed benchmark results, see [this blog](https://lmsys.org/blog/2025-09-10-sglang-hicache/).
|
||||
|
||||
|
||||
## System Design
|
||||
|
||||
### Overall Architecture
|
||||
|
||||
In many modern CPU architectures, the small but fast L1 and L2 caches are private to each core, enabling rapid access to the hottest data, while the larger L3 cache is shared across all cores to significantly reduce redundancy within the cache. Similarly, in HiCache, the L1 and L2 KV caches are private to each inference instance, whereas the L3 KV cache is shared among all inference instances within the cluster.
|
||||
|
||||
### HiRadixTree: Metadata Organization in HiCache
|
||||
|
||||
For KV cache data organization, HiCache builds upon the RadixTree structure introduced in RadixAttention and proposes HiRadixTree. In RadixAttention, each node of the RadixTree corresponds to the KV cache of a consecutive span of tokens in GPU memory. A path from the root to a leaf node represents the prefix of a request, and shared prefixes across multiple requests can reuse the same nodes, thereby avoiding redundant storage.
|
||||
|
||||
HiRadixTree extends this idea: each node corresponds to the KV cache of a span of consecutive tokens and records where that KV cache is stored—whether in local GPU memory, CPU memory, L3 storage, or multiple of these tiers. If stored locally, HiRadixTree maintains precise metadata, including the exact storage address. However, to reduce overhead, HiRadixTree does not store or continuously synchronize metadata for L3 KV cache. Instead, when accessing L3 data, it queries the backend in real time to retrieve the necessary metadata, such as whether the data exists and on which server and location it resides.
|
||||
|
||||
### Overall Workflow
|
||||
|
||||
The workflow of HiCache mainly involves three key operations: **local match**, **prefetch** and **write-back**. When the system receives a new request, it first searches the local L1 and L2 caches for matching KV caches. For parts not found locally, it attempts to prefetch from L3. After prefetching, all required KV caches are loaded into the GPU for computation. Once the prefill computation is complete, the system considers storing the newly generated data into L2 or L3.
|
||||
|
||||

|
||||
|
||||
### Local Match
|
||||
|
||||
Local matching is the first step in HiCache's workflow, where incoming request tokens are matched against the HiRadixTree to locate cached KV data in local memory tiers (L1 GPU memory and L2 host memory).
|
||||
|
||||
The matching algorithm traverses the HiRadixTree from the root node, following child nodes that match the token sequence prefix. At each node, the incoming token sequence is compared with the node’s stored token sequence. When `page_size > 1`, matching is performed at the page granularity to optimize memory access patterns. If a match terminates within a node’s stored sequence, the node is automatically split to create an exact boundary, improving the efficiency of future matches.
|
||||
|
||||
The algorithm returns a continuous prefix of the request, with the first part residing in L1 and the latter part in L2.
|
||||
|
||||
Since the process only requires traversing the local HiRadixTree and does not involve any actual data copying, local matching is extremely fast.
|
||||
|
||||
### Prefetch from L3
|
||||
|
||||
Data prefetching is one of HiCache’s core optimization techniques, designed to proactively load KV caches from L3 storage into local L2 memory, thereby reducing access latency during subsequent operations.
|
||||
|
||||
**Prefetch Trigger Conditions**:
|
||||
After local matching, for the parts not found in L1 or L2, the system queries L3 to retrieve metadata for the next continuous matching KV caches. If the length of hit cache in L3 exceeds a threshold (default: 256 tokens, configurable), a prefetch operation is triggered.
|
||||
|
||||
**Prefetch Strategies**: HiCache provides three different prefetch termination strategies to address different scenario needs:
|
||||
- **best_effort**: Terminates immediately when GPU can execute prefill computation, with no waiting time, suitable for scenarios extremely sensitive to latency.
|
||||
- **wait_complete**: Must wait for all prefetch operations to complete, suitable for scenarios requiring high cache hit rates.
|
||||
- **timeout**: Terminates after specified time or when complete, balancing latency and cache hit rate needs.
|
||||
|
||||
After prefetching stops, the data already fetched is used together with the local data for the prefill computation.
|
||||
|
||||
For **timeout** strategy, HiCache introduces two configuration parameters to support fine-grained control over prefetch timeout conditions:
|
||||
|
||||
* `prefetch_timeout_base`: the base timeout, representing overhead unrelated to the number of tokens (e.g., scheduling and synchronization).
|
||||
* `prefetch_timeout_per_ki_token`: the incremental timeout per thousand tokens.
|
||||
|
||||
The timeout is computed as:
|
||||
|
||||
```
|
||||
timeout = prefetch_timeout_base + prefetch_timeout_per_ki_token * num_token_to_fetch / 1024
|
||||
```
|
||||
|
||||
### Data Write-back
|
||||
|
||||
The write-back mechanism is responsible for moving frequently accessed KV caches from L1 to L2 and L3, enabling larger and longer-term storage as well as cache sharing across instances.
|
||||
|
||||
**Configurable Write-back Policies**: HiCache supports three write-back strategies:
|
||||
|
||||
* **write_through**: Every access is immediately written back to the next level. When bandwidth is sufficient, this strategy provides the strongest caching benefit.
|
||||
* **write_through_selective**: Data is written back only after the access frequency exceeds a threshold. This strategy backs up only hot data, reducing I/O overhead.
|
||||
* **write_back**: Data is written back to the next level only when it is evicted from the upper level. This strategy alleviates storage pressure and is suitable for scenarios where storage capacity is limited but memory utilization must be maximized.
|
||||
|
||||
**Cross-instance Sharing**: When data is written back from L2 to L3, only data not already present in L3 is transferred. KV caches stored in L3 can then be shared across all SGLang instances in the cluster (depending on the L3 backend implementation), significantly improving cache hit rates within the same memory budget.
|
||||
|
||||
### Multi-Rank Synchronization
|
||||
|
||||
During multi-GPU parallel computation, such as tensor parallelism (TP), HiCache must ensure consistent states across different ranks. Therefore, critical computation steps require the use of `all_reduce` for state synchronization.
|
||||
|
||||
For example, during prefetching, `all_reduce(op=min)` is used to ensure that all ranks obtain the same number of L3 hits, preventing inconsistent judgments about whether the prefetch threshold has been reached. Similarly, after prefetching completes or terminates, `all_reduce(op=min)` is again required to guarantee consensus among ranks on the prefix length of the successfully retrieved KV cache.
|
||||
|
||||
### Data Transfer Optimization
|
||||
|
||||
**Zero-Copy Data Transfers**: Both prefetching and write-back involve substantial data movement. Minimizing the number of data copies can significantly improve system performance. HiCache supports passing memory addresses and sizes directly when transferring data from L2 memory to an L3 backend.
|
||||
|
||||
**“Batch-Oriented” Data Organization**: The granularity of data reads and writes has a major impact on performance. To address this, HiCache L3 stores and transfers KV cache data at the granularity of **pages** and supports different data layouts beyond the existing `layer first` scheme, including `page first` and `page first direct`. Under the `page first` and `page first direct` layouts, all KV cache data belonging to the same page is placed in contiguous memory, allowing it to be passed as a single object to L3 using zero-copy transfers.
|
||||
|
||||

|
||||
|
||||
However, because GPU KV computation is naturally performed layer by layer, the GPU inherently operates in a `layer first` layout. When transferring `page first` data from L2 to the GPU, data must be transferred at the granularity of one token per layer. The `page first direct` layout mitigates this issue by grouping together all tokens of a given layer within a page, allowing transfers from L2 to GPU to be aggregated at the page-layer level.
|
||||
|
||||
**CPU-to-GPU Transfer Optimizations**: In HiCache, moving data from CPU memory to GPU is as performance-critical as prefetching data from L3 to L2. HiCache employs several optimizations for this process:
|
||||
|
||||
* **Compute-Transfer Overlap**: During the prefill phase, when transferring data from CPU to GPU, HiCache overlaps layers by concurrently loading the KV cache of layer N+1 while computing layer N. This effectively hides data transfer latency.
|
||||
* **GPU-assisted I/O Kernels**: On top of `cudaMemcpyAsync`, HiCache implements a set of GPU-assisted I/O kernels specifically optimized for KV cache transfers between CPU and GPU. Compared to the baseline approach, these kernels achieve up to 3x higher transfer speed.
|
||||
|
||||
**Write-back Optimization for MLA**: For MHA (Multi-Head Attention) models under multi-TP, each rank holds `1/tp_size` of a token’s KV data. In contrast, for MLA (Multi-Layer Attention) models, all ranks hold the complete and identical KV data for each token. HiCache includes a dedicated optimization for MLA: only one rank initiates the write-back operation, ensuring that data is not redundantly stored across ranks.
|
||||
|
||||
### Integration with PD-Disaggregation Deployment Mode
|
||||
|
||||
SGLang supports a PD (Prefill-Decode) disaggregation deployment mode through the Mooncake TransferEngine (for details, see [this doc](https://docs.sglang.io/advanced_features/pd_disaggregation.html)). In the PD-disaggregation deployment mode, HiCache can be enabled on both the prefill nodes and decode nodes to optimize prefill performance. If enabled on decode nodes, the decode output will also be written back to L3.
|
||||
|
||||
### Unified Interfaces and Rich L3 Storage Backends
|
||||
|
||||
HiCache encapsulates all read, write, and query operations on L3 backends within the `class HiCacheStorage(ABC)`, exposing a set of simple and consistent interfaces. This design supports a wide range of L3 storage backends and allows users to select the one that best fits their specific use cases.
|
||||
|
||||
- **Mooncake**: Mooncake is a high-performance caching system for LLM inference that leverages RDMA and multi-NIC resources to enable zero-copy, ultra-fast data transfers. Try Mooncake [here](https://github.com/sgl-project/sglang/tree/main/python/sglang/srt/mem_cache/storage/mooncake_store).
|
||||
|
||||
- **DeepSeek 3FS (HF3FS)**: HF3FS is a Kubernetes-native distributed storage solution with operator-based deployment. Try HF3FS [here](https://github.com/sgl-project/sglang/tree/main/python/sglang/srt/mem_cache/storage/hf3fs).
|
||||
|
||||
- **NIXL**: NIXL provides a unified API for accessing various storage plugins, including but not limited to DeepSeek's 3FS, GPU Direct Storage (GDS) and Amazon S3-compatible object storage. Try NIXL [here](https://github.com/sgl-project/sglang/tree/main/python/sglang/srt/mem_cache/storage/nixl).
|
||||
|
||||
- **AIBrix KVCache**: AIBrix KVCache is a production-ready KVCache Offloading Framework, which enables efficient memory tiering and low-overhead cross-engine reuse. Try AIBrix KVCache [here](https://github.com/sgl-project/sglang/tree/main/python/sglang/srt/mem_cache/storage/aibrix_kvcache).
|
||||
|
||||
- **HiCacheFile**: A simple file-based storage backend for demonstration purposes.
|
||||
|
||||
Specifically, **LMCache**, an efficient KV cache layer for enterprise-scale LLM inference, provides an alternative solution to HiCache. Try LMCache [here](https://github.com/sgl-project/sglang/tree/main/python/sglang/srt/mem_cache/storage/lmcache).
|
||||
|
||||
## Related Parameters
|
||||
|
||||
- **`--enable-hierarchical-cache`**: Enable hierarchical cache functionality. This is required to use HiCache.
|
||||
|
||||
- **`--hicache-ratio HICACHE_RATIO`**: The ratio of the size of host KV cache memory pool to the size of device pool. For example, a value of 2 means the host memory pool is twice as large as the device memory pool. The value of this parameter must be greater than 1, as the current implementation requires the host memory allocated for the KV cache to be larger than the device memory allocated for the KV cache.
|
||||
|
||||
- **`--hicache-size HICACHE_SIZE`**: The size of host KV cache memory pool in gigabytes. This parameter overrides `hicache-ratio` if set. For example, `--hicache-size 30` allocates 30GB (1GB = 1e9 bytes) for the host memory pool **for each rank**. If there are 8 ranks, then the total memory size is 240GB. Just like `hicache-ratio`, the value of this parameter must be larger than the size of device memory allocated for KV cache.
|
||||
|
||||
**Note**: `--hicache-ratio` and `--hicache-size` are two critical parameters. In general, a larger HiCache size leads to a higher cache hit rate, which improves prefill performance. However, the relationship between cache size and hit rate is not linear. Once most reusable KV data—especially hot tokens—are already cached, further increasing the size may yield only marginal performance gains. Users can set these parameters based on their workload characteristics and performance requirements.
|
||||
|
||||
- **`--page-size PAGE_SIZE`**: The number of tokens per page. This parameter determines the granularity of KV cache storage and retrieval. Larger page sizes reduce metadata overhead and improve I/O efficiency for storage backends, but may lower the cache hit rate when only part of a page matches the stored KV cache. For workloads with long common prefixes, larger pages can improve performance, while workloads with more diverse prefixes may benefit from smaller pages. See [Data Transfer Optimization](#data-transfer-optimization) for how page granularity affects I/O performance.
|
||||
|
||||
- **`--hicache-storage-prefetch-policy {best_effort,wait_complete,timeout}`**: Controls when prefetching from storage should stop. See [Prefetch from L3](#prefetch-from-l3) for details.
|
||||
- `best_effort`: Prefetch as much as possible without blocking
|
||||
- `wait_complete`: Wait for prefetch to complete before proceeding
|
||||
- `timeout`: Terminates after specified time or when complete (Recommended for production environments, as setting an appropriate timeout helps the system meet required SLOs)
|
||||
|
||||
- **`--hicache-write-policy {write_back,write_through,write_through_selective}`**: Controls how data is written from faster to slower memory tiers. See [Data Write-back](#data-write-back) for details.
|
||||
- `write_through`: Immediately writes data to all tiers (strongest caching benefits)
|
||||
- `write_through_selective`: Uses hit-count tracking to back up only frequently accessed data
|
||||
- `write_back`: Writes data back to slower tiers only when eviction is needed (reduces I/O load)
|
||||
|
||||
- **`--hicache-io-backend {direct,kernel}`**: Choose the I/O backend for KV cache transfer between CPU and GPU. See [Data Transfer Optimization](#data-transfer-optimization) for details.
|
||||
- `direct`: Standard CUDA memory copy operations
|
||||
- `kernel`: GPU-assisted I/O kernels (recommended for better performance)
|
||||
|
||||
- **`--hicache-mem-layout {layer_first,page_first,page_first_direct}`**: Memory layout for the host memory pool. See [Data Transfer Optimization](#data-transfer-optimization) for details.
|
||||
- `layer_first`: Compatible with GPU computation kernels (default for GPU memory)
|
||||
- `page_first`: Optimized for I/O efficiency
|
||||
- `page_first_direct`: Groups all tokens of a given layer within a page, allowing transfers from L2 to GPU to be aggregated at the page-layer level
|
||||
|
||||
- **`--hicache-storage-backend {file,mooncake,hf3fs,nixl,aibrix,dynamic}`**: Choose the storage backend for the L3 tier. Built-in backends: file, mooncake, hf3fs, nixl, aibrix. For dynamic backend, use --hicache-storage-backend-extra-config to specify: `backend_name` (custom name), `module_path` (Python module path), `class_name` (backend class name). See [Unified Interfaces and Rich L3 Storage Backends](#unified-interfaces-and-rich-l3-storage-backends) for available backends.
|
||||
|
||||
- **`--enable-lmcache`**: Using LMCache as an alternative hierarchical cache solution.
|
||||
|
||||
- **`--hicache-storage-backend-extra-config HICACHE_STORAGE_BACKEND_EXTRA_CONFIG`**: the extra config can be either
|
||||
- a JSON string containing extra configuration for the storage backend, e.g., `--hicache-storage-backend-extra-config '{"prefetch_threshold":512, "prefetch_timeout_base": 0.5, "prefetch_timeout_per_ki_token": 0.25}' `, or
|
||||
- a TOML or JSON or YAML file specifying the extra configuration for the storage backend (to differentiate from the JSON string input, prepend a `@` in front of the file name), e.g., `--hicache-storage-backend-extra-config "@config.toml"` where `config.toml` is the config file containing the complex configurations. This can be useful when the configuration consists of many or complex key-value pairs (for instance, it is preferred to use a config file for NIXL backend as its configurations can be complex).
|
||||
132
third_party/sglang/docs/advanced_features/hicache_storage_runtime_attach_detach.md
vendored
Normal file
132
third_party/sglang/docs/advanced_features/hicache_storage_runtime_attach_detach.md
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
# Runtime Attach/Detach HiCache Storage Backend (No Restart)
|
||||
|
||||
This document explains how to **dynamically attach/detach the HiCache L3 storage backend at runtime** (e.g., `mooncake` / `hf3fs` / `nixl` / `file` / `aibrix` / `eic`) while **SGLang is already running and serving traffic**, without restarting the process.
|
||||
|
||||
For safety and consistency, the current implementation **strictly requires** these operations to happen only when the service is **idle**:
|
||||
|
||||
- **No running requests**
|
||||
- **No waiting/queued requests**
|
||||
|
||||
If the idle condition is not met, the API will fail fast (HTTP 400) and **will not modify** the current service state.
|
||||
|
||||
---
|
||||
|
||||
## 1. Background and implementation overview
|
||||
|
||||
### 1.1 Architecture / control path
|
||||
|
||||
The control path is:
|
||||
|
||||
1. **HTTP Server** (`python/sglang/srt/entrypoints/http_server.py`)
|
||||
- Exposes `PUT /hicache/storage-backend`, `DELETE /hicache/storage-backend`, `GET /hicache/storage-backend`
|
||||
2. **TokenizerManager** (`python/sglang/srt/managers/tokenizer_communicator_mixin.py`)
|
||||
- Sends the request to the Scheduler via `_Communicator`
|
||||
3. **Scheduler** (`python/sglang/srt/managers/scheduler.py`)
|
||||
- Performs a **strict idle check**
|
||||
- Calls `tree_cache.attach_storage_backend(...)` / `detach_storage_backend(...)`
|
||||
4. **HiRadixCache** (`python/sglang/srt/mem_cache/hiradix_cache.py`)
|
||||
- Parses `hicache_storage_backend_extra_config_json` (supports both backend config and prefetch knobs)
|
||||
- Calls `cache_controller.attach_storage_backend(...)` / `detach_storage_backend(...)`
|
||||
5. **HiCacheController** (`python/sglang/srt/managers/cache_controller.py`)
|
||||
- Creates/destroys the storage backend instance (via `StorageBackendFactory`)
|
||||
- Starts/stops backend background threads at runtime (prefetch/backup)
|
||||
|
||||
---
|
||||
|
||||
## 2. Idle-state requirement (strict)
|
||||
|
||||
The Scheduler uses `is_fully_idle()` which checks:
|
||||
|
||||
- No running batches (including chunked prefill, overlap, pipeline-parallel, and disaggregation paths)
|
||||
- No waiting requests in any queue (waiting, grammar, disagg bootstrap/prealloc/transfer/inflight)
|
||||
- No DLLM staging requests
|
||||
|
||||
If the condition is not met, attach/detach returns an error like:
|
||||
|
||||
- `Reject attach: scheduler is not idle. #queue-req=... #running-req=...`
|
||||
|
||||
> Tip: before switching, drain upstream traffic and wait for the server to become idle, then call attach/detach.
|
||||
|
||||
### 2.1 DP (data parallel) semantics
|
||||
|
||||
When `dp_size > 1`, the tokenizer dispatches the request to **all DP scheduler instances** and aggregates their responses:
|
||||
|
||||
- The final `success` is **true only if all DP ranks return success**
|
||||
- The final `message` concatenates messages from all DP ranks
|
||||
|
||||
This is intended to prevent “silent partial success”, but it also means you may see:
|
||||
|
||||
- Overall **failure** even though **some ranks already succeeded**
|
||||
|
||||
Currently there is **no automatic partial rollback** across DP ranks (see TODO in code). Operationally:
|
||||
|
||||
- Prefer to keep backend config identical across ranks
|
||||
- If attach fails, immediately call detach (best-effort/idempotent), fix config, then retry attach
|
||||
|
||||
---
|
||||
|
||||
## 3. How to use (HTTP Admin API)
|
||||
|
||||
The examples below assume your SGLang HTTP server is at `http://127.0.0.1:30000`.
|
||||
|
||||
### 3.1 Query current storage backend status
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:30000/hicache/storage-backend
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"hicache_storage_backend": "mooncake",
|
||||
"hicache_storage_backend_extra_config": "{\"master_server_address\":\"127.0.0.1:50051\", ...}"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Attach (enable) a storage backend
|
||||
```bash
|
||||
curl -s -X PUT http://127.0.0.1:30000/hicache/storage-backend \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"hicache_storage_backend": "mooncake"
|
||||
}'
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -s -X PUT http://127.0.0.1:30000/hicache/storage-backend \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"hicache_storage_backend": "mooncake",
|
||||
"hicache_storage_backend_extra_config_json": "{\"master_server_address\":\"127.0.0.1:50051\",\"protocol\":\"tcp\",\"global_segment_size\":\"4gb\",\"prefetch_threshold\":256}",
|
||||
"hicache_storage_prefetch_policy": "timeout"
|
||||
}'
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `hicache_storage_backend_extra_config_json` can include both:
|
||||
- **Backend configuration** (e.g., Mooncake master/metadata/protocol, etc.)
|
||||
- **Prefetch configuration** (`prefetch_threshold`, `prefetch_timeout_base`, `prefetch_timeout_per_ki_token`, `hicache_storage_pass_prefix_keys`)
|
||||
|
||||
### 3.3 Detach (disable) the storage backend
|
||||
|
||||
```bash
|
||||
curl -s -X DELETE http://127.0.0.1:30000/hicache/storage-backend
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Detach only makes SGLang **stop using** the L3 storage backend and stops prefetch/backup threads
|
||||
- It **does not automatically delete** data stored in Mooncake/HF3FS (or other remote backends)
|
||||
|
||||
---
|
||||
|
||||
## 4. Behavior and caveats
|
||||
|
||||
- **No restart required**: attach/detach switches in-process at runtime
|
||||
- **Must be idle**: otherwise the request is rejected to avoid consistency issues
|
||||
- **Host KV layout constraints still apply**: for example, Mooncake still requires layouts like `page_first/page_first_direct/page_head`; if the server's HiCache host-memory layout does not satisfy the backend requirements, attach will fail with an error
|
||||
- **Observability**:
|
||||
- After attach, `server_args.hicache_storage_backend*` is updated on both the tokenizer and scheduler sides
|
||||
- If metrics are enabled, attach will create a storage metrics collector in `HiRadixCache` on demand
|
||||
77
third_party/sglang/docs/advanced_features/hyperparameter_tuning.md
vendored
Normal file
77
third_party/sglang/docs/advanced_features/hyperparameter_tuning.md
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# Hyperparameter Tuning
|
||||
|
||||
## Achieving high throughput for offline batch inference
|
||||
|
||||
Achieving a large batch size is the most important thing for attaining high throughput in offline batch inference.
|
||||
When the server is running at full load in a steady state, look for the following in the log:
|
||||
|
||||
```Decode batch. #running-req: 233, #token: 370959, token usage: 0.82, cuda graph: True, gen throughput (token/s): 4594.01, #queue-req: 317```
|
||||
|
||||
### Adjust the request submission speed to control `#queue-req`
|
||||
|
||||
`#queue-req` indicates the number of requests in the queue.
|
||||
If you frequently see `#queue-req: 0`, it suggests that your client code is submitting requests too slowly.
|
||||
A healthy range for `#queue-req` is `100 - 2000`.
|
||||
However, avoid making `#queue-req` too large, as this will increase the scheduling overhead on the server.
|
||||
|
||||
### Achieve a high `token usage`
|
||||
|
||||
`token usage` indicates the KV cache memory utilization of the server. `token usage > 0.9` means good utilization.
|
||||
|
||||
If you frequently see `token usage < 0.9` and `#queue-req > 0`, it means the server is too conservative about taking in new requests. You can decrease `--schedule-conservativeness` to a value like 0.3.
|
||||
The case of a server being too conservative can happen when users send many requests with a large `max_new_tokens` but the requests stop very early due to EOS or stop strings.
|
||||
|
||||
On the other hand, if you see `token usage` very high and you frequently see warnings like
|
||||
`KV cache pool is full. Retract requests. #retracted_reqs: 1, #new_token_ratio: 0.9998 -> 1.0000`, you can increase `--schedule-conservativeness` to a value like 1.3.
|
||||
If you see `KV cache pool is full. Retract requests.` occasionally but not frequently (~1 time per minute), it is okay.
|
||||
|
||||
### Tune `--mem-fraction-static` to increase KV cache pool capacity
|
||||
SGLang allocates memory as follows:
|
||||
|
||||
Total memory usage = model weights + KV cache pool + CUDA graph buffers + activations
|
||||
|
||||
The `--mem-fraction-static` parameter determines how much memory is allocated to the first two components:
|
||||
|
||||
mem_fraction_static = (model weights + KV cache pool) / GPU memory capacity
|
||||
|
||||
To support higher concurrency, you should maximize the KV cache pool capacity by setting `--mem-fraction-static` as high as possible while still reserving enough memory for activations and CUDA graph buffers.
|
||||
|
||||
SGLang uses simple heuristics to set the default value of `--mem-fraction-static`, but you can optimize it for your use cases.
|
||||
As a rule of thumb, reserving 5–8 GB of memory for activations is typically sufficient. You can check this by inspecting the logs just before the server is ready.
|
||||
Look for log entries like this:
|
||||
|
||||
```
|
||||
[2025-08-11 17:17:03] max_total_num_tokens=665690, chunked_prefill_size=8192, max_prefill_tokens=16384, max_running_requests=4096, context_len=65536, available_gpu_mem=13.50 GB
|
||||
```
|
||||
|
||||
Check the `available_gpu_mem` value.
|
||||
- If it is between 5–8 GB, the setting is good.
|
||||
- If it is too high (e.g., 10 - 20 GB), increase `--mem-fraction-static` to allocate more memory to the KV cache.
|
||||
- If it is too low, you risk out-of-memory (OOM) errors later, so decrease `--mem-fraction-static`.
|
||||
|
||||
Another straightforward approach is to increase `--mem-fraction-static` in increments of 0.01 until you encounter OOM errors for your workloads.
|
||||
|
||||
### Avoid out-of-memory errors by tuning `--chunked-prefill-size`, `--mem-fraction-static`, and `--max-running-requests`
|
||||
|
||||
If you encounter out-of-memory (OOM) errors, you can adjust the following parameters:
|
||||
|
||||
- If OOM occurs during prefill, try reducing `--chunked-prefill-size` to `4096` or `2048`. This saves memory but slows down the prefill speed for long prompts.
|
||||
- If OOM occurs during decoding, try lowering `--max-running-requests`.
|
||||
- You can also reduce `--mem-fraction-static` to a smaller value, such as 0.8 or 0.7. This decreases the memory usage of the KV cache memory pool and helps prevent OOM errors during both prefill and decoding. However, it limits maximum concurrency and reduces peak throughput.
|
||||
|
||||
### Tune `--cuda-graph-max-bs`
|
||||
By default, CUDA graph is enabled only for small batch sizes (e.g., less than 160 or 256).
|
||||
However, for some models, especially at large tensor parallelism sizes, CUDA graph can be useful for batch sizes up to 512 or 768.
|
||||
Therefore, it may be beneficial to increase `--cuda-graph-max-bs` to a larger value.
|
||||
Note that CUDA graph consumes more memory, so you may need to reduce `--mem-fraction-static` at the same time.
|
||||
|
||||
### Tune `--dp-size` and `--tp-size`
|
||||
|
||||
Data parallelism is better for throughput. When there is enough GPU memory, always favor data parallelism for throughput. Refer to [SGLang Model Gateway (former Router)](../advanced_features/sgl_model_gateway.md) for a better data parallelism rather than using `dp_size` parameter.
|
||||
|
||||
### Try other options
|
||||
|
||||
- `torch.compile` accelerates small models on small batch sizes. You can enable it with `--enable-torch-compile`.
|
||||
- Try other quantization (e.g. FP8 quantization with `--quantization fp8`)
|
||||
- Try other parallelism strategies (e.g. [expert parallelism](https://lmsys.org/blog/2025-05-05-large-scale-ep/)) or DP attention for deepseek models (with `--enable-dp-attention --dp-size 8`).
|
||||
- If the workload has many shared prefixes, try `--schedule-policy lpm`. Here, `lpm` stands for longest prefix match. It reorders requests to encourage more cache hits but introduces more scheduling overhead.
|
||||
714
third_party/sglang/docs/advanced_features/lora.ipynb
vendored
Normal file
714
third_party/sglang/docs/advanced_features/lora.ipynb
vendored
Normal file
@@ -0,0 +1,714 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# LoRA Serving"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"SGLang enables the use of [LoRA adapters](https://arxiv.org/abs/2106.09685) with a base model. By incorporating techniques from [S-LoRA](https://arxiv.org/pdf/2311.03285) and [Punica](https://arxiv.org/pdf/2310.18547), SGLang can efficiently support multiple LoRA adapters for different sequences within a single batch of inputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Arguments for LoRA Serving"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The following server arguments are relevant for multi-LoRA serving:\n",
|
||||
"\n",
|
||||
"* `enable_lora`: Enable LoRA support for the model. This argument is automatically set to True if `--lora-paths` is provided for backward compatibility.\n",
|
||||
"\n",
|
||||
"* `enable_lora_overlap_loading`: Enable asynchronous LoRA weight loading in order to overlap H2D transfers with GPU compute. This should be enabled if you find that your LoRA workloads are bottlenecked by adapter weight loading, for example when frequently loading large LoRA adapters.\n",
|
||||
"\n",
|
||||
"* `lora_paths`: The list of LoRA adapters to load. Each adapter must be specified in one of the following formats: <PATH> | <NAME>=<PATH> | JSON with schema {\"lora_name\":str,\"lora_path\":str,\"pinned\":bool}.\n",
|
||||
"\n",
|
||||
"* `max_loras_per_batch`: Maximum number of adaptors used by each batch. This argument can affect the amount of GPU memory reserved for multi-LoRA serving, so it should be set to a smaller value when memory is scarce. Defaults to be 8.\n",
|
||||
"\n",
|
||||
"* `max_loaded_loras`: If specified, it limits the maximum number of LoRA adapters loaded in CPU memory at a time. The value must be greater than or equal to `max-loras-per-batch`.\n",
|
||||
"\n",
|
||||
"* `lora_eviction_policy`: LoRA adapter eviction policy when GPU memory pool is full. `lru`: Least Recently Used (default, better cache efficiency). `fifo`: First-In-First-Out.\n",
|
||||
"\n",
|
||||
"* `lora_backend`: The backend of running GEMM kernels for Lora modules. Currently we support Triton LoRA backend (`triton`) and Chunked SGMV backend (`csgmv`). In the future, faster backend built upon Cutlass or Cuda kernels will be added.\n",
|
||||
"\n",
|
||||
"* `max_lora_rank`: The maximum LoRA rank that should be supported. If not specified, it will be automatically inferred from the adapters provided in `--lora-paths`. This argument is needed when you expect to dynamically load adapters of larger LoRA rank after server startup.\n",
|
||||
"\n",
|
||||
"* `lora_target_modules`: The union set of all target modules where LoRA should be applied (e.g., `q_proj`, `k_proj`, `gate_proj`). If not specified, it will be automatically inferred from the adapters provided in `--lora-paths`. This argument is needed when you expect to dynamically load adapters of different target modules after server startup. You can also set it to `all` to enable LoRA for all supported modules. However, enabling LoRA on additional modules introduces a minor performance overhead. If your application is performance-sensitive, we recommend only specifying the modules for which you plan to load adapters.\n",
|
||||
"\n",
|
||||
"* `--max-lora-chunk-size`: Maximum chunk size for the ChunkedSGMV LoRA backend. Only used when --lora-backend is 'csgmv'. Choosing a larger value might improve performance. Please tune this value based on your hardware and workload as needed. Defaults to 16.\n",
|
||||
"\n",
|
||||
"* `tp_size`: LoRA serving along with Tensor Parallelism is supported by SGLang. `tp_size` controls the number of GPUs for tensor parallelism. More details on the tensor sharding strategy can be found in [S-Lora](https://arxiv.org/pdf/2311.03285) paper.\n",
|
||||
"\n",
|
||||
"From client side, the user needs to provide a list of strings as input batch, and a list of adaptor names that each input sequence corresponds to."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage\n",
|
||||
"\n",
|
||||
"### Serving Single Adaptor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Note:** SGLang supports LoRA adapters through two APIs:\n",
|
||||
"\n",
|
||||
"1. **OpenAI-Compatible API** (`/v1/chat/completions`, `/v1/completions`): Use the `model:adapter-name` syntax. See [OpenAI API with LoRA](../basic_usage/openai_api_completions.ipynb#Using-LoRA-Adapters) for examples.\n",
|
||||
"\n",
|
||||
"2. **Native API** (`/generate`): Pass `lora_path` in the request body (shown below)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import requests\n",
|
||||
"\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"from sglang.utils import wait_for_server, terminate_process"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" # Here we set max-loras-per-batch to 2: one slot for adaptor and another one for base model\n",
|
||||
" \"\"\"\n",
|
||||
"python3 -m sglang.launch_server --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --lora-paths lora0=algoprog/fact-generation-llama-3.1-8b-instruct-lora \\\n",
|
||||
" --max-loras-per-batch 2 \\\n",
|
||||
" --log-level warning \\\n",
|
||||
"\"\"\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": {\"max_new_tokens\": 32, \"temperature\": 0},\n",
|
||||
" # The first input uses lora0, and the second input uses the base model\n",
|
||||
" \"lora_path\": [\"lora0\", None],\n",
|
||||
"}\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"print(f\"Output 0: {response.json()[0]['text']}\")\n",
|
||||
"print(f\"Output 1: {response.json()[1]['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Serving Multiple Adaptors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"server_process, port = launch_server_cmd(\"\"\"\n",
|
||||
"python3 -m sglang.launch_server --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --lora-paths lora0=algoprog/fact-generation-llama-3.1-8b-instruct-lora \\\n",
|
||||
" lora1=Nutanix/Meta-Llama-3.1-8B-Instruct_SFT_lora_4_alpha_16_humaneval_raw_json \\\n",
|
||||
" --max-loras-per-batch 2 \\\n",
|
||||
" --log-level warning \\\n",
|
||||
"\"\"\")\n",
|
||||
"\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": {\"max_new_tokens\": 32, \"temperature\": 0},\n",
|
||||
" # The first input uses lora0, and the second input uses lora1\n",
|
||||
" \"lora_path\": [\"lora0\", \"lora1\"],\n",
|
||||
"}\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"print(f\"Output 0: {response.json()[0]['text']}\")\n",
|
||||
"print(f\"Output 1: {response.json()[1]['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Dynamic LoRA loading"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Instead of specifying all adapters during server startup via `--lora-paths`. You can also load & unload LoRA adapters dynamically via the `/load_lora_adapter` and `/unload_lora_adapter` API.\n",
|
||||
"\n",
|
||||
"When using dynamic LoRA loading, it's recommended to explicitly specify both `--max-lora-rank` and `--lora-target-modules` at startup. For backward compatibility, SGLang will infer these values from `--lora-paths` if they are not explicitly provided. However, in that case, you would have to ensure that all dynamically loaded adapters share the same shape (rank and target modules) as those in the initial `--lora-paths` or are strictly \"smaller\"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"lora0 = \"Nutanix/Meta-Llama-3.1-8B-Instruct_SFT_lora_4_alpha_16_humaneval_raw_json\" # rank - 4, target modules - q_proj, k_proj, v_proj, o_proj, gate_proj\n",
|
||||
"lora1 = \"algoprog/fact-generation-llama-3.1-8b-instruct-lora\" # rank - 64, target modules - q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj\n",
|
||||
"lora0_new = \"philschmid/code-llama-3-1-8b-text-to-sql-lora\" # rank - 256, target modules - q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# The `--target-lora-modules` param below is technically not needed, as the server will infer it from lora0 which already has all the target modules specified.\n",
|
||||
"# We are adding it here just to demonstrate usage.\n",
|
||||
"server_process, port = launch_server_cmd(\"\"\"\n",
|
||||
" python3 -m sglang.launch_server --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --cuda-graph-max-bs 2 \\\n",
|
||||
" --max-loras-per-batch 2 \\\n",
|
||||
" --max-lora-rank 256\n",
|
||||
" --lora-target-modules all\n",
|
||||
" --log-level warning\n",
|
||||
" \"\"\")\n",
|
||||
"\n",
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"wait_for_server(url, process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load adapter lora0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" url + \"/load_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora0\",\n",
|
||||
" \"lora_path\": lora0,\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"if response.status_code == 200:\n",
|
||||
" print(\"LoRA adapter loaded successfully.\", response.json())\n",
|
||||
"else:\n",
|
||||
" print(\"Failed to load LoRA adapter.\", response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load adapter lora1:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" url + \"/load_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora1\",\n",
|
||||
" \"lora_path\": lora1,\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"if response.status_code == 200:\n",
|
||||
" print(\"LoRA adapter loaded successfully.\", response.json())\n",
|
||||
"else:\n",
|
||||
" print(\"Failed to load LoRA adapter.\", response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check inference output:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": {\"max_new_tokens\": 32, \"temperature\": 0},\n",
|
||||
" # The first input uses lora0, and the second input uses lora1\n",
|
||||
" \"lora_path\": [\"lora0\", \"lora1\"],\n",
|
||||
"}\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"print(f\"Output from lora0: \\n{response.json()[0]['text']}\\n\")\n",
|
||||
"print(f\"Output from lora1 (updated): \\n{response.json()[1]['text']}\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Unload lora0 and replace it with a different adapter:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" url + \"/unload_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora0\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/load_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora0\",\n",
|
||||
" \"lora_path\": lora0_new,\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"if response.status_code == 200:\n",
|
||||
" print(\"LoRA adapter loaded successfully.\", response.json())\n",
|
||||
"else:\n",
|
||||
" print(\"Failed to load LoRA adapter.\", response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check output again:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": {\"max_new_tokens\": 32, \"temperature\": 0},\n",
|
||||
" # The first input uses lora0, and the second input uses lora1\n",
|
||||
" \"lora_path\": [\"lora0\", \"lora1\"],\n",
|
||||
"}\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"print(f\"Output from lora0: \\n{response.json()[0]['text']}\\n\")\n",
|
||||
"print(f\"Output from lora1 (updated): \\n{response.json()[1]['text']}\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### OpenAI-compatible API usage\n",
|
||||
"\n",
|
||||
"You can use LoRA adapters via the OpenAI-compatible APIs by specifying the adapter in the `model` field using the `base-model:adapter-name` syntax (for example, `qwen/qwen2.5-0.5b-instruct:adapter_a`). For more details and examples, see the “Using LoRA Adapters” section in the OpenAI API documentation: [openai_api_completions.ipynb](../basic_usage/openai_api_completions.ipynb).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### LoRA GPU Pinning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Another advanced option is to specify adapters as `pinned` during loading. When an adapter is pinned, it is permanently assigned to one of the available GPU pool slots (as configured by `--max-loras-per-batch`) and will not be evicted from GPU memory during runtime. Instead, it remains resident until it is explicitly unloaded.\n",
|
||||
"\n",
|
||||
"This can improve performance in scenarios where the same adapter is frequently used across requests, by avoiding repeated memory transfers and reinitialization overhead. However, since GPU pool slots are limited, pinning adapters reduces the flexibility of the system to dynamically load other adapters on demand. If too many adapters are pinned, it may lead to degraded performance, or in the most extreme case (`Number of pinned adapters == max-loras-per-batch`), halt all unpinned requests. Therefore, currently SGLang limits maximal number of pinned adapters to `max-loras-per-batch - 1` to prevent unexpected starvations. \n",
|
||||
"\n",
|
||||
"In the example below, we start a server with `lora1` loaded as pinned, `lora2` and `lora3` loaded as regular (unpinned) adapters. Please note that, we intentionally specify `lora2` and `lora3` in two different formats to demonstrate that both are supported."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"server_process, port = launch_server_cmd(\"\"\"\n",
|
||||
" python3 -m sglang.launch_server --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --cuda-graph-max-bs 8 \\\n",
|
||||
" --max-loras-per-batch 3 \\\n",
|
||||
" --max-lora-rank 256 \\\n",
|
||||
" --lora-target-modules all \\\n",
|
||||
" --lora-paths \\\n",
|
||||
" {\"lora_name\":\"lora0\",\"lora_path\":\"Nutanix/Meta-Llama-3.1-8B-Instruct_SFT_lora_4_alpha_16_humaneval_raw_json\",\"pinned\":true} \\\n",
|
||||
" {\"lora_name\":\"lora1\",\"lora_path\":\"algoprog/fact-generation-llama-3.1-8b-instruct-lora\"} \\\n",
|
||||
" lora2=philschmid/code-llama-3-1-8b-text-to-sql-lora\n",
|
||||
" --log-level warning\n",
|
||||
" \"\"\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"wait_for_server(url, process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can also specify adapter as pinned during dynamic adapter loading. In the example below, we reload `lora2` as pinned adapter:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" url + \"/unload_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora1\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/load_lora_adapter\",\n",
|
||||
" json={\n",
|
||||
" \"lora_name\": \"lora1\",\n",
|
||||
" \"lora_path\": \"algoprog/fact-generation-llama-3.1-8b-instruct-lora\",\n",
|
||||
" \"pinned\": True, # Pin the adapter to GPU\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Verify that the results are expected:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": {\"max_new_tokens\": 32, \"temperature\": 0},\n",
|
||||
" # The first input uses lora0, and the second input uses lora1\n",
|
||||
" \"lora_path\": [\"lora0\", \"lora1\", \"lora2\"],\n",
|
||||
"}\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"print(f\"Output from lora0 (pinned): \\n{response.json()[0]['text']}\\n\")\n",
|
||||
"print(f\"Output from lora1 (pinned): \\n{response.json()[1]['text']}\\n\")\n",
|
||||
"print(f\"Output from lora2 (not pinned): \\n{response.json()[2]['text']}\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Choosing LoRA Backend\n",
|
||||
"\n",
|
||||
"SGLang supports two LoRA backends that you can choose from using the `--lora-backend` argument:\n",
|
||||
"\n",
|
||||
"- `triton`: Basic Triton-based backend.\n",
|
||||
"- `csgmv`: Default chunked SGMV backend optimized for high concurrency scenarios.\n",
|
||||
"\n",
|
||||
"The `csgmv` backend was recently introduced to improve performance especially at high-concurrency scenarios. Our benchmark shows that it achieves 20% to 80% latency improvements over the basic triton backend."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"server_process, port = launch_server_cmd(\"\"\"\n",
|
||||
" python3 -m sglang.launch_server \\\n",
|
||||
" --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --lora-backend csgmv \\\n",
|
||||
" --max-loras-per-batch 16 \\\n",
|
||||
" --lora-paths lora1=path/to/lora1 lora2=path/to/lora2\n",
|
||||
" \"\"\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LoRA Overlap Loading"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"By using the `--enable-lora-overlap-loading` server argument, the SGLang engine is able to overlap the loading of LoRA weights with prefill and decode compute, essentially hiding the data movement for LoRA weights behind GPU computation. Our benchmarks show that under adversarial conditions, enabling this feature can result in a ~35% reduction in median TTFT - (see the [LoRA overlap loading PR](https://github.com/sgl-project/sglang/pull/15512) for detailed benchmarks)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"lora0 = \"Nutanix/Meta-Llama-3.1-8B-Instruct_SFT_lora_4_alpha_16_humaneval_raw_json\"\n",
|
||||
"lora1 = \"algoprog/fact-generation-llama-3.1-8b-instruct-lora\"\n",
|
||||
"lora2 = \"philschmid/code-llama-3-1-8b-text-to-sql-lora\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\"\"\"\n",
|
||||
" python3 -m sglang.launch_server \\\n",
|
||||
" --model-path meta-llama/Meta-Llama-3.1-8B-Instruct \\\n",
|
||||
" --enable-lora \\\n",
|
||||
" --enable-lora-overlap-loading \\\n",
|
||||
" --lora-paths lora0=Nutanix/Meta-Llama-3.1-8B-Instruct_SFT_lora_4_alpha_16_humaneval_raw_json \\\n",
|
||||
" lora1=algoprog/fact-generation-llama-3.1-8b-instruct-lora \\\n",
|
||||
" lora2=philschmid/code-llama-3-1-8b-text-to-sql-lora \\\n",
|
||||
" --max-lora-rank 256 \\\n",
|
||||
" --max-loras-per-batch 2 \\\n",
|
||||
" --max-loaded-loras 4\n",
|
||||
" \"\"\")\n",
|
||||
"\n",
|
||||
"url = f\"http://127.0.0.1:{port}\"\n",
|
||||
"wait_for_server(url, process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"json_data = {\n",
|
||||
" \"text\": [\n",
|
||||
" \"Write a very long fairy-tale.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" \"List 3 countries and their capitals.\",\n",
|
||||
" ],\n",
|
||||
" \"sampling_params\": [\n",
|
||||
" {\"max_new_tokens\": 1024, \"temperature\": 0},\n",
|
||||
" {\"max_new_tokens\": 64, \"temperature\": 0},\n",
|
||||
" {\"max_new_tokens\": 64, \"temperature\": 0},\n",
|
||||
" ],\n",
|
||||
" \"lora_path\": [\"lora0\", \"lora1\", \"lora2\"],\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# lora0 and lora1 will be loaded into the memory pool first, and because max_loras_per_batch = 2, lora2's request will remain in the queue.\n",
|
||||
"# lora1's request will likely finish first, and once it does, lora2 will be loaded. With --enable-lora-overlap-loading, this loading will\n",
|
||||
"# occur asynchronously and thus decoding for lora0's request won't be blocked.\n",
|
||||
"response = requests.post(\n",
|
||||
" url + \"/generate\",\n",
|
||||
" json=json_data,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"for i in range(3):\n",
|
||||
" print(f\"Output from lora{i}: \\n{response.json()[i]['text']}\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Limitations of LoRA Overlap Loading"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"However, LoRA overlap loading is not free and comes with two important caveats:\n",
|
||||
"\n",
|
||||
"1. **Pinned CPU memory requirement**:\n",
|
||||
" Asynchronous H2D memory copies require LoRA weights to be pinned in CPU memory, which is a finite system resource. To mitigate excessive pinned-memory usage, SGLang currently restricts `max_loaded_loras` to be at most 2× `max_loras_per_batch` when LoRA overlap loading is enabled.\n",
|
||||
"\n",
|
||||
"2. **Reduced multi-adapter prefill batching**:\n",
|
||||
" With overlap loading, adapters become available on the GPU at different times because each adapter is loaded asynchronously. This can reduce the scheduler’s ability to form multi-adapter prefill batches, since only requests whose adapters are currently loaded can be grouped together. As a result, requests for different adapters will be scheduled in separate (or smaller) prefill batches, which can increase TTFT when adapter load time is small compared to prefill compute time. This is why LoRA overlap loading is disabled by default: it should only be enabled when users have determined that LoRA weight loading is a bottleneck (EG high adapter churn, heavy adapter weights, or PCIe-bottlenecked workloads).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Example When Overlap Loading Results in Higher Latency"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For instance, suppose we have four LoRA adapters: `lora0`, `lora1`, `lora2`, and `lora3`. Loading any adapter takes 2ms, while the prefill step for requests for that adapter takes 20ms.\n",
|
||||
"\n",
|
||||
"1. **Baseline**:\n",
|
||||
" The engine loads all four adapters synchronously, then runs one combined prefill batch, giving us a total time of ≈ `2 * 4 + 20 = 28ms`\n",
|
||||
"\n",
|
||||
"2. **With LoRA overlap loading enabled**:\n",
|
||||
" The engine begins loading `lora0` and, once it is ready, schedules a prefill batch containing only `lora0` while `lora1` loads in the background. Then it schedules `lora1`’s prefill while `lora2` loads, and so on. In the worst case where prefill cannot be batched across adapters, total time is ≈ `2 + 4 * 20 = 82ms`\n",
|
||||
"\n",
|
||||
"In this scenario, overlap loading reduces adapter-load overhead, but the loss of multi-adapter prefill batching dominates and leads to higher TTFT."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Future Works\n",
|
||||
"\n",
|
||||
"The development roadmap for LoRA-related features can be found in this [issue](https://github.com/sgl-project/sglang/issues/2929). Other features, including Embedding Layer, Unified Paging, Cutlass backend are still under development."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
108
third_party/sglang/docs/advanced_features/object_storage.md
vendored
Normal file
108
third_party/sglang/docs/advanced_features/object_storage.md
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
# Loading Models from Object Storage
|
||||
|
||||
SGLang supports direct loading of models from object storage (S3 and Google Cloud Storage) without requiring a full local download. This feature uses the `runai_streamer` load format to stream model weights directly from cloud storage, significantly reducing startup time and local storage requirements.
|
||||
|
||||
## Overview
|
||||
|
||||
When loading models from object storage, SGLang uses a two-phase approach:
|
||||
|
||||
1. **Metadata Download** (once, before process launch): Configuration files and tokenizer files are downloaded to a local cache
|
||||
2. **Weight Streaming** (lazy, during model loading): Model weights are streamed directly from object storage as needed
|
||||
|
||||
## Supported Storage Backends
|
||||
|
||||
1. **Amazon S3**: `s3://bucket-name/path/to/model/`
|
||||
2. **Google Cloud Storage**: `gs://bucket-name/path/to/model/`
|
||||
3. **Azure Blob**: `az://some-azure-container/path/`
|
||||
4. **S3 compatible**: `s3://bucket-name/path/to/model/`
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Simply provide an object storage URI as the model path:
|
||||
|
||||
```bash
|
||||
# S3
|
||||
python -m sglang.launch_server \
|
||||
--model-path s3://my-bucket/models/llama-3-8b/ \
|
||||
--load-format runai_streamer
|
||||
|
||||
# Google Cloud Storage
|
||||
python -m sglang.launch_server \
|
||||
--model-path gs://my-bucket/models/llama-3-8b/ \
|
||||
--load-format runai_streamer
|
||||
```
|
||||
|
||||
**Note**: The `--load-format runai_streamer` is automatically detected when using object storage URIs, so you can omit it:
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path s3://my-bucket/models/llama-3-8b/
|
||||
```
|
||||
|
||||
### With Tensor Parallelism
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path gs://my-bucket/models/llama-70b/ \
|
||||
--tp 4 \
|
||||
--model-loader-extra-config '{"distributed": true}'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Load Format
|
||||
|
||||
The `runai_streamer` load format is specifically designed for object storage, ssd and shared file systems
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path s3://bucket/model/ \
|
||||
--load-format runai_streamer
|
||||
```
|
||||
|
||||
### Extended Configuration Parameters
|
||||
|
||||
Use `--model-loader-extra-config` to pass additional configuration as a JSON string:
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path s3://bucket/model/ \
|
||||
--model-loader-extra-config '{
|
||||
"distributed": true,
|
||||
"concurrency": 8,
|
||||
"memory_limit": 2147483648
|
||||
}'
|
||||
```
|
||||
|
||||
#### Available Parameters
|
||||
|
||||
| Parameter | Type | Description | Default |
|
||||
|-----------|------|-------------|---------|
|
||||
| `distributed` | bool | Enable distributed streaming for multi-GPU setups. Automatically set to `true` for object storage paths and cuda alike devices. | Auto-detected |
|
||||
| `concurrency` | int | Number of concurrent download streams. Higher values can improve throughput for large models. | 4 |
|
||||
| `memory_limit` | int | Memory limit (in bytes) for the streaming buffer. | System-dependent |
|
||||
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Distributed Streaming
|
||||
|
||||
For multi-GPU setups, enable distributed streaming to parallelize weight loading between the processes:
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path s3://bucket/model/ \
|
||||
--tp 8 \
|
||||
--model-loader-extra-config '{"distributed": true}'
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Supported Formats**: Currently only supports `.safetensors` weight format (recommended format)
|
||||
- **Supported Device**: Distributed streaming is supported on cuda alike devices. Otherwise fallback to non distributed streaming
|
||||
|
||||
## See Also
|
||||
|
||||
- [Runai model streamer documentation](https://github.com/run-ai/runai-model-streamer)
|
||||
35
third_party/sglang/docs/advanced_features/observability.md
vendored
Normal file
35
third_party/sglang/docs/advanced_features/observability.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Observability
|
||||
|
||||
## Production Metrics
|
||||
SGLang exposes the following metrics via Prometheus. You can enable them by adding `--enable-metrics` when launching the server.
|
||||
You can query them by:
|
||||
```
|
||||
curl http://localhost:30000/metrics
|
||||
```
|
||||
|
||||
See [Production Metrics](../references/production_metrics.md) and [Production Request Tracing](../references/production_request_trace.md) for more details.
|
||||
|
||||
## Logging
|
||||
|
||||
By default, SGLang does not log any request contents. You can log them by using `--log-requests`.
|
||||
You can control the verbosity by using `--log-request-level`.
|
||||
See [Logging](server_arguments.md#logging) for more details.
|
||||
|
||||
## Request Dump and Replay
|
||||
|
||||
You can dump all requests and replay them later for benchmarking or other purposes.
|
||||
|
||||
To start dumping, use the following command to send a request to a server:
|
||||
```
|
||||
python3 -m sglang.srt.managers.configure_logging --url http://localhost:30000 --dump-requests-folder /tmp/sglang_request_dump --dump-requests-threshold 100
|
||||
```
|
||||
The server will dump the requests into a pickle file for every 100 requests.
|
||||
|
||||
To replay the request dump, use `scripts/playground/replay_request_dump.py`.
|
||||
|
||||
## Crash Dump and Replay
|
||||
Sometimes the server might crash, and you may want to debug the cause of the crash.
|
||||
SGLang supports crash dumping, which will dump all requests from the 5 minutes before the crash, allowing you to replay the requests and debug the reason later.
|
||||
|
||||
To enable crash dumping, use `--crash-dump-folder /tmp/crash_dump`.
|
||||
To replay the crash dump, use `scripts/playground/replay_request_dump.py`.
|
||||
352
third_party/sglang/docs/advanced_features/pd_disaggregation.md
vendored
Normal file
352
third_party/sglang/docs/advanced_features/pd_disaggregation.md
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
# PD Disaggregation
|
||||
|
||||
## Why and What is PD Disaggregation?
|
||||
|
||||
Large Language Model (LLM) inference comprises two distinct phases: **Prefill** and **Decode**. The Prefill phase is computation-intensive, processing the entire input sequence, while the Decode phase is memory-intensive, managing the Key-Value (KV) cache for token generation. Traditionally, these phases are handled within a unified engine, where combined scheduling of prefill and decode batches introduces inefficiencies. To address these challenges, we introduce **Prefill and Decoding (PD) Disaggregation** in SGLang.
|
||||
|
||||
### Issues with Unified Scheduling
|
||||
|
||||
The conventional unified engine, which processes prefill and decode batches together, results in two significant problems:
|
||||
|
||||
1. **Prefill Interruption**: Incoming prefill batches frequently interrupt ongoing decode batches, causing substantial delays in token generation.
|
||||
2. **DP Attention Imbalance**: In data-parallel (DP) attention, one DP worker may process a prefill batch while another handles a decode batch simultaneously, leading to increased decode latency.
|
||||
|
||||
PD Disaggregation resolves these by separating the two stages, enabling tailored optimizations for each.
|
||||
|
||||
For the design details, please refer to [link](https://docs.google.com/document/d/1rQXJwKd5b9b1aOzLh98mnyMhBMhlxXA5ATZTHoQrwvc/edit?tab=t.0).
|
||||
|
||||
Currently, we support Mooncake and NIXL as the transfer engine.
|
||||
|
||||
## Profiling in PD Disaggregation Mode
|
||||
|
||||
When you need to profile prefill or decode workers in PD disaggregation mode, please refer to the [Profile In PD Disaggregation Mode](https://docs.sglang.io/developer_guide/benchmark_and_profiling.html#profile-in-pd-disaggregation-mode) section in the Benchmark and Profiling guide. Due to torch profiler limitations, prefill and decode workers must be profiled separately using dedicated command-line options.
|
||||
|
||||
## Router Integration
|
||||
|
||||
For deploying PD disaggregation at scale with load balancing and fault tolerance, SGLang provides a router. The router can distribute requests between prefill and decode instances using various routing policies. For detailed information on setting up routing with PD disaggregation, including configuration options and deployment patterns, see the [SGLang Model Gateway (former Router)](../advanced_features/sgl_model_gateway.md#prefill-decode-disaggregation).
|
||||
|
||||
|
||||
## Mooncake
|
||||
### Requirements
|
||||
|
||||
```bash
|
||||
uv pip install mooncake-transfer-engine
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
### Llama Single Node
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--port 30000 \
|
||||
--disaggregation-ib-device mlx5_roce0
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode decode \
|
||||
--port 30001 \
|
||||
--base-gpu-id 1 \
|
||||
--disaggregation-ib-device mlx5_roce0
|
||||
python -m sglang_router.launch_router --pd-disaggregation --prefill http://127.0.0.1:30000 --decode http://127.0.0.1:30001 --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### DeepSeek Multi-Node
|
||||
|
||||
```bash
|
||||
# prefill 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-ib-device ${device_name} \
|
||||
--disaggregation-mode prefill \
|
||||
--host ${local_ip} \
|
||||
--port 30000 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${prefill_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8
|
||||
# prefill 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-ib-device ${device_name} \
|
||||
--disaggregation-mode prefill \
|
||||
--host ${local_ip} \
|
||||
--port 30000 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${prefill_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8
|
||||
# decode 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-ib-device ${device_name} \
|
||||
--disaggregation-mode decode \
|
||||
--host ${local_ip} \
|
||||
--port 30001 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${decode_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8 \
|
||||
--max-running-requests 128
|
||||
# decode 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-ib-device ${device_name} \
|
||||
--disaggregation-mode decode \
|
||||
--host ${local_ip} \
|
||||
--port 30001 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${decode_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8 \
|
||||
--max-running-requests 128
|
||||
```
|
||||
### Advanced Configuration
|
||||
|
||||
PD Disaggregation with Mooncake supports the following environment variables for fine-grained control over system behavior.
|
||||
|
||||
#### NVLink Transport Configuration
|
||||
To enable NVLink transport for KV cache transfers with the mooncake backend (recommended for NVL72 deployments), set the following environment variables. Note that auxiliary data transfer will still use TCP as a temporary workaround.
|
||||
|
||||
```bash
|
||||
export SGLANG_MOONCAKE_CUSTOM_MEM_POOL=NVLINK
|
||||
export MC_FORCE_MNNVL=True
|
||||
```
|
||||
|
||||
The `SGLANG_MOONCAKE_CUSTOM_MEM_POOL` environment variable enables the custom memory pool. Supported values are `NVLINK` (or `True`), `BAREX`, and `INTRA_NODE_NVLINK`.
|
||||
|
||||
#### Prefill Server Configuration
|
||||
| Variable | Description | Default |
|
||||
|:--------:|:-----------:|:--------:
|
||||
| **`SGLANG_DISAGGREGATION_THREAD_POOL_SIZE`** | Controls the total number of worker threads for KVCache transfer operations per TP rank | A dynamic value calculated by `int(0.75 * os.cpu_count()) // 8)`, which is limited to be larger than 4 and less than 12 to ensure efficiency and prevent thread race conditions |
|
||||
| **`SGLANG_DISAGGREGATION_QUEUE_SIZE`** | Sets the number of parallel transfer queues. KVCache transfer requests from multiple decode instances will be sharded into these queues so that they can share the threads and the transfer bandwidth at the same time. If it is set to `1`, then we transfer requests one by one according to fcfs strategy | `4` |
|
||||
| **`SGLANG_DISAGGREGATION_BOOTSTRAP_TIMEOUT`** | Timeout (seconds) for receiving destination KV indices during request initialization | `300` |
|
||||
| **`SGLANG_DISAGGREGATION_BOOTSTRAP_ENTRY_CLEANUP_INTERVAL`** | Interval (seconds) between cleanups of bootstrap entries | `120` |
|
||||
|
||||
If a greater mean TTFT is acceptable, you can `export SGLANG_DISAGGREGATION_BOOTSTRAP_TIMEOUT=600` (10 minutes) to relax the timeout condition.
|
||||
Please be aware that this setting will cause prefill instances to take a longer time to clean up the affected memory resources when a running decode node loses connection.
|
||||
|
||||
#### Decode Server Configuration
|
||||
| Variable | Description | Default |
|
||||
|:--------:|:-----------:|:--------:
|
||||
| **`SGLANG_DISAGGREGATION_HEARTBEAT_INTERVAL`** | Interval (seconds) between health checks to prefill bootstrap servers | `5.0` |
|
||||
| **`SGLANG_DISAGGREGATION_HEARTBEAT_MAX_FAILURE`** | Consecutive heartbeat failures before marking prefill server offline | `2` |
|
||||
| **`SGLANG_DISAGGREGATION_WAITING_TIMEOUT`** | Timeout (seconds) for receiving KV Cache after request initialization | `300` |
|
||||
|
||||
If a greater mean TTFT is acceptable, you can `export SGLANG_DISAGGREGATION_WAITING_TIMEOUT=600` (10 minutes) to relax the timeout condition.
|
||||
|
||||
|
||||
## NIXL
|
||||
### Requirements
|
||||
|
||||
Install via pip.
|
||||
|
||||
```bash
|
||||
pip install nixl
|
||||
```
|
||||
|
||||
Or build from source - may be required if you already have UCX installed.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ai-dynamo/nixl.git
|
||||
cd nixl
|
||||
pip install . --config-settings=setup-args="-Ducx_path=/path/to/ucx"
|
||||
```
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
### Llama Single Node
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--port 30000 \
|
||||
--disaggregation-transfer-backend nixl
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode decode \
|
||||
--port 30001 \
|
||||
--base-gpu-id 1 \
|
||||
--disaggregation-transfer-backend nixl
|
||||
python -m sglang_router.launch_router --pd-disaggregation --prefill http://127.0.0.1:30000 --decode http://127.0.0.1:30001 --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### DeepSeek Multi-Node
|
||||
|
||||
```bash
|
||||
# prefill 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend nixl \
|
||||
--disaggregation-mode prefill \
|
||||
--host ${local_ip} \
|
||||
--port 30000 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${prefill_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8
|
||||
# prefill 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend nixl \
|
||||
--disaggregation-mode prefill \
|
||||
--host ${local_ip} \
|
||||
--port 30000 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${prefill_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8
|
||||
# decode 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend nixl \
|
||||
--disaggregation-mode decode \
|
||||
--host ${local_ip} \
|
||||
--port 30001 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${decode_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8 \
|
||||
--max-running-requests 128
|
||||
# decode 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend nixl \
|
||||
--disaggregation-mode decode \
|
||||
--host ${local_ip} \
|
||||
--port 30001 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${decode_master_ip}:5000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1 \
|
||||
--tp-size 16 \
|
||||
--dp-size 8 \
|
||||
--enable-dp-attention \
|
||||
--moe-a2a-backend deepep \
|
||||
--mem-fraction-static 0.8 \
|
||||
--max-running-requests 128
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
#### NIXL Backend Selection
|
||||
|
||||
By default, NIXL uses the **UCX** backend for KV cache transfers. You can select a different NIXL plugin backend depending on your infrastructure using the environment variable `SGLANG_DISAGGREGATION_NIXL_BACKEND`.
|
||||
|
||||
Example: `export SGLANG_DISAGGREGATION_NIXL_BACKEND=LIBFABRIC`
|
||||
|
||||
**Available backends:** UCX (default), LIBFABRIC, or any installed NIXL plugin.
|
||||
|
||||
Example usage:
|
||||
```bash
|
||||
export SGLANG_DISAGGREGATION_NIXL_BACKEND=LIBFABRIC
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--disaggregation-transfer-backend nixl \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
## ASCEND
|
||||
|
||||
### Usage
|
||||
|
||||
Use ascend backend with [memfabric_hybrid](https://gitcode.com/Ascend/memfabric_hybrid) and ASCEND_MF_STORE_URL being set
|
||||
|
||||
```bash
|
||||
pip install memfabric-hybrid==1.0.0
|
||||
export ASCEND_MF_STORE_URL="tcp://xxx.xx.xxx.xxx:xxxx"
|
||||
```
|
||||
Use mooncake backend, more details can be found in mooncake section.
|
||||
```bash
|
||||
export ENABLE_ASCEND_TRANSFER_WITH_MOONCAKE=true
|
||||
```
|
||||
ASCEND_NPU_PHY_ID need to be set in container env
|
||||
```bash
|
||||
export ASCEND_NPU_PHY_ID=xxx
|
||||
```
|
||||
|
||||
|
||||
### Llama Single Node
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode prefill \
|
||||
--port 30000 \
|
||||
--disaggregation-transfer-backend ascend
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disaggregation-mode decode \
|
||||
--port 30001 \
|
||||
--base-gpu-id 1 \
|
||||
--disaggregation-transfer-backend ascend
|
||||
python -m sglang_router.launch_router --pd-disaggregation --prefill http://127.0.0.1:30000 --decode http://127.0.0.1:30001 --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### DeepSeek Multi-Node
|
||||
|
||||
```bash
|
||||
# prefill 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend ascend \
|
||||
--disaggregation-mode prefill \
|
||||
--host ${local_ip} \
|
||||
--port 30000 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${prefill_master_ip}:5000 \
|
||||
--nnodes 1 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16
|
||||
# decode 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3-0324 \
|
||||
--disaggregation-transfer-backend ascend \
|
||||
--disaggregation-mode decode \
|
||||
--host ${local_ip} \
|
||||
--port 30001 \
|
||||
--trust-remote-code \
|
||||
--dist-init-addr ${decode_master_ip}:5000 \
|
||||
--nnodes 1 \
|
||||
--node-rank 0 \
|
||||
--tp-size 16
|
||||
```
|
||||
189
third_party/sglang/docs/advanced_features/piecewise_cuda_graph.md
vendored
Normal file
189
third_party/sglang/docs/advanced_features/piecewise_cuda_graph.md
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
# Piecewise CUDA Graph
|
||||
|
||||
## Motivation
|
||||
|
||||
Standard CUDA graphs capture the entire model forward pass as a single graph. This works well for decode (fixed batch size), but not for extend/prefill where the number of tokens varies across iterations.
|
||||
|
||||
Piecewise CUDA Graph (PCG) solves this by splitting the model's computation graph into pieces (roughly one per layer) at "split points" (e.g., MoE dispatch ops). Each piece is captured as a separate CUDA graph for a set of pre-defined token lengths. At runtime, the input is padded to the nearest captured size, and each piece is replayed. This eliminates kernel launch overhead for prefill/extend while still supporting dynamic shapes.
|
||||
|
||||
Recently we **enabled PCG by default**, which means that the old `--enable-piecewise-cuda-graph` flag is deprecated. Use `--disable-piecewise-cuda-graph` to turn it off.
|
||||
|
||||
## Usage
|
||||
|
||||
PCG is enabled by default for supported configurations. No extra flags needed:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct
|
||||
```
|
||||
|
||||
### Disable PCG
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--disable-piecewise-cuda-graph
|
||||
```
|
||||
|
||||
### Custom capture sizes
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Llama-3.1-8B-Instruct \
|
||||
--piecewise-cuda-graph-max-tokens 2048
|
||||
```
|
||||
|
||||
### Server Args
|
||||
|
||||
| Argument | Default | Description |
|
||||
|---|---|---|
|
||||
| `--disable-piecewise-cuda-graph` | `False` | Disable PCG for extend/prefill. |
|
||||
| `--enforce-piecewise-cuda-graph` | `False` | Force-enable PCG, skipping all auto-disable conditions. For testing only. |
|
||||
| `--piecewise-cuda-graph-max-tokens` | `None` (auto) | Maximum token count to capture. Defaults to `chunked_prefill_size` (non-MLA) or `2048` (MLA). |
|
||||
| `--piecewise-cuda-graph-tokens` | `None` (auto) | Explicit list of token lengths to capture. Auto-generated if not set. |
|
||||
| `--piecewise-cuda-graph-compiler` | `"eager"` | Compiler backend for the captured subgraphs. Choices: `eager`, `inductor`. |
|
||||
| ~~`--enable-piecewise-cuda-graph`~~ | — | **Deprecated.** PCG is now enabled by default. Use `--enforce-piecewise-cuda-graph` to skip auto-disable conditions. |
|
||||
|
||||
## Bug Report
|
||||
|
||||
PCG is enabled by default but is still in an experimental stage. Since PCG relies on `torch.compile` to trace the model's forward pass, most bugs are introduced by torch compile tracing failures (e.g., untraceable ops, dynamic control flow, or graph breaks). If you encounter any issues related to PCG, please disable it by adding `--disable-piecewise-cuda-graph` to your launch command and report the bug at [GitHub Issues](https://github.com/sgl-project/sglang/issues/new/choose). We greatly appreciate your help in improving this feature.
|
||||
|
||||
### For Users
|
||||
|
||||
If you see an error message like the following during server startup, it is a PCG bug:
|
||||
|
||||
```
|
||||
Piecewise CUDA Graph is enabled by default as an experimental feature.
|
||||
To work around this error, add --disable-piecewise-cuda-graph to your launch command.
|
||||
Please report this issue at https://github.com/sgl-project/sglang/issues/new/choose
|
||||
```
|
||||
|
||||
To work around it, add `--disable-piecewise-cuda-graph` to your launch command. When filing a bug report, please include:
|
||||
1. The full error traceback
|
||||
2. Model name and quantization method
|
||||
3. Launch command with all arguments
|
||||
4. GPU type and driver version
|
||||
|
||||
### For Developers
|
||||
|
||||
Since PCG relies on `torch.compile` to trace the model's forward pass, newly developed CUDA kernels (both JIT kernels and sgl-kernels) are typically not compatible with `torch.compile` out of the box. The tracing will fail on untraceable operations such as JIT compilation, file I/O, or dynamic module loading inside the kernel.
|
||||
|
||||
To make a kernel compatible with PCG, you need to register it as a custom op using `register_custom_op` from `sglang.srt.utils.custom_op`. This wraps the kernel as an opaque node in the compiled graph so that `torch.compile` will not trace inside it.
|
||||
|
||||
**Example usage (JIT kernel):**
|
||||
|
||||
```python
|
||||
from sglang.srt.utils.custom_op import register_custom_op
|
||||
|
||||
# Inplace operator (no return value)
|
||||
@register_custom_op(mutates_args=["output_q", "output_s"])
|
||||
def per_token_group_quant_8bit(
|
||||
input: torch.Tensor,
|
||||
output_q: torch.Tensor,
|
||||
output_s: torch.Tensor,
|
||||
) -> None:
|
||||
# kernel implementation ...
|
||||
```
|
||||
|
||||
**Example usage (operator with output):**
|
||||
|
||||
```python
|
||||
# out_shape indicates which argument has the same shape as the output
|
||||
@register_custom_op(mutates_args=["x"], out_shape=0)
|
||||
def add(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
|
||||
return x.add_(y)
|
||||
```
|
||||
|
||||
For wrapping external library functions (e.g., FlashInfer kernels), use `register_custom_op_from_extern` instead. See `python/sglang/srt/utils/custom_op.py` for full API documentation.
|
||||
|
||||
## How it works
|
||||
### Torch compile backend
|
||||
|
||||
PCG uses `torch.compile` with a custom backend (`SGLangBackend`) to split and compile the model's forward pass. The flow is:
|
||||
|
||||
```
|
||||
model.forward wrapper
|
||||
→ torch.compile(..., backend=SGLangBackend)
|
||||
→ FX graph
|
||||
→ split_graph() at registered split ops
|
||||
→ split_gm (top-level graph that chains the pieces)
|
||||
→ replace capturable submodules with CUDAPiecewiseBackend
|
||||
→ runtime dispatch: eager split ops + per-piece capture/replay
|
||||
```
|
||||
|
||||
- **Install**: `install_torch_compiled()` replaces `model.forward` with a wrapper function. When `is_in_piecewise_cuda_graph()` returns True, the wrapper dispatches to the compiled callable; otherwise it falls back to the original forward. The first invocation through this path triggers Dynamo tracing and graph compilation — CUDA graph replay only happens after the capture phase completes.
|
||||
|
||||
- **Split**: When `torch.compile` traces the model, `SGLangBackend` receives the FX graph and calls `split_graph()`. Ops listed in `CompilationConfig.split_ops` are treated as split points, so the graph is cut at each one. These split-op submodules are left to run eagerly at runtime, while the surrounding submodules are compiled and wrapped by `CUDAPiecewiseBackend`. The result is a top-level "stitching graph" (`split_gm`) with children such as `submod_0`, `submod_1`, … interleaving capturable subgraphs and eager split-op submodules.
|
||||
|
||||
- **Replace**: `PiecewiseCompileInterpreter` iterates over each capturable submodule in `split_gm`, compiles it for general (dynamic) shapes, and replaces it in-place with a `CUDAPiecewiseBackend` instance. Split-op submodules (e.g., attention, all-reduce) are left as-is and run eagerly at runtime.
|
||||
|
||||
- **Dispatch**: At runtime, calling `split_gm` executes the stitching graph, which calls each submodule in order. Split-op submodules run eagerly. Each `CUDAPiecewiseBackend` submodule goes through three phases:
|
||||
- **Compile warmup** — runs the general-shape compiled path.
|
||||
- **Capture** — for each capture size, runs one warmup pass then records a CUDA graph.
|
||||
- **Steady-state replay** — replays the captured CUDA graph for each forward pass.
|
||||
|
||||
### Piecewise cuda graph runner
|
||||
|
||||
`PiecewiseCudaGraphRunner` orchestrates the full lifecycle through three phases:
|
||||
|
||||
- **Compile** — Warms up JIT kernels with a dummy forward pass, then wraps the model with `torch.compile`, triggering Dynamo tracing to split the FX graph and create `CUDAPiecewiseBackend` instances for each subgraph piece.
|
||||
|
||||
- **Capture** — Iterates over capture sizes in reverse order (largest first). For each size, runs the forward pass twice (one warmup, one CUDA graph capture).
|
||||
|
||||
- **Replay** — At runtime, finds the smallest captured size >= actual token count via binary search, copies inputs into static buffers with zero-padding, replays the captured CUDA graphs, and slices outputs back to the actual token count.
|
||||
|
||||
### Memory optimization
|
||||
|
||||
The memory cost of PCG comes from two parts: **torch memory allocator** and **non-torch memory**.
|
||||
|
||||
The torch memory allocator overhead is trivial thanks to several optimizations: a global shared memory pool is reused across all CUDA graph runners and capture sizes, capture is done in reverse order (large to small) so smaller graphs reuse memory allocated by larger ones, and output tensors of the last subgraph are stored as weak references to maximize memory reuse.
|
||||
|
||||
The main memory overhead comes from non-torch memory — the CUDA graph objects themselves require GPU memory to store the recorded kernel launch parameters and internal state. This overhead scales with the number of captured sizes, which is why `piecewise_cuda_graph_max_tokens` is capped conservatively by default.
|
||||
|
||||
### Shape configuration
|
||||
Piecewise CUDA graph pre-captures graphs for a set of token counts. At runtime, the actual token count is rounded up to the nearest captured size (via binary search), and the corresponding graph is replayed. If the token count exceeds the largest captured size, the runtime falls back to the normal (non-graph) forward path.
|
||||
|
||||
The default capture schedule is auto-generated with increasing granularity:
|
||||
|
||||
| Token range | Step size |
|
||||
|-------------|-----------|
|
||||
| 4 – 32 | 4 |
|
||||
| 48 – 256 | 16 |
|
||||
| 288 – 512 | 32 |
|
||||
| 576 – 1024 | 64 |
|
||||
| 1280 – 4096 | 256 |
|
||||
| 4096+ | 512 |
|
||||
|
||||
For the auto-generated schedule, sizes are capped at `--piecewise-cuda-graph-max-tokens`. The default cap is `chunked_prefill_size` for non-MLA models and `2048` for MLA backend models. If `--max-total-tokens` is set, the cap is further limited to not exceed it. Additionally, Llama-2 models are auto-capped at 4096 tokens as a temporary workaround.
|
||||
|
||||
## Compatibility
|
||||
|
||||
PCG is auto-disabled in the following scenarios. We are actively working on expanding compatibility — support for many of these will be coming soon.
|
||||
|
||||
- Disabled model architectures (e.g., `DeepseekV32ForCausalLM`)
|
||||
- Speculative decoding
|
||||
- DP attention
|
||||
- Pipeline parallelism (`pp_size > 1`)
|
||||
- Non-CUDA hardware (AMD ROCm, Ascend NPU)
|
||||
- MoE A2A backend
|
||||
- LoRA
|
||||
- Multimodal / VLM models
|
||||
- DLLM (diffusion LLM)
|
||||
- Deterministic inference
|
||||
- PD disaggregation
|
||||
- Expert distribution recorder / EPLB
|
||||
|
||||
Use `--enforce-piecewise-cuda-graph` to skip all auto-disable checks (for testing/debugging only).
|
||||
|
||||
## Code Reference
|
||||
|
||||
| File | Description |
|
||||
|---|---|
|
||||
| `python/sglang/srt/model_executor/piecewise_cuda_graph_runner.py` | Main runner: init, capture, replay |
|
||||
| `python/sglang/srt/compilation/compile.py` | `install_torch_compiled` trampoline |
|
||||
| `python/sglang/srt/compilation/backend.py` | `SGLangBackend`, graph splitting, piecewise compilation |
|
||||
| `python/sglang/srt/compilation/cuda_piecewise_backend.py` | Per-subgraph CUDA graph capture/replay |
|
||||
| `python/sglang/srt/compilation/piecewise_context_manager.py` | Global context flags and `ForwardContext` |
|
||||
| `python/sglang/srt/compilation/compilation_config.py` | Capture sizes, split ops, compiler config |
|
||||
| `python/sglang/srt/utils/custom_op.py` | `register_custom_op` for torch.compile compatibility |
|
||||
| `python/sglang/srt/server_args.py` | Server arguments and auto-disable logic |
|
||||
116
third_party/sglang/docs/advanced_features/pipeline_parallelism.md
vendored
Normal file
116
third_party/sglang/docs/advanced_features/pipeline_parallelism.md
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
# Pipeline Parallelism for Long Context
|
||||
|
||||
## Why Pipeline Parallelism?
|
||||
|
||||
As Large Language Models (LLMs) scale toward trillion-parameter architectures and "infinite" context windows, the underlying serving infrastructure must evolve toward more granular, cross-node parallelization strategies. While KV cache techniques effectively mitigate redundant computation, they cannot circumvent the prohibitive Time to First Token (TTFT) inherent in ultra-long sequences with extremely large initial Input Token Length (ITL). Although Tensor Parallelism (TP) remains the conventional approach for intra-node scaling, it frequently encounters communication bottlenecks during multi-node deployments. On the other hand, pipeline parallelism only requires cross-node communication at the boundaries of each pipeline stage, which can achieve better computation-communication overlap compared to a large TP. Therefore, it is also a promising parallelization strategy for improving throughput.
|
||||
|
||||
Detailed analysis can be found in this [blog](https://lmsys.org/blog/2026-01-15-chunked-pipeline/).
|
||||
|
||||
## Implementation Refactoring based on Async Communication
|
||||
With Dynamic Chunked Prefill, pipeline parallelism has the potential to reduce the TTFT of long-context inputs. For each request, its input tokens can be partitioned into multiple chunks, each no longer than the chunked prefill size. Different chunks of the same request can be processed simultaneously by different nodes, thus parallelizing the processing and reducing TTFT. SGLang has supported Pipeline Parallelism (#5724) for some time and made it compatible with the PD Disaggregation feature (#8846), but the implementation was not perfect and had significant room for performance improvements.
|
||||
|
||||
To eliminate this performance hazard, SGLang implements a Micro-batching Event Loop with non-blocking asynchronous peer-to-peer (P2P) communication to overlap GPU computation with CPU metadata processing and PP communication. This ensures that while one micro-batch is being computed on the GPU, the next one is already being prepared and moved into position effectively, ensuring the pipeline remains as saturated as possible. This approach was first proposed in #7979 and has been redesigned and included in #11852.
|
||||
|
||||
The key mechanisms of the implementation include:
|
||||
|
||||
* **Decoupled Sync/Async Logic in the Event Loop:** The scheduler uses `async_send` in `_pp_send_pyobj_to_next_stage`. Instead of waiting for a transfer to complete, it returns a `P2PWork` handle. The actual synchronization (`P2PWork.work.wait()`) is deferred until `_pp_commit_comm_work` is called, allowing the CPU to perform other work—like scheduling the next batch or processing metadata—while data is in flight.
|
||||
* **Multi-Stream Execution:** In addition to the main `default_stream`, which serves as the synchronization stream, SGLang utilizes dedicated `forward_stream` and `copy_stream` to execute forward pass GPU computation and Data-to-Host (D2H) memory transfers separately for better overlapping. While `_pp_launch_batch` is executing the current micro-batch on the GPU for the current stage, the CPU processes the previous micro-batch's results using `_pp_process_batch_result`.
|
||||
|
||||
## Guidance about Dynamic Chunking
|
||||
|
||||
### Why Dynamic Chunking
|
||||
Chunked prefill with a fixed size can cause bubbles in the pipeline, especially when the pp size is large. The main reason behind this phenomenon is that the model has a non-uniform running time, even though each chunk size is identical (brought by the Transformer structure). The larger the prefix sequence length, the longer the running time of the chunk. And these bubbles will be propagated to the next stage, and will significantly degrade the scale efficiency of larger pp ranks.
|
||||
|
||||
To address this issue, SGLang introduces a dynamic chunking mechanism to predict the optimal size for the next chunk such that it satisfies this condition:
|
||||
|
||||
Runtime(L + Next Chunk Size) - Runtime(L) = Runtime(Initial Chunk Size)
|
||||
|
||||
where ***L*** denotes the Prefix Sequence Length. By profiling a series of requests with different ITLs, we model the cumulative runtime as a quadratic function of sequence length. Using this model, we solve the optimal next chunk size for any given prefix length ***L***. Since the computation complexity of the Attention mechanism scales with ***L***, the next chunk size will be progressively reduced as ***L*** grows to maintain an aligned chunk execution time across pipeline stages.
|
||||
|
||||
Based on this method, the scheduler can predict and dynamically reduce the chunk size during runtime to minimize the bubbles caused by the stage misalignment. To be noticed, the scheduler does not use the raw predicted value. To facilitate efficient KVCache memory management and ensure affinity with hardware execution efficiency, the value is aligned downward to the nearest multiple of max(`--page-size`, 64).
|
||||
|
||||
|
||||
### Chunked Prefill Size and Smoothing Factor
|
||||
|
||||
When `--enable-dynamic-chunking` is enabled, each chunk size of a sequence is determined dynamically based on the quadratic model that predicts the next chunk size based on the estimated runtime of the initial chunk length. In this case, we use `--chunked-prefill-size` to set up the initial chunk size. When switching to the dynamic chunking mode, the initial chunk size (`--chunked-prefill-size`) should be set to a larger value comparable to the original chunked prefill size, so that there won't be too many chunks.
|
||||
|
||||
**`SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR`** is an environmental variable that controls the smoothing factor for the dynamic chunking algorithm, defaulting to 0.75. It determines how much the chunk size can change during the prefill phase. A larger value means a more aggressive chunk size change, which may lead to better performance but also to greater chunk size changes (the chunk size at the end may become very small, which could lead to performance degradation) and more total chunks. When it is set to 1, the chunk size will be adjusted strictly based on the aforementioned quadratic model that predicts the next chunk size. A smaller value means a more conservative chunk size change, which may lead to smaller chunk size changes and fewer total chunks. When it is set to 0, the chunk size will not be adjusted dynamically, so it is identical to the traditional way with a fixed chunked prefill size.
|
||||
|
||||
Due to the variation in hardware, models, and target workloads, a static configuration is seldom optimal across all scenarios. Consequently, achieving peak performance necessitates a degree of hyperparameter tuning when switching to the dynamic chunking mode.
|
||||
|
||||
**Tuning Guidance for Dynamic Chunked Prefill**
|
||||
|
||||
* **Step 1 \- Iterate to find the optimal fixed chunked prefill size for the targeted PP size**: Different PP sizes for targeted ITL may have different optimal chunked prefill sizes. Therefore, users should iterate to obtain the baseline according to the available resources for scaling.
|
||||
* **Step 2 \- Initial Chunk Size Selection for Dynamic Chunking**: Set the initial size to 2× or 3× the optimal fixed chunked prefill size. This reduces the total number of chunks and prevents "tail chunks" from underutilizing hardware. To maintain efficiency for extremely large Input Token Lengths (ITL), the dynamic predictor automatically ensures subsequent chunks are at least 1/4 of this initial size. In addition, it is recommended to use a larger initial chunk size (e.g., 4× the optimal fixed chunked prefill size) for such cases as well.
|
||||
* **Step 3 \- Smooth Factor Adjustment**: This factor controls how strictly the chunk size adjusts the prediction given by the quadratic performance fitting model.
|
||||
* 1.0: Follows the model strictly.
|
||||
* **0.6 – 0.85 (Recommended)**: Typical range for the best balance between dynamic scaling and hardware stability. Through experiments, we find that a range between 0.6 and 0.85 typically yields the best performance for dynamic chunking.
|
||||
* 0: Disables dynamic adjustment, reverting to traditional fixed-size chunking.
|
||||
* **Another small optimization tip:** Put the larger partition in the higher PP rank when the layers are not evenly divisible across ranks. It can increase the GPU utilization when a larger PP rank is waiting for the previous stage’s result, hence reducing the bubbles on higher PP ranks. If we take DeepSeek-V3.1 as an example, `SGLANG_PP_LAYER_PARTITION=15,15,15,16` usually performs better than `16,15,15,15`.
|
||||
|
||||
## Best Practice for Long Context
|
||||
|
||||
### Tuning the Chunked Prefill Size
|
||||
Optimizing the chunked prefill size is crucial for balancing pipeline efficiency and resource utilization. The ideal size depends on factors including model architecture, hardware configuration, and typical input lengths. We recommend starting with a small chunk size, such as 4K, and gradually increasing it until you find the optimal size for your specific use case (Different targeted ITL and PP Sizes may have different optimal chunked prefill sizes. Therefore, users should iterate to obtain the baseline according to the available resources for scaling). Alternatively, you can analyze the hardware capacity and determine the optimal chunk size based on the roofline model.
|
||||
|
||||
### Enable Dynamic Chunking and Adjust Smoothing Factor for Ultra-long ITL
|
||||
SGLang also offers a dynamic chunking solution that could further improve performance. This feature is currently an experimental feature that requires a certain amount of tuning experimentation and may not be suitable for all workloads. In addition, fine-tuning the smoothing factor can help optimize performance for specific workloads and model characteristics.
|
||||
|
||||
### Case Study on NVIDIA H20
|
||||
|
||||
When evaluating pipeline parallelism with fixed chunked prefill sizes from 2K to 16K, experiment results show that a 4K chunk size delivered optimal prefill TTFT performance for the DeepSeek-V3.1, and a 6K chunk size delivered optimal prefill TTFT performance for the Qwen3-235B-A22B-FP8.
|
||||
|
||||
When enabling dynamic chunking, we first scale the optimal fixed chunked prefill size by a factor of 3 as the initial chunk size. Through experimentation, we found that a multiplier of 2-3 provides an appropriate balance—avoiding excessive initial pipeline bubbles while ensuring that subsequent chunks don't become too small as context length increases. With the default dynamic chunking smoothing factor of 0.75, we performed parameter tuning and determined that a value of 0.65 works optimally with the 12K initial chunk size for the DeepSeek-V3.1, while a value of 0.8 works optimally with the 18K initial chunk size for the Qwen3-235B-A22B-FP8.
|
||||
|
||||
#### DeepSeek-V3.1 with 128K Input Token Length
|
||||
```bash
|
||||
# prefill node 0 (fixed chunked prefill size)
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3.1 --trust-remote-code \
|
||||
--nnodes 4 --node-rank 0 --tp 8 --pp-size 4 \
|
||||
--port 30000 --dist-init-addr <MASTER_NODE_IP> \
|
||||
--disable-radix-cache --mem-fraction-static 0.8 \
|
||||
--attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
|
||||
--max-running-requests 128 --chunked-prefill-size 4096
|
||||
```
|
||||
|
||||
```bash
|
||||
# prefill node 0 (with dynamic chunking)
|
||||
export SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR=0.65
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-V3.1 --trust-remote-code \
|
||||
--nnodes 4 --node-rank 0 --tp 8 --pp-size 4 \
|
||||
--port 30000 --dist-init-addr <MASTER_NODE_IP> \
|
||||
--disable-radix-cache --mem-fraction-static 0.8 \
|
||||
--attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
|
||||
--max-running-requests 128 --chunked-prefill-size 12288 --enable-dynamic-chunking
|
||||
```
|
||||
|
||||
#### Qwen3-235B-A22B-FP8 with 128K Input Token Length
|
||||
```bash
|
||||
# prefill node 0 (fixed chunked prefill size)
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-235B-A22B-FP8 --trust-remote-code \
|
||||
--nnodes 4 --node-rank 0 --tp 4 --pp-size 8 \
|
||||
--port 30000 --dist-init-addr <MASTER_NODE_IP> \
|
||||
--disable-radix-cache --mem-fraction-static 0.8 \
|
||||
--attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
|
||||
--max-running-requests 128 --chunked-prefill-size 6144
|
||||
```
|
||||
|
||||
```bash
|
||||
# prefill node 0 (with dynamic chunking)
|
||||
export SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR=0.8
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path Qwen/Qwen3-235B-A22B-FP8 --trust-remote-code \
|
||||
--nnodes 4 --node-rank 0 --tp 4 --pp-size 8 \
|
||||
--port 30000 --dist-init-addr <MASTER_NODE_IP> \
|
||||
--disable-radix-cache --mem-fraction-static 0.8 \
|
||||
--attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
|
||||
--max-running-requests 128 --chunked-prefill-size 18432 --enable-dynamic-chunking
|
||||
```
|
||||
|
||||
Note: `--disable-radix-cache` is enabled only for reproducible benchmarking purposes. It is not recommended to use it in production.
|
||||
|
||||
## Best Practice for Pipeline Parallelism with PD Disaggregation
|
||||
To be added. Stay tuned for the latest updates on Pipeline Parallelism with PD Disaggregation.
|
||||
603
third_party/sglang/docs/advanced_features/quantization.md
vendored
Normal file
603
third_party/sglang/docs/advanced_features/quantization.md
vendored
Normal file
@@ -0,0 +1,603 @@
|
||||
# Quantization
|
||||
|
||||
SGLang supports various quantization methods, including offline quantization and online dynamic quantization.
|
||||
|
||||
Offline quantization loads pre-quantized model weights directly during inference. This is required for quantization methods
|
||||
such as GPTQ and AWQ, which collect and pre-compute various statistics from the original weights using the calibration dataset.
|
||||
|
||||
Online quantization dynamically computes scaling parameters—such as the maximum/minimum values of model weights—during runtime.
|
||||
Like NVIDIA FP8 training's [delayed scaling](https://docs.nvidia.com/deeplearning/transformer-engine/user-guide/examples/fp8_primer.html#Mixed-precision-training-with-FP8) mechanism, online quantization calculates the appropriate scaling factors
|
||||
on-the-fly to convert high-precision weights into a lower-precision format.
|
||||
|
||||
**Note: For better performance, usability and convenience, offline quantization is recommended over online quantization.**
|
||||
|
||||
If you use a pre-quantized model, do not add `--quantization` to enable online quantization at the same time.
|
||||
For popular pre-quantized models, please visit [Unsloth](https://huggingface.co/unsloth), [NVIDIA ModelOpt](https://huggingface.co/collections/nvidia/inference-optimized-checkpoints-with-model-optimizer)
|
||||
or [NeuralMagic](https://huggingface.co/collections/neuralmagic) collections on HF for some
|
||||
popular quality validated quantized models. Quantized models must be validated via benchmarks post-quantization
|
||||
to guard against abnormal quantization loss regressions.
|
||||
|
||||
## Platform Compatibility
|
||||
|
||||
The following table summarizes quantization method support across NVIDIA and AMD GPUs, Ascend NPUs.
|
||||
|
||||
| Method | NVIDIA GPUs | AMD GPUs (MI300X/MI325X/MI350X) | Ascend NPUs (A2/A3) | Notes |
|
||||
|--------|:-----------:|:-------------------------------:|:-----------------------:|-------|
|
||||
| `fp8` | Yes | Yes | WIP | Aiter or Triton backend on AMD |
|
||||
| `mxfp4` | Yes | Yes | WIP | Requires CDNA3/CDNA4 with MXFP support; uses Aiter |
|
||||
| `blockwise_int8` | Yes | Yes | No | Triton-based, works on both platforms |
|
||||
| `w8a8_int8` | Yes | Yes | No | |
|
||||
| `w8a8_fp8` | Yes | Yes | No | Aiter or Triton FP8 on AMD |
|
||||
| `awq` | Yes | Yes | Yes | Uses Triton dequantize on AMD (vs. optimized CUDA kernels on NVIDIA). Uses CANN kernels on Ascend|
|
||||
| `gptq` | Yes | Yes | Yes | Uses Triton or vLLM kernels on AMD. Uses CANN kernels on Ascend|
|
||||
| `compressed-tensors` | Yes | Yes | Partial | Aiter paths for FP8/MoE on AMD. Uses CANN kernels on Ascend, `FP8` not supported yet|
|
||||
| `quark` | Yes | Yes | No | AMD Quark quantization; Aiter GEMM paths on AMD |
|
||||
| `auto-round` | Yes | Yes | Partial | Platform-agnostic (Intel auto-round). Uses CANN kernels on Ascend|
|
||||
| `quark_int4fp8_moe` | No | Yes | No | AMD-only; online INT4-to-FP8 MoE quantization (CDNA3/CDNA4) |
|
||||
| `awq_marlin` | Yes | No | No | Marlin kernels are CUDA-only |
|
||||
| `gptq_marlin` | Yes | No | No | Marlin kernels are CUDA-only |
|
||||
| `gguf` | Yes | No | WIP | CUDA-only kernels in sgl-kernel |
|
||||
| `modelopt` / `modelopt_fp8` | Yes (Hopper/SM90+) | No | No | [NVIDIA ModelOpt](https://github.com/NVIDIA/Model-Optimizer); requires NVIDIA hardware |
|
||||
| `modelopt_fp4` | Yes (Blackwell/SM100+) | No | No | [NVIDIA ModelOpt](https://github.com/NVIDIA/Model-Optimizer); native FP4 on Blackwell (B200, GB200) |
|
||||
| `petit_nvfp4` | No | Yes (MI250/MI300X/MI325X) | No | Enables NVFP4 on ROCm via [Petit](https://github.com/causalflow-ai/petit-kernel); use `modelopt_fp4` on NVIDIA Blackwell. Auto-selected when loading NVFP4 models on AMD. See [LMSYS blog](https://lmsys.org/blog/2025-09-21-petit-amdgpu/) and [AMD ROCm blog](https://rocm.blogs.amd.com/artificial-intelligence/fp4-mixed-precision/README.html). |
|
||||
| `bitsandbytes` | Yes | Experimental | No | Depends on bitsandbytes ROCm support |
|
||||
| `torchao` (`int4wo`, etc.) | Yes | Partial | No | `int4wo` not supported on AMD; other methods may work |
|
||||
| `modelslim` | No | No | Yes | Ascend quantization; Uses CANN kernels |
|
||||
|
||||
On AMD, several of these methods use [Aiter](https://github.com/ROCm/aiter) for acceleration -- set `SGLANG_USE_AITER=1` where noted. See [AMD GPU setup](../platforms/amd_gpu.md) for installation and configuration details.
|
||||
|
||||
On Ascend, various layers quantization configurations are supported, see [Ascend NPU quantization](../platforms/ascend/ascend_npu_quantization.md) for details.
|
||||
|
||||
## GEMM Backends for FP4/FP8 Quantization
|
||||
|
||||
:::{note}
|
||||
Backend selection is supported only for **blockwise FP8** and **NVFP4** GEMM. When running FP8 or FP4 quantized models, you can select the GEMM backend via `--fp8-gemm-backend` and `--fp4-gemm-backend`.
|
||||
:::
|
||||
|
||||
### `--fp8-gemm-backend` (Blockwise FP8 GEMM)
|
||||
|
||||
| Backend | Hardware | Description |
|
||||
|---------|----------|-------------|
|
||||
| `auto` | All | Auto-selects based on hardware |
|
||||
| `deep_gemm` | SM90, SM100 | JIT-compiled; enabled when DeepGEMM is installed |
|
||||
| `flashinfer_trtllm` | SM100 | FlashInfer TensorRT-LLM backend; optimal for low-latency |
|
||||
| `flashinfer_cutlass` | SM100/120 | FlashInfer CUTLASS groupwise FP8 GEMM |
|
||||
| `flashinfer_deepgemm` | SM90 | Uses swapAB optimization for small M dimensions in decoding |
|
||||
| `cutlass` | SM90, SM100/120 | sgl-kernel CUTLASS |
|
||||
| `triton` | All | Fallback; widely compatible |
|
||||
| `aiter` | ROCm | AMD AITER backend |
|
||||
|
||||
**`auto` selection order:** 1) DeepGEMM (SM90/SM100, installed); 2) FlashInfer TRTLLM (SM100, FlashInfer available); 3) CUTLASS (SM90/SM100/120); 4) AITER (AMD); 5) Triton. **Exception:** SM120 always resolves to Triton.
|
||||
|
||||
### `--fp4-gemm-backend` (NVFP4 GEMM)
|
||||
|
||||
| Backend | Hardware | Description |
|
||||
|---------|----------|-------------|
|
||||
| `auto` | SM100/120 | Auto-selects: `flashinfer_cudnn` on SM120; `flashinfer_cutlass` on SM100 |
|
||||
| `cutlass` | SM100/120 | SGLang CUTLASS kernel |
|
||||
| `flashinfer_cutlass` | SM100/120 | FlashInfer CUTLASS backend |
|
||||
| `flashinfer_cudnn` | SM100/120 (CUDA 13+, cuDNN 9.15+) | FlashInfer cuDNN backend; used on SM120 for performance |
|
||||
| `flashinfer_trtllm` | SM100 | FlashInfer TensorRT-LLM backend |
|
||||
|
||||
When FlashInfer is unavailable for NVFP4, the SGLang CUTLASS kernel is used as an automatic fallback.
|
||||
|
||||
## Offline Quantization
|
||||
|
||||
To load already quantized models, simply load the model weights and config. **Again, if the model has been quantized offline,
|
||||
there's no need to add `--quantization` argument when starting the engine. The quantization method will be parsed from the
|
||||
downloaded Hugging Face or msModelSlim config. For example, DeepSeek V3/R1 models are already in FP8, so do not add redundant parameters.**
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path hugging-quants/Meta-Llama-3.1-8B-Instruct-AWQ-INT4 \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
Take note, if your model is **per-channel quantized (INT8 or FP8) with per-token dynamic quantization activation**, you can opt to include `--quantization w8a8_int8` or `--quantization w8a8_fp8` to invoke the corresponding CUTLASS int8_kernel or fp8_kernel in sgl-kernel. This action will ignore the Hugging Face config's quantization settings. For instance, with `neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8-dynamic`, if you execute with `--quantization w8a8_fp8`, the system will use the `W8A8Fp8Config` from SGLang to invoke the sgl-kernel, rather than the `CompressedTensorsConfig` for vLLM kernels.
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8-dynamic \
|
||||
--quantization w8a8_fp8 \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
### Examples of Offline Model Quantization
|
||||
|
||||
#### Using [Unsloth](https://docs.unsloth.ai/basics/inference-and-deployment/sglang-guide)
|
||||
|
||||
We strongly suggest the use of Unsloth to quantize and load the model. Please refer to [SGLang Deployment & Inference Guide with Unsloth](https://docs.unsloth.ai/basics/inference-and-deployment/sglang-guide).
|
||||
|
||||
#### Using [auto-round](https://github.com/intel/auto-round)
|
||||
|
||||
```bash
|
||||
# Install
|
||||
pip install auto-round
|
||||
```
|
||||
|
||||
- LLM quantization
|
||||
|
||||
```py
|
||||
# for LLM
|
||||
from auto_round import AutoRound
|
||||
model_id = "meta-llama/Llama-3.2-1B-Instruct"
|
||||
quant_path = "Llama-3.2-1B-Instruct-autoround-4bit"
|
||||
# Scheme examples: "W2A16", "W3A16", "W4A16", "W8A16", "NVFP4", "MXFP4" (no real kernels), "GGUF:Q4_K_M", etc.
|
||||
scheme = "W4A16"
|
||||
format = "auto_round"
|
||||
autoround = AutoRound(model_id, scheme=scheme)
|
||||
autoround.quantize_and_save(quant_path, format=format) # quantize and save
|
||||
|
||||
```
|
||||
|
||||
- VLM quantization
|
||||
```py
|
||||
# for VLMs
|
||||
from auto_round import AutoRoundMLLM
|
||||
model_name = "Qwen/Qwen2-VL-2B-Instruct"
|
||||
quant_path = "Qwen2-VL-2B-Instruct-autoround-4bit"
|
||||
scheme = "W4A16"
|
||||
format = "auto_round"
|
||||
autoround = AutoRoundMLLM(model_name, scheme)
|
||||
autoround.quantize_and_save(quant_path, format=format) # quantize and save
|
||||
|
||||
```
|
||||
|
||||
- Command Line Usage (Gaudi/CPU/Intel GPU/CUDA)
|
||||
|
||||
```bash
|
||||
auto-round \
|
||||
--model meta-llama/Llama-3.2-1B-Instruct \
|
||||
--bits 4 \
|
||||
--group_size 128 \
|
||||
--format "auto_round" \
|
||||
--output_dir ./tmp_autoround
|
||||
```
|
||||
|
||||
- known issues
|
||||
|
||||
Several limitations currently affect offline quantized model loading in sglang, These issues might be resolved in future updates of sglang. If you experience any problems, consider using Hugging Face Transformers as an alternative.
|
||||
|
||||
1. Mixed-bit Quantization Limitations
|
||||
|
||||
Mixed-bit quantization is not fully supported. Due to vLLM's layer fusion (e.g., QKV fusion), applying different bit-widths to components within the same fused layer can lead to compatibility issues.
|
||||
|
||||
|
||||
2. Limited Support for Quantized MoE Models
|
||||
|
||||
Quantized MoE models may encounter inference issues due to kernel limitations (e.g., lack of support for mlp.gate layer quantization). please try to skip quantizing these layers to avoid such errors.
|
||||
|
||||
|
||||
3. Limited Support for Quantized VLMs
|
||||
<details>
|
||||
<summary>VLM failure cases</summary>
|
||||
|
||||
Qwen2.5-VL-7B
|
||||
|
||||
auto_round:auto_gptq format: Accuracy is close to zero.
|
||||
|
||||
GPTQ format: Fails with:
|
||||
```
|
||||
The output size is not aligned with the quantized weight shape
|
||||
```
|
||||
auto_round:auto_awq and AWQ format: These work as expected.
|
||||
</details>
|
||||
|
||||
#### Using [GPTQModel](https://github.com/ModelCloud/GPTQModel)
|
||||
|
||||
```bash
|
||||
# install
|
||||
pip install gptqmodel --no-build-isolation -v
|
||||
```
|
||||
|
||||
```py
|
||||
from datasets import load_dataset
|
||||
from gptqmodel import GPTQModel, QuantizeConfig
|
||||
|
||||
model_id = "meta-llama/Llama-3.2-1B-Instruct"
|
||||
quant_path = "Llama-3.2-1B-Instruct-gptqmodel-4bit"
|
||||
|
||||
calibration_dataset = load_dataset(
|
||||
"allenai/c4", data_files="en/c4-train.00001-of-01024.json.gz",
|
||||
split="train"
|
||||
).select(range(1024))["text"]
|
||||
|
||||
quant_config = QuantizeConfig(bits=4, group_size=128) # quantization config
|
||||
model = GPTQModel.load(model_id, quant_config) # load model
|
||||
|
||||
model.quantize(calibration_dataset, batch_size=2) # quantize
|
||||
model.save(quant_path) # save model
|
||||
```
|
||||
|
||||
#### Using [LLM Compressor](https://github.com/vllm-project/llm-compressor/)
|
||||
|
||||
```bash
|
||||
# install
|
||||
pip install llmcompressor
|
||||
```
|
||||
|
||||
Here, we take quantize `meta-llama/Meta-Llama-3-8B-Instruct` to `FP8` as an example to elaborate on how to do offline quantization.
|
||||
|
||||
```python
|
||||
from transformers import AutoTokenizer
|
||||
from llmcompressor.transformers import SparseAutoModelForCausalLM
|
||||
from llmcompressor.transformers import oneshot
|
||||
from llmcompressor.modifiers.quantization import QuantizationModifier
|
||||
|
||||
# Step 1: Load the original model.
|
||||
MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
|
||||
|
||||
model = SparseAutoModelForCausalLM.from_pretrained(
|
||||
MODEL_ID, device_map="auto", torch_dtype="auto")
|
||||
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
|
||||
|
||||
# Step 2: Perform offline quantization.
|
||||
# Step 2.1: Configure the simple PTQ quantization.
|
||||
recipe = QuantizationModifier(
|
||||
targets="Linear", scheme="FP8_DYNAMIC", ignore=["lm_head"])
|
||||
|
||||
# Step 2.2: Apply the quantization algorithm.
|
||||
oneshot(model=model, recipe=recipe)
|
||||
|
||||
# Step 3: Save the model.
|
||||
SAVE_DIR = MODEL_ID.split("/")[1] + "-FP8-Dynamic"
|
||||
model.save_pretrained(SAVE_DIR)
|
||||
tokenizer.save_pretrained(SAVE_DIR)
|
||||
```
|
||||
|
||||
Then, you can directly use the quantized model with `SGLang`, by using the following command:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path $PWD/Meta-Llama-3-8B-Instruct-FP8-Dynamic \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
#### Using [NVIDIA ModelOpt](https://github.com/NVIDIA/Model-Optimizer)
|
||||
|
||||
NVIDIA Model Optimizer (ModelOpt) provides advanced quantization techniques optimized for NVIDIA hardware.
|
||||
|
||||
**Offline vs. Online Quantization:**
|
||||
|
||||
SGLang supports two modes for ModelOpt.
|
||||
|
||||
* **Offline Quantization (pre-quantized):**
|
||||
* **Usage:** Download a pre-quantized model from Hugging Face or run `hf_ptq.py` once to create a new quantized checkpoint. Then load this quantized checkpoint.
|
||||
* **Pros:** Fast server startup, quantization can be validated before deployment, efficient resource usage.
|
||||
* **Cons:** Requires an extra preparation step.
|
||||
|
||||
* **Online Quantization (quant and serve):**
|
||||
* **Usage:** Load a standard BF16/FP16 model and add a flag. The engine applies quantization *on startup*.
|
||||
* **Pros:** Convenient (no new checkpoint needed).
|
||||
* **Cons:** **High startup time**, increases VRAM usage during initialization (risk of OOM).
|
||||
|
||||
The following sections guide you through using the Offline path: loading pre-quantized models or creating your own checkpoints.
|
||||
|
||||
##### Using Pre-Quantized Checkpoints
|
||||
|
||||
If a model is already quantized (e.g., from Hugging Face), you can load it directly.
|
||||
|
||||
* **FP8 Models:**
|
||||
Use `--quantization modelopt_fp8`.
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path nvidia/Llama-3.1-8B-Instruct-FP8 \
|
||||
--quantization modelopt_fp8 \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
* **FP4 Models:**
|
||||
Use `--quantization modelopt_fp4`.
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path nvidia/Llama-3.3-70B-Instruct-NVFP4 \
|
||||
--quantization modelopt_fp4 \
|
||||
--port 30000
|
||||
```
|
||||
|
||||
##### Creating Your Own Quantized Checkpoints
|
||||
|
||||
If a pre-quantized checkpoint is not available for your model, you can create one using NVIDIA Model Optimizer's `hf_ptq.py` script.
|
||||
|
||||
**Why quantize?**
|
||||
- Reduce VRAM usage
|
||||
- Higher throughput and lower latency
|
||||
- More flexible deployment (on smaller GPUs)
|
||||
|
||||
**What can be quantized?**
|
||||
- The entire model
|
||||
- MLP layers only
|
||||
- KV cache
|
||||
|
||||
**Key options in `hf_ptq.py`:**
|
||||
|
||||
`--qformat`: Quantization formats `fp8`, `nvfp4`, `nvfp4_mlp_only`
|
||||
|
||||
`--kv_cache_qformat`: KV cache quantization format (default: `fp8`)
|
||||
|
||||
**Note:** The default `kv_cache_qformat` may not be optimal for all use cases. Consider setting this explicitly.
|
||||
|
||||
**Hardware requirements:** Hopper and higher are recommended. Insufficient GPU memory may cause weight offloading, resulting in extremely long quantization time.
|
||||
|
||||
For detailed usage and supported model architectures, see [NVIDIA Model Optimizer LLM PTQ](https://github.com/NVIDIA/Model-Optimizer/tree/main/examples/llm_ptq).
|
||||
|
||||
SGLang includes a streamlined workflow for quantizing models with ModelOpt and automatically exporting them for deployment.
|
||||
|
||||
##### Installation
|
||||
|
||||
First, install ModelOpt:
|
||||
|
||||
```bash
|
||||
pip install nvidia-modelopt
|
||||
```
|
||||
|
||||
##### Quantization and Export Workflow
|
||||
|
||||
SGLang provides an example script that demonstrates the complete ModelOpt quantization and export workflow. Run from the SGLang repository root (see [modelopt_quantize_and_export.py](https://github.com/sgl-project/sglang/blob/main/examples/usage/modelopt_quantize_and_export.py)):
|
||||
|
||||
```bash
|
||||
# Quantize and export a model using ModelOpt FP8 quantization
|
||||
python examples/usage/modelopt_quantize_and_export.py quantize \
|
||||
--model-path TinyLlama/TinyLlama-1.1B-Chat-v1.0 \
|
||||
--export-dir ./quantized_tinyllama_fp8 \
|
||||
--quantization-method modelopt_fp8
|
||||
|
||||
# For FP4 quantization (requires Blackwell GPU)
|
||||
python examples/usage/modelopt_quantize_and_export.py quantize \
|
||||
--model-path TinyLlama/TinyLlama-1.1B-Chat-v1.0 \
|
||||
--export-dir ./quantized_tinyllama_fp4 \
|
||||
--quantization-method modelopt_fp4
|
||||
```
|
||||
|
||||
##### Available Quantization Methods
|
||||
|
||||
- `modelopt_fp8`: FP8 quantization with optimal performance on NVIDIA Hopper and Blackwell GPUs
|
||||
- `modelopt_fp4`: FP4 quantization with optimal performance on Nvidia Blackwell GPUs
|
||||
|
||||
##### Python API Usage
|
||||
|
||||
You can also use ModelOpt quantization programmatically:
|
||||
|
||||
```python
|
||||
import sglang as sgl
|
||||
from sglang.srt.configs.device_config import DeviceConfig
|
||||
from sglang.srt.configs.load_config import LoadConfig
|
||||
from sglang.srt.configs.model_config import ModelConfig
|
||||
from sglang.srt.model_loader.loader import get_model_loader
|
||||
|
||||
# Configure model with ModelOpt quantization and export
|
||||
model_config = ModelConfig(
|
||||
model_path="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
|
||||
quantization="modelopt_fp8", # or "modelopt_fp4"
|
||||
trust_remote_code=True,
|
||||
)
|
||||
|
||||
load_config = LoadConfig(
|
||||
modelopt_export_path="./exported_model",
|
||||
modelopt_checkpoint_save_path="./checkpoint.pth", # optional, fake quantized checkpoint
|
||||
)
|
||||
device_config = DeviceConfig(device="cuda")
|
||||
|
||||
# Load and quantize the model (export happens automatically)
|
||||
model_loader = get_model_loader(load_config, model_config)
|
||||
quantized_model = model_loader.load_model(
|
||||
model_config=model_config,
|
||||
device_config=device_config,
|
||||
)
|
||||
```
|
||||
|
||||
##### Deploying Quantized Models
|
||||
|
||||
After quantization and export, you can deploy the model with SGLang:
|
||||
|
||||
```bash
|
||||
# Deploy the exported quantized model
|
||||
python -m sglang.launch_server \
|
||||
--model-path ./quantized_tinyllama_fp8 \
|
||||
--quantization modelopt \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
Or using the Python API (use the same path as `modelopt_export_path` from the quantize step):
|
||||
|
||||
```python
|
||||
import sglang as sgl
|
||||
|
||||
def main():
|
||||
# Deploy exported ModelOpt quantized model
|
||||
# Path must match modelopt_export_path from quantize step (e.g., ./exported_model)
|
||||
llm = sgl.Engine(
|
||||
model_path="./exported_model",
|
||||
quantization="modelopt",
|
||||
)
|
||||
|
||||
# Run inference
|
||||
prompts = [
|
||||
"Hello, how are you?",
|
||||
"What is the capital of France?",
|
||||
]
|
||||
sampling_params = {
|
||||
"temperature": 0.8,
|
||||
"top_p": 0.95,
|
||||
"max_new_tokens": 100,
|
||||
}
|
||||
|
||||
outputs = llm.generate(prompts, sampling_params)
|
||||
|
||||
for i, output in enumerate(outputs):
|
||||
print(f"Prompt: {prompts[i]}")
|
||||
print(f"Output: {output['text']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
```
|
||||
|
||||
##### Advanced Features
|
||||
|
||||
**Checkpoint Management**: Save and restore fake quantized checkpoints for reuse:
|
||||
|
||||
```bash
|
||||
# Save the fake quantized checkpoint during quantization
|
||||
python examples/usage/modelopt_quantize_and_export.py quantize \
|
||||
--model-path meta-llama/Llama-3.2-1B-Instruct \
|
||||
--export-dir ./quantized_model \
|
||||
--quantization-method modelopt_fp8 \
|
||||
--checkpoint-save-path ./my_checkpoint.pth
|
||||
|
||||
# The checkpoint can be reused for future quantization runs and skip calibration
|
||||
```
|
||||
|
||||
**Export-only Workflow**: If you have a pre-existing fake quantized ModelOpt checkpoint, you can export it directly. See [LoadConfig](https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/configs/load_config.py) for the full API:
|
||||
|
||||
```python
|
||||
from sglang.srt.configs.device_config import DeviceConfig
|
||||
from sglang.srt.configs.load_config import LoadConfig
|
||||
from sglang.srt.configs.model_config import ModelConfig
|
||||
from sglang.srt.model_loader.loader import get_model_loader
|
||||
|
||||
model_config = ModelConfig(
|
||||
model_path="meta-llama/Llama-3.2-1B-Instruct",
|
||||
quantization="modelopt_fp8",
|
||||
trust_remote_code=True,
|
||||
)
|
||||
|
||||
load_config = LoadConfig(
|
||||
modelopt_checkpoint_restore_path="./my_checkpoint.pth",
|
||||
modelopt_export_path="./exported_model",
|
||||
)
|
||||
|
||||
# Load and export the model (DeviceConfig defaults to device="cuda")
|
||||
model_loader = get_model_loader(load_config, model_config)
|
||||
model_loader.load_model(model_config=model_config, device_config=DeviceConfig())
|
||||
```
|
||||
|
||||
##### Benefits of ModelOpt
|
||||
|
||||
- **Hardware Optimization**: Specifically optimized for NVIDIA GPU architectures
|
||||
- **Advanced Quantization**: Supports cutting-edge FP8 and FP4 quantization techniques
|
||||
- **Seamless Integration**: Automatic export to HuggingFace format for easy deployment
|
||||
- **Calibration-based**: Uses calibration datasets for optimal quantization quality
|
||||
- **Production Ready**: Enterprise-grade quantization with NVIDIA support
|
||||
|
||||
#### Using [ModelSlim](https://gitcode.com/Ascend/msmodelslim)
|
||||
MindStudio-ModelSlim (msModelSlim) is a model offline quantization compression tool launched by MindStudio and optimized for Ascend hardware.
|
||||
|
||||
- **Installation**
|
||||
|
||||
```bash
|
||||
# Clone repo and install msmodelslim:
|
||||
git clone https://gitcode.com/Ascend/msmodelslim.git
|
||||
cd msmodelslim
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
- **LLM quantization**
|
||||
|
||||
Download the original floating-point weights of the large model. Taking Qwen3-32B as an example, you can go to [Qwen3-32B](https://huggingface.co/Qwen/Qwen3-32B) to obtain the original model weights. Then install other dependencies (related to the model, refer to the huggingface model card).
|
||||
> Note: You can find pre-quantized validated models on [modelscope/Eco-Tech](https://modelscope.cn/models/Eco-Tech).
|
||||
|
||||
_Traditional quantification methods require the preparation of calibration data files (```.jsonl``` formats) for calibration in the quantification process._
|
||||
```bash
|
||||
Qwen3-32B/ # floating-point model downloaded from official HF (or modelscope) repo
|
||||
msmodelslim/ # msmodelslim repo
|
||||
|----- lab_calib # calibration date folder (put your dataset here in ```.jsonl``` format or use pre-prepared ones)
|
||||
|----- some file (such as laos_calib.jsonl)
|
||||
|----- lab_practice # best practice folder with configs for quantization
|
||||
|----- model folder (such as qwen3_5_moe folder) # folder with quantization configs
|
||||
|----- quant_config (such as qwen3_5_moe_w8a8.yaml) # quantization config
|
||||
|----- another folders
|
||||
output_folder/ # generated by below command
|
||||
|----- quant_model_weights-00001-of-0001.safetensors # quantized weights
|
||||
|----- quant_model_description.json # file with description of the quantization methods for each layer (```W4A4_DYNAMIC```, etc.)
|
||||
|----- another files (such as config.json, tokenizer.json, etc.)
|
||||
```
|
||||
Run quantization using one-click quantization (recommended):
|
||||
```bash
|
||||
msmodelslim quant \
|
||||
--model_path ${MODEL_PATH} \
|
||||
--save_path ${SAVE_PATH} \
|
||||
--device npu:0,1 \
|
||||
--model_type Qwen3-32B \
|
||||
--quant_type w8a8 \
|
||||
--trust_remote_code True
|
||||
```
|
||||
|
||||
- **Usage Example**
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path $PWD/Qwen3-32B-w8a8 \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
- **Available Quantization Methods**:
|
||||
- [x] ```W4A4_DYNAMIC``` linear with online quantization of activations
|
||||
- [x] ```W8A8``` linear with offline quantization of activations
|
||||
- [x] ```W8A8_DYNAMIC``` linear with online quantization of activations
|
||||
- [x] ```W4A4_DYNAMIC``` MOE with online quantization of activations
|
||||
- [x] ```W4A8_DYNAMIC``` MOE with online quantization of activations
|
||||
- [x] ```W8A8_DYNAMIC``` MOE with online quantization of activations
|
||||
- [ ] ```W4A8``` linear TBD
|
||||
- [ ] ```W4A16``` linear TBD
|
||||
- [ ] ```W48A16``` linear TBD
|
||||
- [ ] ```W4A16``` MoE in progress
|
||||
- [ ] ```W8A16``` MoE in progress
|
||||
- [ ] ```KV Cache``` in progress
|
||||
- [ ] ```Attention``` in progress
|
||||
|
||||
|
||||
For more detailed examples of quantization of models, as well as information about their support, see the [examples](https://gitcode.com/Ascend/msmodelslim/blob/master/example/README.md) section in ModelSLim repo.
|
||||
|
||||
## Online Quantization
|
||||
|
||||
To enable online quantization, you can simply specify `--quantization` in the command line. For example, you can launch the server with the following command to enable `FP8` quantization for model `meta-llama/Meta-Llama-3.1-8B-Instruct`:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--quantization fp8 \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
Our team is working on supporting more online quantization methods. SGLang will soon support methods including but not limited to `["awq", "gptq", "marlin", "gptq_marlin", "awq_marlin", "bitsandbytes", "gguf"]`.
|
||||
|
||||
### torchao online quantization method
|
||||
|
||||
SGLang also supports quantization methods based on [torchao](https://github.com/pytorch/ao). You can simply specify `--torchao-config` in the command line to support this feature. For example, if you want to enable `int4wo-128` for model `meta-llama/Meta-Llama-3.1-8B-Instruct`, you can launch the server with the following command:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--torchao-config int4wo-128 \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
SGLang supports the following quantization methods based on torchao `["int8dq", "int8wo", "fp8wo", "fp8dq-per_tensor", "fp8dq-per_row", "int4wo-32", "int4wo-64", "int4wo-128", "int4wo-256"]`.
|
||||
|
||||
Note: According to [this issue](https://github.com/sgl-project/sglang/issues/2219#issuecomment-2561890230), `"int8dq"` method currently has some bugs when using together with cuda graph capture. So we suggest to disable cuda graph capture when using `"int8dq"` method. Namely, please use the following command:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--torchao-config int8dq \
|
||||
--disable-cuda-graph \
|
||||
--port 30000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
### `quark_int4fp8_moe` online quantization method
|
||||
|
||||
SGLang running on AMD GPUs (CDNA3 or CDNA4 architecture) supports the quantization method `--quantization quark_int4fp8_moe`, that will replace [MoE layers](https://github.com/sgl-project/sglang/blob/v0.4.8/python/sglang/srt/layers/moe/fused_moe_triton/layer.py#L271) originally in high precision (bfloat16, float16 or float32) to use weights dynamically quantized to int4, that are upcasted to float8 during inference to run compute in float8 precision with activations dynamically quantized on the fly to float8.
|
||||
|
||||
Other layers (e.g. projections in the attention layers) have their weights quantized online to float8 directly.
|
||||
|
||||
## Reference
|
||||
|
||||
- [GPTQModel](https://github.com/ModelCloud/GPTQModel)
|
||||
- [LLM Compressor](https://github.com/vllm-project/llm-compressor/)
|
||||
- [NVIDIA Model Optimizer (ModelOpt)](https://github.com/NVIDIA/Model-Optimizer)
|
||||
- [NVIDIA Model Optimizer LLM PTQ](https://github.com/NVIDIA/Model-Optimizer/tree/main/examples/llm_ptq)
|
||||
- [Petit: NVFP4 on ROCm](https://github.com/causalflow-ai/petit-kernel) — [LMSYS blog](https://lmsys.org/blog/2025-09-21-petit-amdgpu/), [AMD ROCm blog](https://rocm.blogs.amd.com/artificial-intelligence/fp4-mixed-precision/README.html)
|
||||
- [Torchao: PyTorch Architecture Optimization](https://github.com/pytorch/ao)
|
||||
- [vLLM Quantization](https://docs.vllm.ai/en/latest/quantization/)
|
||||
- [auto-round](https://github.com/intel/auto-round)
|
||||
- [ModelSlim](https://gitcode.com/Ascend/msmodelslim)
|
||||
162
third_party/sglang/docs/advanced_features/quantized_kv_cache.md
vendored
Normal file
162
third_party/sglang/docs/advanced_features/quantized_kv_cache.md
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
# Quantized KV Cache
|
||||
|
||||
Quantized KV cache reduces the memory footprint of key-value cache storage by using lower-precision data types (FP8 or FP4) instead of the default model precision in BF16. During autoregressive generation, LLMs cache previously computed key-value pairs to avoid redundant calculations. The KV cache typically consumes a significant portion of GPU memory, especially for long sequences.
|
||||
|
||||
Quantized KV cache is a memory optimization technique that primarily benefits throughput by allowing more tokens to be cached, but may introduce minimal accuracy degradation depending on the quantization format used.
|
||||
|
||||
```{warning}
|
||||
**Performance Warning**: When quantized KV cache must be dequantized before use in attention operations, performance can be extremely slow if dequantization is not fused with the attention kernel. Always verify that your chosen attention backend supports quantized KV cache. Backends without fused support may experience significant throughput degradation, potentially negating the memory benefits.
|
||||
|
||||
**Backend Support**: Not all attention backends support quantized KV cache. Refer to [Attention Backend](attention_backend.md) for which backends support it.
|
||||
```
|
||||
|
||||
## Supported Formats
|
||||
|
||||
SGLang supports the following quantized KV cache formats:
|
||||
|
||||
### FP8 Format
|
||||
|
||||
[OCP (Open Compute Project)](https://www.opencompute.org) specifies two common 8-bit floating point formats:
|
||||
|
||||
- **E5M2** (5 exponent bits, 2 mantissa bits): Larger dynamic range (±57344.0), lower precision
|
||||
- **E4M3** (4 exponent bits, 3 mantissa bits): Higher precision, smaller dynamic range (±240.0)
|
||||
|
||||
### FP4 Format
|
||||
|
||||
```{warning}
|
||||
FP4 quantization is currently experimental.
|
||||
```
|
||||
|
||||
[OCP (Open Compute Project)](https://www.opencompute.org) specifies MXFP4 (Microscaling FP4), a 4-bit floating-point format:
|
||||
|
||||
- **E2M1** (1 sign bit, 2 exponent bits, 1 mantissa bit): Uses block-based microscaling where tensors are divided into blocks of consecutive elements, with each block sharing a single 8-bit exponential scaling factor. While OCP specifies blocks of 32 elements, SGLang's current implementation uses blocks of 16 elements for KV cache quantization.
|
||||
|
||||
## Usage
|
||||
|
||||
### Enabling Quantized KV Cache
|
||||
|
||||
To enable quantized KV cache, use the `--kv-cache-dtype` argument when launching the server:
|
||||
|
||||
```bash
|
||||
# Enable FP8 E5M2 KV cache
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-R1-0528 \
|
||||
--kv-cache-dtype fp8_e5m2 \
|
||||
|
||||
# Enable FP8 E4M3 KV cache
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path deepseek-ai/DeepSeek-R1-0528 \
|
||||
--kv-cache-dtype fp8_e4m3 \
|
||||
|
||||
# Enable FP4 E2M1 KV cache
|
||||
python3 -m sglang.launch_server \
|
||||
--model-path nvidia/DeepSeek-R1-0528-NVFP4 \
|
||||
--kv-cache-dtype fp4_e2m1 \
|
||||
```
|
||||
|
||||
### Scaling Factors
|
||||
|
||||
FP8 quantization requires scaling factors to properly quantize and dequantize the KV cache.
|
||||
|
||||
```{note}
|
||||
Currently, only per-tensor (scalar) scaling factors are supported.
|
||||
```
|
||||
|
||||
Scaling factors can be:
|
||||
|
||||
- **Loaded from checkpoints**: Pre-quantized models (e.g., ModelOpt) may include `k_scale` and `v_scale` parameters that are automatically loaded
|
||||
- **Provided via JSON**: Supply scaling factors via `--quantization-param-path`.
|
||||
|
||||
The JSON file should follow this format:
|
||||
|
||||
```json
|
||||
{
|
||||
"kv_cache": {
|
||||
"dtype": "float8_e4m3fn",
|
||||
"scaling_factor": {
|
||||
"0": {
|
||||
"0": 1.0,
|
||||
"1": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Where the outer keys in `scaling_factor` are tensor parallel ranks and inner keys are layer indices.
|
||||
|
||||
```{warning}
|
||||
If scaling factors are not provided and not found in the checkpoint, it will default to 1.0, which may cause accuracy issues.
|
||||
```
|
||||
|
||||
```{tip}
|
||||
**FP4 (MXFP4)**: Unlike FP8, FP4 quantization handles scaling factors automatically on-the-fly during quantization and dequantization. No pre-quantized models or external scaling factor files are required—the block-based scaling factors are computed dynamically as needed.
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memory Savings
|
||||
|
||||
Quantized KV cache provides significant memory savings:
|
||||
- **BF16 → FP4**: Supports approximately 3.56× more tokens than BF16 (accounting for scaling factor overhead)
|
||||
|
||||
```{note}
|
||||
FP4 and FP8 quantization require additional memory for block-based scaling factors, which reduces the effective memory savings compared to the raw bit-width reduction. FP4 with block size 16 supports approximately 1.78× more tokens than FP8, and approximately 3.56× more tokens than BF16. The relative token capacity between FP8 and BF16 can be derived from these ratios.
|
||||
```
|
||||
|
||||
This enables longer context lengths or more concurrent requests within the same memory budget.
|
||||
|
||||
### Accuracy Impact
|
||||
|
||||
#### FP8 Accuracy
|
||||
|
||||
FP8 E4M3 quantization typically introduces minimal accuracy degradation. The impact depends on model architecture, sequence length, and quantization format (generally, E4M3 has better accuracy than E5M2).
|
||||
|
||||
#### FP4 Accuracy
|
||||
|
||||
FP4 (MXFP4) quantization provides significant memory savings with varying accuracy impact depending on model size and dataset complexity. Preliminary accuracy test results from [PR #10078](https://github.com/sgl-project/sglang/pull/10078) (MLA) and [PR #12612](https://github.com/sgl-project/sglang/pull/12612) (MHA) show:
|
||||
|
||||
**Large Models (e.g., Qwen3-235B-A22B, DeepSeek-R1-0528)**
|
||||
|
||||
On large-scale models, FP4 maintains accuracy close to FP8/BF16, especially on simpler datasets:
|
||||
|
||||
| Model | Dataset | KV16 | KV8 (FP8 E4M3) | KV4 (FP4 E2M1) |
|
||||
|-------|---------|------|----------------|----------------|
|
||||
| Qwen3-235B-A22B | gsm8k | 0.9168 | 0.9181 | 0.9186 |
|
||||
| Qwen3-235B-A22B | aime25 | 0.7733 | 0.7333 | 0.6000 |
|
||||
| Qwen3-235B-A22B | gpqa_diamond | 0.7010 | 0.6899 | 0.6778 |
|
||||
| DeepSeek-R1-0528 | gsm8k | 0.9157 | 0.9154 | 0.9124 |
|
||||
| DeepSeek-R1-0528 | aime25 | 0.5067 | 0.4934 | 0.4000 |
|
||||
| DeepSeek-R1-0528 | gpqa_diamond | 0.7707 | 0.7697 | 0.7273 |
|
||||
|
||||
**Smaller Models (e.g., GPT-OSS-120B)**
|
||||
|
||||
On smaller models, FP4 shows more pronounced accuracy drops, particularly on challenging datasets:
|
||||
|
||||
| Model | Dataset | KV16 | KV8 (FP8 E4M3) | KV4 (FP4 E2M1) |
|
||||
|-------|---------|------|----------------|----------------|
|
||||
| GPT-OSS-120B | gsm8k | 0.9161 | 0.9163 | 0.9152 |
|
||||
| GPT-OSS-120B | aime25 | 0.7533 | 0.7667 | 0.3533 |
|
||||
| GPT-OSS-120B | gpqa_diamond | 0.5081 | 0.5434 | 0.3202 |
|
||||
|
||||
**Key Observations:**
|
||||
|
||||
- **Simple datasets (e.g., gsm8k)**: FP4 maintains accuracy close to FP8/BF16 across model sizes
|
||||
- **Model size matters**: Large models (200B+ parameters) generally tolerate FP4 quantization better than smaller models
|
||||
- **Context length**: Accuracy degradation may be more pronounced in long-context scenarios, as the accumulation of the quantization error may become significant.
|
||||
|
||||
```{tip}
|
||||
Evaluate FP4 accuracy on your specific model and workload. Large models on simpler tasks typically show minimal degradation, while smaller models or complex reasoning tasks may require FP8 or BF16 for acceptable accuracy.
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Use pre-quantized models**: Prefer models quantized offline with scaling factors included in the checkpoint.
|
||||
- **Choose the right format**: Use `fp8_e4m3` for better accuracy (recommended), `fp8_e5m2` for larger dynamic range, or `fp4_e2m1` for maximum memory savings (experimental)
|
||||
- **Check backend compatibility**: Verify that your chosen attention backend supports quantized KV cache
|
||||
|
||||
```{seealso}
|
||||
- [Quantization](quantization.md)
|
||||
- [Attention Backend](attention_backend.md)
|
||||
- [Server Arguments](server_arguments.md)
|
||||
```
|
||||
72
third_party/sglang/docs/advanced_features/rfork.md
vendored
Normal file
72
third_party/sglang/docs/advanced_features/rfork.md
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# R-Fork
|
||||
|
||||
R-Fork (Tensor Remote Fork) is a novel weight loading methodology that leverages efficient inter-node GPU-to-GPU data transfer path to load tensors from a running SGLang instance to a new instance with zero-copy. It can significantly optimize the SGLang instance boot-up time by reducing model weights loading from several minutes to mere seconds.
|
||||
|
||||
To learn more details about R-Fork, please check **<a href=https://lmsys.org/blog/2025-12-10-rfork/> R-Fork blog </a>**
|
||||
|
||||
## Usage
|
||||
|
||||
| Argument | Usage |
|
||||
|--------------|--------------------------------------------|
|
||||
| load-format | set to `remote_instance` to enable R-Fork. |
|
||||
| remote-instance-weight-loader-backend | `nccl`, `transfer_engine`, or `modelexpress`. Default is `nccl`. |
|
||||
| remote-instance-weight-loader-seed-instance-ip | IP address of the seed instance who will provide the model weight. Used by `nccl` and `transfer_engine` backends. |
|
||||
| remote-instance-weight-loader-seed-instance-service-port | the port that the seed instance's HTTP server is listening on. Used by `nccl` and `transfer_engine` backends. |
|
||||
| remote-instance-weight-loader-send-weights-group-ports | the list of available ports on the seed instance that will be used to build NCCL communication groups between seed and client instance. Only needed by `nccl` backend. |
|
||||
| remote-instance-weight-loader-start-seed-via-transfer-engine | set to start seed service that supports TransferEngine as backend. Needed for seed instances when using `transfer_engine` as backend. |
|
||||
| modelexpress-config | JSON config for `modelexpress` backend. Keys: `"url"` (required, gRPC host:port of ModelExpress server), `"model_name"` (optional, defaults to `--model-path`), `"source"` (optional bool, `true` for seed mode). |
|
||||
|
||||
### NCCL as backend
|
||||
|
||||
seed instance:
|
||||
```shell
|
||||
python -m sglang.launch_server [args]
|
||||
```
|
||||
|
||||
client instance:
|
||||
```shell
|
||||
python -m sglang.launch_server [args] \
|
||||
--load-format remote_instance \
|
||||
--remote-instance-weight-loader-seed-instance-ip [seed_instance_ip] \
|
||||
--remote-instance-weight-loader-seed-instance-service-port [seed_instance_service_port] \
|
||||
--remote-instance-weight-loader-send-weights-group-ports [send_weights_nccl_group_ports_list] \
|
||||
--remote-instance-weight-loader-backend nccl
|
||||
```
|
||||
|
||||
### TransferEngine as backend
|
||||
|
||||
seed instance:
|
||||
```shell
|
||||
python -m sglang.launch_server [args] \
|
||||
--remote-instance-weight-loader-start-seed-via-transfer-engine
|
||||
```
|
||||
|
||||
```shell
|
||||
python -m sglang.launch_server [args] \
|
||||
--load-format remote_instance \
|
||||
--remote-instance-weight-loader-seed-instance-ip [seed_instance_ip] \
|
||||
--remote-instance-weight-loader-seed-instance-service-port [seed_instance_service_port] \
|
||||
--remote-instance-weight-loader-backend transfer_engine
|
||||
```
|
||||
|
||||
### ModelExpress as backend
|
||||
|
||||
[ModelExpress](https://github.com/ai-dynamo/modelexpress) is a coordination service that manages P2P weight transfer metadata. It removes the need for direct seed IP/port configuration by providing a centralized registry that seeds publish to and clients discover from. Under the hood it uses TransferEngine (Mooncake) for the actual RDMA data transfer.
|
||||
|
||||
A running ModelExpress server is required. See the [ModelExpress documentation](https://github.com/ai-dynamo/modelexpress) for setup instructions.
|
||||
|
||||
seed instance:
|
||||
```shell
|
||||
python -m sglang.launch_server [args] \
|
||||
--modelexpress-config '{"url": "[modelexpress_grpc_host:port]", "model_name": "[model_name]", "source": true}'
|
||||
```
|
||||
|
||||
client instance:
|
||||
```shell
|
||||
python -m sglang.launch_server [args] \
|
||||
--load-format remote_instance \
|
||||
--remote-instance-weight-loader-backend modelexpress \
|
||||
--modelexpress-config '{"url": "[modelexpress_grpc_host:port]", "model_name": "[model_name]"}'
|
||||
```
|
||||
|
||||
The seed publishes its TransferEngine session ID and tensor layout to ModelExpress. The client queries ModelExpress to discover the seed, then pulls weights directly via RDMA. This enables dynamic seed discovery without hardcoding IPs, and supports multiple models through a single ModelExpress instance.
|
||||
377
third_party/sglang/docs/advanced_features/separate_reasoning.ipynb
vendored
Normal file
377
third_party/sglang/docs/advanced_features/separate_reasoning.ipynb
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Reasoning Parser\n",
|
||||
"\n",
|
||||
"SGLang supports parsing reasoning content out from \"normal\" content for reasoning models such as [DeepSeek R1](https://huggingface.co/deepseek-ai/DeepSeek-R1).\n",
|
||||
"\n",
|
||||
"## Supported Models & Parsers\n",
|
||||
"\n",
|
||||
"| Model | Reasoning tags | Parser | Notes |\n",
|
||||
"|---------|-----------------------------|------------------|-------|\n",
|
||||
"| [DeepSeek‑R1 series](https://huggingface.co/collections/deepseek-ai/deepseek-r1-678e1e131c0169c0bc89728d) | `<think>` … `</think>` | `deepseek-r1` | Supports all variants (R1, R1-0528, R1-Distill) |\n",
|
||||
"| [DeepSeek‑V3 series](https://huggingface.co/deepseek-ai/DeepSeek-V3.1) | `<think>` … `</think>` | `deepseek-v3` | Including [DeepSeek‑V3.2](https://huggingface.co/deepseek-ai/DeepSeek-V3.2-Exp). Supports `thinking` parameter |\n",
|
||||
"| [Standard Qwen3 models](https://huggingface.co/collections/Qwen/qwen3-67dd247413f0e2e4f653967f) | `<think>` … `</think>` | `qwen3` | Supports `enable_thinking` parameter |\n",
|
||||
"| [Qwen3-Thinking models](https://huggingface.co/Qwen/Qwen3-235B-A22B-Thinking-2507) | `<think>` … `</think>` | `qwen3` or `qwen3-thinking` | Always generates thinking content |\n",
|
||||
"| [Kimi K2 Thinking](https://huggingface.co/moonshotai/Kimi-K2-Thinking) | `◁think▷` … `◁/think▷` | `kimi_k2` | Uses special thinking delimiters. Also requires `--tool-call-parser kimi_k2` for tool use. |\n",
|
||||
"| [GPT OSS](https://huggingface.co/openai/gpt-oss-120b) | `<\\|channel\\|>analysis<\\|message\\|>` … `<\\|end\\|>` | `gpt-oss` | N/A |\n",
|
||||
"### Model-Specific Behaviors\n",
|
||||
"\n",
|
||||
"**DeepSeek-R1 Family:**\n",
|
||||
"- DeepSeek-R1: No `<think>` start tag, jumps directly to thinking content\n",
|
||||
"- DeepSeek-R1-0528: Generates both `<think>` start and `</think>` end tags\n",
|
||||
"- Both are handled by the same `deepseek-r1` parser\n",
|
||||
"\n",
|
||||
"**DeepSeek-V3 Family:**\n",
|
||||
"- DeepSeek-V3.1/V3.2: Hybrid model supporting both thinking and non-thinking modes, use the `deepseek-v3` parser and `thinking` parameter (NOTE: not `enable_thinking`)\n",
|
||||
"\n",
|
||||
"**Qwen3 Family:**\n",
|
||||
"- Standard Qwen3 (e.g., Qwen3-2507): Use `qwen3` parser, supports `enable_thinking` in chat templates\n",
|
||||
"- Qwen3-Thinking (e.g., Qwen3-235B-A22B-Thinking-2507): Use `qwen3` or `qwen3-thinking` parser, always thinks\n",
|
||||
"\n",
|
||||
"**Kimi K2:**\n",
|
||||
"- Kimi K2 Thinking: Uses special `◁think▷` and `◁/think▷` tags. For agentic tool use, also specify `--tool-call-parser kimi_k2`.\n",
|
||||
"\n",
|
||||
"**GPT OSS:**\n",
|
||||
"- GPT OSS: Uses special `<|channel|>analysis<|message|>` and `<|end|>` tags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage\n",
|
||||
"\n",
|
||||
"### Launching the Server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Specify the `--reasoning-parser` option."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"from sglang.utils import wait_for_server, print_highlight, terminate_process\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" \"python3 -m sglang.launch_server --model-path deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --host 0.0.0.0 --reasoning-parser deepseek-r1 --log-level warning\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that `--reasoning-parser` defines the parser used to interpret responses."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### OpenAI Compatible API\n",
|
||||
"\n",
|
||||
"Using the OpenAI compatible API, the contract follows the [DeepSeek API design](https://api-docs.deepseek.com/guides/reasoning_model) established with the release of DeepSeek-R1:\n",
|
||||
"\n",
|
||||
"- `reasoning_content`: The content of the CoT.\n",
|
||||
"- `content`: The content of the final answer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Initialize OpenAI-like client\n",
|
||||
"client = OpenAI(api_key=\"None\", base_url=f\"http://0.0.0.0:{port}/v1\")\n",
|
||||
"model_name = client.models.list().data[0].id\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What is 1+3?\",\n",
|
||||
" }\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Non-Streaming Request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response_non_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0.6,\n",
|
||||
" top_p=0.95,\n",
|
||||
" stream=False, # Non-streaming\n",
|
||||
" extra_body={\"separate_reasoning\": True},\n",
|
||||
")\n",
|
||||
"print_highlight(\"==== Reasoning ====\")\n",
|
||||
"print_highlight(response_non_stream.choices[0].message.reasoning_content)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(response_non_stream.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Streaming Request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0.6,\n",
|
||||
" top_p=0.95,\n",
|
||||
" stream=True, # Non-streaming\n",
|
||||
" extra_body={\"separate_reasoning\": True},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"reasoning_content = \"\"\n",
|
||||
"content = \"\"\n",
|
||||
"for chunk in response_stream:\n",
|
||||
" if chunk.choices[0].delta.content:\n",
|
||||
" content += chunk.choices[0].delta.content\n",
|
||||
" if chunk.choices[0].delta.reasoning_content:\n",
|
||||
" reasoning_content += chunk.choices[0].delta.reasoning_content\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Reasoning ====\")\n",
|
||||
"print_highlight(reasoning_content)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Optionally, you can buffer the reasoning content to the last reasoning chunk (or the first chunk after the reasoning content)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0.6,\n",
|
||||
" top_p=0.95,\n",
|
||||
" stream=True, # Non-streaming\n",
|
||||
" extra_body={\"separate_reasoning\": True, \"stream_reasoning\": False},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"reasoning_content = \"\"\n",
|
||||
"content = \"\"\n",
|
||||
"for chunk in response_stream:\n",
|
||||
" if chunk.choices[0].delta.content:\n",
|
||||
" content += chunk.choices[0].delta.content\n",
|
||||
" if chunk.choices[0].delta.reasoning_content:\n",
|
||||
" reasoning_content += chunk.choices[0].delta.reasoning_content\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Reasoning ====\")\n",
|
||||
"print_highlight(reasoning_content)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The reasoning separation is enable by default when specify . \n",
|
||||
"**To disable it, set the `separate_reasoning` option to `False` in request.**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response_non_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0.6,\n",
|
||||
" top_p=0.95,\n",
|
||||
" stream=False, # Non-streaming\n",
|
||||
" extra_body={\"separate_reasoning\": False},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Original Output ====\")\n",
|
||||
"print_highlight(response_non_stream.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### SGLang Native API "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import AutoTokenizer\n",
|
||||
"\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\")\n",
|
||||
"input = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"gen_url = f\"http://localhost:{port}/generate\"\n",
|
||||
"gen_data = {\n",
|
||||
" \"text\": input,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"skip_special_tokens\": False,\n",
|
||||
" \"max_new_tokens\": 1024,\n",
|
||||
" \"temperature\": 0.6,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"gen_response = requests.post(gen_url, json=gen_data).json()[\"text\"]\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Original Output ====\")\n",
|
||||
"print_highlight(gen_response)\n",
|
||||
"\n",
|
||||
"parse_url = f\"http://localhost:{port}/separate_reasoning\"\n",
|
||||
"separate_reasoning_data = {\n",
|
||||
" \"text\": gen_response,\n",
|
||||
" \"reasoning_parser\": \"deepseek-r1\",\n",
|
||||
"}\n",
|
||||
"separate_reasoning_response_json = requests.post(\n",
|
||||
" parse_url, json=separate_reasoning_data\n",
|
||||
").json()\n",
|
||||
"print_highlight(\"==== Reasoning ====\")\n",
|
||||
"print_highlight(separate_reasoning_response_json[\"reasoning_text\"])\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(separate_reasoning_response_json[\"text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Offline Engine API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sglang as sgl\n",
|
||||
"from sglang.srt.parser.reasoning_parser import ReasoningParser\n",
|
||||
"from sglang.utils import print_highlight\n",
|
||||
"\n",
|
||||
"llm = sgl.Engine(model_path=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\")\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\")\n",
|
||||
"input = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"sampling_params = {\n",
|
||||
" \"max_new_tokens\": 1024,\n",
|
||||
" \"skip_special_tokens\": False,\n",
|
||||
" \"temperature\": 0.6,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
"}\n",
|
||||
"result = llm.generate(prompt=input, sampling_params=sampling_params)\n",
|
||||
"\n",
|
||||
"generated_text = result[\"text\"] # Assume there is only one prompt\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Original Output ====\")\n",
|
||||
"print_highlight(generated_text)\n",
|
||||
"\n",
|
||||
"parser = ReasoningParser(\"deepseek-r1\")\n",
|
||||
"reasoning_text, text = parser.parse_non_stream(generated_text)\n",
|
||||
"print_highlight(\"==== Reasoning ====\")\n",
|
||||
"print_highlight(reasoning_text)\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm.shutdown()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Supporting New Reasoning Model Schemas\n",
|
||||
"\n",
|
||||
"For future reasoning models, you can implement the reasoning parser as a subclass of `BaseReasoningFormatDetector` in `python/sglang/srt/reasoning_parser.py` and specify the reasoning parser for new reasoning model schemas accordingly."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
569
third_party/sglang/docs/advanced_features/server_arguments.md
vendored
Normal file
569
third_party/sglang/docs/advanced_features/server_arguments.md
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
# Server Arguments
|
||||
|
||||
This page provides a list of server arguments used in the command line to configure the behavior
|
||||
and performance of the language model server during deployment. These arguments enable users to
|
||||
customize key aspects of the server, including model selection, parallelism policies,
|
||||
memory management, and optimization techniques.
|
||||
You can find all arguments by `python3 -m sglang.launch_server --help`
|
||||
|
||||
## Common launch commands
|
||||
|
||||
- To use a configuration file, create a YAML file with your server arguments and specify it with `--config`. CLI arguments will override config file values.
|
||||
|
||||
```bash
|
||||
# Create config.yaml
|
||||
cat > config.yaml << EOF
|
||||
model-path: meta-llama/Meta-Llama-3-8B-Instruct
|
||||
host: 0.0.0.0
|
||||
port: 30000
|
||||
tensor-parallel-size: 2
|
||||
enable-metrics: true
|
||||
log-requests: true
|
||||
EOF
|
||||
|
||||
# Launch server with config file
|
||||
python -m sglang.launch_server --config config.yaml
|
||||
```
|
||||
|
||||
- To enable multi-GPU tensor parallelism, add `--tp 2`. If it reports the error "peer access is not supported between these two devices", add `--enable-p2p-check` to the server launch command.
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server --model-path meta-llama/Meta-Llama-3-8B-Instruct --tp 2
|
||||
```
|
||||
|
||||
- To enable multi-GPU data parallelism, add `--dp 2`. Data parallelism is better for throughput if there is enough memory. It can also be used together with tensor parallelism. The following command uses 4 GPUs in total. We recommend [SGLang Model Gateway (former Router)](../advanced_features/sgl_model_gateway.md) for data parallelism.
|
||||
|
||||
```bash
|
||||
python -m sglang_router.launch_server --model-path meta-llama/Meta-Llama-3-8B-Instruct --dp 2 --tp 2
|
||||
```
|
||||
|
||||
- If you see out-of-memory errors during serving, try to reduce the memory usage of the KV cache pool by setting a smaller value of `--mem-fraction-static`. The default value is `0.9`.
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server --model-path meta-llama/Meta-Llama-3-8B-Instruct --mem-fraction-static 0.7
|
||||
```
|
||||
|
||||
- See [hyperparameter tuning](hyperparameter_tuning.md) on tuning hyperparameters for better performance.
|
||||
- For docker and Kubernetes runs, you need to set up shared memory which is used for communication between processes. See `--shm-size` for docker and `/dev/shm` size update for Kubernetes manifests.
|
||||
- If you see out-of-memory errors during prefill for long prompts, try to set a smaller chunked prefill size.
|
||||
|
||||
```bash
|
||||
python -m sglang.launch_server --model-path meta-llama/Meta-Llama-3-8B-Instruct --chunked-prefill-size 4096
|
||||
```
|
||||
- To enable fp8 weight quantization, add `--quantization fp8` on a fp16 checkpoint or directly load a fp8 checkpoint without specifying any arguments.
|
||||
- To enable fp8 kv cache quantization, add `--kv-cache-dtype fp8_e4m3` or `--kv-cache-dtype fp8_e5m2`.
|
||||
- To enable deterministic inference and batch invariant operations, add `--enable-deterministic-inference`. More details can be found in [deterministic inference document](../advanced_features/deterministic_inference.md).
|
||||
- If the model does not have a chat template in the Hugging Face tokenizer, you can specify a [custom chat template](../references/custom_chat_template.md). If the tokenizer has multiple named templates (e.g., 'default', 'tool_use'), you can select one using `--hf-chat-template-name tool_use`.
|
||||
- To run tensor parallelism on multiple nodes, add `--nnodes 2`. If you have two nodes with two GPUs on each node and want to run TP=4, let `sgl-dev-0` be the hostname of the first node and `50000` be an available port, you can use the following commands. If you meet deadlock, please try to add `--disable-cuda-graph`
|
||||
- (Note: This feature is out of maintenance and might cause error) To enable `torch.compile` acceleration, add `--enable-torch-compile`. It accelerates small models on small batch sizes. By default, the cache path is located at `/tmp/torchinductor_root`, you can customize it using environment variable `TORCHINDUCTOR_CACHE_DIR`. For more details, please refer to [PyTorch official documentation](https://pytorch.org/tutorials/recipes/torch_compile_caching_tutorial.html) and [Enabling cache for torch.compile](https://docs.sglang.io/references/torch_compile_cache.html).
|
||||
```bash
|
||||
# Node 0
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3-8B-Instruct \
|
||||
--tp 4 \
|
||||
--dist-init-addr sgl-dev-0:50000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 0
|
||||
|
||||
# Node 1
|
||||
python -m sglang.launch_server \
|
||||
--model-path meta-llama/Meta-Llama-3-8B-Instruct \
|
||||
--tp 4 \
|
||||
--dist-init-addr sgl-dev-0:50000 \
|
||||
--nnodes 2 \
|
||||
--node-rank 1
|
||||
```
|
||||
|
||||
Please consult the documentation below and [server_args.py](https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/server_args.py) to learn more about the arguments you may provide when launching a server.
|
||||
|
||||
## Model and tokenizer
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--model-path`<br>`--model` | The path of the model weights. This can be a local folder or a Hugging Face repo ID. | `None` | Type: str |
|
||||
| `--tokenizer-path` | The path of the tokenizer. | `None` | Type: str |
|
||||
| `--tokenizer-mode` | Tokenizer mode. 'auto' will use the fast tokenizer if available, and 'slow' will always use the slow tokenizer. | `auto` | `auto`, `slow` |
|
||||
| `--tokenizer-worker-num` | The worker num of the tokenizer manager. | `1` | Type: int |
|
||||
| `--skip-tokenizer-init` | If set, skip init tokenizer and pass input_ids in generate request. | `False` | bool flag (set to enable) |
|
||||
| `--load-format` | The format of the model weights to load. "auto" will try to load the weights in the safetensors format and fall back to the pytorch bin format if safetensors format is not available. "pt" will load the weights in the pytorch bin format. "safetensors" will load the weights in the safetensors format. "npcache" will load the weights in pytorch format and store a numpy cache to speed up the loading. "dummy" will initialize the weights with random values, which is mainly for profiling."gguf" will load the weights in the gguf format. "bitsandbytes" will load the weights using bitsandbytes quantization."layered" loads weights layer by layer so that one can quantize a layer before loading another to make the peak memory envelope smaller. "flash_rl" will load the weights in flash_rl format. "fastsafetensors" and "private" are also supported. "runai_streamer" enables direct model loading from object storage and shared file systems.| `auto` | `auto`, `pt`, `safetensors`, `npcache`, `dummy`, `sharded_state`, `gguf`, `bitsandbytes`, `layered`, `flash_rl`, `remote`, `remote_instance`, `fastsafetensors`, `private`, `runai_streamer` |
|
||||
| `--model-loader-extra-config` | Extra config for model loader. This will be passed to the model loader corresponding to the chosen load_format. | `{}` | Type: str |
|
||||
| `--trust-remote-code` | Whether or not to allow for custom models defined on the Hub in their own modeling files. | `False` | bool flag (set to enable) |
|
||||
| `--context-length` | The model's maximum context length. Defaults to None (will use the value from the model's config.json instead). | `None` | Type: int |
|
||||
| `--is-embedding` | Whether to use a CausalLM as an embedding model. | `False` | bool flag (set to enable) |
|
||||
| `--enable-multimodal` | Enable the multimodal functionality for the served model. If the model being served is not multimodal, nothing will happen | `None` | bool flag (set to enable) |
|
||||
| `--revision` | The specific model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version. | `None` | Type: str |
|
||||
| `--model-impl` | Which implementation of the model to use. * "auto" will try to use the SGLang implementation if it exists and fall back to the Transformers implementation if no SGLang implementation is available. * "sglang" will use the SGLang model implementation. * "transformers" will use the Transformers model implementation. | `auto` | Type: str |
|
||||
|
||||
## HTTP server
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--host` | The host of the HTTP server. | `127.0.0.1` | Type: str |
|
||||
| `--port` | The port of the HTTP server. | `30000` | Type: int |
|
||||
| `--fastapi-root-path` | App is behind a path based routing proxy. | `""` | Type: str |
|
||||
| `--grpc-mode` | If set, use gRPC server instead of HTTP server. | `False` | bool flag (set to enable) |
|
||||
| `--skip-server-warmup` | If set, skip warmup. | `False` | bool flag (set to enable) |
|
||||
| `--warmups` | Specify custom warmup functions (csv) to run before server starts eg. --warmups=warmup_name1,warmup_name2 will run the functions `warmup_name1` and `warmup_name2` specified in warmup.py before the server starts listening for requests | `None` | Type: str |
|
||||
| `--nccl-port` | The port for NCCL distributed environment setup. Defaults to a random port. | `None` | Type: int |
|
||||
| `--checkpoint-engine-wait-weights-before-ready` | If set, the server will wait for initial weights to be loaded via checkpoint-engine or other update methods before serving inference requests. | `False` | bool flag (set to enable) |
|
||||
|
||||
## Quantization and data type
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--dtype` | Data type for model weights and activations. * "auto" will use FP16 precision for FP32 and FP16 models, and BF16 precision for BF16 models. * "half" for FP16. Recommended for AWQ quantization. * "float16" is the same as "half". * "bfloat16" for a balance between precision and range. * "float" is shorthand for FP32 precision. * "float32" for FP32 precision. | `auto` | `auto`, `half`, `float16`, `bfloat16`, `float`, `float32` |
|
||||
| `--quantization` | The quantization method. | `None` | `awq`, `fp8`, `gptq`, `marlin`, `gptq_marlin`, `awq_marlin`, `bitsandbytes`, `gguf`, `modelopt`, `modelopt_fp8`, `modelopt_fp4`, `petit_nvfp4`, `w8a8_int8`, `w8a8_fp8`, `moe_wna16`, `qoq`, `w4afp8`, `mxfp4`, `mxfp8`, `auto-round`, `compressed-tensors`, `modelslim`, `quark_int4fp8_moe` |
|
||||
| `--quantization-param-path` | Path to the JSON file containing the KV cache scaling factors. This should generally be supplied, when KV cache dtype is FP8. Otherwise, KV cache scaling factors default to 1.0, which may cause accuracy issues. | `None` | Type: Optional[str] |
|
||||
| `--kv-cache-dtype` | Data type for kv cache storage. "auto" will use model data type. "bf16" or "bfloat16" for BF16 KV cache. "fp8_e5m2" and "fp8_e4m3" are supported for CUDA 11.8+. "fp4_e2m1" (only mxfp4) is supported for CUDA 12.8+ and PyTorch 2.8.0+ | `auto` | `auto`, `fp8_e5m2`, `fp8_e4m3`, `bf16`, `bfloat16`, `fp4_e2m1` |
|
||||
| `--enable-fp32-lm-head` | If set, the LM head outputs (logits) are in FP32. | `False` | bool flag (set to enable) |
|
||||
| `--modelopt-quant` | The ModelOpt quantization configuration. Supported values: 'fp8', 'int4_awq', 'w4a8_awq', 'nvfp4', 'nvfp4_awq'. This requires the NVIDIA Model Optimizer library to be installed: pip install nvidia-modelopt | `None` | Type: str |
|
||||
| `--modelopt-checkpoint-restore-path` | Path to restore a previously saved ModelOpt quantized checkpoint. If provided, the quantization process will be skipped and the model will be loaded from this checkpoint. | `None` | Type: str |
|
||||
| `--modelopt-checkpoint-save-path` | Path to save the ModelOpt quantized checkpoint after quantization. This allows reusing the quantized model in future runs. | `None` | Type: str |
|
||||
| `--modelopt-export-path` | Path to export the quantized model in HuggingFace format after ModelOpt quantization. The exported model can then be used directly with SGLang for inference. If not provided, the model will not be exported. | `None` | Type: str |
|
||||
| `--quantize-and-serve` | Quantize the model with ModelOpt and immediately serve it without exporting. This is useful for development and prototyping. For production, it's recommended to use separate quantization and deployment steps. | `False` | bool flag (set to enable) |
|
||||
| `--rl-quant-profile` | Path to the FlashRL quantization profile. Required when using --load-format flash_rl. | `None` | Type: str |
|
||||
|
||||
## Memory and scheduling
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--mem-fraction-static` | The fraction of the memory used for static allocation (model weights and KV cache memory pool). Use a smaller value if you see out-of-memory errors. | `None` | Type: float |
|
||||
| `--max-running-requests` | The maximum number of running requests. | `None` | Type: int |
|
||||
| `--max-queued-requests` | The maximum number of queued requests. This option is ignored when using disaggregation-mode. | `None` | Type: int |
|
||||
| `--max-total-tokens` | The maximum number of tokens in the memory pool. If not specified, it will be automatically calculated based on the memory usage fraction. This option is typically used for development and debugging purposes. | `None` | Type: int |
|
||||
| `--chunked-prefill-size` | The maximum number of tokens in a chunk for the chunked prefill. Setting this to -1 means disabling chunked prefill. | `None` | Type: int |
|
||||
| `--prefill-max-requests` | The maximum number of requests in a prefill batch. If not specified, there is no limit. | `None` | Type: int |
|
||||
| `--enable-dynamic-chunking` | Enable dynamic chunk size adjustment for pipeline parallelism. When enabled, chunk sizes are dynamically calculated based on fitted function to maintain consistent execution time across chunks. | `False` | bool flag (set to enable) |
|
||||
| `--max-prefill-tokens` | The maximum number of tokens in a prefill batch. The real bound will be the maximum of this value and the model's maximum context length. | `16384` | Type: int |
|
||||
| `--schedule-policy` | The scheduling policy of the requests. | `fcfs` | `lpm`, `random`, `fcfs`, `dfs-weight`, `lof`, `priority`, `routing-key` |
|
||||
| `--enable-priority-scheduling` | Enable priority scheduling. Requests with higher priority integer values will be scheduled first by default. | `False` | bool flag (set to enable) |
|
||||
| `--abort-on-priority-when-disabled` | If set, abort requests that specify a priority when priority scheduling is disabled. | `False` | bool flag (set to enable) |
|
||||
| `--schedule-low-priority-values-first` | If specified with --enable-priority-scheduling, the scheduler will schedule requests with lower priority integer values first. | `False` | bool flag (set to enable) |
|
||||
| `--priority-scheduling-preemption-threshold` | Minimum difference in priorities for an incoming request to have to preempt running request(s). | `10` | Type: int |
|
||||
| `--schedule-conservativeness` | How conservative the schedule policy is. A larger value means more conservative scheduling. Use a larger value if you see requests being retracted frequently. | `1.0` | Type: float |
|
||||
| `--page-size` | The number of tokens in a page. | `1` | Type: int |
|
||||
| `--swa-full-tokens-ratio` | The ratio of SWA layer KV tokens / full layer KV tokens, regardless of the number of swa:full layers. It should be between 0 and 1. E.g. 0.5 means if each swa layer has 50 tokens, then each full layer has 100 tokens. | `0.8` | Type: float |
|
||||
| `--disable-hybrid-swa-memory` | Disable the hybrid SWA memory. | `False` | bool flag (set to enable) |
|
||||
| `--radix-eviction-policy` | The eviction policy of radix trees. 'lru' stands for Least Recently Used, 'lfu' stands for Least Frequently Used. | `lru` | `lru`, `lfu` |
|
||||
| `--enable-prefill-delayer` | Enable prefill delayer for DP attention to reduce idle time. | `False` | bool flag (set to enable) |
|
||||
| `--prefill-delayer-max-delay-passes` | Maximum forward passes to delay prefill. | `30` | Type: int |
|
||||
| `--prefill-delayer-token-usage-low-watermark` | Token usage low watermark for prefill delayer. | `None` | Type: float |
|
||||
| `--prefill-delayer-forward-passes-buckets` | Custom buckets for prefill delayer forward passes histogram. 0 and max_delay_passes-1 will be auto-added. | `None` | List[float] |
|
||||
| `--prefill-delayer-wait-seconds-buckets` | Custom buckets for prefill delayer wait seconds histogram. 0 will be auto-added. | `None` | List[float] |
|
||||
|
||||
## Runtime options
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--device` | The device to use ('cuda', 'xpu', 'hpu', 'npu', 'cpu'). Defaults to auto-detection if not specified. | `None` | Type: str |
|
||||
| `--tensor-parallel-size`<br>`--tp-size` | The tensor parallelism size. | `1` | Type: int |
|
||||
| `--pipeline-parallel-size`<br>`--pp-size` | The pipeline parallelism size. | `1` | Type: int |
|
||||
| `--attention-context-parallel-size`<br>`--attn-cp-size`| The attention context parallelism size. | `1` | Type: int|
|
||||
| `--moe-data-parallel-size`<br>`--moe-dp-size`| The moe data parallelism size. | `1` | Type: int|
|
||||
| `--pp-max-micro-batch-size` | The maximum micro batch size in pipeline parallelism. | `None` | Type: int |
|
||||
| `--pp-async-batch-depth` | The async batch depth of pipeline parallelism. | `0` | Type: int |
|
||||
| `--stream-interval` | The interval (or buffer size) for streaming in terms of the token length. A smaller value makes streaming smoother, while a larger value makes the throughput higher | `1` | Type: int |
|
||||
| `--incremental-streaming-output` | Whether to output as a sequence of disjoint segments. | `False` | bool flag (set to enable) |
|
||||
| `--random-seed` | The random seed. | `None` | Type: int |
|
||||
| `--constrained-json-whitespace-pattern` | (outlines and llguidance backends only) Regex pattern for syntactic whitespaces allowed in JSON constrained output. For example, to allow the model to generate consecutive whitespaces, set the pattern to [\n\t ]* | `None` | Type: str |
|
||||
| `--constrained-json-disable-any-whitespace` | (xgrammar and llguidance backends only) Enforce compact representation in JSON constrained output. | `False` | bool flag (set to enable) |
|
||||
| `--watchdog-timeout` | Set watchdog timeout in seconds. If a forward batch takes longer than this, the server will crash to prevent hanging. | `300` | Type: float |
|
||||
| `--soft-watchdog-timeout` | Set soft watchdog timeout in seconds. If a forward batch takes longer than this, the server will dump information for debugging. | `None` | Type: float |
|
||||
| `--dist-timeout` | Set timeout for torch.distributed initialization. | `None` | Type: int |
|
||||
| `--download-dir` | Model download directory for huggingface. | `None` | Type: str |
|
||||
| `--model-checksum` | Model file integrity verification. If provided without value, uses model-path as HF repo ID. Otherwise, provide checksums JSON file path or HuggingFace repo ID. | `None` | Type: str |
|
||||
| `--base-gpu-id` | The base GPU ID to start allocating GPUs from. Useful when running multiple instances on the same machine. | `0` | Type: int |
|
||||
| `--gpu-id-step` | The delta between consecutive GPU IDs that are used. For example, setting it to 2 will use GPU 0,2,4,... | `1` | Type: int |
|
||||
| `--sleep-on-idle` | Reduce CPU usage when sglang is idle. | `False` | bool flag (set to enable) |
|
||||
| `--custom-sigquit-handler` | Register a custom sigquit handler so you can do additional cleanup after the server is shutdown. This is only available for Engine, not for CLI. | `None` | Type: str |
|
||||
|
||||
## Logging
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--log-level` | The logging level of all loggers. | `info` | Type: str |
|
||||
| `--log-level-http` | The logging level of HTTP server. If not set, reuse --log-level by default. | `None` | Type: str |
|
||||
| `--log-requests` | Log metadata, inputs, outputs of all requests. The verbosity is decided by --log-requests-level | `False` | bool flag (set to enable) |
|
||||
| `--log-requests-level` | 0: Log metadata (no sampling parameters). 1: Log metadata and sampling parameters. 2: Log metadata, sampling parameters and partial input/output. 3: Log every input/output. | `2` | `0`, `1`, `2`, `3` |
|
||||
| `--log-requests-format` | Format for request logging: 'text' (human-readable) or 'json' (structured) | `text` | `text`, `json` |
|
||||
| `--log-requests-target` | Target(s) for request logging: 'stdout' and/or directory path(s) for file output. Can specify multiple targets, e.g., '--log-requests-target stdout /my/path'. | `None` | List[str] |
|
||||
| `--uvicorn-access-log-exclude-prefixes` | Exclude uvicorn access logs whose request path starts with any of these prefixes. Defaults to empty (disabled). | `[]` | List[str] |
|
||||
| `--crash-dump-folder` | Folder path to dump requests from the last 5 min before a crash (if any). If not specified, crash dumping is disabled. | `None` | Type: str |
|
||||
| `--show-time-cost` | Show time cost of custom marks. | `False` | bool flag (set to enable) |
|
||||
| `--enable-metrics` | Enable log prometheus metrics. | `False` | bool flag (set to enable) |
|
||||
| `--enable-mfu-metrics` | Enable estimated MFU-related prometheus metrics. | `False` | bool flag (set to enable) |
|
||||
| `--enable-metrics-for-all-schedulers` | Enable --enable-metrics-for-all-schedulers when you want schedulers on all TP ranks (not just TP 0) to record request metrics separately. This is especially useful when dp_attention is enabled, as otherwise all metrics appear to come from TP 0. | `False` | bool flag (set to enable) |
|
||||
| `--tokenizer-metrics-custom-labels-header` | Specify the HTTP header for passing custom labels for tokenizer metrics. | `x-custom-labels` | Type: str |
|
||||
| `--tokenizer-metrics-allowed-custom-labels` | The custom labels allowed for tokenizer metrics. The labels are specified via a dict in '--tokenizer-metrics-custom-labels-header' field in HTTP requests, e.g., {'label1': 'value1', 'label2': 'value2'} is allowed if '--tokenizer-metrics-allowed-custom-labels label1 label2' is set. | `None` | List[str] |
|
||||
| `--bucket-time-to-first-token` | The buckets of time to first token, specified as a list of floats. | `None` | List[float] |
|
||||
| `--bucket-inter-token-latency` | The buckets of inter-token latency, specified as a list of floats. | `None` | List[float] |
|
||||
| `--bucket-e2e-request-latency` | The buckets of end-to-end request latency, specified as a list of floats. | `None` | List[float] |
|
||||
| `--collect-tokens-histogram` | Collect prompt/generation tokens histogram. | `False` | bool flag (set to enable) |
|
||||
| `--prompt-tokens-buckets` | The buckets rule of prompt tokens. Supports 3 rule types: 'default' uses predefined buckets; 'tse <middle> <base> <count>' generates two sides exponential distributed buckets (e.g., 'tse 1000 2 8' generates buckets [984.0, 992.0, 996.0, 998.0, 1000.0, 1002.0, 1004.0, 1008.0, 1016.0]).); 'custom <value1> <value2> ...' uses custom bucket values (e.g., 'custom 10 50 100 500'). | `None` | List[str] |
|
||||
| `--generation-tokens-buckets` | The buckets rule for generation tokens histogram. Supports 3 rule types: 'default' uses predefined buckets; 'tse <middle> <base> <count>' generates two sides exponential distributed buckets (e.g., 'tse 1000 2 8' generates buckets [984.0, 992.0, 996.0, 998.0, 1000.0, 1002.0, 1004.0, 1008.0, 1016.0]).); 'custom <value1> <value2> ...' uses custom bucket values (e.g., 'custom 10 50 100 500'). | `None` | List[str] |
|
||||
| `--gc-warning-threshold-secs` | The threshold for long GC warning. If a GC takes longer than this, a warning will be logged. Set to 0 to disable. | `0.0` | Type: float |
|
||||
| `--decode-log-interval` | The log interval of decode batch. | `40` | Type: int |
|
||||
| `--enable-request-time-stats-logging` | Enable per request time stats logging | `False` | bool flag (set to enable) |
|
||||
| `--kv-events-config` | Config in json format for NVIDIA dynamo KV event publishing. Publishing will be enabled if this flag is used. | `None` | Type: str |
|
||||
| `--enable-trace` | Enable opentelemetry trace | `False` | bool flag (set to enable) |
|
||||
| `--otlp-traces-endpoint` | Config opentelemetry collector endpoint if --enable-trace is set. format: <ip>:<port> | `localhost:4317` | Type: str |
|
||||
|
||||
## RequestMetricsExporter configuration
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--export-metrics-to-file` | Export performance metrics for each request to local file (e.g. for forwarding to external systems). | `False` | bool flag (set to enable) |
|
||||
| `--export-metrics-to-file-dir` | Directory path for writing performance metrics files (required when --export-metrics-to-file is enabled). | `None` | Type: str |
|
||||
|
||||
## API related
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--api-key` | Set API key of the server. It is also used in the OpenAI API compatible server. | `None` | Type: str |
|
||||
| `--admin-api-key` | Set **admin API key** for administrative/control endpoints (e.g., weights update, cache flush, `/server_info`). Endpoints marked as admin-only require `Authorization: Bearer <admin_api_key>` when this is set. | `None` | Type: str |
|
||||
| `--served-model-name` | Override the model name returned by the v1/models endpoint in OpenAI API server. | `None` | Type: str |
|
||||
| `--weight-version` | Version identifier for the model weights. Defaults to 'default' if not specified. | `default` | Type: str |
|
||||
| `--chat-template` | The builtin chat template name or the path of the chat template file. This is only used for OpenAI-compatible API server. | `None` | Type: str |
|
||||
| `--hf-chat-template-name` | When the HuggingFace tokenizer has multiple chat templates (e.g., 'default', 'tool_use', 'rag'), specify which named template to use. If not set, the first available template is used. | `None` | Type: str |
|
||||
| `--completion-template` | The builtin completion template name or the path of the completion template file. This is only used for OpenAI-compatible API server. only for code completion currently. | `None` | Type: str |
|
||||
| `--file-storage-path` | The path of the file storage in backend. | `sglang_storage` | Type: str |
|
||||
| `--enable-cache-report` | Return number of cached tokens in usage.prompt_tokens_details for each openai request. | `False` | bool flag (set to enable) |
|
||||
| `--reasoning-parser` | Specify the parser for reasoning models. Supported parsers: [deepseek-r1, deepseek-v3, glm45, gpt-oss, kimi, qwen3, qwen3-thinking, step3]. | `None` | `deepseek-r1`, `deepseek-v3`, `glm45`, `gpt-oss`, `kimi`, `qwen3`, `qwen3-thinking`, `step3` |
|
||||
| `--tool-call-parser` | Specify the parser for handling tool-call interactions. Supported parsers: [deepseekv3, deepseekv31, glm, glm45, glm47, gpt-oss, kimi_k2, llama3, mistral, pythonic, qwen, qwen25, qwen3_coder, step3]. | `None` | `deepseekv3`, `deepseekv31`, `glm`, `glm45`, `glm47`, `gpt-oss`, `kimi_k2`, `llama3`, `mistral`, `pythonic`, `qwen`, `qwen25`, `qwen3_coder`, `step3`, `gigachat3` |
|
||||
| `--tool-server` | Either 'demo' or a comma-separated list of tool server urls to use for the model. If not specified, no tool server will be used. | `None` | Type: str |
|
||||
| `--sampling-defaults` | Where to get default sampling parameters. 'openai' uses SGLang/OpenAI defaults (temperature=1.0, top_p=1.0, etc.). 'model' uses the model's generation_config.json to get the recommended sampling parameters if available. Default is 'model'. | `model` | `openai`, `model` |
|
||||
|
||||
## Data parallelism
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--data-parallel-size`<br>`--dp-size` | The data parallelism size. | `1` | Type: int |
|
||||
| `--load-balance-method` | The load balancing strategy for data parallelism. The `total_tokens` algorithm can only be used when DP attention is applied. This algorithm performs load balancing based on the real-time token load of the DP workers. | `auto` | `auto`, `round_robin`, `follow_bootstrap_room`, `total_requests`, `total_tokens` |
|
||||
|
||||
## Multi-node distributed serving
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--dist-init-addr`<br>`--nccl-init-addr` | The host address for initializing distributed backend (e.g., `192.168.0.2:25000`). | `None` | Type: str |
|
||||
| `--nnodes` | The number of nodes. | `1` | Type: int |
|
||||
| `--node-rank` | The node rank. | `0` | Type: int |
|
||||
|
||||
## Model override args
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--json-model-override-args` | A dictionary in JSON string format used to override default model configurations. | `{}` | Type: str |
|
||||
| `--preferred-sampling-params` | json-formatted sampling settings that will be returned in /get_model_info | `None` | Type: str |
|
||||
|
||||
## LoRA
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-lora` | Enable LoRA support for the model. This argument is automatically set to `True` if `--lora-paths` is provided for backward compatibility. | `False` | Bool flag (set to enable) |
|
||||
| `--enable-lora-overlap-loading` | Enable asynchronous LoRA weight loading in order to overlap H2D transfers with GPU compute. This should be enabled if you find that your LoRA workloads are bottlenecked by adapter weight loading, for example when frequently loading large LoRA adapters. | `False` | Bool flag (set to enable)
|
||||
| `--max-lora-rank` | The maximum LoRA rank that should be supported. If not specified, it will be automatically inferred from the adapters provided in `--lora-paths`. This argument is needed when you expect to dynamically load adapters of larger LoRA rank after server startup. | `None` | Type: int |
|
||||
| `--lora-target-modules` | The union set of all target modules where LoRA should be applied (e.g., `q_proj`, `k_proj`, `gate_proj`). If not specified, it will be automatically inferred from the adapters provided in `--lora-paths`. You can also set it to `all` to enable LoRA for all supported modules; note this may introduce minor performance overhead. | `None` | `q_proj`, `k_proj`, `v_proj`, `o_proj`, `gate_proj`, `up_proj`, `down_proj`, `qkv_proj`, `gate_up_proj`, `all` |
|
||||
| `--lora-paths` | The list of LoRA adapters to load. Each adapter must be specified in one of the following formats: `<PATH>` \| `<NAME>=<PATH>` \| JSON with schema `{"lora_name": str, "lora_path": str, "pinned": bool}`. | `None` | Type: List[str] / JSON objects |
|
||||
| `--max-loras-per-batch` | Maximum number of adapters for a running batch, including base-only requests. | `8` | Type: int |
|
||||
| `--max-loaded-loras` | If specified, limits the maximum number of LoRA adapters loaded in CPU memory at a time. Must be ≥ `--max-loras-per-batch`. | `None` | Type: int |
|
||||
| `--lora-eviction-policy` | LoRA adapter eviction policy when the GPU memory pool is full. | `lru` | `lru`, `fifo` |
|
||||
| `--lora-backend` | Choose the kernel backend for multi-LoRA serving. | `csgmv` | `triton`, `csgmv`, `ascend`, `torch_native` |
|
||||
| `--max-lora-chunk-size` | Maximum chunk size for the ChunkedSGMV LoRA backend. Only used when `--lora-backend` is `csgmv`. Larger values may improve performance. | `16` | `16`, `32`, `64`, `128` |
|
||||
|
||||
## Kernel Backends (Attention, Sampling, Grammar, GEMM)
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--attention-backend` | Choose the kernels for attention layers. | `None` | `triton`, `torch_native`, `flex_attention`, `nsa`, `cutlass_mla`, `fa3`, `fa4`, `flashinfer`, `flashmla`, `trtllm_mla`, `trtllm_mha`, `dual_chunk_flash_attn`, `aiter`, `wave`, `intel_amx`, `ascend` |
|
||||
| `--prefill-attention-backend` | Choose the kernels for prefill attention layers (have priority over --attention-backend). | `None` | `triton`, `torch_native`, `flex_attention`, `nsa`, `cutlass_mla`, `fa3`, `fa4`, `flashinfer`, `flashmla`, `trtllm_mla`, `trtllm_mha`, `dual_chunk_flash_attn`, `aiter`, `wave`, `intel_amx`, `ascend` |
|
||||
| `--decode-attention-backend` | Choose the kernels for decode attention layers (have priority over --attention-backend). | `None` | `triton`, `torch_native`, `flex_attention`, `nsa`, `cutlass_mla`, `fa3`, `fa4`, `flashinfer`, `flashmla`, `trtllm_mla`, `trtllm_mha`, `dual_chunk_flash_attn`, `aiter`, `wave`, `intel_amx`, `ascend` |
|
||||
| `--sampling-backend` | Choose the kernels for sampling layers. | `None` | `flashinfer`, `pytorch`, `ascend` |
|
||||
| `--grammar-backend` | Choose the backend for grammar-guided decoding. | `None` | `xgrammar`, `outlines`, `llguidance`, `none` |
|
||||
| `--mm-attention-backend` | Set multimodal attention backend. | `None` | `sdpa`, `fa3`, `fa4`, `triton_attn`, `ascend_attn`, `aiter_attn` |
|
||||
| `--nsa-prefill-backend` | Choose the NSA backend for the prefill stage (overrides `--attention-backend` when running DeepSeek NSA-style attention). | `flashmla_sparse` | `flashmla_sparse`, `flashmla_kv`, `flashmla_auto`, `fa3`, `tilelang`, `aiter`, `trtllm` |
|
||||
| `--nsa-decode-backend` | Choose the NSA backend for the decode stage when running DeepSeek NSA-style attention. Overrides `--attention-backend` for decoding. | `fa3` | `flashmla_sparse`, `flashmla_kv`, `fa3`, `tilelang`, `aiter`, `trtllm` |
|
||||
| `--fp8-gemm-backend` | Choose the runner backend for Blockwise FP8 GEMM operations. Options: 'auto' (default, auto-selects based on hardware), 'deep_gemm' (JIT-compiled; enabled by default on NVIDIA Hopper (SM90) and Blackwell (SM100) when DeepGEMM is installed), 'flashinfer_trtllm' (FlashInfer TRTLLM backend; SM100/SM103 only), 'flashinfer_cutlass' (FlashInfer CUTLASS backend, SM120 only), 'flashinfer_deepgemm' (Hopper SM90 only, uses swapAB optimization for small M dimensions in decoding), 'cutlass' (optimal for Hopper/Blackwell GPUs and high-throughput), 'triton' (fallback, widely compatible), 'aiter' (ROCm only).| `auto` | `auto`, `deep_gemm`, `flashinfer_trtllm`, `flashinfer_cutlass`, `flashinfer_deepgemm`, `cutlass`, `triton`, `aiter` |
|
||||
| `--fp4-gemm-backend` | Choose the runner backend for NVFP4 GEMM operations. Options: 'flashinfer_cutlass' (default), 'auto' (auto-selects between flashinfer_cudnn/flashinfer_cutlass based on CUDA/cuDNN version), 'flashinfer_cudnn' (FlashInfer cuDNN backend, optimal on CUDA 13+ with cuDNN 9.15+), 'flashinfer_trtllm' (FlashInfer TensorRT-LLM backend, requires different weight preparation with shuffling). All backends are from FlashInfer; when FlashInfer is unavailable, sgl-kernel CUTLASS is used as an automatic fallback.| `flashinfer_cutlass` | `auto`, `flashinfer_cudnn`, `flashinfer_cutlass`, `flashinfer_trtllm` |
|
||||
| `--disable-flashinfer-autotune` | Flashinfer autotune is enabled by default. Set this flag to disable the autotune. | `False` | bool flag (set to enable) |
|
||||
|
||||
## Speculative decoding
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--speculative-algorithm` | Speculative algorithm. | `None` | `EAGLE`, `EAGLE3`, `NEXTN`, `STANDALONE`, `NGRAM` |
|
||||
| `--speculative-draft-model-path`<br>`--speculative-draft-model` | The path of the draft model weights. This can be a local folder or a Hugging Face repo ID. | `None` | Type: str |
|
||||
| `--speculative-draft-model-revision` | The specific draft model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version. | `None` | Type: str |
|
||||
| `--speculative-draft-load-format` | The format of the draft model weights to load. If not specified, will use the same format as --load-format. Use 'dummy' to initialize draft model weights with random values for profiling. | `None` | Same as --load-format options |
|
||||
| `--speculative-num-steps` | The number of steps sampled from draft model in Speculative Decoding. | `None` | Type: int |
|
||||
| `--speculative-eagle-topk` | The number of tokens sampled from the draft model in eagle2 each step. | `None` | Type: int |
|
||||
| `--speculative-num-draft-tokens` | The number of tokens sampled from the draft model in Speculative Decoding. | `None` | Type: int |
|
||||
| `--speculative-accept-threshold-single` | Accept a draft token if its probability in the target model is greater than this threshold. | `1.0` | Type: float |
|
||||
| `--speculative-accept-threshold-acc` | The accept probability of a draft token is raised from its target probability p to min(1, p / threshold_acc). | `1.0` | Type: float |
|
||||
| `--speculative-token-map` | The path of the draft model's small vocab table. | `None` | Type: str |
|
||||
| `--speculative-attention-mode` | Attention backend for speculative decoding operations (both target verify and draft extend). Can be one of 'prefill' (default) or 'decode'. | `prefill` | `prefill`, `decode` |
|
||||
| `--speculative-draft-attention-backend` | Attention backend for speculative decoding drafting. | `None` | Same as attention backend options |
|
||||
| `--speculative-moe-runner-backend` | MOE backend for EAGLE speculative decoding, see --moe-runner-backend for options. Same as moe runner backend if unset. | `None` | Same as --moe-runner-backend options |
|
||||
| `--speculative-moe-a2a-backend` | MOE A2A backend for EAGLE speculative decoding, see --moe-a2a-backend for options. Same as moe a2a backend if unset. | `None` | Same as --moe-a2a-backend options |
|
||||
| `--speculative-draft-model-quantization` | The quantization method for speculative model. | `None` | Same as --quantization options |
|
||||
|
||||
## Ngram speculative decoding
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--speculative-ngram-min-bfs-breadth` | The minimum breadth for BFS (Breadth-First Search) in ngram speculative decoding. | `1` | Type: int |
|
||||
| `--speculative-ngram-max-bfs-breadth` | The maximum breadth for BFS (Breadth-First Search) in ngram speculative decoding. | `10` | Type: int |
|
||||
| `--speculative-ngram-match-type` | Ngram tree-building mode. `BFS` selects recency-based expansion and `PROB` selects frequency-based expansion. This setting is forwarded to the ngram cache implementation. | `BFS` | `BFS`, `PROB` |
|
||||
| `--speculative-ngram-max-trie-depth` | Maximum suffix length stored and matched by the ngram trie. | `18` | Type: int |
|
||||
| `--speculative-ngram-capacity` | The cache capacity for ngram speculative decoding. | `10000000` | Type: int |
|
||||
|
||||
## Multi-layer Eagle speculative decoding
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-multi-layer-eagle` | Enable multi-layer Eagle speculative decoding. | `False` | bool flag (set to enable) |
|
||||
|
||||
## MoE
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--expert-parallel-size`<br>`--ep-size`<br>`--ep` | The expert parallelism size. | `1` | Type: int |
|
||||
| `--moe-a2a-backend` | Select the backend for all-to-all communication for expert parallelism. | `none` | `none`, `deepep`, `mooncake`, `mori`, `nixl`, `ascend_fuseep`|
|
||||
| `--moe-runner-backend` | Choose the runner backend for MoE. | `auto` | `auto`, `deep_gemm`, `triton`, `triton_kernel`, `flashinfer_trtllm`, `flashinfer_trtllm_routed`, `flashinfer_cutlass`, `flashinfer_mxfp4`, `flashinfer_cutedsl`, `cutlass` |
|
||||
| `--flashinfer-mxfp4-moe-precision` | Choose the computation precision of flashinfer mxfp4 moe | `default` | `default`, `bf16` |
|
||||
| `--enable-flashinfer-allreduce-fusion` | Enable FlashInfer allreduce fusion with Residual RMSNorm. | `False` | bool flag (set to enable) |
|
||||
| `--enable-aiter-allreduce-fusion` | Enable aiter allreduce fusion with Residual RMSNorm. | `False` | bool flag (set to enable) |
|
||||
| `--deepep-mode` | Select the mode when enable DeepEP MoE, could be `normal`, `low_latency` or `auto`. Default is `auto`, which means `low_latency` for decode batch and `normal` for prefill batch. | `auto` | `normal`, `low_latency`, `auto` |
|
||||
| `--ep-num-redundant-experts` | Allocate this number of redundant experts in expert parallel. | `0` | Type: int |
|
||||
| `--ep-dispatch-algorithm` | The algorithm to choose ranks for redundant experts in expert parallel. | `None` | Type: str |
|
||||
| `--init-expert-location` | Initial location of EP experts. | `trivial` | Type: str |
|
||||
| `--enable-eplb` | Enable EPLB algorithm | `False` | bool flag (set to enable) |
|
||||
| `--eplb-algorithm` | Chosen EPLB algorithm | `auto` | Type: str |
|
||||
| `--eplb-rebalance-num-iterations` | Number of iterations to automatically trigger a EPLB re-balance. | `1000` | Type: int |
|
||||
| `--eplb-rebalance-layers-per-chunk` | Number of layers to rebalance per forward pass. | `None` | Type: int |
|
||||
| `--eplb-min-rebalancing-utilization-threshold` | Minimum threshold for GPU average utilization to trigger EPLB rebalancing. Must be in the range [0.0, 1.0]. | `1.0` | Type: float |
|
||||
| `--expert-distribution-recorder-mode` | Mode of expert distribution recorder. | `None` | Type: str |
|
||||
| `--expert-distribution-recorder-buffer-size` | Circular buffer size of expert distribution recorder. Set to -1 to denote infinite buffer. | `None` | Type: int |
|
||||
| `--enable-expert-distribution-metrics` | Enable logging metrics for expert balancedness | `False` | bool flag (set to enable) |
|
||||
| `--deepep-config` | Tuned DeepEP config suitable for your own cluster. It can be either a string with JSON content or a file path. | `None` | Type: str |
|
||||
| `--moe-dense-tp-size` | TP size for MoE dense MLP layers. This flag is useful when, with large TP size, there are errors caused by weights in MLP layers having dimension smaller than the min dimension GEMM supports. | `None` | Type: int |
|
||||
| `--elastic-ep-backend` | Specify the collective communication backend for elastic EP. Currently supports 'mooncake'. | `none` | `none`, `mooncake` |
|
||||
| `--enable-elastic-expert-backup` | Enable elastic EP backend to backup expert weights in DRAM feature. Currently supports 'mooncake'.| `False` | bool flag (set to enable) |
|
||||
| `--mooncake-ib-device` | The InfiniBand devices for Mooncake Backend transfer, accepts multiple comma-separated devices (e.g., --mooncake-ib-device mlx5_0,mlx5_1). Default is None, which triggers automatic device detection when Mooncake Backend is enabled. | `None` | Type: str |
|
||||
|
||||
## Mamba Cache
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--max-mamba-cache-size` | The maximum size of the mamba cache. | `None` | Type: int |
|
||||
| `--mamba-ssm-dtype` | The data type of the SSM states in mamba cache. | `float32` | `float32`, `bfloat16`, `float16` |
|
||||
| `--mamba-full-memory-ratio` | The ratio of mamba state memory to full kv cache memory. | `0.9` | Type: float |
|
||||
| `--mamba-scheduler-strategy` | The strategy to use for mamba scheduler. `auto` currently defaults to `no_buffer`. 1. `no_buffer` does not support overlap scheduler due to not allocating extra mamba state buffers. Branching point caching support is feasible but not implemented. 2. `extra_buffer` supports overlap schedule by allocating extra mamba state buffers to track mamba state for caching (mamba state usage per running req becomes `2x` for non-spec; `1+(1/(2+speculative_num_draft_tokens))x` for spec dec (e.g. 1.16x if speculative_num_draft_tokens==4)). 2a. `extra_buffer` is strictly better for non-KV-cache-bound cases; for KV-cache-bound cases, the tradeoff depends on whether enabling overlap outweighs reduced max running requests. 2b. mamba caching at radix cache branching point is strictly better than non-branch but requires kernel support (currently only FLA backend), currently only extra_buffer supports branching. | `auto` | `auto`, `no_buffer`, `extra_buffer` |
|
||||
| `--mamba-track-interval` | The interval (in tokens) to track the mamba state during decode. Only used when `--mamba-scheduler-strategy` is `extra_buffer`. Must be divisible by page_size if set, and must be >= speculative_num_draft_tokens when using speculative decoding. | `256` | Type: int |
|
||||
|
||||
## Hierarchical cache
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-hierarchical-cache` | Enable hierarchical cache | `False` | bool flag (set to enable) |
|
||||
| `--hicache-ratio` | The ratio of the size of host KV cache memory pool to the size of device pool. | `2.0` | Type: float |
|
||||
| `--hicache-size` | The size of host KV cache memory pool in gigabytes, which will override the hicache_ratio if set. | `0` | Type: int |
|
||||
| `--hicache-write-policy` | The write policy of hierarchical cache. | `write_through` | `write_back`, `write_through`, `write_through_selective` |
|
||||
| `--hicache-io-backend` | The IO backend for KV cache transfer between CPU and GPU | `kernel` | `direct`, `kernel`, `kernel_ascend` |
|
||||
| `--hicache-mem-layout` | The layout of host memory pool for hierarchical cache. | `layer_first` | `layer_first`, `page_first`, `page_first_direct`, `page_first_kv_split`, `page_head` |
|
||||
| `--hicache-storage-backend` | The storage backend for hierarchical KV cache. Built-in backends: file, mooncake, hf3fs, nixl, aibrix. For dynamic backend, use --hicache-storage-backend-extra-config to specify: backend_name (custom name), module_path (Python module path), class_name (backend class name). | `None` | `file`, `mooncake`, `hf3fs`, `nixl`, `aibrix`, `dynamic`, `eic` |
|
||||
| `--hicache-storage-prefetch-policy` | Control when prefetching from the storage backend should stop. | `best_effort` | `best_effort`, `wait_complete`, `timeout` |
|
||||
| `--hicache-storage-backend-extra-config` | A dictionary in JSON string format, or a string starting with a `@` followed by a config file in JSON/YAML/TOML format, containing extra configuration for the storage backend. | `None` | Type: str |
|
||||
|
||||
## Hierarchical sparse attention
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--hierarchical-sparse-attention-extra-config` | A dictionary in JSON string format for hierarchical sparse attention configuration. Required fields: `algorithm` (str), `backend` (str). All other fields are algorithm-specific and passed to the algorithm constructor. | `None` | Type: str |
|
||||
|
||||
## LMCache
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-lmcache` | Using LMCache as an alternative hierarchical cache solution | `False` | bool flag (set to enable) |
|
||||
|
||||
## Ktransformers
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--kt-weight-path` | [ktransformers parameter] The path of the quantized expert weights for amx kernel. A local folder. | `None` | Type: str |
|
||||
| `--kt-method` | [ktransformers parameter] Quantization formats for CPU execution. | `AMXINT4` | Type: str |
|
||||
| `--kt-cpuinfer` | [ktransformers parameter] The number of CPUInfer threads. | `None` | Type: int |
|
||||
| `--kt-threadpool-count` | [ktransformers parameter] One-to-one with the number of NUMA nodes (one thread pool per NUMA). | `2` | Type: int |
|
||||
| `--kt-num-gpu-experts` | [ktransformers parameter] The number of GPU experts. | `None` | Type: int |
|
||||
| `--kt-max-deferred-experts-per-token` | [ktransformers parameter] Maximum number of experts deferred to CPU per token. All MoE layers except the final one use this value; the final layer always uses 0. | `None` | Type: int |
|
||||
|
||||
## Diffusion LLM
|
||||
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--dllm-algorithm` | The diffusion LLM algorithm, such as LowConfidence. | `None` | Type: str |
|
||||
| `--dllm-algorithm-config` | The diffusion LLM algorithm configurations. Must be a YAML file. | `None` | Type: str |
|
||||
|
||||
## Double Sparsity
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-double-sparsity` | Enable double sparsity attention | `False` | bool flag (set to enable) |
|
||||
| `--ds-channel-config-path` | The path of the double sparsity channel config | `None` | Type: str |
|
||||
| `--ds-heavy-channel-num` | The number of heavy channels in double sparsity attention | `32` | Type: int |
|
||||
| `--ds-heavy-token-num` | The number of heavy tokens in double sparsity attention | `256` | Type: int |
|
||||
| `--ds-heavy-channel-type` | The type of heavy channels in double sparsity attention | `qk` | Type: str |
|
||||
| `--ds-sparse-decode-threshold` | The minimum decode sequence length required before the double-sparsity backend switches from the dense fallback to the sparse decode kernel. | `4096` | Type: int |
|
||||
|
||||
## Offloading
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--cpu-offload-gb` | How many GBs of RAM to reserve for CPU offloading. | `0` | Type: int |
|
||||
| `--offload-group-size` | Number of layers per group in offloading. | `-1` | Type: int |
|
||||
| `--offload-num-in-group` | Number of layers to be offloaded within a group. | `1` | Type: int |
|
||||
| `--offload-prefetch-step` | Steps to prefetch in offloading. | `1` | Type: int |
|
||||
| `--offload-mode` | Mode of offloading. | `cpu` | Type: str |
|
||||
|
||||
## Args for multi-item scoring
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--multi-item-scoring-delimiter` | Delimiter token ID for multi-item scoring. Used to combine Query and Items into a single sequence: Query<delimiter>Item1<delimiter>Item2<delimiter>... This enables efficient batch processing of multiple items against a single query. | `None` | Type: int |
|
||||
|
||||
## Optimization/debug options
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--disable-radix-cache` | Disable RadixAttention for prefix caching. | `False` | bool flag (set to enable) |
|
||||
| `--cuda-graph-max-bs` | Set the maximum batch size for cuda graph. It will extend the cuda graph capture batch size to this value. | `None` | Type: int |
|
||||
| `--cuda-graph-bs` | Set the list of batch sizes for cuda graph. | `None` | List[int] |
|
||||
| `--disable-cuda-graph` | Disable cuda graph. | `False` | bool flag (set to enable) |
|
||||
| `--disable-cuda-graph-padding` | Disable cuda graph when padding is needed. Still uses cuda graph when padding is not needed. | `False` | bool flag (set to enable) |
|
||||
| `--enable-profile-cuda-graph` | Enable profiling of cuda graph capture. | `False` | bool flag (set to enable) |
|
||||
| `--enable-cudagraph-gc` | Enable garbage collection during CUDA graph capture. If disabled (default), GC is frozen during capture to speed up the process. | `False` | bool flag (set to enable) |
|
||||
| `--enable-layerwise-nvtx-marker` | Enable layerwise NVTX profiling annotations for the model. This adds NVTX markers to every layer for detailed per-layer performance analysis with Nsight Systems. | `False` | bool flag (set to enable) |
|
||||
| `--enable-nccl-nvls` | Enable NCCL NVLS for prefill heavy requests when available. | `False` | bool flag (set to enable) |
|
||||
| `--enable-symm-mem` | Enable NCCL symmetric memory for fast collectives. | `False` | bool flag (set to enable) |
|
||||
| `--disable-flashinfer-cutlass-moe-fp4-allgather` | Disables quantize before all-gather for flashinfer cutlass moe. | `False` | bool flag (set to enable) |
|
||||
| `--enable-tokenizer-batch-encode` | Enable batch tokenization for improved performance when processing multiple text inputs. Do not use with image inputs, pre-tokenized input_ids, or input_embeds. | `False` | bool flag (set to enable) |
|
||||
| `--disable-tokenizer-batch-decode` | Disable batch decoding when decoding multiple completions. | `False` | bool flag (set to enable) |
|
||||
| `--disable-outlines-disk-cache` | Disable disk cache of outlines to avoid possible crashes related to file system or high concurrency. | `False` | bool flag (set to enable) |
|
||||
| `--disable-custom-all-reduce` | Disable the custom all-reduce kernel and fall back to NCCL. | `False` | bool flag (set to enable) |
|
||||
| `--enable-mscclpp` | Enable using mscclpp for small messages for all-reduce kernel and fall back to NCCL. | `False` | bool flag (set to enable) |
|
||||
| `--enable-torch-symm-mem` | Enable using torch symm mem for all-reduce kernel and fall back to NCCL. Only supports CUDA device SM90 and above. SM90 supports world size 4, 6, 8. SM10 supports world size 6, 8. | `False` | bool flag (set to enable) |
|
||||
| `--disable-overlap-schedule` | Disable the overlap scheduler, which overlaps the CPU scheduler with GPU model worker. | `False` | bool flag (set to enable) |
|
||||
| `--enable-mixed-chunk` | Enabling mixing prefill and decode in a batch when using chunked prefill. | `False` | bool flag (set to enable) |
|
||||
| `--enable-dp-attention` | Enabling data parallelism for attention and tensor parallelism for FFN. The dp size should be equal to the tp size. Currently DeepSeek-V2 and Qwen 2/3 MoE models are supported. | `False` | bool flag (set to enable) |
|
||||
| `--enable-dp-lm-head` | Enable vocabulary parallel across the attention TP group to avoid all-gather across DP groups, optimizing performance under DP attention. | `False` | bool flag (set to enable) |
|
||||
| `--enable-two-batch-overlap` | Enabling two micro batches to overlap. | `False` | bool flag (set to enable) |
|
||||
| `--enable-single-batch-overlap` | Let computation and communication overlap within one micro batch. | `False` | bool flag (set to enable) |
|
||||
| `--tbo-token-distribution-threshold` | The threshold of token distribution between two batches in micro-batch-overlap, determines whether to two-batch-overlap or two-chunk-overlap. Set to 0 denote disable two-chunk-overlap. | `0.48` | Type: float |
|
||||
| `--enable-torch-compile` | Optimize the model with torch.compile. Experimental feature. | `False` | bool flag (set to enable) |
|
||||
| `--enable-torch-compile-debug-mode` | Enable debug mode for torch compile. | `False` | bool flag (set to enable) |
|
||||
| `--disable-piecewise-cuda-graph` | Disable piecewise cuda graph for extend/prefill. PCG is enabled by default. | `False` | bool flag (set to disable) |
|
||||
| `--enforce-piecewise-cuda-graph` | Enforce piecewise cuda graph, skipping all auto-disable conditions. For testing only. | `False` | bool flag (set to enable) |
|
||||
| `--piecewise-cuda-graph-tokens` | Set the list of tokens when using piecewise cuda graph. | `None` | Type: JSON list |
|
||||
| `--piecewise-cuda-graph-compiler` | Set the compiler for piecewise cuda graph. Choices are: eager, inductor. | `eager` | `eager`, `inductor` |
|
||||
| `--torch-compile-max-bs` | Set the maximum batch size when using torch compile. | `32` | Type: int |
|
||||
| `--piecewise-cuda-graph-max-tokens` | Set the maximum tokens when using piecewise cuda graph. | `4096` | Type: int |
|
||||
| `--torchao-config` | Optimize the model with torchao. Experimental feature. Current choices are: int8dq, int8wo, int4wo-<group_size>, fp8wo, fp8dq-per_tensor, fp8dq-per_row | `` | Type: str |
|
||||
| `--enable-nan-detection` | Enable the NaN detection for debugging purposes. | `False` | bool flag (set to enable) |
|
||||
| `--enable-p2p-check` | Enable P2P check for GPU access, otherwise the p2p access is allowed by default. | `False` | bool flag (set to enable) |
|
||||
| `--triton-attention-reduce-in-fp32` | Cast the intermediate attention results to fp32 to avoid possible crashes related to fp16. This only affects Triton attention kernels. | `False` | bool flag (set to enable) |
|
||||
| `--triton-attention-num-kv-splits` | The number of KV splits in flash decoding Triton kernel. Larger value is better in longer context scenarios. The default value is 8. | `8` | Type: int |
|
||||
| `--triton-attention-split-tile-size` | The size of split KV tile in flash decoding Triton kernel. Used for deterministic inference. | `None` | Type: int |
|
||||
| `--num-continuous-decode-steps` | Run multiple continuous decoding steps to reduce scheduling overhead. This can potentially increase throughput but may also increase time-to-first-token latency. The default value is 1, meaning only run one decoding step at a time. | `1` | Type: int |
|
||||
| `--delete-ckpt-after-loading` | Delete the model checkpoint after loading the model. | `False` | bool flag (set to enable) |
|
||||
| `--enable-memory-saver` | Allow saving memory using release_memory_occupation and resume_memory_occupation | `False` | bool flag (set to enable) |
|
||||
| `--enable-weights-cpu-backup` | Save model weights to CPU memory during release_weights_occupation and resume_weights_occupation | `False` | bool flag (set to enable) |
|
||||
| `--enable-draft-weights-cpu-backup` | Save draft model weights to CPU memory during release_weights_occupation and resume_weights_occupation | `False` | bool flag (set to enable) |
|
||||
| `--allow-auto-truncate` | Allow automatically truncating requests that exceed the maximum input length instead of returning an error. | `False` | bool flag (set to enable) |
|
||||
| `--enable-custom-logit-processor` | Enable users to pass custom logit processors to the server (disabled by default for security) | `False` | bool flag (set to enable) |
|
||||
| `--flashinfer-mla-disable-ragged` | Not using ragged prefill wrapper when running flashinfer mla | `False` | bool flag (set to enable) |
|
||||
| `--disable-shared-experts-fusion` | Disable shared experts fusion optimization for deepseek v3/r1. | `False` | bool flag (set to enable) |
|
||||
| `--disable-chunked-prefix-cache` | Disable chunked prefix cache feature for deepseek, which should save overhead for short sequences. | `False` | bool flag (set to enable) |
|
||||
| `--disable-fast-image-processor` | Adopt base image processor instead of fast image processor. | `False` | bool flag (set to enable) |
|
||||
| `--keep-mm-feature-on-device` | Keep multimodal feature tensors on device after processing to save D2H copy. | `False` | bool flag (set to enable) |
|
||||
| `--enable-return-hidden-states` | Enable returning hidden states with responses. | `False` | bool flag (set to enable) |
|
||||
| `--enable-return-routed-experts` | Enable returning routed experts of each layer with responses. | `False` | bool flag (set to enable) |
|
||||
| `--scheduler-recv-interval` | The interval to poll requests in scheduler. Can be set to >1 to reduce the overhead of this. | `1` | Type: int |
|
||||
| `--numa-node` | Sets the numa node for the subprocesses. i-th element corresponds to i-th subprocess. | `None` | List[int] |
|
||||
| `--enable-deterministic-inference` | Enable deterministic inference mode with batch invariant ops. | `False` | bool flag (set to enable) |
|
||||
| `--rl-on-policy-target` | The training system that SGLang needs to match for true on-policy. | `None` | `fsdp` |
|
||||
| `--enable-attn-tp-input-scattered` | Allow input of attention to be scattered when only using tensor parallelism, to reduce the computational load of operations such as qkv latent. | `False` | bool flag (set to enable) |
|
||||
| `--enable-nsa-prefill-context-parallel` | Enable context parallelism used in the long sequence prefill phase of DeepSeek v3.2. | `False` | bool flag (set to enable) |
|
||||
| `--nsa-prefill-cp-mode` | Token splitting mode for the prefill phase of DeepSeek v3.2 under context parallelism. Optional values: `round-robin-split`(default),`in-seq-split`. `round-robin-split` distributes tokens across ranks based on `token_idx % cp_size`. It supports multi-batch prefill, fused MoE, and FP8 KV cache. | `in-seq-split` | `in-seq-split`, `round-robin-split` |
|
||||
| `--enable-fused-qk-norm-rope` | Enable fused qk normalization and rope rotary embedding. | `False` | bool flag (set to enable) |
|
||||
| `--enable-precise-embedding-interpolation` | Enable corner alignment for resize of embeddings grid to ensure more accurate(but slower) evaluation of interpolated embedding values. | `False` | bool flag (set to enable) |
|
||||
|
||||
## Dynamic batch tokenizer
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-dynamic-batch-tokenizer` | Enable async dynamic batch tokenizer for improved performance when multiple requests arrive concurrently. | `False` | bool flag (set to enable) |
|
||||
| `--dynamic-batch-tokenizer-batch-size` | [Only used if --enable-dynamic-batch-tokenizer is set] Maximum batch size for dynamic batch tokenizer. | `32` | Type: int |
|
||||
| `--dynamic-batch-tokenizer-batch-timeout` | [Only used if --enable-dynamic-batch-tokenizer is set] Timeout in seconds for batching tokenization requests. | `0.002` | Type: float |
|
||||
|
||||
## Debug tensor dumps
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--debug-tensor-dump-output-folder` | The output folder for dumping tensors. | `None` | Type: str |
|
||||
| `--debug-tensor-dump-layers` | The layer ids to dump. Dump all layers if not specified. | `None` | Type: JSON list |
|
||||
| `--debug-tensor-dump-input-file` | The input filename for dumping tensors | `None` | Type: str |
|
||||
| `--debug-tensor-dump-inject` | Inject the outputs from jax as the input of every layer. | `False` | Type: str |
|
||||
|
||||
## PD disaggregation
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--disaggregation-mode` | Only used for PD disaggregation. "prefill" for prefill-only server, and "decode" for decode-only server. If not specified, it is not PD disaggregated | `null` | `null`, `prefill`, `decode` |
|
||||
| `--disaggregation-transfer-backend` | The backend for disaggregation transfer. Default is mooncake. | `mooncake` | `mooncake`, `nixl`, `ascend`, `fake` |
|
||||
| `--disaggregation-bootstrap-port` | Bootstrap server port on the prefill server. Default is 8998. | `8998` | Type: int |
|
||||
| `--disaggregation-ib-device` | The InfiniBand devices for disaggregation transfer, accepts single device (e.g., --disaggregation-ib-device mlx5_0) or multiple comma-separated devices (e.g., --disaggregation-ib-device mlx5_0,mlx5_1). Default is None, which triggers automatic device detection when mooncake backend is enabled. | `None` | Type: str |
|
||||
| `--disaggregation-decode-enable-offload-kvcache` | Enable async KV cache offloading on decode server (PD mode). | `False` | bool flag (set to enable) |
|
||||
| `--num-reserved-decode-tokens` | Number of decode tokens that will have memory reserved when adding new request to the running batch. | `512` | Type: int |
|
||||
| `--disaggregation-decode-polling-interval` | The interval to poll requests in decode server. Can be set to >1 to reduce the overhead of this. | `1` | Type: int |
|
||||
|
||||
## Encode prefill disaggregation
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--encoder-only` | For MLLM with an encoder, launch an encoder-only server | `False` | bool flag (set to enable) |
|
||||
| `--language-only` | For VLM, load weights for the language model only. | `False` | bool flag (set to enable) |
|
||||
| `--encoder-transfer-backend` | The backend for encoder disaggregation transfer. Default is zmq_to_scheduler. | `zmq_to_scheduler` | `zmq_to_scheduler`, `zmq_to_tokenizer`, `mooncake` |
|
||||
| `--encoder-urls` | List of encoder server urls. | `[]` | Type: JSON list |
|
||||
|
||||
## Custom weight loader
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--custom-weight-loader` | The custom dataloader which used to update the model. Should be set with a valid import path, such as my_package.weight_load_func | `None` | List[str] |
|
||||
| `--weight-loader-disable-mmap` | Disable mmap while loading weight using safetensors. | `False` | bool flag (set to enable) |
|
||||
| `--remote-instance-weight-loader-seed-instance-ip` | The ip of the seed instance for loading weights from remote instance. | `None` | Type: str |
|
||||
| `--remote-instance-weight-loader-seed-instance-service-port` | The service port of the seed instance for loading weights from remote instance. | `None` | Type: int |
|
||||
| `--remote-instance-weight-loader-send-weights-group-ports` | The communication group ports for loading weights from remote instance. | `None` | Type: JSON list |
|
||||
| `--remote-instance-weight-loader-backend` | The backend for loading weights from remote instance. Can be 'transfer_engine' or 'nccl'. Default is 'nccl'. | `nccl` | `transfer_engine`, `nccl` |
|
||||
| `--remote-instance-weight-loader-start-seed-via-transfer-engine` | Start seed server via transfer engine backend for remote instance weight loader. | `False` | bool flag (set to enable) |
|
||||
|
||||
## For PD-Multiplexing
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-pdmux` | Enable PD-Multiplexing, PD running on greenctx stream. | `False` | bool flag (set to enable) |
|
||||
| `--pdmux-config-path` | The path of the PD-Multiplexing config file. | `None` | Type: str |
|
||||
| `--sm-group-num` | Number of sm partition groups. | `8` | Type: int |
|
||||
|
||||
## Configuration file support
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--config` | Read CLI options from a config file. Must be a YAML file with configuration options. | `None` | Type: str |
|
||||
|
||||
## For Multi-Modal
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--mm-max-concurrent-calls` | The max concurrent calls for async mm data processing. | `32` | Type: int |
|
||||
| `--mm-per-request-timeout` | The timeout for each multi-modal request in seconds. | `10.0` | Type: int |
|
||||
| `--enable-broadcast-mm-inputs-process` | Enable broadcast mm-inputs process in scheduler. | `False` | bool flag (set to enable) |
|
||||
| `--mm-process-config` | Multimodal preprocessing config, a json config contains keys: `image`, `video`, `audio`. | `{}` | Type: JSON / Dict |
|
||||
| `--mm-enable-dp-encoder` | Enabling data parallelism for mm encoder. The dp size will be set to the tp size automatically. | `False` | bool flag (set to enable) |
|
||||
| `--limit-mm-data-per-request` | Limit the number of multimodal inputs per request. e.g. '{"image": 1, "video": 1, "audio": 1}' | `None` | Type: JSON / Dict |
|
||||
| `--enable-mm-global-cache` | Enable Mooncake-backed global multimodal embedding cache on encoder servers so repeated images can reuse cached ViT embeddings instead of recomputing them. | `False` | bool flag (set to enable) |
|
||||
|
||||
## For checkpoint decryption
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--decrypted-config-file` | The path of the decrypted config file. | `None` | Type: str |
|
||||
| `--decrypted-draft-config-file` | The path of the decrypted draft config file. | `None` | Type: str |
|
||||
| `--enable-prefix-mm-cache` | Enable prefix multimodal cache. Currently only supports mm-only. | `False` | bool flag (set to enable) |
|
||||
|
||||
## Forward hooks
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--forward-hooks` | JSON-formatted list of forward hook specifications. Each element must include `target_modules` (list of glob patterns matched against `model.named_modules()` names) and `hook_factory` (Python import path to a factory, e.g. `my_package.hooks:make_hook`). An optional `name` field is used for logging, and an optional `config` object is passed as a `dict` to the factory. | `None` | Type: JSON list |
|
||||
|
||||
## Deprecated arguments
|
||||
| Argument | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `--enable-ep-moe` | NOTE: --enable-ep-moe is deprecated. Please set `--ep-size` to the same value as `--tp-size` instead. | `None` | N/A |
|
||||
| `--enable-deepep-moe` | NOTE: --enable-deepep-moe is deprecated. Please set `--moe-a2a-backend` to 'deepep' instead. | `None` | N/A |
|
||||
| `--prefill-round-robin-balance` | Note: Note: --prefill-round-robin-balance is deprecated now. | `None` | N/A |
|
||||
| `--enable-flashinfer-cutlass-moe` | NOTE: --enable-flashinfer-cutlass-moe is deprecated. Please set `--moe-runner-backend` to 'flashinfer_cutlass' instead. | `None` | N/A |
|
||||
| `--enable-flashinfer-cutedsl-moe` | NOTE: --enable-flashinfer-cutedsl-moe is deprecated. Please set `--moe-runner-backend` to 'flashinfer_cutedsl' instead. | `None` | N/A |
|
||||
| `--enable-flashinfer-trtllm-moe` | NOTE: --enable-flashinfer-trtllm-moe is deprecated. Please set `--moe-runner-backend` to 'flashinfer_trtllm' instead. | `None` | N/A |
|
||||
| `--enable-triton-kernel-moe` | NOTE: --enable-triton-kernel-moe is deprecated. Please set `--moe-runner-backend` to 'triton_kernel' instead. | `None` | N/A |
|
||||
| `--enable-flashinfer-mxfp4-moe` | NOTE: --enable-flashinfer-mxfp4-moe is deprecated. Please set `--moe-runner-backend` to 'flashinfer_mxfp4' instead. | `None` | N/A |
|
||||
| `--crash-on-nan` | Crash the server on nan logprobs. | `False` | Type: str |
|
||||
| `--hybrid-kvcache-ratio` | Mix ratio in [0,1] between uniform and hybrid kv buffers (0.0 = pure uniform: swa_size / full_size = 1)(1.0 = pure hybrid: swa_size / full_size = local_attention_size / context_length) | `None` | Optional[float] |
|
||||
| `--load-watch-interval` | The interval of load watching in seconds. | `0.1` | Type: float |
|
||||
| `--nsa-prefill` | Choose the NSA backend for the prefill stage (overrides `--attention-backend` when running DeepSeek NSA-style attention). | `flashmla_sparse` | `flashmla_sparse`, `flashmla_decode`, `fa3`, `tilelang`, `aiter` |
|
||||
| `--nsa-decode` | Choose the NSA backend for the decode stage when running DeepSeek NSA-style attention. Overrides `--attention-backend` for decoding. | `flashmla_kv` | `flashmla_prefill`, `flashmla_kv`, `fa3`, `tilelang`, `aiter` |
|
||||
1725
third_party/sglang/docs/advanced_features/sgl_model_gateway.md
vendored
Normal file
1725
third_party/sglang/docs/advanced_features/sgl_model_gateway.md
vendored
Normal file
File diff suppressed because it is too large
Load Diff
271
third_party/sglang/docs/advanced_features/sglang_for_rl.md
vendored
Normal file
271
third_party/sglang/docs/advanced_features/sglang_for_rl.md
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
# SGLang for RL Systems
|
||||
|
||||
This document is a practical guide for infrastructure teams integrating SGLang into RL and post-training systems. It focuses on the operational pain points in the loop (rollout, evaluation, training, weight sync) and maps them to concrete SGLang APIs, flags, and integration patterns. The focus is on maximizing rollout efficiency, accuracy and stability while keeping rollout-serving behavior aligned in production environments.
|
||||
|
||||
## Why SGLang for RL Lifecycle?
|
||||
|
||||
Let's embrace a guiding principle from early DeepMind's RL engineering:
|
||||
|
||||
**Be a library, not a framework.**
|
||||
|
||||
This philosophy empowers innovation by providing SGLang as flexible tools, not rigid structures. Here are five reasons to use SGLang for your RL lifecycle:
|
||||
|
||||
* **Fine-Grained Engine Sleep and Wake Up**: facilitate maximum-powered rollout and training
|
||||
* **Open-To-Use Refit Functionality**: diverse methods for co-location or disaggregation
|
||||
* **Easy To Postpone Generation**: enable partial rollout and dedicated rollout control
|
||||
* **Deterministic Inference**: achieve deterministic inference to enable zero training-inference mismatch
|
||||
* **Load Balancing Router**: cache-aware load-balancing for high-throughput rollout
|
||||
|
||||
The following sections cover these aspects in detail.
|
||||
|
||||
## Fine-Grained Engine Sleep and Wake Up
|
||||
|
||||
Rollout and training are both memory-intensive, and co-locating them on the same GPUs often leads to memory pressure and slow handoffs. SGLang provides a memory-aware sleep/wake mechanism that releases KV cache and weights while keeping the server process alive, then resumes them for rollout without a full restart. This avoids repeated disk I/O and CUDA graph recapture during each RL step.
|
||||
|
||||
Under the hood, the RL team uses CUDA-graph-aware weight offload via [torch_memory_saver](https://github.com/fzyzcjy/torch_memory_saver) to preserve virtual memory addresses for graph replay. For details, see: [Efficient RL Training - Optimizing Memory Usage in verl](https://hebiao064.github.io/rl-memory-management).
|
||||
|
||||
### Server flag
|
||||
|
||||
Enable memory saver support when launching the server:
|
||||
|
||||
```
|
||||
--enable-memory-saver
|
||||
```
|
||||
|
||||
### Release Memory
|
||||
|
||||
**Endpoint:** `POST /release_memory_occupation`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `tags` | Which memory regions to release. If omitted, all are released. | `None` | Type: list[str], values: `kv_cache`, `weights` |
|
||||
<!-- python/sglang/srt/managers/io_struct.py#L1381 currently only supports `kv_cache`, `weights` -->
|
||||
**Behavior notes:**
|
||||
|
||||
- This call asserts there are no ongoing requests. Ensure the engine is idle before calling it.
|
||||
- If `kv_cache` is released, SGLang flushes cache; subsequent requests will rebuild KV cache as needed.
|
||||
|
||||
### Resume Memory
|
||||
|
||||
**Endpoint:** `POST /resume_memory_occupation`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `tags` | Which memory regions to resume. If omitted, all are resumed. | `None` | Type: list[str], values: `kv_cache`, `weights` |
|
||||
<!-- python/sglang/srt/managers/io_struct.py#L1393 currently only supports `kv_cache`, `weights` -->
|
||||
|
||||
## Open-To-Use Refit Functionality
|
||||
|
||||
After training completes each step, rollout engines must be refit with new weights. SGLang supports three refit strategies so you can match your infrastructure style (co-located vs disaggregated) and scaling needs. Each strategy maps to a concrete API with clear request schemas. For a deeper dive into SGLang's weight update utilities, see [RL System Deep Thinking: Weight Update Mechanisms](https://github.com/zhaochenyang20/Awesome-ML-SYS-Tutorial/blob/main/rlhf/sys-design/readme-1-EN.md).
|
||||
|
||||
**How to choose:**
|
||||
|
||||
- **From disk** is simplest and best for elastic rollout scaling and checkpointing.
|
||||
- **From tensor** is best for co-located training/rollout when you can pass in-memory tensors.
|
||||
- **From distributed** is best for disaggregated training/rollout with dedicated communication groups (NCCL/IB).
|
||||
|
||||
### Update Weights from Disk
|
||||
|
||||
**When to use:**
|
||||
|
||||
- Save checkpoint to disk and update weights from disk
|
||||
- Dynamic scaling (new rollout instances can load from the same checkpoint)
|
||||
|
||||
**Why it works well:**
|
||||
|
||||
This path trades some I/O overhead for simplicity and flexibility. It integrates naturally with checkpointing and makes it trivial to add new rollout engines: point them at the same checkpoint and call the API. It is also the safest option for high availability because the checkpoint itself is the source of truth.
|
||||
|
||||
**Endpoint:** `POST /update_weights_from_disk`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `model_path` | The model path with the new weights. | Required | Type: str |
|
||||
| `load_format` | The format to load the weights. | `None` | Type: str |
|
||||
| `abort_all_requests` | Abort all running requests before update. | `False` | Type: bool |
|
||||
| `weight_version` | Optional weight version label tracked by the server. | `None` | Type: str |
|
||||
| `is_async` | Perform weight load asynchronously. | `False` | Type: bool |
|
||||
| `torch_empty_cache` | Empty torch cache. | `False` | Type: bool |
|
||||
| `keep_pause` | Keep scheduler paused after update. | `False` | Type: bool |
|
||||
| `recapture_cuda_graph` | Recapture CUDA graphs after update. | `False` | Type: bool |
|
||||
| `token_step` | Trainer step id for rollout bookkeeping. | `0` | Type: int |
|
||||
| `flush_cache` | Flush KV cache after update. | `True` | Type: bool |
|
||||
|
||||
**Response body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `success` | Whether the update succeeded. | - | Type: bool |
|
||||
| `message` | Status / error message. | - | Type: str |
|
||||
| `num_paused_requests` | Number of paused requests during update. | `0` | Type: int |
|
||||
|
||||
**Python Engine API:** `engine.update_weights_from_disk(model_path, load_format=None)`
|
||||
|
||||
**Diffusion engine (SGLang-Diffusion):** The diffusion engine exposes the same `POST /update_weights_from_disk` endpoint with the following behavior:
|
||||
|
||||
- **All-or-nothing with rollback:** if any module fails to load, all previously updated modules are rolled back to the original weights by reloading from the original model path. No partial updates are left behind. If rollback itself fails, the exception propagates so the caller knows the model is in an inconsistent state.
|
||||
- **Offload-aware:** when layerwise offload (`--dit-layerwise-offload`) is enabled, the diffusion offload manager replaces GPU parameters with small `torch.empty((1,))` placeholders while real weights live in consolidated pinned CPU buffers. A naive `param.data.copy_()` would fail with a shape mismatch. Instead, the updater dynamically detects active offload managers and writes new weights directly into their CPU buffers, bypassing the placeholders entirely. For any layer that happens to be prefetched on GPU at update time, the live GPU tensor is also updated so the change takes effect immediately. This requires no extra GPU memory and does not disturb the offload state.
|
||||
- **DTensor-aware:** parameters distributed via `torch.distributed.tensor` (tensor parallelism) are updated through `distribute_tensor` so that each shard is correctly placed on the right device mesh.
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `model_path` | The model path with the new weights. | Required | Type: str |
|
||||
| `flush_cache` | Flush TeaCache state after update. | `True` | Type: bool |
|
||||
| `target_modules` | List of module names to update (e.g. `["transformer"]`). If omitted, all `nn.Module` components are updated. | `None` | Type: list[str] |
|
||||
|
||||
**Response body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `success` | Whether the update succeeded. | - | Type: bool |
|
||||
| `message` | Status / error message. | - | Type: str |
|
||||
|
||||
> **Note:** The diffusion engine (SGLang-Diffusion) does not currently support hot refit (updating weights while inference is in progress). The diffusion scheduler processes one request at a time and completes the entire inference before handling the next request, so weight updates and inference never run concurrently.
|
||||
|
||||
### Update Weights from Tensor
|
||||
|
||||
**When to use:**
|
||||
|
||||
- Co-located training and rollout, where training can provide tensors directly
|
||||
- Fast in-memory updates
|
||||
|
||||
**Important constraints:**
|
||||
|
||||
This strategy requires the training process and rollout engine to share access to the tensors. Co-located setups must keep the model on GPU; moving tensors to CPU will break the update path. For high-performance MoE or specialized attention kernels, co-location may limit some optimizations compared to disaggregated rollouts.
|
||||
|
||||
**Endpoint:** `POST /update_weights_from_tensor`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `serialized_named_tensors` | Per-TP serialized tensor payloads. | Required | Type: list[str|bytes] |
|
||||
| `load_format` | Optional load format selector. | `None` | `None`, `direct`, `flattened_bucket`, or a custom loader path string |
|
||||
| `flush_cache` | Flush KV cache after update. | `True` | Type: bool |
|
||||
| `abort_all_requests` | Abort all running requests before update. | `False` | Type: bool |
|
||||
| `weight_version` | Optional version label tracked by the server. | `None` | Type: str |
|
||||
|
||||
**Note:** The serialized tensor payloads must be created with `MultiprocessingSerializer.serialize(...)` and should be base64-safe strings.
|
||||
|
||||
**Python Engine API:** `engine.update_weights_from_tensor(named_tensors, load_format=None, flush_cache=True)`
|
||||
|
||||
### Update Weights from Distributed Group
|
||||
|
||||
**When to use:**
|
||||
|
||||
- Disaggregated training and rollout
|
||||
- NCCL or IB-backed weight broadcast from training workers to rollout workers
|
||||
|
||||
**How it works:**
|
||||
|
||||
Training workers gather weights (typically on TP rank 0), broadcast them to the rollout group, and each rollout TP shard loads the parameters it needs. This avoids disk I/O and keeps training and rollout decoupled, at the cost of managing a dedicated communication group.
|
||||
|
||||
**Initialize weight update group**
|
||||
|
||||
**Endpoint:** `POST /init_weights_update_group`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `master_address` | Group master address. | Required | Type: str |
|
||||
| `master_port` | Group master port. | Required | Type: int |
|
||||
| `rank_offset` | Offset for local rank mapping. | Required | Type: int |
|
||||
| `world_size` | Total world size. | Required | Type: int |
|
||||
| `group_name` | Group name. | `weight_update_group` | Type: str |
|
||||
| `backend` | Communication backend. | `nccl` | Type: str |
|
||||
|
||||
**Update weight**
|
||||
|
||||
**Endpoint:** `POST /update_weights_from_distributed`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `names` | Parameter names to update. | Required | Type: list[str] |
|
||||
| `dtypes` | Dtype strings for each parameter. | Required | Type: list[str] |
|
||||
| `shapes` | Tensor shapes. | Required | Type: list[list[int]] |
|
||||
| `group_name` | Group name. | `weight_update_group` | Type: str |
|
||||
| `flush_cache` | Flush KV cache after update. | `True` | Type: bool |
|
||||
| `abort_all_requests` | Abort all running requests before update. | `False` | Type: bool |
|
||||
| `weight_version` | Optional version label. | `None` | Type: str |
|
||||
| `load_format` | Optional format selector. | `None` | `None` or `flattened_bucket` |
|
||||
|
||||
**Destroy weights update group**
|
||||
|
||||
**Endpoint:** `POST /destroy_weights_update_group`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `group_name` | Group name. | `weight_update_group` | Type: str |
|
||||
|
||||
**Python Engine APIs:**
|
||||
|
||||
- `engine.init_weights_update_group(...)`
|
||||
- `engine.update_weights_from_distributed(names, dtypes, shapes, ...)`
|
||||
- `engine.destroy_weights_update_group(group_name)`
|
||||
|
||||
## Easy To Postpone Generation
|
||||
|
||||
Multi-turn RL rollouts often suffer from long-tail requests that block the entire batch. A small number of slow interactions can stall all GPUs, and the long-tail behavior makes profiling and monitoring difficult.
|
||||
|
||||
SGLang exposes explicit pause/resume APIs so you can pause slow requests and continue them later. This pattern matches systems like [APRIL](https://arxiv.org/abs/2509.18521), terminate once enough responses are collected, and recycle incomplete responses in the next step. The result is higher GPU utilization without discarding partial work.
|
||||
|
||||
`pause_generation` --- update weights --- `continue_generation` is the correct execution flow when updating weights from training. An update can only happen when SGLang is not actively processing inference tasks.
|
||||
|
||||
### Pause Generation
|
||||
|
||||
**Endpoint:** `POST /pause_generation`
|
||||
|
||||
**Request body:**
|
||||
|
||||
| Field | Description | Defaults | Options |
|
||||
| --- | --- | --- | --- |
|
||||
| `mode` | Pause mode. | `abort` | `abort`, `retract`, `in_place` |
|
||||
|
||||
**Modes:**
|
||||
|
||||
- `abort`: Default behavior, identical to `abort` endpoint with `abort_all` set. Pending requests from `waiting_queue` and `running_queue` will be returned immediately to the caller.
|
||||
- `retract`: Put engine in "paused" state. Move running requests back to waiting queue. KV cache can be flushed and recomputed later.
|
||||
- `in_place`: Put engine in "paused" state without changing states of the requests. Running requests rely on availability of KV caches to continue, so any subsequent `flush_cache` call will be unsuccessful.
|
||||
|
||||
### Continue Generation
|
||||
|
||||
**Endpoint:** `POST /continue_generation`
|
||||
|
||||
## Deterministic Inference
|
||||
|
||||
In many RL stacks, rollout and training are implemented with different kernels or batching behavior. Even when weights are identical, token probabilities can drift, silently breaking the on-policy assumption. This is the training–inference mismatch problem.
|
||||
|
||||
SGLang supports a deterministic inference mode that reduces non-determinism across batch shapes. This mitigates variance introduced by runtime batching and kernel selection. To further achieve true on-policy training, you need to modify the training engine to use the same deterministic kernels. For implementation details, see these miles examples: [True On-Policy](https://github.com/radixark/miles/tree/main/examples/true_on_policy) and [True On-Policy for VLM](https://github.com/radixark/miles/tree/main/examples/true_on_policy_vlm). For additional context, see the blog post [Let Speed Be With Stability: All-In-One Solution to Training-Inference Mismatch with Miles](https://github.com/zhaochenyang20/Awesome-ML-SYS-Tutorial/blob/main/rlhf/slime/mismatch/blog-en.md).
|
||||
|
||||
**Server flag:**
|
||||
|
||||
```
|
||||
--enable-deterministic-inference
|
||||
```
|
||||
|
||||
For more details, see [Deterministic Inference](deterministic_inference.md)
|
||||
|
||||
## Load Balancing Router
|
||||
|
||||
SGLang Model Gateway is the recommended control plane for large‑scale RL rollouts. It provides async, non‑blocking request handling, cache‑aware load balancing, and fault‑tolerant routing across rollout and reward servers. This lets you keep GPUs saturated while avoiding long‑tail stalls and brittle, engine‑local concurrency logic. It has been deployed in the training of GLM 4.5+ models and proven to be highly efficient in production-level large-scale RL workloads.
|
||||
|
||||
Key benefits for RL infrastructure:
|
||||
|
||||
- **Async non-blocking efficiency**: SGLang’s native async server/router architecture (HTTPS/gRPC) manages concurrency automatically. This guarantees maximum GPU saturation and effective continuous batching without requiring complex, manual implementation by engineers.
|
||||
- **Elasticity and fault tolerance**: By encapsulating the reward model and rollout as independent servers, SGLang decouples them logically and physically. This architecture provides robust disaster recovery for large-scale distributed training; if a server fails, the router automatically redirects traffic to healthy nodes, ensuring the training process continues without interruption.
|
||||
- **Training–Inference alignment**: Using the SGLang Model Gateway for both training and inference ensures "What You See Is What You Get." This eliminates score discrepancies and the painful backend alignment issues often caused by using different engines for training versus deployment.
|
||||
- **Dynamic load balancing and long-tail mitigation**: Unlike static partitioning, the SGLang Model Gateway enables request-level dynamic dispatching for multi-turn RL. It can distribute different turns of a conversation across different servers to balance workloads and eliminate long-tail latency caused by varying sequence lengths.
|
||||
|
||||
For deployment and configuration, see: [SGLang Model Gateway](sgl_model_gateway.md)
|
||||
563
third_party/sglang/docs/advanced_features/speculative_decoding.md
vendored
Normal file
563
third_party/sglang/docs/advanced_features/speculative_decoding.md
vendored
Normal file
@@ -0,0 +1,563 @@
|
||||
# Speculative Decoding
|
||||
|
||||
SGLang provides several speculative decoding options, including EAGLE-2/EAGLE-3, MTP, classic draft-model decoding, and an NGRAM-based variant. Our implementation aims to maximize speed and efficiency and is considered to be among the fastest in open-source LLM engines.
|
||||
|
||||
## Summary
|
||||
|
||||
### Jump to sections
|
||||
|
||||
- [EAGLE Decoding](#eagle-decoding)
|
||||
- [EAGLE-2 Decoding](#eagle-2-decoding)
|
||||
- [EAGLE-2 Decoding with torch.compile](#eagle-2-decoding-with-torchcompile)
|
||||
- [EAGLE-2 Decoding via Frequency-Ranked Speculative Sampling](#eagle-2-decoding-via-frequency-ranked-speculative-sampling)
|
||||
- [EAGLE-3 Decoding](#eagle-3-decoding)
|
||||
- [Multi Token Prediction](#multi-token-prediction)
|
||||
- [Standalone Speculative Decoding (Small Draft Model)](#standalone-speculative-decoding-small-draft-model)
|
||||
- [Speculative Decoding V2 (Overlap Scheduler)](#speculative-decoding-v2-overlap-scheduler)
|
||||
- [Ngram Speculative Decoding](#ngram-speculative-decoding)
|
||||
- [Full Parameter Reference](#full-parameter-reference)
|
||||
- [OOM Troubleshooting](#oom-troubleshooting)
|
||||
- [References](#references)
|
||||
|
||||
### Quick guidance
|
||||
|
||||
- **Best speed/quality (recommended)**: Use **EAGLE-3** with `--speculative-algorithm EAGLE3`.
|
||||
- **Strong default / broad compatibility**: Use **EAGLE-2** with `--speculative-algorithm EAGLE`.
|
||||
- **Lower `lm_head` overhead for EAGLE-2**: Enable **FR-Spec** with `--speculative-token-map`.
|
||||
- **Model is MTP-enabled**: Use **MTP via speculative decoding** (often with small `speculative_num_steps/topk/num_draft_tokens`, see the example section).
|
||||
- **You have a smaller draft LLM**: Use **STANDALONE** (`--speculative-algorithm STANDALONE`).
|
||||
- **No extra model available**: Use **NGRAM** (`--speculative-algorithm NGRAM`, CUDA-only).
|
||||
- **Want overlap scheduler (experimental)**: Enable **SpecV2** with `SGLANG_ENABLE_SPEC_V2=True` (requires `--speculative-eagle-topk 1`).
|
||||
|
||||
### Method comparison (mini table)
|
||||
|
||||
| Method | Draft source | Separate draft model? | How to enable | Notes / constraints |
|
||||
|---|---|---:|---|---|
|
||||
| EAGLE-2 | EAGLE draft model (feature drafting + tree) | Typically yes | `--speculative-algorithm EAGLE` + `--speculative-draft-model-path ...` | Tune `--speculative-num-steps`, `--speculative-eagle-topk`, `--speculative-num-draft-tokens` |
|
||||
| EAGLE-2 + `torch.compile` | Same as EAGLE-2 | Typically yes | Add `--enable-torch-compile` (optionally `--torch-compile-max-bs`) | Benefit varies by hardware/model; benchmark to verify |
|
||||
| EAGLE-2 + FR-Spec | Same as EAGLE-2 + token subset | Typically yes | Add `--speculative-token-map ...` | Reduces `lm_head` overhead with high-frequency token vocab |
|
||||
| EAGLE-3 | EAGLE3 draft model | Yes | `--speculative-algorithm EAGLE3` + `--speculative-draft-model-path ...` | Best throughput in the benchmark below |
|
||||
| MTP | Built-in multi-token heads (model-specific) | Often no | See **Multi Token Prediction** section | Uses speculative workflow; draft path may be auto-handled for some models |
|
||||
| STANDALONE | Smaller draft LLM (token-level) | Yes | `--speculative-algorithm STANDALONE` + `--speculative-draft-model-path ...` | Does **not** support `--enable-dp-attention` |
|
||||
| SpecV2 (experimental) | V2 workers + overlap scheduler | N/A | `SGLANG_ENABLE_SPEC_V2=True` | Only supports `--speculative-eagle-topk 1`; applies to `EAGLE`, `EAGLE3`, `STANDALONE` |
|
||||
| NGRAM | Ngram cache from previous tokens | No | `--speculative-algorithm NGRAM` | CUDA-only; no `--enable-dp-attention`; disables overlap scheduler & mixed chunked prefill |
|
||||
|
||||
### Performance Highlights
|
||||
|
||||
Please see below for the huge improvements on throughput for LLaMA-Instruct 3.1 8B tested on MT bench that can be achieved via EAGLE3 decoding.
|
||||
For further details please see the [EAGLE3 paper](https://arxiv.org/pdf/2503.01840).
|
||||
|
||||
| Method | Throughput (tokens/s) |
|
||||
|--------|----------------|
|
||||
| SGLang (w/o speculative, 1x H100) | 158.34 tokens/s |
|
||||
| SGLang + EAGLE-2 (1x H100) | 244.10 tokens/s |
|
||||
| SGLang + EAGLE-3 (1x H100) | 373.25 tokens/s |
|
||||
|
||||
---
|
||||
|
||||
## EAGLE Decoding
|
||||
|
||||
To enable EAGLE speculative decoding the following parameters are relevant:
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|---|---|---|
|
||||
| `--speculative-draft-model-path` | Draft model path/weights. **Typically required** for EAGLE/EAGLE3 and STANDALONE. For some MTP-enabled models, this can be omitted. | `None` |
|
||||
| `--speculative-num-steps` | Depth of autoregressive drafting. Increases speculation range but risks rejection cascades. | Auto (`5` for Llama/Grok; `3` for many other models) |
|
||||
| `--speculative-eagle-topk` | Branching factor per step. Improves candidate diversity and acceptance rate, but increases memory/compute consumption. | Auto (`4` for Llama/Grok; `1` for many other models) |
|
||||
| `--speculative-num-draft-tokens` | Maximum parallel verification capacity. Allows deeper tree evaluation but increases GPU memory usage. | Auto (`8` for Llama/Grok; `4` for many other models). If `topk=1`, it is adjusted to `num_steps + 1`. |
|
||||
| `--speculative-accept-threshold-single` | Acceptance threshold for single-token verification. Lower values accept more aggressively. | `1.0` |
|
||||
| `--speculative-accept-threshold-acc` | Accumulated acceptance threshold across steps. | `1.0` |
|
||||
| `--speculative-attention-mode` | Attention mode for speculative operations (`prefill` or `decode`), affecting both target verification and draft extension. | `"prefill"` |
|
||||
| `--speculative-draft-attention-backend` | Override attention backend for the draft model. | `None` (same as target) |
|
||||
| `--speculative-draft-model-quantization` | Quantization method for the draft model. Use `"unquant"` to force no quantization even when the target model is quantized. | Same as target model |
|
||||
| `--speculative-draft-model-revision` | Specific revision/commit of the draft model to load. | `None` (auto-set to `"main"` when `--speculative-draft-model-path` is set and revision is omitted) |
|
||||
| `--speculative-draft-load-format` | Load format for the draft model weights. | `None` |
|
||||
|
||||
These parameters are mostly the same for EAGLE-2 and EAGLE-3. `--speculative-token-map` is ignored for EAGLE-3 models.
|
||||
For `--speculative-num-steps`, `--speculative-eagle-topk`, and `--speculative-num-draft-tokens`: leave all three unset to use auto-tuning, or set all three explicitly when tuning.
|
||||
|
||||
You can find the best combinations of these parameters with [bench_speculative.py](https://github.com/sgl-project/sglang/blob/main/scripts/playground/bench_speculative.py).
|
||||
|
||||
|
||||
### EAGLE-2 Decoding
|
||||
|
||||
You can enable EAGLE-2 Decoding by setting `--speculative-algorithm EAGLE` and choosing an appropriate model.
|
||||
|
||||
**Launch the server:**
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Llama-2-7b-chat-hf \
|
||||
--speculative-algorithm EAGLE \
|
||||
--speculative-draft-model-path lmsys/sglang-EAGLE-llama2-chat-7B \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-eagle-topk 4 \
|
||||
--speculative-num-draft-tokens 16 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### EAGLE-2 Decoding with `torch.compile`
|
||||
|
||||
You can optionally enable `torch.compile` to apply kernel-level optimizations (operator fusion, autotune) to the draft model. The actual speedup depends on your hardware, model architecture, and batch size. In some configurations (e.g., small draft models on H100 where cuBLAS is already optimal and CUDA graphs are enabled), the benefit may be negligible. We recommend benchmarking with and without this flag on your specific setup to verify whether it helps.
|
||||
|
||||
To enable it, add `--enable-torch-compile` and optionally set `--torch-compile-max-bs`:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Llama-2-7b-chat-hf \
|
||||
--speculative-algorithm EAGLE \
|
||||
--speculative-draft-model-path lmsys/sglang-EAGLE-llama2-chat-7B \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-eagle-topk 4 \
|
||||
--speculative-num-draft-tokens 16 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--enable-torch-compile \
|
||||
--torch-compile-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### EAGLE-2 Decoding via Frequency-Ranked Speculative Sampling
|
||||
|
||||
By employing a truncated high-frequency token vocabulary in the draft model, EAGLE speculative decoding reduces `lm_head` computational overhead while accelerating the pipeline without quality degradation. For more details, check out [the paper](https://arxiv.org/pdf/2502.14856).
|
||||
|
||||
In our implementation, set `--speculative-token-map` to enable the optimization. You can get the high-frequency tokens in FR-Spec from [this model](https://huggingface.co/thunlp/LLaMA3-Instruct-8B-FR-Spec). Or you can obtain high-frequency tokens by directly downloading these tokens from [this repo](https://github.com/thunlp/FR-Spec/tree/main?tab=readme-ov-file#prepare-fr-spec-vocabulary-subset).
|
||||
|
||||
Thanks for the contribution from [Weilin Zhao](https://github.com/Achazwl) and [Zhousx](https://github.com/Zhou-sx).
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3-8B-Instruct \
|
||||
--speculative-algorithm EAGLE \
|
||||
--speculative-draft-model-path lmsys/sglang-EAGLE-LLaMA3-Instruct-8B \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-eagle-topk 4 \
|
||||
--speculative-num-draft-tokens 16 \
|
||||
--speculative-token-map thunlp/LLaMA3-Instruct-8B-FR-Spec/freq_32768.pt \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--dtype float16 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="meta-llama/Meta-Llama-3-8B-Instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### EAGLE-3 Decoding
|
||||
|
||||
You can enable EAGLE-3 decoding by setting `--speculative-algorithm EAGLE3` and choosing an appropriate model.
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model meta-llama/Meta-Llama-3.1-8B-Instruct \
|
||||
--speculative-algorithm EAGLE3 \
|
||||
--speculative-draft-model-path jamesliu1/sglang-EAGLE3-Llama-3.1-Instruct-8B \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-eagle-topk 4 \
|
||||
--speculative-num-draft-tokens 16 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--dtype float16 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi Token Prediction
|
||||
|
||||
We support [MTP (Multi-Token Prediction)](https://arxiv.org/pdf/2404.19737) in SGLang by using speculative decoding. We use `XiaomiMiMo/MiMo-7B-RL` as an example here (for DeepSeek MTP usage, refer to [deepseek_v32 doc](../basic_usage/deepseek_v32.md#multi-token-prediction)).
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model XiaomiMiMo/MiMo-7B-RL \
|
||||
--host 0.0.0.0 \
|
||||
--trust-remote-code \
|
||||
--speculative-algorithm EAGLE \
|
||||
--speculative-num-steps 1 \
|
||||
--speculative-eagle-topk 1 \
|
||||
--speculative-num-draft-tokens 2 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
url = "http://localhost:30000/v1/chat/completions"
|
||||
|
||||
data = {
|
||||
"model": "XiaomiMiMo/MiMo-7B-RL",
|
||||
"messages": [{"role": "user", "content": "What is the capital of France?"}],
|
||||
}
|
||||
|
||||
response = requests.post(url, json=data)
|
||||
print(response.json())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Standalone Speculative Decoding (Small Draft Model)
|
||||
|
||||
Besides EAGLE/MTP, SGLang also supports **token-level speculative decoding** using a smaller **draft model**. Enable it with `--speculative-algorithm STANDALONE` and provide a draft model via `--speculative-draft-model-path`.
|
||||
|
||||
Relevant parameters:
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|---|---|---|
|
||||
| `--speculative-draft-model-path` | Draft model weights (smaller than the target model). | `None` |
|
||||
| `--speculative-num-steps` | Draft depth (how many steps the draft model runs autoregressively). | `3` (auto default for STANDALONE) |
|
||||
| `--speculative-eagle-topk` | Branching factor (token candidates per step). | `1` (auto default for STANDALONE) |
|
||||
| `--speculative-num-draft-tokens` | Verification capacity. | `4` (auto default for STANDALONE) |
|
||||
| `--speculative-draft-model-quantization` | Quantization for the draft model. Use `"unquant"` to disable quantization on the draft even when the target is quantized. | Same as target |
|
||||
|
||||
> **Note:** Standalone speculative decoding currently **does not support** `--enable-dp-attention`.
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--speculative-algorithm STANDALONE \
|
||||
--speculative-draft-model-path Qwen/Qwen2.5-1.5B-Instruct \
|
||||
--speculative-num-steps 4 \
|
||||
--speculative-eagle-topk 2 \
|
||||
--speculative-num-draft-tokens 7 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="Qwen/Qwen2.5-7B-Instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Speculative Decoding V2 (Overlap Scheduler)
|
||||
|
||||
SGLang provides an **experimental Speculative Decoding V2** implementation that enables an overlap scheduler and uses V2 speculative workers (e.g. `StandaloneWorkerV2`, `EAGLEWorkerV2`).
|
||||
|
||||
To enable it, set the environment variable:
|
||||
- `SGLANG_ENABLE_SPEC_V2=True`
|
||||
|
||||
Notes:
|
||||
- SpecV2 currently only supports `--speculative-eagle-topk 1`. When SpecV2 is enabled, **set `--speculative-eagle-topk 1` explicitly**.
|
||||
- If you explicitly set `--speculative-eagle-topk > 1`, the server will error.
|
||||
- If you omit `--speculative-eagle-topk`, auto-tuning may pick `topk > 1` for some models (e.g. Llama). This is incompatible with SpecV2 and may not always trigger an immediate config error, so set `--speculative-eagle-topk 1` explicitly.
|
||||
- This applies to `EAGLE`, `EAGLE3`, and `STANDALONE`.
|
||||
|
||||
```bash
|
||||
SGLANG_ENABLE_SPEC_V2=True python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--speculative-algorithm STANDALONE \
|
||||
--speculative-draft-model-path Qwen/Qwen2.5-1.5B-Instruct \
|
||||
--speculative-num-steps 4 \
|
||||
--speculative-eagle-topk 1 \
|
||||
--speculative-num-draft-tokens 5 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="Qwen/Qwen2.5-7B-Instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ngram Speculative Decoding
|
||||
|
||||
SGLang also supports **ngram-based speculative decoding** (no separate draft model). It retrieves draft tokens from an ngram cache built from previously generated tokens, and then verifies them with the target model.
|
||||
|
||||
Enable it with:
|
||||
- `--speculative-algorithm NGRAM`
|
||||
|
||||
### Ngram-specific parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|---|---|---|
|
||||
| `--speculative-num-draft-tokens` | Number of draft tokens verified per step. | `12` |
|
||||
| `--speculative-ngram-min-bfs-breadth` | Minimum BFS breadth. | `1` |
|
||||
| `--speculative-ngram-max-bfs-breadth` | Maximum BFS breadth. | `10` |
|
||||
| `--speculative-ngram-match-type` | Ngram tree-building mode: `"BFS"` for recency-based expansion or `"PROB"` for frequency-based expansion. | `"BFS"` |
|
||||
| `--speculative-ngram-max-trie-depth` | Maximum suffix length stored and matched by the ngram trie. | `18` |
|
||||
| `--speculative-ngram-capacity` | Cache capacity (number of entries). | `10,000,000` |
|
||||
|
||||
Notes:
|
||||
- Ngram speculative decoding **only supports CUDA**.
|
||||
- It currently **does not support** `--enable-dp-attention`.
|
||||
- It disables the overlap scheduler and mixed chunked prefill.
|
||||
- If `--speculative-ngram-max-bfs-breadth > 1` (thus `speculative_eagle_topk > 1`) and `page_size > 1`, use `--attention-backend flashinfer`; otherwise the server will error.
|
||||
- Optional: set `SGLANG_NGRAM_FORCE_GREEDY_VERIFY=True` to force greedy verification.
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--speculative-algorithm NGRAM \
|
||||
--speculative-num-draft-tokens 16 \
|
||||
--speculative-ngram-max-bfs-breadth 10 \
|
||||
--mem-fraction-static 0.7 \
|
||||
--cuda-graph-max-bs 8 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
**Send a request:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
client = openai.Client(base_url="http://127.0.0.1:30000/v1", api_key="None")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="Qwen/Qwen2.5-7B-Instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 3 countries and their capitals."},
|
||||
],
|
||||
temperature=0,
|
||||
max_tokens=64,
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Full Parameter Reference
|
||||
|
||||
Below is a comprehensive list of all speculative decoding parameters available in SGLang:
|
||||
|
||||
### Core parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `--speculative-algorithm` | `str` | `None` | Algorithm to use: `EAGLE`, `EAGLE3`, `STANDALONE`, `NGRAM`, `NEXTN` (alias of `EAGLE`) |
|
||||
| `--speculative-draft-model-path` | `str` | `None` | Path to the draft model weights |
|
||||
| `--speculative-draft-model-revision` | `str` | `None` | Specific revision/commit of the draft model (`"main"` is auto-used when draft path is set and revision is omitted) |
|
||||
| `--speculative-draft-load-format` | `str` | `None` | Load format for draft model weights |
|
||||
| `--speculative-num-steps` | `int` | `None` (auto-chosen when omitted) | Autoregressive drafting depth |
|
||||
| `--speculative-eagle-topk` | `int` | `None` (auto-chosen when omitted) | Branching factor per drafting step |
|
||||
| `--speculative-num-draft-tokens` | `int` | `None` (auto-chosen when omitted) | Maximum number of draft tokens for verification |
|
||||
| `--speculative-accept-threshold-single` | `float` | `1.0` | Single-token acceptance threshold |
|
||||
| `--speculative-accept-threshold-acc` | `float` | `1.0` | Accumulated acceptance threshold |
|
||||
| `--speculative-token-map` | `str` | `None` | Path to FR-Spec high-frequency token map |
|
||||
| `--speculative-attention-mode` | `str` | `"prefill"` | Attention mode for speculative operations (`"prefill"` or `"decode"`) |
|
||||
| `--speculative-draft-attention-backend` | `str` | `None` | Override attention backend for the draft model |
|
||||
| `--speculative-moe-runner-backend` | `str` | `None` | MoE runner backend for the draft model |
|
||||
| `--speculative-moe-a2a-backend` | `str` | `None` | MoE all-to-all backend for the draft model |
|
||||
| `--speculative-draft-model-quantization` | `str` | Same as target | Quantization for the draft model (`"unquant"` to disable) |
|
||||
|
||||
### Ngram-specific parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `--speculative-ngram-min-bfs-breadth` | `int` | `1` | Minimum BFS breadth |
|
||||
| `--speculative-ngram-max-bfs-breadth` | `int` | `10` | Maximum BFS breadth |
|
||||
| `--speculative-ngram-match-type` | `str` | `"BFS"` | Ngram tree-building mode: `"BFS"` for recency-based expansion or `"PROB"` for frequency-based expansion |
|
||||
| `--speculative-ngram-max-trie-depth` | `int` | `18` | Maximum suffix length stored and matched by the ngram trie |
|
||||
| `--speculative-ngram-capacity` | `int` | `10,000,000` | Cache capacity |
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `SGLANG_ENABLE_SPEC_V2` | `False` | Enable Speculative Decoding V2 (overlap scheduler) |
|
||||
| `SGLANG_NGRAM_FORCE_GREEDY_VERIFY` | `False` | Force greedy verification for ngram decoding |
|
||||
|
||||
### Other related flags
|
||||
|
||||
| Parameter | Description |
|
||||
|---|---|
|
||||
| `--enable-multi-layer-eagle` | Enable multi-layer EAGLE (auto-enabled for MiMoV2 and Step3p5 models) |
|
||||
| `--enable-torch-compile` | Enable `torch.compile` for kernel-level optimizations |
|
||||
| `--torch-compile-max-bs` | Maximum batch size for `torch.compile` |
|
||||
|
||||
---
|
||||
|
||||
## OOM Troubleshooting
|
||||
|
||||
> [!WARNING]
|
||||
> **Out of Memory (OOM)?** Speculative decoding may increase GPU memory usage because the draft tree, CUDA graphs, and verification-related buffers consume additional VRAM. If you encounter OOM errors, try the following adjustments.
|
||||
|
||||
### Step 1: Lower static memory fraction (most effective)
|
||||
|
||||
```bash
|
||||
--mem-fraction-static 0.5 # when omitted, this value is auto-computed
|
||||
```
|
||||
|
||||
- `--mem-fraction-static` controls the memory budget for model weights + KV cache pool.
|
||||
- Lowering it directly increases dynamic headroom for activations and CUDA graph buffers.
|
||||
- If omitted, SGLang auto-estimates this value from other settings, and those auto settings can still be too aggressive for some workloads.
|
||||
|
||||
### Step 2: Reduce CUDA graph batch size
|
||||
|
||||
```bash
|
||||
# Fewer CUDA graph captures = less memory reserved
|
||||
--cuda-graph-max-bs 4 # or even 2 for tight memory situations
|
||||
```
|
||||
|
||||
- If omitted, `--cuda-graph-max-bs` is auto-selected based on GPU memory and TP size, and can be much larger on high-memory GPUs.
|
||||
|
||||
### Step 3: Reduce draft tree size
|
||||
|
||||
These three parameters directly control how much memory the draft tree consumes:
|
||||
|
||||
```bash
|
||||
# Before (aggressive, high memory)
|
||||
--speculative-num-steps 5 --speculative-eagle-topk 8 --speculative-num-draft-tokens 64
|
||||
|
||||
# After (conservative, lower memory)
|
||||
--speculative-num-steps 3 --speculative-eagle-topk 1 --speculative-num-draft-tokens 4
|
||||
```
|
||||
|
||||
### Step 4: Limit concurrent requests
|
||||
|
||||
```bash
|
||||
# Fewer concurrent requests lowers in-flight load and can reduce OOM risk
|
||||
--max-running-requests 4
|
||||
```
|
||||
|
||||
### Quick OOM recovery recipe
|
||||
|
||||
If you're hitting OOM and just want something that works, start with this minimal configuration and scale up:
|
||||
|
||||
```bash
|
||||
python3 -m sglang.launch_server \
|
||||
--model <your-model> \
|
||||
--speculative-algorithm EAGLE \
|
||||
--speculative-draft-model-path <your-draft-model> \
|
||||
--speculative-num-steps 3 \
|
||||
--speculative-eagle-topk 1 \
|
||||
--speculative-num-draft-tokens 4 \
|
||||
--cuda-graph-max-bs 2 \
|
||||
--mem-fraction-static 0.5 \
|
||||
--max-running-requests 4 \
|
||||
--log-level warning
|
||||
```
|
||||
|
||||
Then gradually increase `--speculative-num-draft-tokens`, `--speculative-eagle-topk`, and `--cuda-graph-max-bs`. Increase `--mem-fraction-static` last, only after the run is stable.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
EAGLE process is as follows:
|
||||
|
||||
- Within EAGLE the draft model predicts the next feature vector, i.e. the last hidden state of the original LLM, using the feature sequence $(f_1, ..., f_k)$ and the token sequence $(t_2, ..., t_{k+1})$.
|
||||
- The next token is then sampled from $p_{k+2}=\text{LMHead}(f_{k+1})$. Afterwards, the two sequences are extended in a tree style—branching out multiple potential continuations, with the branching factor per step controlled by the `speculative_eagle_topk` parameter—to ensure a more coherent connection of context, and are given as input again.
|
||||
- In SGLang's EAGLE-2 implementation, the draft tree is expanded for the configured steps and then reranked to select the top `speculative_num_draft_tokens` final nodes as draft tokens.
|
||||
- EAGLE-3 removes the feature prediction objective, incorporates low and mid-layer features, and is trained in an on-policy manner.
|
||||
|
||||
This enhances drafting accuracy by operating on features instead of tokens for more regular inputs and by additionally passing tokens from the next timestep to reduce sampling randomness. For more details, see the [EAGLE-2](https://arxiv.org/abs/2406.16858) and [EAGLE-3](https://arxiv.org/abs/2503.01840) papers.
|
||||
|
||||
For guidance on how to train your own EAGLE model please see the [EAGLE repo](https://github.com/SafeAILab/EAGLE/tree/main?tab=readme-ov-file#train). For EAGLE-3 training specifically, check out [SpecForge](https://github.com/sgl-project/SpecForge), the SGLang team's training framework designed for EAGLE-3 speculative decoding models with seamless porting to SGLang serving. See the [SpecForge documentation](https://docs.sglang.ai/SpecForge/) and [blog post](https://lmsys.org/blog/2025-07-25-spec-forge) for details.
|
||||
994
third_party/sglang/docs/advanced_features/structured_outputs.ipynb
vendored
Normal file
994
third_party/sglang/docs/advanced_features/structured_outputs.ipynb
vendored
Normal file
@@ -0,0 +1,994 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Structured Outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can specify a JSON schema, [regular expression](https://en.wikipedia.org/wiki/Regular_expression) or [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) to constrain the model output. The model output will be guaranteed to follow the given constraints. Only one constraint parameter (`json_schema`, `regex`, or `ebnf`) can be specified for a request.\n",
|
||||
"\n",
|
||||
"SGLang supports three grammar backends:\n",
|
||||
"\n",
|
||||
"- [XGrammar](https://github.com/mlc-ai/xgrammar)(default): Supports JSON schema, regular expression, and EBNF constraints.\n",
|
||||
"- [Outlines](https://github.com/dottxt-ai/outlines): Supports JSON schema and regular expression constraints.\n",
|
||||
"- [Llguidance](https://github.com/guidance-ai/llguidance): Supports JSON schema, regular expression, and EBNF constraints.\n",
|
||||
"\n",
|
||||
"We suggest using XGrammar for its better performance and utility. XGrammar currently uses the [GGML BNF format](https://github.com/ggerganov/llama.cpp/blob/master/grammars/README.md). For more details, see [XGrammar technical overview](https://blog.mlc.ai/2024/11/22/achieving-efficient-flexible-portable-structured-generation-with-xgrammar).\n",
|
||||
"\n",
|
||||
"To use Outlines, simply add `--grammar-backend outlines` when launching the server.\n",
|
||||
"To use llguidance, add `--grammar-backend llguidance` when launching the server.\n",
|
||||
"If no backend is specified, XGrammar will be used as the default.\n",
|
||||
"\n",
|
||||
"For better output quality, **It's advisable to explicitly include instructions in the prompt to guide the model to generate the desired format.** For example, you can specify, 'Please generate the output in the following JSON format: ...'.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## OpenAI Compatible API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"from sglang.utils import wait_for_server, print_highlight, terminate_process\n",
|
||||
"\n",
|
||||
"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" \"python -m sglang.launch_server --model-path meta-llama/Meta-Llama-3.1-8B-Instruct --host 0.0.0.0 --log-level warning\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)\n",
|
||||
"client = openai.Client(base_url=f\"http://127.0.0.1:{port}/v1\", api_key=\"None\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON\n",
|
||||
"\n",
|
||||
"you can directly define a JSON schema or use [Pydantic](https://docs.pydantic.dev/latest/) to define and validate the response."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Please generate the information of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=128,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": {\n",
|
||||
" \"name\": \"foo\",\n",
|
||||
" # convert the pydantic model to json schema\n",
|
||||
" \"schema\": CapitalInfo.model_json_schema(),\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response_content = response.choices[0].message.content\n",
|
||||
"# validate the JSON response by the pydantic model\n",
|
||||
"capital_info = CapitalInfo.model_validate_json(response_content)\n",
|
||||
"print_highlight(f\"Validated response: {capital_info.model_dump_json()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Give me the information of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=128,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": {\"name\": \"foo\", \"schema\": json.loads(json_schema)},\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ebnf_grammar = \"\"\"\n",
|
||||
"root ::= city | description\n",
|
||||
"city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\n",
|
||||
"description ::= city \" is \" status\n",
|
||||
"status ::= \"the capital of \" country\n",
|
||||
"country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": \"You are a helpful geography bot.\"},\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Give me the information of the capital of France.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=32,\n",
|
||||
" extra_body={\"ebnf\": ebnf_grammar},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=128,\n",
|
||||
" extra_body={\"regex\": \"(Paris|London)\"},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Structural Tag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tool_get_current_weather = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_weather\",\n",
|
||||
" \"description\": \"Get the current weather in a given location\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"city\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The city to find the weather for, e.g. 'San Francisco'\",\n",
|
||||
" },\n",
|
||||
" \"state\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"the two-letter abbreviation for the state that the city is\"\n",
|
||||
" \" in, e.g. 'CA' which would mean 'California'\",\n",
|
||||
" },\n",
|
||||
" \"unit\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The unit to fetch the temperature in\",\n",
|
||||
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" \"required\": [\"city\", \"state\", \"unit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"tool_get_current_date = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_date\",\n",
|
||||
" \"description\": \"Get the current date and time for a given timezone\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"timezone\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The timezone to fetch the current date and time for, e.g. 'America/New_York'\",\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"timezone\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"schema_get_current_weather = tool_get_current_weather[\"function\"][\"parameters\"]\n",
|
||||
"schema_get_current_date = tool_get_current_date[\"function\"][\"parameters\"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_messages():\n",
|
||||
" return [\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": f\"\"\"\n",
|
||||
"# Tool Instructions\n",
|
||||
"- Always execute python code in messages that you share.\n",
|
||||
"- When looking for real time information use relevant functions if available else fallback to brave_search\n",
|
||||
"You have access to the following functions:\n",
|
||||
"Use the function 'get_current_weather' to: Get the current weather in a given location\n",
|
||||
"{tool_get_current_weather[\"function\"]}\n",
|
||||
"Use the function 'get_current_date' to: Get the current date and time for a given timezone\n",
|
||||
"{tool_get_current_date[\"function\"]}\n",
|
||||
"If a you choose to call a function ONLY reply in the following format:\n",
|
||||
"<{{start_tag}}={{function_name}}>{{parameters}}{{end_tag}}\n",
|
||||
"where\n",
|
||||
"start_tag => `<function`\n",
|
||||
"parameters => a JSON dict with the function argument name as key and function argument value as value.\n",
|
||||
"end_tag => `</function>`\n",
|
||||
"Here is an example,\n",
|
||||
"<function=example_function_name>{{\"example_name\": \"example_value\"}}</function>\n",
|
||||
"Reminder:\n",
|
||||
"- Function calls MUST follow the specified format\n",
|
||||
"- Required parameters MUST be specified\n",
|
||||
"- Only call one function at a time\n",
|
||||
"- Put the entire function call reply on one line\n",
|
||||
"- Always add your sources when using search results to answer the user query\n",
|
||||
"You are a helpful assistant.\"\"\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"You are in New York. Please get the current date and time, and the weather.\",\n",
|
||||
" },\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"messages = get_messages()\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=messages,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Support for XGrammar latest structural tag format\n",
|
||||
"# <https://xgrammar.mlc.ai/docs/tutorials/structural_tag.html>\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
|
||||
" messages=messages,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"format\": {\n",
|
||||
" \"type\": \"triggered_tags\",\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" \"tags\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_weather,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_date,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"at_least_one\": False,\n",
|
||||
" \"stop_after_first\": False,\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Native API and SGLang Runtime (SRT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"import json\n",
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"from transformers import AutoTokenizer\n",
|
||||
"\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"meta-llama/Meta-Llama-3.1-8B-Instruct\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Make API request\n",
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Here is the information of the capital of France in the JSON format.\\n\",\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 64,\n",
|
||||
" \"json_schema\": json.dumps(CapitalInfo.model_json_schema()),\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"print_highlight(response.json())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"response_data = json.loads(response.json()[\"text\"])\n",
|
||||
"# validate the response by the pydantic model\n",
|
||||
"capital_info = CapitalInfo.model_validate(response_data)\n",
|
||||
"print_highlight(f\"Validated response: {capital_info.model_dump_json()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# JSON\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 64,\n",
|
||||
" \"json_schema\": json_schema,\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Give me the information of the capital of France.\",\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"max_new_tokens\": 128,\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"n\": 3,\n",
|
||||
" \"ebnf\": (\n",
|
||||
" \"root ::= city | description\\n\"\n",
|
||||
" 'city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\\n'\n",
|
||||
" 'description ::= city \" is \" status\\n'\n",
|
||||
" 'status ::= \"the capital of \" country\\n'\n",
|
||||
" 'country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"'\n",
|
||||
" ),\n",
|
||||
" },\n",
|
||||
" \"stream\": False,\n",
|
||||
" \"return_logprob\": False,\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Paris is the capital of\",\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 64,\n",
|
||||
" \"regex\": \"(France|England)\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Structural Tag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import AutoTokenizer\n",
|
||||
"\n",
|
||||
"# generate an answer\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"meta-llama/Meta-Llama-3.1-8B-Instruct\")\n",
|
||||
"\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"payload = {\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"response = requests.post(f\"http://localhost:{port}/generate\", json=payload)\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Support for XGrammar latest structural tag format\n",
|
||||
"# <https://xgrammar.mlc.ai/docs/tutorials/structural_tag.html>\n",
|
||||
"payload = {\n",
|
||||
" \"text\": text,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"format\": {\n",
|
||||
" \"type\": \"triggered_tags\",\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" \"tags\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_weather,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_date,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"at_least_one\": False,\n",
|
||||
" \"stop_after_first\": False,\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"response = requests.post(f\"http://localhost:{port}/generate\", json=payload)\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Offline Engine API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sglang as sgl\n",
|
||||
"\n",
|
||||
"llm = sgl.Engine(\n",
|
||||
" model_path=\"meta-llama/Meta-Llama-3.1-8B-Instruct\", grammar_backend=\"xgrammar\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of China in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of France in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of Ireland in the JSON format.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.1,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"json_schema\": json.dumps(CapitalInfo.model_json_schema()),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\") # validate the output by the pydantic model\n",
|
||||
" capital_info = CapitalInfo.model_validate_json(output[\"text\"])\n",
|
||||
" print_highlight(f\"Validated output: {capital_info.model_dump_json()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of China in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of France in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of Ireland in the JSON format.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"sampling_params = {\"temperature\": 0.1, \"top_p\": 0.95, \"json_schema\": json_schema}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of France.\",\n",
|
||||
" \"Give me the information of the capital of Germany.\",\n",
|
||||
" \"Give me the information of the capital of Italy.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.8,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"ebnf\": (\n",
|
||||
" \"root ::= city | description\\n\"\n",
|
||||
" 'city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\\n'\n",
|
||||
" 'description ::= city \" is \" status\\n'\n",
|
||||
" 'status ::= \"the capital of \" country\\n'\n",
|
||||
" 'country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"'\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Please provide information about London as a major global city:\",\n",
|
||||
" \"Please provide information about Paris as a major global city:\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"sampling_params = {\"temperature\": 0.8, \"top_p\": 0.95, \"regex\": \"(France|England)\"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Structural Tag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"prompts = [text]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.8,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Support for XGrammar latest structural tag format\n",
|
||||
"# <https://xgrammar.mlc.ai/docs/tutorials/structural_tag.html>\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.8,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"format\": {\n",
|
||||
" \"type\": \"triggered_tags\",\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" \"tags\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_weather,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"content\": {\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": schema_get_current_date,\n",
|
||||
" },\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"at_least_one\": False,\n",
|
||||
" \"stop_after_first\": False,\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print_highlight(\"===============================\")\n",
|
||||
" print_highlight(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm.shutdown()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
841
third_party/sglang/docs/advanced_features/structured_outputs_for_reasoning_models.ipynb
vendored
Normal file
841
third_party/sglang/docs/advanced_features/structured_outputs_for_reasoning_models.ipynb
vendored
Normal file
@@ -0,0 +1,841 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Structured Outputs For Reasoning Models\n",
|
||||
"\n",
|
||||
"When working with reasoning models that use special tokens like `<think>...</think>` to denote reasoning sections, you might want to allow free-form text within these sections while still enforcing grammar constraints on the rest of the output.\n",
|
||||
"\n",
|
||||
"SGLang provides a feature to disable grammar restrictions within reasoning sections. This is particularly useful for models that need to perform complex reasoning steps before providing a structured output.\n",
|
||||
"\n",
|
||||
"To enable this feature, use the `--reasoning-parser` flag which decide the think_end_token, such as `</think>`, when launching the server. You can also specify the reasoning parser using the `--reasoning-parser` flag.\n",
|
||||
"\n",
|
||||
"## Supported Models\n",
|
||||
"\n",
|
||||
"Currently, SGLang supports the following reasoning models:\n",
|
||||
"- [DeepSeek R1 series](https://huggingface.co/collections/deepseek-ai/deepseek-r1-678e1e131c0169c0bc89728d): The reasoning content is wrapped with `<think>` and `</think>` tags.\n",
|
||||
"- [QwQ](https://huggingface.co/Qwen/QwQ-32B): The reasoning content is wrapped with `<think>` and `</think>` tags.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Usage\n",
|
||||
"\n",
|
||||
"## OpenAI Compatible API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Specify the `--grammar-backend`, `--reasoning-parser` option."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"from sglang.utils import wait_for_server, print_highlight, terminate_process\n",
|
||||
"\n",
|
||||
"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" \"python -m sglang.launch_server --model-path deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --host 0.0.0.0 --reasoning-parser deepseek-r1 --log-level warning\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)\n",
|
||||
"client = openai.Client(base_url=f\"http://127.0.0.1:{port}/v1\", api_key=\"None\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON\n",
|
||||
"\n",
|
||||
"you can directly define a JSON schema or use [Pydantic](https://docs.pydantic.dev/latest/) to define and validate the response."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Give me the information and population of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=2048,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": {\n",
|
||||
" \"name\": \"foo\",\n",
|
||||
" # convert the pydantic model to json schema\n",
|
||||
" \"schema\": CapitalInfo.model_json_schema(),\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\n",
|
||||
" f\"reasoing_content: {response.choices[0].message.reasoning_content}\\n\\ncontent: {response.choices[0].message.content}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Give me the information and population of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=2048,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"json_schema\",\n",
|
||||
" \"json_schema\": {\"name\": \"foo\", \"schema\": json.loads(json_schema)},\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\n",
|
||||
" f\"reasoing_content: {response.choices[0].message.reasoning_content}\\n\\ncontent: {response.choices[0].message.content}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ebnf_grammar = \"\"\"\n",
|
||||
"root ::= city | description\n",
|
||||
"city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\n",
|
||||
"description ::= city \" is \" status\n",
|
||||
"status ::= \"the capital of \" country\n",
|
||||
"country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": \"You are a helpful geography bot.\"},\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Give me the information and population of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=2048,\n",
|
||||
" extra_body={\"ebnf\": ebnf_grammar},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\n",
|
||||
" f\"reasoing_content: {response.choices[0].message.reasoning_content}\\n\\ncontent: {response.choices[0].message.content}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"assistant\", \"content\": \"What is the capital of France?\"},\n",
|
||||
" ],\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=2048,\n",
|
||||
" extra_body={\"regex\": \"(Paris|London)\"},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\n",
|
||||
" f\"reasoing_content: {response.choices[0].message.reasoning_content}\\n\\ncontent: {response.choices[0].message.content}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Structural Tag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tool_get_current_weather = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_weather\",\n",
|
||||
" \"description\": \"Get the current weather in a given location\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"city\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The city to find the weather for, e.g. 'San Francisco'\",\n",
|
||||
" },\n",
|
||||
" \"state\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"the two-letter abbreviation for the state that the city is\"\n",
|
||||
" \" in, e.g. 'CA' which would mean 'California'\",\n",
|
||||
" },\n",
|
||||
" \"unit\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The unit to fetch the temperature in\",\n",
|
||||
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" \"required\": [\"city\", \"state\", \"unit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"tool_get_current_date = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_date\",\n",
|
||||
" \"description\": \"Get the current date and time for a given timezone\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"timezone\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The timezone to fetch the current date and time for, e.g. 'America/New_York'\",\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"timezone\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"schema_get_current_weather = tool_get_current_weather[\"function\"][\"parameters\"]\n",
|
||||
"schema_get_current_date = tool_get_current_date[\"function\"][\"parameters\"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_messages():\n",
|
||||
" return [\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": f\"\"\"\n",
|
||||
"# Tool Instructions\n",
|
||||
"- Always execute python code in messages that you share.\n",
|
||||
"- When looking for real time information use relevant functions if available else fallback to brave_search\n",
|
||||
"You have access to the following functions:\n",
|
||||
"Use the function 'get_current_weather' to: Get the current weather in a given location\n",
|
||||
"{tool_get_current_weather[\"function\"]}\n",
|
||||
"Use the function 'get_current_date' to: Get the current date and time for a given timezone\n",
|
||||
"{tool_get_current_date[\"function\"]}\n",
|
||||
"If a you choose to call a function ONLY reply in the following format:\n",
|
||||
"<{{start_tag}}={{function_name}}>{{parameters}}{{end_tag}}\n",
|
||||
"where\n",
|
||||
"start_tag => `<function`\n",
|
||||
"parameters => a JSON dict with the function argument name as key and function argument value as value.\n",
|
||||
"end_tag => `</function>`\n",
|
||||
"Here is an example,\n",
|
||||
"<function=example_function_name>{{\"example_name\": \"example_value\"}}</function>\n",
|
||||
"Reminder:\n",
|
||||
"- Function calls MUST follow the specified format\n",
|
||||
"- Required parameters MUST be specified\n",
|
||||
"- Only call one function at a time\n",
|
||||
"- Put the entire function call reply on one line\n",
|
||||
"- Always add your sources when using search results to answer the user query\n",
|
||||
"You are a helpful assistant.\"\"\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"You are in New York. Please get the current date and time, and the weather.\",\n",
|
||||
" },\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"messages = get_messages()\n",
|
||||
"\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" messages=messages,\n",
|
||||
" response_format={\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\n",
|
||||
" f\"reasoing_content: {response.choices[0].message.reasoning_content}\\n\\ncontent: {response.choices[0].message.content}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Native API and SGLang Runtime (SRT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> Note: For native API, as a work-around, you need to set `require_reasoning` argument to `True` to ensure the model will think before generating the structured output. It's not required for chat-completion API."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"from transformers import AutoTokenizer\n",
|
||||
"\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"assistant\",\n",
|
||||
" \"content\": \"Give me the information and population of the capital of France in the JSON format.\",\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"# Make API request\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"require_reasoning\": True,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"json_schema\": json.dumps(CapitalInfo.model_json_schema()),\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"print(response.json())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"reasoing_content = response.json()[\"text\"].split(\"</think>\")[0]\n",
|
||||
"content = response.json()[\"text\"].split(\"</think>\")[1]\n",
|
||||
"print_highlight(f\"reasoing_content: {reasoing_content}\\n\\ncontent: {content}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# JSON\n",
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": text,\n",
|
||||
" \"require_reasoning\": True,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"json_schema\": json_schema,\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": \"Give me the information of the capital of France.\",\n",
|
||||
" \"require_reasoning\": True,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"n\": 3,\n",
|
||||
" \"ebnf\": (\n",
|
||||
" \"root ::= city | description\\n\"\n",
|
||||
" 'city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\\n'\n",
|
||||
" 'description ::= city \" is \" status\\n'\n",
|
||||
" 'status ::= \"the capital of \" country\\n'\n",
|
||||
" 'country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"'\n",
|
||||
" ),\n",
|
||||
" },\n",
|
||||
" \"stream\": False,\n",
|
||||
" \"return_logprob\": False,\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = requests.post(\n",
|
||||
" f\"http://localhost:{port}/generate\",\n",
|
||||
" json={\n",
|
||||
" \"text\": \"Paris is the capital of\",\n",
|
||||
" \"require_reasoning\": True,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"regex\": \"(France|England)\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"print(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Structural Tag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"payload = {\n",
|
||||
" \"text\": text,\n",
|
||||
" \"require_reasoning\": True,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"response = requests.post(f\"http://localhost:{port}/generate\", json=payload)\n",
|
||||
"print_highlight(response.json())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Offline Engine API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sglang as sgl\n",
|
||||
"\n",
|
||||
"llm = sgl.Engine(\n",
|
||||
" model_path=\"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n",
|
||||
" reasoning_parser=\"deepseek-r1\",\n",
|
||||
" grammar_backend=\"xgrammar\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### JSON"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Using Pydantic**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of China in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of France in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of Ireland in the JSON format.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the schema using Pydantic\n",
|
||||
"class CapitalInfo(BaseModel):\n",
|
||||
" name: str = Field(..., pattern=r\"^\\w+$\", description=\"Name of the capital city\")\n",
|
||||
" population: int = Field(..., description=\"Population of the capital city\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"json_schema\": json.dumps(CapitalInfo.model_json_schema()),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print(\"===============================\")\n",
|
||||
" print(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**JSON Schema Directly**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of China in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of France in the JSON format.\",\n",
|
||||
" \"Give me the information of the capital of Ireland in the JSON format.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"json_schema = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"name\": {\"type\": \"string\", \"pattern\": \"^[\\\\w]+$\"},\n",
|
||||
" \"population\": {\"type\": \"integer\"},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"name\", \"population\"],\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"sampling_params = {\"temperature\": 0, \"max_new_tokens\": 2048, \"json_schema\": json_schema}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print(\"===============================\")\n",
|
||||
" print(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EBNF\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Give me the information of the capital of France.\",\n",
|
||||
" \"Give me the information of the capital of Germany.\",\n",
|
||||
" \"Give me the information of the capital of Italy.\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.8,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"ebnf\": (\n",
|
||||
" \"root ::= city | description\\n\"\n",
|
||||
" 'city ::= \"London\" | \"Paris\" | \"Berlin\" | \"Rome\"\\n'\n",
|
||||
" 'description ::= city \" is \" status\\n'\n",
|
||||
" 'status ::= \"the capital of \" country\\n'\n",
|
||||
" 'country ::= \"England\" | \"France\" | \"Germany\" | \"Italy\"'\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print(\"===============================\")\n",
|
||||
" print(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Regular expression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompts = [\n",
|
||||
" \"Please provide information about London as a major global city:\",\n",
|
||||
" \"Please provide information about Paris as a major global city:\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"sampling_params = {\"temperature\": 0.8, \"top_p\": 0.95, \"regex\": \"(France|England)\"}\n",
|
||||
"\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print(\"===============================\")\n",
|
||||
" print(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, return_dict=False\n",
|
||||
")\n",
|
||||
"prompts = [text]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"temperature\": 0.8,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"max_new_tokens\": 2048,\n",
|
||||
" \"structural_tag\": json.dumps(\n",
|
||||
" {\n",
|
||||
" \"type\": \"structural_tag\",\n",
|
||||
" \"structures\": [\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_weather>\",\n",
|
||||
" \"schema\": schema_get_current_weather,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"begin\": \"<function=get_current_date>\",\n",
|
||||
" \"schema\": schema_get_current_date,\n",
|
||||
" \"end\": \"</function>\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"triggers\": [\"<function=\"],\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Send POST request to the API endpoint\n",
|
||||
"outputs = llm.generate(prompts, sampling_params)\n",
|
||||
"for prompt, output in zip(prompts, outputs):\n",
|
||||
" print(\"===============================\")\n",
|
||||
" print(f\"Prompt: {prompt}\\nGenerated text: {output['text']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm.shutdown()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
856
third_party/sglang/docs/advanced_features/tool_parser.ipynb
vendored
Normal file
856
third_party/sglang/docs/advanced_features/tool_parser.ipynb
vendored
Normal file
@@ -0,0 +1,856 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Tool Parser\n",
|
||||
"\n",
|
||||
"This guide demonstrates how to use SGLang’s [Function calling](https://platform.openai.com/docs/guides/function-calling) functionality."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Currently supported parsers:\n",
|
||||
"\n",
|
||||
"| Parser | Supported Models | Notes |\n",
|
||||
"|---|---|---|\n",
|
||||
"| `deepseekv3` | DeepSeek-v3 (e.g., `deepseek-ai/DeepSeek-V3-0324`) | Recommend adding `--chat-template ./examples/chat_template/tool_chat_template_deepseekv3.jinja` to launch command. |\n",
|
||||
"| `deepseekv31` | DeepSeek-V3.1 and DeepSeek-V3.2-Exp (e.g. `deepseek-ai/DeepSeek-V3.1`, `deepseek-ai/DeepSeek-V3.2-Exp`) | Recommend adding `--chat-template ./examples/chat_template/tool_chat_template_deepseekv31.jinja` (Or ..deepseekv32.jinja for DeepSeek-V3.2) to launch command. |\n",
|
||||
"| `deepseekv32` | DeepSeek-V3.2 (`deepseek-ai/DeepSeek-V3.2`) | |\n",
|
||||
"| `glm` | GLM series (e.g. `zai-org/GLM-4.6`) | |\n",
|
||||
"| `gpt-oss` | GPT-OSS (e.g., `openai/gpt-oss-120b`, `openai/gpt-oss-20b`, `lmsys/gpt-oss-120b-bf16`, `lmsys/gpt-oss-20b-bf16`) | The gpt-oss tool parser filters out analysis channel events and only preserves normal text. This can cause the content to be empty when explanations are in the analysis channel. To work around this, complete the tool round by returning tool results as `role=\"tool\"` messages, which enables the model to generate the final content. |\n",
|
||||
"| `kimi_k2` | `moonshotai/Kimi-K2-Instruct` | |\n",
|
||||
"| `llama3` | Llama 3.1 / 3.2 / 3.3 (e.g. `meta-llama/Llama-3.1-8B-Instruct`, `meta-llama/Llama-3.2-1B-Instruct`, `meta-llama/Llama-3.3-70B-Instruct`) | |\n",
|
||||
"| `llama4` | Llama 4 (e.g. `meta-llama/Llama-4-Scout-17B-16E-Instruct`) | |\n",
|
||||
"| `mistral` | Mistral (e.g. `mistralai/Mistral-7B-Instruct-v0.3`, `mistralai/Mistral-Nemo-Instruct-2407`, `mistralai/Mistral-7B-v0.3`) | |\n",
|
||||
"| `pythonic` | Llama-3.2 / Llama-3.3 / Llama-4 | Model outputs function calls as Python code. Requires `--tool-call-parser pythonic` and is recommended to use with a specific chat template. |\n",
|
||||
"| `qwen` | Qwen series (e.g. `Qwen/Qwen3-Next-80B-A3B-Instruct`, `Qwen/Qwen3-VL-30B-A3B-Thinking`) except Qwen3-Coder| |\n",
|
||||
"| `qwen3_coder` | Qwen3-Coder (e.g. `Qwen/Qwen3-Coder-30B-A3B-Instruct`) | |\n",
|
||||
"| `step3` | Step-3 | |\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## OpenAI Compatible API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Launching the Server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"from sglang.utils import wait_for_server, print_highlight, terminate_process\n",
|
||||
"from openai import OpenAI\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" \"python3 -m sglang.launch_server --model-path Qwen/Qwen2.5-7B-Instruct --tool-call-parser qwen25 --host 0.0.0.0 --log-level warning\" # qwen25\n",
|
||||
")\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that `--tool-call-parser` defines the parser used to interpret responses."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define Tools for Function Call\n",
|
||||
"Below is a Python snippet that shows how to define a tool as a dictionary. The dictionary includes a tool name, a description, and property defined Parameters."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define tools\n",
|
||||
"tools = [\n",
|
||||
" {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_weather\",\n",
|
||||
" \"description\": \"Get the current weather in a given location\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"city\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The city to find the weather for, e.g. 'San Francisco'\",\n",
|
||||
" },\n",
|
||||
" \"state\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"the two-letter abbreviation for the state that the city is\"\n",
|
||||
" \" in, e.g. 'CA' which would mean 'California'\",\n",
|
||||
" },\n",
|
||||
" \"unit\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The unit to fetch the temperature in\",\n",
|
||||
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" \"required\": [\"city\", \"state\", \"unit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define Messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_messages():\n",
|
||||
" return [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"What's the weather like in Boston today? Output a reasoning before act, then use the tools to help you.\",\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"messages = get_messages()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Initialize the Client"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Initialize OpenAI-like client\n",
|
||||
"client = OpenAI(api_key=\"None\", base_url=f\"http://0.0.0.0:{port}/v1\")\n",
|
||||
"model_name = client.models.list().data[0].id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Non-Streaming Request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Non-streaming mode test\n",
|
||||
"response_non_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0,\n",
|
||||
" top_p=0.95,\n",
|
||||
" max_tokens=1024,\n",
|
||||
" stream=False, # Non-streaming\n",
|
||||
" tools=tools,\n",
|
||||
")\n",
|
||||
"print_highlight(\"Non-stream response:\")\n",
|
||||
"print_highlight(response_non_stream)\n",
|
||||
"print_highlight(\"==== content ====\")\n",
|
||||
"print_highlight(response_non_stream.choices[0].message.content)\n",
|
||||
"print_highlight(\"==== tool_calls ====\")\n",
|
||||
"print_highlight(response_non_stream.choices[0].message.tool_calls)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Handle Tools\n",
|
||||
"When the engine determines it should call a particular tool, it will return arguments or partial arguments through the response. You can parse these arguments and later invoke the tool accordingly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"name_non_stream = response_non_stream.choices[0].message.tool_calls[0].function.name\n",
|
||||
"arguments_non_stream = (\n",
|
||||
" response_non_stream.choices[0].message.tool_calls[0].function.arguments\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(f\"Final streamed function call name: {name_non_stream}\")\n",
|
||||
"print_highlight(f\"Final streamed function call arguments: {arguments_non_stream}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Streaming Request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Streaming mode test\n",
|
||||
"print_highlight(\"Streaming response:\")\n",
|
||||
"response_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0,\n",
|
||||
" top_p=0.95,\n",
|
||||
" max_tokens=1024,\n",
|
||||
" stream=True, # Enable streaming\n",
|
||||
" tools=tools,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"texts = \"\"\n",
|
||||
"tool_calls = []\n",
|
||||
"name = \"\"\n",
|
||||
"arguments = \"\"\n",
|
||||
"for chunk in response_stream:\n",
|
||||
" if chunk.choices[0].delta.content:\n",
|
||||
" texts += chunk.choices[0].delta.content\n",
|
||||
" if chunk.choices[0].delta.tool_calls:\n",
|
||||
" tool_calls.append(chunk.choices[0].delta.tool_calls[0])\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(texts)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Tool Call ====\")\n",
|
||||
"for tool_call in tool_calls:\n",
|
||||
" print_highlight(tool_call)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Handle Tools\n",
|
||||
"When the engine determines it should call a particular tool, it will return arguments or partial arguments through the response. You can parse these arguments and later invoke the tool accordingly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Parse and combine function call arguments\n",
|
||||
"arguments = []\n",
|
||||
"for tool_call in tool_calls:\n",
|
||||
" if tool_call.function.name:\n",
|
||||
" print_highlight(f\"Streamed function call name: {tool_call.function.name}\")\n",
|
||||
"\n",
|
||||
" if tool_call.function.arguments:\n",
|
||||
" arguments.append(tool_call.function.arguments)\n",
|
||||
"\n",
|
||||
"# Combine all fragments into a single JSON string\n",
|
||||
"full_arguments = \"\".join(arguments)\n",
|
||||
"print_highlight(f\"streamed function call arguments: {full_arguments}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define a Tool Function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This is a demonstration, define real function according to your usage.\n",
|
||||
"def get_current_weather(city: str, state: str, unit: \"str\"):\n",
|
||||
" return (\n",
|
||||
" f\"The weather in {city}, {state} is 85 degrees {unit}. It is \"\n",
|
||||
" \"partly cloudly, with highs in the 90's.\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"available_tools = {\"get_current_weather\": get_current_weather}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"### Execute the Tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"messages.append(response_non_stream.choices[0].message)\n",
|
||||
"\n",
|
||||
"# Call the corresponding tool function\n",
|
||||
"tool_call = messages[-1].tool_calls[0]\n",
|
||||
"tool_name = tool_call.function.name\n",
|
||||
"tool_to_call = available_tools[tool_name]\n",
|
||||
"result = tool_to_call(**(json.loads(tool_call.function.arguments)))\n",
|
||||
"print_highlight(f\"Function call result: {result}\")\n",
|
||||
"# messages.append({\"role\": \"tool\", \"content\": result, \"name\": tool_name})\n",
|
||||
"messages.append(\n",
|
||||
" {\n",
|
||||
" \"role\": \"tool\",\n",
|
||||
" \"tool_call_id\": tool_call.id,\n",
|
||||
" \"content\": str(result),\n",
|
||||
" \"name\": tool_name,\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(f\"Updated message history: {messages}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Send Results Back to Model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"final_response = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0,\n",
|
||||
" top_p=0.95,\n",
|
||||
" stream=False,\n",
|
||||
" tools=tools,\n",
|
||||
")\n",
|
||||
"print_highlight(\"Non-stream response:\")\n",
|
||||
"print_highlight(final_response)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(final_response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Native API and SGLang Runtime (SRT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import AutoTokenizer\n",
|
||||
"import requests\n",
|
||||
"\n",
|
||||
"# generate an answer\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(\"Qwen/Qwen2.5-7B-Instruct\")\n",
|
||||
"\n",
|
||||
"messages = get_messages()\n",
|
||||
"\n",
|
||||
"input = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=False, add_generation_prompt=True, tools=tools, return_dict=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"gen_url = f\"http://localhost:{port}/generate\"\n",
|
||||
"gen_data = {\n",
|
||||
" \"text\": input,\n",
|
||||
" \"sampling_params\": {\n",
|
||||
" \"skip_special_tokens\": False,\n",
|
||||
" \"max_new_tokens\": 1024,\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"gen_response = requests.post(gen_url, json=gen_data).json()[\"text\"]\n",
|
||||
"print_highlight(\"==== Response ====\")\n",
|
||||
"print_highlight(gen_response)\n",
|
||||
"\n",
|
||||
"# parse the response\n",
|
||||
"parse_url = f\"http://localhost:{port}/parse_function_call\"\n",
|
||||
"\n",
|
||||
"function_call_input = {\n",
|
||||
" \"text\": gen_response,\n",
|
||||
" \"tool_call_parser\": \"qwen25\",\n",
|
||||
" \"tools\": tools,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"function_call_response = requests.post(parse_url, json=function_call_input)\n",
|
||||
"function_call_response_json = function_call_response.json()\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print(function_call_response_json[\"normal_text\"])\n",
|
||||
"print_highlight(\"==== Calls ====\")\n",
|
||||
"print(\"function name: \", function_call_response_json[\"calls\"][0][\"name\"])\n",
|
||||
"print(\"function arguments: \", function_call_response_json[\"calls\"][0][\"parameters\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Offline Engine API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sglang as sgl\n",
|
||||
"from sglang.srt.function_call.function_call_parser import FunctionCallParser\n",
|
||||
"from sglang.srt.managers.io_struct import Tool, Function\n",
|
||||
"\n",
|
||||
"llm = sgl.Engine(model_path=\"Qwen/Qwen2.5-7B-Instruct\")\n",
|
||||
"tokenizer = llm.tokenizer_manager.tokenizer\n",
|
||||
"input_ids = tokenizer.apply_chat_template(\n",
|
||||
" messages, tokenize=True, add_generation_prompt=True, tools=tools, return_dict=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Note that for gpt-oss tool parser, adding \"no_stop_trim\": True\n",
|
||||
"# to make sure the tool call token <call> is not trimmed.\n",
|
||||
"\n",
|
||||
"sampling_params = {\n",
|
||||
" \"max_new_tokens\": 1024,\n",
|
||||
" \"temperature\": 0,\n",
|
||||
" \"top_p\": 0.95,\n",
|
||||
" \"skip_special_tokens\": False,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# 1) Offline generation\n",
|
||||
"result = llm.generate(input_ids=input_ids, sampling_params=sampling_params)\n",
|
||||
"generated_text = result[\"text\"] # Assume there is only one prompt\n",
|
||||
"\n",
|
||||
"print_highlight(\"=== Offline Engine Output Text ===\")\n",
|
||||
"print_highlight(generated_text)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# 2) Parse using FunctionCallParser\n",
|
||||
"def convert_dict_to_tool(tool_dict: dict) -> Tool:\n",
|
||||
" function_dict = tool_dict.get(\"function\", {})\n",
|
||||
" return Tool(\n",
|
||||
" type=tool_dict.get(\"type\", \"function\"),\n",
|
||||
" function=Function(\n",
|
||||
" name=function_dict.get(\"name\"),\n",
|
||||
" description=function_dict.get(\"description\"),\n",
|
||||
" parameters=function_dict.get(\"parameters\"),\n",
|
||||
" ),\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [convert_dict_to_tool(raw_tool) for raw_tool in tools]\n",
|
||||
"\n",
|
||||
"parser = FunctionCallParser(tools=tools, tool_call_parser=\"qwen25\")\n",
|
||||
"normal_text, calls = parser.parse_non_stream(generated_text)\n",
|
||||
"\n",
|
||||
"print_highlight(\"=== Parsing Result ===\")\n",
|
||||
"print(\"Normal text portion:\", normal_text)\n",
|
||||
"print_highlight(\"Function call portion:\")\n",
|
||||
"for call in calls:\n",
|
||||
" # call: ToolCallItem\n",
|
||||
" print_highlight(f\" - tool name: {call.name}\")\n",
|
||||
" print_highlight(f\" parameters: {call.parameters}\")\n",
|
||||
"\n",
|
||||
"# 3) If needed, perform additional logic on the parsed functions, such as automatically calling the corresponding function to obtain a return value, etc."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm.shutdown()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tool Choice Mode\n",
|
||||
"\n",
|
||||
"SGLang supports OpenAI's `tool_choice` parameter to control when and which tools the model should call. This feature is implemented using EBNF (Extended Backus-Naur Form) grammar to ensure reliable tool calling behavior.\n",
|
||||
"\n",
|
||||
"### Supported Tool Choice Options\n",
|
||||
"\n",
|
||||
"- **`tool_choice=\"required\"`**: Forces the model to call at least one tool\n",
|
||||
"- **`tool_choice={\"type\": \"function\", \"function\": {\"name\": \"specific_function\"}}`**: Forces the model to call a specific function\n",
|
||||
"\n",
|
||||
"### Backend Compatibility\n",
|
||||
"\n",
|
||||
"Tool choice is fully supported with the **Xgrammar backend**, which is the default grammar backend (`--grammar-backend xgrammar`). However, it may not be fully supported with other backends such as `outlines`.\n",
|
||||
"\n",
|
||||
"### Example: Required Tool Choice"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from openai import OpenAI\n",
|
||||
"from sglang.utils import wait_for_server, print_highlight, terminate_process\n",
|
||||
"from sglang.test.doc_patch import launch_server_cmd\n",
|
||||
"\n",
|
||||
"# Start a new server session for tool choice examples\n",
|
||||
"server_process_tool_choice, port_tool_choice = launch_server_cmd(\n",
|
||||
" \"python3 -m sglang.launch_server --model-path Qwen/Qwen2.5-7B-Instruct --tool-call-parser qwen25 --host 0.0.0.0 --log-level warning\"\n",
|
||||
")\n",
|
||||
"wait_for_server(\n",
|
||||
" f\"http://localhost:{port_tool_choice}\", process=server_process_tool_choice\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Initialize client for tool choice examples\n",
|
||||
"client_tool_choice = OpenAI(\n",
|
||||
" api_key=\"None\", base_url=f\"http://0.0.0.0:{port_tool_choice}/v1\"\n",
|
||||
")\n",
|
||||
"model_name_tool_choice = client_tool_choice.models.list().data[0].id\n",
|
||||
"\n",
|
||||
"# Example with tool_choice=\"required\" - forces the model to call a tool\n",
|
||||
"messages_required = [\n",
|
||||
" {\"role\": \"user\", \"content\": \"Hello, what is the capital of France?\"}\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"# Define tools\n",
|
||||
"tools = [\n",
|
||||
" {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_weather\",\n",
|
||||
" \"description\": \"Get the current weather in a given location\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"city\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The city to find the weather for, e.g. 'San Francisco'\",\n",
|
||||
" },\n",
|
||||
" \"unit\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The unit to fetch the temperature in\",\n",
|
||||
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" \"required\": [\"city\", \"unit\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"response_required = client_tool_choice.chat.completions.create(\n",
|
||||
" model=model_name_tool_choice,\n",
|
||||
" messages=messages_required,\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=1024,\n",
|
||||
" tools=tools,\n",
|
||||
" tool_choice=\"required\", # Force the model to call a tool\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\"Response with tool_choice='required':\")\n",
|
||||
"print(\"Content:\", response_required.choices[0].message.content)\n",
|
||||
"print(\"Tool calls:\", response_required.choices[0].message.tool_calls)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Example: Specific Function Choice\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Example with specific function choice - forces the model to call a specific function\n",
|
||||
"messages_specific = [\n",
|
||||
" {\"role\": \"user\", \"content\": \"What are the most attactive places in France?\"}\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"response_specific = client_tool_choice.chat.completions.create(\n",
|
||||
" model=model_name_tool_choice,\n",
|
||||
" messages=messages_specific,\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=1024,\n",
|
||||
" tools=tools,\n",
|
||||
" tool_choice={\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\"name\": \"get_current_weather\"},\n",
|
||||
" }, # Force the model to call the specific get_current_weather function\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print_highlight(\"Response with specific function choice:\")\n",
|
||||
"print(\"Content:\", response_specific.choices[0].message.content)\n",
|
||||
"print(\"Tool calls:\", response_specific.choices[0].message.tool_calls)\n",
|
||||
"\n",
|
||||
"if response_specific.choices[0].message.tool_calls:\n",
|
||||
" tool_call = response_specific.choices[0].message.tool_calls[0]\n",
|
||||
" print_highlight(f\"Called function: {tool_call.function.name}\")\n",
|
||||
" print_highlight(f\"Arguments: {tool_call.function.arguments}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"terminate_process(server_process_tool_choice)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Pythonic Tool Call Format (Llama-3.2 / Llama-3.3 / Llama-4)\n",
|
||||
"\n",
|
||||
"Some Llama models (such as Llama-3.2-1B, Llama-3.2-3B, Llama-3.3-70B, and Llama-4) support a \"pythonic\" tool call format, where the model outputs function calls as Python code, e.g.:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"[get_current_weather(city=\"San Francisco\", state=\"CA\", unit=\"celsius\")]\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"- The output is a Python list of function calls, with arguments as Python literals (not JSON).\n",
|
||||
"- Multiple tool calls can be returned in the same list:\n",
|
||||
"```python\n",
|
||||
"[get_current_weather(city=\"San Francisco\", state=\"CA\", unit=\"celsius\"),\n",
|
||||
" get_current_weather(city=\"New York\", state=\"NY\", unit=\"fahrenheit\")]\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"For more information, refer to Meta’s documentation on [Zero shot function calling](https://github.com/meta-llama/llama-models/blob/main/models/llama4/prompt_format.md#zero-shot-function-calling---system-message).\n",
|
||||
"\n",
|
||||
"Note that this feature is still under development on Blackwell.\n",
|
||||
"\n",
|
||||
"### How to enable\n",
|
||||
"- Launch the server with `--tool-call-parser pythonic`\n",
|
||||
"- You may also specify --chat-template with the improved template for the model (e.g., `--chat-template=examples/chat_template/tool_chat_template_llama4_pythonic.jinja`).\n",
|
||||
"This is recommended because the model expects a special prompt format to reliably produce valid pythonic tool call outputs. The template ensures that the prompt structure (e.g., special tokens, message boundaries like `<|eom|>`, and function call delimiters) matches what the model was trained or fine-tuned on. If you do not use the correct chat template, tool calling may fail or produce inconsistent results.\n",
|
||||
"\n",
|
||||
"#### Forcing Pythonic Tool Call Output Without a Chat Template\n",
|
||||
"If you don't want to specify a chat template, you must give the model extremely explicit instructions in your messages to enforce pythonic output. For example, for `Llama-3.2-1B-Instruct`, you need:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"\n",
|
||||
"server_process, port = launch_server_cmd(\n",
|
||||
" \" python3 -m sglang.launch_server --model-path meta-llama/Llama-3.2-1B-Instruct --tool-call-parser pythonic --tp 1 --log-level warning\" # llama-3.2-1b-instruct\n",
|
||||
")\n",
|
||||
"wait_for_server(f\"http://localhost:{port}\", process=server_process)\n",
|
||||
"\n",
|
||||
"tools = [\n",
|
||||
" {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_weather\",\n",
|
||||
" \"description\": \"Get the current weather for a given location.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"location\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The name of the city or location.\",\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"location\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_tourist_attractions\",\n",
|
||||
" \"description\": \"Get a list of top tourist attractions for a given city.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"city\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The name of the city to find attractions for.\",\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\"city\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_messages():\n",
|
||||
" return [\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": (\n",
|
||||
" \"You are a travel assistant. \"\n",
|
||||
" \"When asked to call functions, ALWAYS respond ONLY with a python list of function calls, \"\n",
|
||||
" \"using this format: [func_name1(param1=value1, param2=value2), func_name2(param=value)]. \"\n",
|
||||
" \"Do NOT use JSON, do NOT use variables, do NOT use any other format. \"\n",
|
||||
" \"Here is an example:\\n\"\n",
|
||||
" '[get_weather(location=\"Paris\"), get_tourist_attractions(city=\"Paris\")]'\n",
|
||||
" ),\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": (\n",
|
||||
" \"I'm planning a trip to Tokyo next week. What's the weather like and what are some top tourist attractions? \"\n",
|
||||
" \"Propose parallel tool calls at once, using the python list of function calls format as shown above.\"\n",
|
||||
" ),\n",
|
||||
" },\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"messages = get_messages()\n",
|
||||
"\n",
|
||||
"client = openai.Client(base_url=f\"http://localhost:{port}/v1\", api_key=\"xxxxxx\")\n",
|
||||
"model_name = client.models.list().data[0].id\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"response_non_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0,\n",
|
||||
" top_p=0.9,\n",
|
||||
" stream=False, # Non-streaming\n",
|
||||
" tools=tools,\n",
|
||||
")\n",
|
||||
"print_highlight(\"Non-stream response:\")\n",
|
||||
"print_highlight(response_non_stream)\n",
|
||||
"\n",
|
||||
"response_stream = client.chat.completions.create(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=messages,\n",
|
||||
" temperature=0,\n",
|
||||
" top_p=0.9,\n",
|
||||
" stream=True,\n",
|
||||
" tools=tools,\n",
|
||||
")\n",
|
||||
"texts = \"\"\n",
|
||||
"tool_calls = []\n",
|
||||
"name = \"\"\n",
|
||||
"arguments = \"\"\n",
|
||||
"\n",
|
||||
"for chunk in response_stream:\n",
|
||||
" if chunk.choices[0].delta.content:\n",
|
||||
" texts += chunk.choices[0].delta.content\n",
|
||||
" if chunk.choices[0].delta.tool_calls:\n",
|
||||
" tool_calls.append(chunk.choices[0].delta.tool_calls[0])\n",
|
||||
"\n",
|
||||
"print_highlight(\"Streaming Response:\")\n",
|
||||
"print_highlight(\"==== Text ====\")\n",
|
||||
"print_highlight(texts)\n",
|
||||
"\n",
|
||||
"print_highlight(\"==== Tool Call ====\")\n",
|
||||
"for tool_call in tool_calls:\n",
|
||||
" print_highlight(tool_call)\n",
|
||||
"\n",
|
||||
"terminate_process(server_process)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **Note:** \n",
|
||||
"> The model may still default to JSON if it was heavily finetuned on that format. Prompt engineering (including examples) is the only way to increase the chance of pythonic output if you are not using a chat template."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## How to support a new model?\n",
|
||||
"1. Update the TOOLS_TAG_LIST in sglang/srt/function_call_parser.py with the model’s tool tags. Currently supported tags include:\n",
|
||||
"```\n",
|
||||
"\tTOOLS_TAG_LIST = [\n",
|
||||
"\t “<|plugin|>“,\n",
|
||||
"\t “<function=“,\n",
|
||||
"\t “<tool_call>“,\n",
|
||||
"\t “<|python_tag|>“,\n",
|
||||
"\t “[TOOL_CALLS]”\n",
|
||||
"\t]\n",
|
||||
"```\n",
|
||||
"2. Create a new detector class in sglang/srt/function_call_parser.py that inherits from BaseFormatDetector. The detector should handle the model’s specific function call format. For example:\n",
|
||||
"```\n",
|
||||
" class NewModelDetector(BaseFormatDetector):\n",
|
||||
"```\n",
|
||||
"3. Add the new detector to the MultiFormatParser class that manages all the format detectors."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
379
third_party/sglang/docs/advanced_features/vlm_query.ipynb
vendored
Normal file
379
third_party/sglang/docs/advanced_features/vlm_query.ipynb
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Query VLM with Offline Engine\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to use SGLang's **offline Engine API** to query VLMs. We will demonstrate usage with Qwen2.5-VL and Llama 4. This section demonstrates three different calling approaches:\n",
|
||||
"\n",
|
||||
"1. **Basic Call**: Directly pass images and text.\n",
|
||||
"2. **Processor Output**: Use HuggingFace processor for data preprocessing.\n",
|
||||
"3. **Precomputed Embeddings**: Pre-calculate image features to improve inference efficiency."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Understanding the Three Input Formats\n",
|
||||
"\n",
|
||||
"SGLang supports three ways to pass visual data, each optimized for different scenarios:\n",
|
||||
"\n",
|
||||
"### 1. **Raw Images** - Simplest approach\n",
|
||||
"- Pass PIL Images, file paths, URLs, or base64 strings directly\n",
|
||||
"- SGLang handles all preprocessing automatically\n",
|
||||
"- Best for: Quick prototyping, simple applications\n",
|
||||
"\n",
|
||||
"### 2. **Processor Output** - For custom preprocessing\n",
|
||||
"- Pre-process images with HuggingFace processor\n",
|
||||
"- Pass the complete processor output dict with `format: \"processor_output\"`\n",
|
||||
"- Best for: Custom image transformations, integration with existing pipelines\n",
|
||||
"- Requirement: Must use `input_ids` instead of text prompt\n",
|
||||
"\n",
|
||||
"### 3. **Precomputed Embeddings** - For maximum performance\n",
|
||||
"- Pre-calculate visual embeddings using the vision encoder\n",
|
||||
"- Pass embeddings with `format: \"precomputed_embedding\"`\n",
|
||||
"- Best for: Repeated queries on same images, caching, high-throughput serving\n",
|
||||
"- Performance gain: Avoids redundant vision encoder computation (30-50% speedup)\n",
|
||||
"\n",
|
||||
"**Key Rule**: Within a single request, use only one format for all images. Don't mix formats.\n",
|
||||
"\n",
|
||||
"The examples below demonstrate all three approaches with both Qwen2.5-VL and Llama 4 models."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Querying Qwen2.5-VL Model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()\n",
|
||||
"\n",
|
||||
"import sglang.test.doc_patch # noqa: F401\n",
|
||||
"\n",
|
||||
"model_path = \"Qwen/Qwen2.5-VL-3B-Instruct\"\n",
|
||||
"chat_template = \"qwen2-vl\"\n",
|
||||
"example_image_url = \"https://raw.githubusercontent.com/sgl-project/sglang/main/examples/assets/example_image.png\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from io import BytesIO\n",
|
||||
"import requests\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"from sglang.srt.parser.conversation import chat_templates\n",
|
||||
"\n",
|
||||
"image = Image.open(BytesIO(requests.get(example_image_url).content))\n",
|
||||
"\n",
|
||||
"conv = chat_templates[chat_template].copy()\n",
|
||||
"conv.append_message(conv.roles[0], f\"What's shown here: {conv.image_token}?\")\n",
|
||||
"conv.append_message(conv.roles[1], \"\")\n",
|
||||
"conv.image_data = [image]\n",
|
||||
"\n",
|
||||
"print(\"Generated prompt text:\")\n",
|
||||
"print(conv.get_prompt())\n",
|
||||
"print(f\"\\nImage size: {image.size}\")\n",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Basic Offline Engine API Call"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sglang import Engine\n",
|
||||
"\n",
|
||||
"llm = Engine(model_path=model_path, chat_template=chat_template, log_level=\"warning\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"out = llm.generate(prompt=conv.get_prompt(), image_data=[image])\n",
|
||||
"print(\"Model response:\")\n",
|
||||
"print(out[\"text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Call with Processor Output\n",
|
||||
"\n",
|
||||
"Using a HuggingFace processor to preprocess text and images, and passing the `processor_output` directly into `Engine.generate`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import AutoProcessor\n",
|
||||
"\n",
|
||||
"processor = AutoProcessor.from_pretrained(model_path, use_fast=True)\n",
|
||||
"processor_output = processor(\n",
|
||||
" images=[image], text=conv.get_prompt(), return_tensors=\"pt\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"out = llm.generate(\n",
|
||||
" input_ids=processor_output[\"input_ids\"][0].detach().cpu().tolist(),\n",
|
||||
" image_data=[dict(processor_output, format=\"processor_output\")],\n",
|
||||
")\n",
|
||||
"print(\"Response using processor output:\")\n",
|
||||
"print(out[\"text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "10",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Call with Precomputed Embeddings\n",
|
||||
"\n",
|
||||
"You can pre-calculate image features to avoid repeated visual encoding processes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "11",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import AutoProcessor\n",
|
||||
"from transformers import Qwen2_5_VLForConditionalGeneration\n",
|
||||
"\n",
|
||||
"processor = AutoProcessor.from_pretrained(model_path, use_fast=True)\n",
|
||||
"model = Qwen2_5_VLForConditionalGeneration.from_pretrained(model_path).eval()\n",
|
||||
"vision = model.model.visual.cuda()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "12",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"processor_output = processor(\n",
|
||||
" images=[image], text=conv.get_prompt(), return_tensors=\"pt\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"input_ids = processor_output[\"input_ids\"][0].detach().cpu().tolist()\n",
|
||||
"\n",
|
||||
"precomputed_embeddings = vision(\n",
|
||||
" processor_output[\"pixel_values\"].cuda(), processor_output[\"image_grid_thw\"].cuda()\n",
|
||||
")\n",
|
||||
"precomputed_embeddings = precomputed_embeddings.pooler_output\n",
|
||||
"\n",
|
||||
"multi_modal_item = dict(\n",
|
||||
" processor_output,\n",
|
||||
" format=\"precomputed_embedding\",\n",
|
||||
" feature=precomputed_embeddings,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"out = llm.generate(input_ids=input_ids, image_data=[multi_modal_item])\n",
|
||||
"print(\"Response using precomputed embeddings:\")\n",
|
||||
"print(out[\"text\"])\n",
|
||||
"\n",
|
||||
"llm.shutdown()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "13",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Querying Llama 4 Vision Model\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"model_path = \"meta-llama/Llama-4-Scout-17B-16E-Instruct\"\n",
|
||||
"chat_template = \"llama-4\"\n",
|
||||
"\n",
|
||||
"from io import BytesIO\n",
|
||||
"import requests\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"from sglang.srt.parser.conversation import chat_templates\n",
|
||||
"\n",
|
||||
"# Download the same example image\n",
|
||||
"image = Image.open(BytesIO(requests.get(example_image_url).content))\n",
|
||||
"\n",
|
||||
"conv = chat_templates[chat_template].copy()\n",
|
||||
"conv.append_message(conv.roles[0], f\"What's shown here: {conv.image_token}?\")\n",
|
||||
"conv.append_message(conv.roles[1], \"\")\n",
|
||||
"conv.image_data = [image]\n",
|
||||
"\n",
|
||||
"print(\"Llama 4 generated prompt text:\")\n",
|
||||
"print(conv.get_prompt())\n",
|
||||
"print(f\"Image size: {image.size}\")\n",
|
||||
"\n",
|
||||
"image\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "14",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Llama 4 Basic Call\n",
|
||||
"\n",
|
||||
"Llama 4 requires more computational resources, so it's configured with multi-GPU parallelism (tp_size=4) and larger context length.\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"llm = Engine(\n",
|
||||
" model_path=model_path,\n",
|
||||
" enable_multimodal=True,\n",
|
||||
" attention_backend=\"fa3\",\n",
|
||||
" tp_size=4,\n",
|
||||
" context_length=65536,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"out = llm.generate(prompt=conv.get_prompt(), image_data=[image])\n",
|
||||
"print(\"Llama 4 response:\")\n",
|
||||
"print(out[\"text\"])\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "15",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Call with Processor Output\n",
|
||||
"\n",
|
||||
"Using HuggingFace processor to preprocess data can reduce computational overhead during inference.\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from transformers import AutoProcessor\n",
|
||||
"\n",
|
||||
"processor = AutoProcessor.from_pretrained(model_path, use_fast=True)\n",
|
||||
"processor_output = processor(\n",
|
||||
" images=[image], text=conv.get_prompt(), return_tensors=\"pt\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"out = llm.generate(\n",
|
||||
" input_ids=processor_output[\"input_ids\"][0].detach().cpu().tolist(),\n",
|
||||
" image_data=[dict(processor_output, format=\"processor_output\")],\n",
|
||||
")\n",
|
||||
"print(\"Response using processor output:\")\n",
|
||||
"print(out)\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "16",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Call with Precomputed Embeddings\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from transformers import AutoProcessor\n",
|
||||
"from transformers import Llama4ForConditionalGeneration\n",
|
||||
"\n",
|
||||
"processor = AutoProcessor.from_pretrained(model_path, use_fast=True)\n",
|
||||
"model = Llama4ForConditionalGeneration.from_pretrained(\n",
|
||||
" model_path, torch_dtype=\"auto\"\n",
|
||||
").eval()\n",
|
||||
"\n",
|
||||
"vision = model.vision_model.cuda()\n",
|
||||
"multi_modal_projector = model.multi_modal_projector.cuda()\n",
|
||||
"\n",
|
||||
"print(f'Image pixel values shape: {processor_output[\"pixel_values\"].shape}')\n",
|
||||
"input_ids = processor_output[\"input_ids\"][0].detach().cpu().tolist()\n",
|
||||
"\n",
|
||||
"# Process image through vision encoder\n",
|
||||
"image_outputs = vision(\n",
|
||||
" processor_output[\"pixel_values\"].to(\"cuda\"), \n",
|
||||
" aspect_ratio_ids=processor_output[\"aspect_ratio_ids\"].to(\"cuda\"),\n",
|
||||
" aspect_ratio_mask=processor_output[\"aspect_ratio_mask\"].to(\"cuda\"),\n",
|
||||
" output_hidden_states=False\n",
|
||||
")\n",
|
||||
"image_features = image_outputs.last_hidden_state\n",
|
||||
"\n",
|
||||
"# Flatten image features and pass through multimodal projector\n",
|
||||
"vision_flat = image_features.view(-1, image_features.size(-1))\n",
|
||||
"precomputed_embeddings = multi_modal_projector(vision_flat)\n",
|
||||
"\n",
|
||||
"# Build precomputed embedding data item\n",
|
||||
"mm_item = dict(\n",
|
||||
" processor_output, \n",
|
||||
" format=\"precomputed_embedding\", \n",
|
||||
" feature=precomputed_embeddings\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Use precomputed embeddings for efficient inference\n",
|
||||
"out = llm.generate(input_ids=input_ids, image_data=[mm_item])\n",
|
||||
"print(\"Llama 4 precomputed embedding response:\")\n",
|
||||
"print(out[\"text\"])\n",
|
||||
"```"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"jupytext": {
|
||||
"cell_metadata_filter": "-all",
|
||||
"custom_cell_magics": "kql",
|
||||
"encoding": "# -*- coding: utf-8 -*-",
|
||||
"text_representation": {
|
||||
"extension": ".py",
|
||||
"format_name": "light",
|
||||
"format_version": "1.5",
|
||||
"jupytext_version": "1.16.1"
|
||||
}
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user