ASR Concurrent Stream Server
Concurrent streaming gRPC server and client for Qwen3-ASR with vLLM continuous batching.
Installation
pip install asr-concurrent-stream
For client-side audio loading and VAD segmentation:
pip install asr-concurrent-stream[client]
For development (regenerating protobuf code):
pip install asr-concurrent-stream[dev]
Note: The server requires vllm and transformers to run the Qwen3-ASR model. The model is loaded directly via vLLM's AsyncLLMEngine — no separate qwen-asr package needed.
Quick Start
Start the server
asr-concurrent-server
The server reads configuration from environment variables:
| Variable | Default | Description |
|---|---|---|
ASR_PORT |
8000 |
gRPC server port |
ASR_MODEL_PATH |
Qwen/Qwen3-ASR-1.7B |
Model path or HuggingFace ID |
ASR_GPU_MEMORY_UTILIZATION |
0.30 |
GPU memory fraction |
ASR_MAX_MODEL_LEN |
4096 |
Maximum model sequence length |
ASR_MAX_NUM_BATCHED_TOKENS |
2048 |
Max tokens per vLLM batch |
ASR_MAX_NUM_SEQS |
16 |
Max concurrent sequences in vLLM |
ASR_MAX_CONCURRENT_STREAMS |
15 |
Maximum concurrent streams (excess queued) |
ASR_HEALTH_PORT |
8080 |
HTTP health check port |
Run the client
asr-concurrent-client --audio path/to/audio.wav --language Italian
Architecture
vLLM Continuous Batching
The server uses vLLM's AsyncLLMEngine for GPU-level continuous batching. Each stream has its own worker coroutine that submits inference requests to the shared async engine. vLLM's scheduler dynamically batches prefill and decode across all active streams.
Client gRPC bidir stream
→ Per-stream asyncio.Queue + worker
→ AsyncLLMEngine.generate() (shared across streams)
→ vLLM scheduler batches at GPU level
→ Results → per-stream queue → gRPC response yield
Sliding Window ASR
Instead of re-encoding the full accumulated audio for each chunk (quadratic cost), the server uses a fixed sliding window:
- Past context: 2.0s before current chunk
- Present: 0.5s current chunk
- Future context: 0.5s lookahead from buffer
This bounds prefill cost to ~3 seconds of audio regardless of total utterance length.
Stream Queuing
When MAX_CONCURRENT_STREAMS is reached, new streams are queued instead of rejected. As soon as one stream finishes, the next queued stream starts automatically.
Performance
| Concurrent Streams | Last chunk → Final (avg) | Wall time |
|---|---|---|
| 1 | 60ms | — |
| 10 | 83ms | 5.5s |
| 50 | 171ms | 5.7s |
Dependencies
| Package | Purpose |
|---|---|
grpcio |
gRPC framework |
numpy |
Audio array handling |
protobuf |
Protocol buffer serialization |
vllm |
vLLM async inference engine with continuous batching |
transformers |
Model processor and tokenizer |
Optional client dependencies: soundfile or scipy (for audio loading/resampling), torch (for Silero VAD segmentation).
Docker image for the Qwen3-ASR concurrent streaming gRPC server.
Image Composition
| Layer | Detail |
|---|---|
| Base OS | Ubuntu 24.04 LTS |
| CUDA | NVIDIA CUDA 12.8.0 (nvidia/cuda:12.8.0-runtime-ubuntu24.04) |
| Python | 3.12 (deadsnakes PPA), installed into /opt/venv |
| Model | Qwen/Qwen3-ASR-1.7B baked into the image |
| Server port | Container 8002 (map to any host port at runtime) |
Installation Sequence
- System dependencies —
curl,git,wget,build-essential,ffmpeg,libsox-*,libsndfile1,libopus0,libffi-dev. - Python 3.12 — installed via deadsnakes PPA, venv created at
/opt/venv, binaries symlinked to/usr/local/bin. - pip — upgraded to latest
pip,setuptools,wheel. vllm— installs vLLM with all required dependencies (transformers, torch, etc.). The server uses vLLM'sAsyncLLMEnginedirectly to load and run the Qwen3-ASR model.- flash-attention — prebuilt wheel for CUDA 12.8 + PyTorch 2.9 + Python 3.12.
- Project files —
ASR_Concurrent_Stream/copied to/app/ASR_Concurrent_Stream/. - Model download —
Qwen/Qwen3-ASR-1.7Bdownloaded from HuggingFace to/app/models/Qwen3-ASR-1.7B. - Launcher patch —
run_asr_grpc_server.shis updated:PORT→8002MODEL_PATH→/app/models/Qwen3-ASR-1.7BQWEN3_ASR_PATH→ site-packagesqwen_asrpath
Build & Run
# Build
docker build -t qwen3-asr-server:latest .
# Run (map host port 8002 to container port 8002)
docker run -d \
--name qwen3-asr-server \
--gpus all \
-p 8002:8002 \
--shm-size=16g \
--ulimit memlock=-1 \
--restart unless-stopped \
qwen3-asr-server:latest
# View logs
docker logs -f qwen3-asr-server
# Stop & remove
docker stop qwen3-asr-server && docker rm qwen3-asr-server
Server Configuration
Hard-coded defaults in server/run_asr_grpc_server.sh:
| Parameter | Value |
|---|---|
| Port | 8002 |
| Model | /app/models/Qwen3-ASR-1.7B |
| GPU memory utilization | 0.35 |
| Max model length | 4096 |
| Max concurrent streams | 50 |
Project Structure (in image)
/app/
├── ASR_Concurrent_Stream/
│ ├── proto/ # gRPC protobuf definitions
│ ├── server/
│ │ ├── asr_grpc_server.py
│ │ ├── _launch_server.py
│ │ ├── stream_manager.py
│ │ ├── inference_coordinator.py
│ │ ├── asr_model.py
│ │ ├── asr_processor.py
│ │ ├── asr_utils.py
│ │ └── run_asr_grpc_server.sh
│ └── client/
│ └── example_client.py
└── models/
└── Qwen3-ASR-1.7B/ # Pre-downloaded model
Metadata
Release files for asr-concurrent-stream 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| asr_concurrent_stream-1.0.2.tar.gz | 23.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asr_concurrent_stream-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.4 kB
Release files / asr_concurrent_stream-1.0.2.tar.gz
| Download URL | asr_concurrent_stream-1.0.2.tar.gz |
|---|---|
| Size | 23.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
28b4b9fc08d7def6eacf55e19e698f450cfd93ed615964eecd99e934aa09205b
|
|
BLAKE2b-256 checksum How to use checksums |
a0f8e8b2df87d711535d350363259458c592cf676562f43afe4f82bd1c2ec679
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / asr_concurrent_stream-1.0.2-py3-none-any.whl
| Download URL | asr_concurrent_stream-1.0.2-py3-none-any.whl |
|---|---|
| Size | 27.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0cda1af8b0a0c3c6bc183565bfaba01ce5dbd3a1daf25e6d33dfbbec5fdadbfd
|
|
BLAKE2b-256 checksum How to use checksums |
d24b577b5993e96ed74b0fe66f9b05702c93ba4d7c2fff1e447a2b2b248b1ba3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency log