Skip to content

Commit 160a270

Browse files
feat(core+docs): enable startup warmup with token cap adjustments and stop token resolution
- Enabled the ASR startup warmup by default, setting `STARTUP_WARMUP=1` and `STARTUP_WARMUP_TOKENS=512`. - Refined warmup token logic to use `MAX_NEW_TOKENS` as a fallback default for consistency. - Introduced stop token ID resolution in the ASR inference pipeline for deterministic behavior. - Updated Dockerfile, Taskfile, README, and documentation to reflect changes in warmup behavior and token handling. - Improved health readiness checks to account for startup warmup impact.
1 parent 34476bf commit 160a270

7 files changed

Lines changed: 51 additions & 9 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
6666
QWEN_ASR_CUDAGRAPH_MODE=PIECEWISE \
6767
QWEN_ASR_CUDAGRAPH_CAPTURE_SIZES=1,2 \
6868
QWEN_ASR_MAX_CUDAGRAPH_CAPTURE_SIZE=2 \
69-
QWEN_ASR_STARTUP_WARMUP=0 \
70-
QWEN_ASR_STARTUP_WARMUP_TOKENS=1 \
69+
QWEN_ASR_STARTUP_WARMUP=1 \
70+
QWEN_ASR_STARTUP_WARMUP_TOKENS=512 \
7171
QWEN_ASR_TRACE_REQUESTS=0 \
7272
PORT=8000 \
7373
HOST=0.0.0.0

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ Common environment variables:
203203
| `QWEN_ASR_MAX_NUM_BATCHED_TOKENS` | `2048` | vLLM batch-token cap |
204204
| `QWEN_ASR_MAX_INFERENCE_BATCH_SIZE` | `2` | ASR inference batch cap |
205205
| `QWEN_ASR_MAX_NEW_TOKENS` | `512` | Max generated tokens |
206+
| `QWEN_ASR_STARTUP_WARMUP` | `1` | Run a decode warmup before the service reports healthy |
207+
| `QWEN_ASR_STARTUP_WARMUP_TOKENS` | `512` | Token cap used by startup warmup |
206208
| `QWEN_ASR_PERFORMANCE_PROFILE` | `balanced` | Startup/runtime graph profile |
207209
| `VLLM_CACHE_ROOT` | `/app/.cache/vllm` | vLLM/Torch compile cache path |
208210

Taskfile.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ vars:
4545
CUDAGRAPH_MODE: 'PIECEWISE'
4646
CUDAGRAPH_CAPTURE_SIZES: '1,2'
4747
MAX_CUDAGRAPH_CAPTURE_SIZE: '2'
48-
STARTUP_WARMUP: '0'
49-
STARTUP_WARMUP_TOKENS: '1'
48+
STARTUP_WARMUP: '1'
49+
STARTUP_WARMUP_TOKENS: '512'
5050
TRACE_REQUESTS: '0'
5151
BACKEND_KWARGS: ''
5252
ALIGNER_KWARGS: ''

docs/dockerhub.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,11 @@ Common knobs:
181181
- `QWEN_ASR_MAX_MODEL_LEN=2048`
182182
- `QWEN_ASR_MAX_NUM_BATCHED_TOKENS=2048`
183183
- `QWEN_ASR_MAX_NEW_TOKENS=512`
184+
- `QWEN_ASR_STARTUP_WARMUP=1`
185+
- `QWEN_ASR_STARTUP_WARMUP_TOKENS=512`
184186
- `QWEN_ASR_PERFORMANCE_PROFILE=balanced|throughput|custom`
185187

186-
Decoding temperature is fixed at `0` for deterministic transcription.
188+
Decoding temperature is fixed at `0` for deterministic transcription. Startup warmup intentionally makes `/health` wait until the normal decode path is ready, so the first API transcription after readiness does not pay vLLM's lazy generation cost.
187189

188190
## Responsible Use and Privacy
189191

qwen_asr/inference/qwen3_asr.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,32 @@
3636
def _trace_requests_enabled() -> bool:
3737
return os.getenv("QWEN_ASR_TRACE_REQUESTS", "0").strip().lower() in {"1", "true", "yes", "y"}
3838

39+
40+
def _resolve_stop_token_ids(processor: Any) -> List[int]:
41+
tokenizer = getattr(processor, "tokenizer", None)
42+
if tokenizer is None:
43+
return []
44+
45+
token_ids: List[int] = []
46+
for token in ("<|im_end|>", "<|endoftext|>"):
47+
try:
48+
token_id = tokenizer.convert_tokens_to_ids(token)
49+
except Exception:
50+
continue
51+
if isinstance(token_id, int) and token_id >= 0 and token_id not in token_ids:
52+
token_ids.append(token_id)
53+
54+
eos_token_id = getattr(tokenizer, "eos_token_id", None)
55+
if isinstance(eos_token_id, int) and eos_token_id >= 0 and eos_token_id not in token_ids:
56+
token_ids.append(eos_token_id)
57+
58+
pad_token_id = getattr(tokenizer, "pad_token_id", None)
59+
if isinstance(pad_token_id, int) and pad_token_id >= 0 and pad_token_id not in token_ids:
60+
token_ids.append(pad_token_id)
61+
62+
return token_ids
63+
64+
3965
from .qwen3_forced_aligner import Qwen3ForcedAligner
4066
from .utils import (
4167
MAX_ASR_INPUT_SECONDS,
@@ -291,7 +317,12 @@ def LLM(
291317
# ASR/translation must remain deterministic: preserve the upstream
292318
# explicit zero-temperature sampling instead of falling back to model
293319
# generation config defaults.
294-
sampling_params = SamplingParams(temperature=0.0, max_tokens=max_new_tokens)
320+
stop_token_ids = _resolve_stop_token_ids(processor)
321+
sampling_kwargs: Dict[str, Any] = {"temperature": 0.0, "max_tokens": max_new_tokens}
322+
if stop_token_ids:
323+
sampling_kwargs["stop_token_ids"] = stop_token_ids
324+
log_startup(f"vLLM stop_token_ids: {stop_token_ids}")
325+
sampling_params = SamplingParams(**sampling_kwargs)
295326

296327
forced_aligner_model = None
297328
if forced_aligner is not None:
@@ -333,7 +364,14 @@ def warm_up(self, *, max_new_tokens: int = 1) -> None:
333364

334365
sampling_cls = type(original_sampling_params)
335366
try:
336-
self.sampling_params = sampling_cls(temperature=0.0, max_tokens=max(1, int(max_new_tokens)))
367+
sampling_kwargs: Dict[str, Any] = {
368+
"temperature": 0.0,
369+
"max_tokens": max(1, int(max_new_tokens)),
370+
}
371+
stop_token_ids = getattr(original_sampling_params, "stop_token_ids", None)
372+
if stop_token_ids:
373+
sampling_kwargs["stop_token_ids"] = list(stop_token_ids)
374+
self.sampling_params = sampling_cls(**sampling_kwargs)
337375
silence = np.zeros((SAMPLE_RATE // 2,), dtype=np.float32)
338376
self.transcribe(audio=(silence, SAMPLE_RATE), language="English", return_time_stamps=False)
339377
finally:

qwen_asr/server/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ def run_server(
821821

822822
warmup_enabled = os.getenv("QWEN_ASR_STARTUP_WARMUP", "0").strip().lower() in {"1", "true", "yes", "y"}
823823
if warmup_enabled:
824-
warmup_tokens = int(os.getenv("QWEN_ASR_STARTUP_WARMUP_TOKENS", "1"))
824+
warmup_tokens = int(os.getenv("QWEN_ASR_STARTUP_WARMUP_TOKENS", os.getenv("QWEN_ASR_MAX_NEW_TOKENS", "512")))
825825
with StartupTimer(f"startup ASR warmup max_new_tokens={warmup_tokens}"):
826826
asr.warm_up(max_new_tokens=warmup_tokens)
827827

qwen_asr/server/openai_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ def run_server(
610610

611611
warmup_enabled = os.getenv("QWEN_ASR_STARTUP_WARMUP", "0").strip().lower() in {"1", "true", "yes", "y"}
612612
if warmup_enabled:
613-
warmup_tokens = int(os.getenv("QWEN_ASR_STARTUP_WARMUP_TOKENS", "1"))
613+
warmup_tokens = int(os.getenv("QWEN_ASR_STARTUP_WARMUP_TOKENS", os.getenv("QWEN_ASR_MAX_NEW_TOKENS", "512")))
614614
with StartupTimer(f"startup ASR warmup max_new_tokens={warmup_tokens}"):
615615
asr.warm_up(max_new_tokens=warmup_tokens)
616616

0 commit comments

Comments
 (0)