🎙️ QwenCleo-ASR
The best open-source model for Egyptian Arabic & code-switching speech recognition
Built on Qwen3-ASR-1.7B, fine-tuned for Egyptian dialect and Arabic ↔ English code-switching.
QwenCleo — the name carries three meanings: Qwen, the powerful base model it is built on; Queen, signalling a model that reigns over its domain; and Cleo, for Cleopatra, the queen of Egypt — because this model is tailored for Egyptian Arabic. 👑🏺
QwenCleo-ASR is, to our knowledge, the best open-source ASR model for Egyptian Arabic and Arabic/English code-switching. It cuts the error rate of the strong Qwen3-ASR base roughly in half, and correctly keeps embedded English tech/loan words in Latin script (engineering, download, React, at least) instead of mangling them into broken Arabic.
- 🎯 Egyptian dialect — tuned on hundreds of hours of Egyptian podcast speech.
- 🔀 Code-switching — keeps English terms in
code-script, Arabic in Arabic. - 🥇 State-of-the-art (open) — beats Qwen3-ASR base, NVIDIA Nemotron, Cohere, and every Whisper variant on our Egyptian + CS test set.
- 📦
pip install qwencleo-asr— inference & chunked long-audio transcription in three lines. - ⚡ Real streaming — token-by-token via vLLM (
asr.stream(...)), plus a mic web demo. - 🚀 Serving — FastAPI server, Gradio demo, OpenAI-compatible vLLM endpoint.
📊 Results
WER / CER (%) on an Egyptian-Arabic + code-switching test set (3,699 utterances). Lower is better. All models scored with the same Egyptian-aware normalization.
| Rank | Model | Params | WER all | CER all | WER · AR | CER · AR | WER · CS | CER · CS |
|---|---|---|---|---|---|---|---|---|
| 🥇 | QwenCleo-ASR | 1.7B | 19.85 | 10.52 | 19.08 | 10.30 | 20.75 | 10.81 |
| 🥈 | Arabic-Whisper-CodeSwitching | 1.55B | 37.98 | 17.86 | 38.48 | 18.71 | 36.92 | 16.22 |
| 🥉 | NVIDIA Nemotron-3.5 | 0.6B | 38.04 | 19.72 | 36.23 | 16.43 | 41.44 | 25.66 |
| 4 | Qwen3-ASR-1.7B (base) | 1.7B | 41.51 | 20.86 | 40.59 | 18.52 | 43.20 | 25.04 |
| 5 | Whisper Large-v3 Turbo (FT) | 0.81B | 50.83 | 22.86 | 48.37 | 18.42 | 55.08 | 37.84 |
| 6 | Cohere Transcribe 03-2026 | 2.0B | 53.20 | 39.12 | 47.95 | 33.45 | 63.76 | 49.66 |
| 7 | MasriSwitch-Gemma3n | 8B | 57.30 | 30.19 | 60.38 | 32.91 | 51.32 | 25.13 |
| 8 | Whisper Large-v3 | 1.54B | 63.94 | 39.76 | 49.25 | 22.76 | 59.32 | 31.52 |
| 9 | Whisper Large-v2 | 1.54B | 72.34 | 48.73 | 60.75 | 33.21 | 66.85 | 40.75 |
| 10 | Whisper Large-v3 Turbo | 0.81B | 73.83 | 46.86 | 59.37 | 29.42 | 66.08 | 37.84 |
| 11 | Whisper Medium | 0.76B | 80.46 | 53.19 | 74.77 | 41.76 | 74.15 | 44.90 |
| 12 | Whisper Small | 0.24B | 89.99 | 60.34 | 77.42 | 42.53 | 87.09 | 55.22 |
| 13 | Whisper Tiny | 0.04B | 124.68 | 89.42 | 116.02 | 77.74 | 110.67 | 74.57 |
🗣️ Sample outputs
Real transcriptions from the test set, scored after Egyptian-aware normalization. Examples span short, medium, and long clips in both Egyptian Arabic and code-switching. These are picked to be honest — on some clips other models also do well; QwenCleo's edge is consistency, dialect fidelity, and keeping English terms in Latin script.
✅ = matches ground truth · ⚠️ = minor slips · ❌ = clear errors. Bold marks the wrong spans.
🔀 Code-switching
Short
✅ Ground truth ااه بحس ستايله حلو وشكله حلو كاريزما وهو بيلعب
| Model | Output |
|---|---|
| 🥇 QwenCleo | بحس style ه حلو وشكله حلو كاريزما وهو بيلعب ⚠️ |
| Qwen3-ASR (base) | أه بحس طايله حلو وشكله حلو وكرزمه وهو يلعب ❌ |
| Cohere | اااااااا بحس ستايله حلو وشكله حلو وكاريزما وهو بيلعب ✅ |
| Nemotron | آه بحس ستايله حلو وشكله حلو كاريزما هو بيلعب ✅ |
| Arabic-Whisper-CS | بحس style و حلو وشكل وحلو و charisma وهو بيلعب ⚠️ |
| MasriSwitch | بحس ستايله حلو و شكله حلو و كاريزما وهو بيعلق ❌ |
Medium
✅ Ground truth — ده حصل ازاي قرار اصلا انك تعمل قناة
YouTube
| Model | Output |
|---|---|
| 🥇 QwenCleo | ده حصل ازاي قرار اصلا انك تعمل قناة YouTube ✅ |
| Qwen3-ASR (base) | ده حصل إزاي قرار أصلا إنك تعمل قناة يوتيوب ⚠️ |
| Cohere | ده حصل ازاي قرار اصلا انك تعمل قناه يوتيوب ⚠️ |
| Nemotron | ده حصل إزاي قرار أصلاً إنك تعمل قناة يوتيوب ⚠️ |
| Arabic-Whisper-CS | دا حصل ازاي قرار اصلا انك تعمل قناة يوتيوب ⚠️ |
| MasriSwitch | ده حصل ازاي قرار أصلا إنك تعمل قناة YouTube ✅ |
✅ Ground truth — خليني اوضح بس هدفها ان الطالب يجي هو
already
| Model | Output |
|---|---|
| 🥇 QwenCleo | خليني اوضح بس هدفها ان الطالب يجي هو already ✅ |
| Qwen3-ASR (base) | خلينا أوضح بس هدفه إن الطالب يجي هو أرريدي ❌ |
| Cohere | خليني اوضح بس هدفها ان الطالب يجي هو اوردي ❌ |
| Nemotron | خلينا وضح بس هدفها إن الطالب يجي هو أوردي ❌ |
| Arabic-Whisper-CS | خليني اوضح بس هدفها ان الطالب ييجي هو already ✅ |
| MasriSwitch | خليني أوضح بس الأهداف إن الطالب بيجي هو already ⚠️ |
Long
✅ Ground truth — ما هو الفكرة جاية فين النهارده
averageالعربية اللي ممكن تجيبها من أوروبا ما بين 23 ل 28000dollarsتقريبا
| Model | Output |
|---|---|
| 🥇 QwenCleo | ما هو الفكره جايه فين النهارده average العربيه اللي ممكن تجيبها من اوروبا ما بين 23 ل 28000 dollars ⚠️ |
| Qwen3-ASR (base) | ما هو الفكرة جاية فين النهاردة أفرج العربية اللي ممكن تجابها من أوروبا ما بين 23 ل 28000 دولار ⚠️ |
| Cohere | ما هو الفكره جايه فين النهارده افريقيا العربيه اللي ممكن تجيبها من اوروبا ما بين 23 ل 28000 دولار ❌ |
| Nemotron | ما هو الفكرة جاية فين النهار ده أفريد العربية اللي ممكن تجيبها من أوروبا ما بين 23 ل 28000 دولار ثلاثة ❌ |
| Arabic-Whisper-CS | ما هو الفكرة جاية فين النهار دا average العربية اللي ممكن تجيبها من أوروبا ما بين 23 ل 28 ألف دولار تقريبا ⚠️ |
| MasriSwitch | ما هو الفكرة جاية فين النهاردة average العربية اللي ممكن تجيبها من أوروبا ما بين 23 لـ28 ألف دولار تقريبًا ⚠️ |
✅ Ground truth — فبتديك كل ال
soft skillsبشكلindirectحرفيا بتديهالك بالمعلقه ليه بتتعلم تشتغل على كل الحاجات بتاعهMicrosoft Excel PowerPoint Word whateverبعدين كمان بتتعلم ان انت تتكلم مع الناسpublic speaking
| Model | Output |
|---|---|
| 🥇 QwenCleo | فبتديك كل الsoft skills بشكل indirect حرفيا بتديهالك بالمعلقه ليه بتتعلم تشتغل على كل الحاجات بتاعه Microsoft Excel, PowerPoint, Word و whatever بعدين كمان بتتعلم ان انت تتكلم مع الناس public speaking ✅ |
| Arabic-Whisper-CS | فبتديك كل ال soft skills بشكل indirect حرفيا بتديهالك بالمعلقة ليه بتتعلم تشتغل على كل الحاجات بتاعة Microsoft, Excel, PowerPoint, Word whatever بعدين كمان بتتعلم أن أنت ⚠️ |
| MasriSwitch | فبتديك كل ال soft skills بشكل indirect حرفيا بتديهالك بالمعلاة ليه بتتعلم تشتغل على كل الحاجات بتاعة Microsoft Excel, PowerPoint, Word whatever بعدين كمان بتتعلم أن أنت ⚠️ |
🇪🇬 Egyptian Arabic
Pure-Arabic clips (no English). After normalization these reflect genuine word-level accuracy, not spelling/number-format differences.
Short
✅ Ground truth — الشهرة مرت بمرحلتين معاك
| Model | Output |
|---|---|
| 🥇 QwenCleo | الشهرة مرت بمرحلتين معاك ✅ |
| Qwen3-ASR (base) | الشهرة مرت بمرحلتين معك ⚠️ |
| Cohere | الشهره مرت بمرحلتين معاك ✅ |
| Nemotron | الشهرة مرت بمرحلتين معك ⚠️ |
| Arabic-Whisper-CS | الشهرة مرت بمرحلتين معاك ✅ |
| MasriSwitch | الشهرة مرت بمرحلتين معاك ✅ |
Medium
✅ Ground truth — لا ركز عشان انت دلوقتي هتتحاسب على الكلام ده
| Model | Output |
|---|---|
| 🥇 QwenCleo | لا ركز عشان انت دلوقتي هتتحاسب على الكلام ده ✅ |
| Qwen3-ASR (base) | لا ركز عشان أنت دلوقتي هتحسب على كلام ده ⚠️ |
| Cohere | لا ركز عشان انت دلوقتي هتتحاسب عالكلام ده ✅ |
| Nemotron | لأ ركز على شان أنت دي الوقت هتتحسب على الكلام ده ❌ |
| Arabic-Whisper-CS | لأ ركز عشان انت دلوقت هتتحسب على الكلام ده ⚠️ |
| MasriSwitch | لأ ركز عشان انت دلوقتي هتتحاسب على الكلام ده ✅ |
✅ Ground truth — بصراحة كانت من أسعد أيام حياتي لما شفت اللي هما فرحانين
| Model | Output |
|---|---|
| 🥇 QwenCleo | بصراحة كانت من أسعد أيام حياتي لما شفت اللي هما فرحانين ✅ |
| Qwen3-ASR (base) | بصراحة كنت من أسعد أيام حياتي لما شوفت اللي هم فرحاني ❌ |
| Cohere | بصراحه كانت قبل اسعد قيم حياتي لما شوفت اللي هم فرحانين ❌ |
| Nemotron | بصراحة كانت من أسعد قام حاتي لما شفت اللي هم فرحين ❌ |
| Arabic-Whisper-CS | بصراحة كانت أبنة أسعد أقام حياتي لما شوفت اللي هم فرحانين ❌ |
| MasriSwitch | بصراحة كانت من أسعد أيام حياتي لما شفت اللي هم فرحانين ✅ |
Long
✅ Ground truth — انا النهاردة مثلا ساكن في مدينة نصر وشغلي في المعادي فانا من البيت للشغل بقطع 20 كيلو في اليوم
| Model | Output |
|---|---|
| 🥇 QwenCleo | انا النهارده مثلا ساكن في مدينة نصر وشغلي في المعادي فانا من البيت للشغل بقطع 20 كيلو في اليوم ✅ |
| Qwen3-ASR (base) | أنا النهاردة مثلاً ساكن في مدينة نصر وشغلي في المعادي فأنا من البيت للشغل لبقيت 20 كيلو في اليوم ⚠️ |
| Cohere | انا النهارده مثلا ساكن في مدينه مصر وشغلي في المعادي فانا من البيت للشغل بقطع 20 كيلو في اليوم ⚠️ |
| Nemotron | أنا النهار ده مثلاً ساكن في مدينة نصر وشغلي في المعادي فأنا من البيت للشغل بقطع 20 كيلو في اليوم ✅ |
| Arabic-Whisper-CS | أنا النهاردة مثلا ساكن في مدينة نصر و شغلي في المعادي فانا من البيت للشغل بقطع 20 كيلو في اليوم ✅ |
| MasriSwitch | أنا النهاردة مثلا ساكن في مدينة نصر وشغلي في المعادي فأنا من البيت للشغل بقطع 20 كيلو في اليوم ✅ |
✅ Ground truth — اشتغلت كان فيه معاناة في تحضير المنهج
| Model | Output |
|---|---|
| 🥇 QwenCleo | اشتغلت كان فيه معاناة في تحضير المنهج ✅ |
| Qwen3-ASR (base) | اشتغلت كان في معاناة في تحضير المناك ❌ |
| Cohere | اشتغلت كان في معاناه في تحضير المنعكس ❌ |
| Nemotron | آه استقالت كان في معناه في تحضير المناك ❌ |
| Arabic-Whisper-CS | اشتغلت كان في معناه في تحضير المنهج ✅ |
| MasriSwitch | إشتغلت كان في معنية في تحضير المنهج ❌ |
📦 Installation
Install the right torch first. A plain
pip installpulls the newest torch (built for the latest CUDA), which fails on older drivers with "NVIDIA driver too old". Install a torch build matching your driver before the package, then add QwenCleo with--no-depsso torch is never reinstalled.Pick the wheel index for your CUDA driver —
cu121(driver ≥ 12.1, e.g. CUDA 12.2),cu118(driver ≥ 11.8), orcpu. Check yours withnvidia-smi.
For inference & chunked transcription (PyPI)
conda create -n qwencleo-asr python=3.12 -y
conda activate qwencleo-asr
# 1) torch matching your driver (cu121 shown — change the index for yours)
pip install torch==2.5.1 torchaudio==2.5.1 \
--index-url https://download.pytorch.org/whl/cu121
# 2) QwenCleo without touching torch, then its remaining deps
pip install qwencleo-asr --no-deps
pip install "qwen-asr>=0.0.6" numpy soundfile huggingface_hub
That's all you need for the Python API and the qwencleo CLI.
For serving / Gradio / vLLM (clone the repo)
conda create -n qwencleo-asr python=3.12 -y
conda activate qwencleo-asr
# 1) torch matching your driver, first
pip install torch==2.5.1 torchaudio==2.5.1 \
--index-url https://download.pytorch.org/whl/cu121
# 2) the repo (without re-resolving torch) + serving deps
git clone https://github.com/MohammedAly22/qwencleo-asr.git
cd qwencleo-asr
pip install -e . --no-deps
pip install "qwen-asr>=0.0.6" numpy soundfile huggingface_hub
pip install -r requirements-serving.txt
Verify torch sees the GPU before running:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
# -> 2.5.1+cu121 True
🚀 Usage
Python — basic transcription
from qwencleo_asr import QwenCleoASR
asr = QwenCleoASR() # loads mohammedaly22/QwenCleo-ASR
result = asr.transcribe("clip.wav") # language defaults to "Arabic"
print(result.text)
Batch, auto-detect language, and Egyptian normalization:
results = asr.transcribe(["a.wav", "b.wav"], language=None) # auto-detect
clean = asr.transcribe("clip.wav", normalize=True) # normalized text
Python — chunked transcription of long audio / mic
from qwencleo_asr import QwenCleoASR, stream_file
asr = QwenCleoASR()
for chunk in stream_file(asr, "long_podcast.wav", chunk_s=20, overlap_s=2):
print(f"[{chunk.start:.0f}-{chunk.end:.0f}s] {chunk.text}")
ℹ️ This is chunked transcription, not true streaming. It splits long/live audio into overlapping windows and transcribes each — convenient for captioning without a server, but latency is per-window. For genuine token-by-token streaming, use the vLLM path below.
Python — true streaming (vLLM)
QwenCleo inherits Qwen3-ASR's real token-by-token streaming via vLLM. Two one-time setup commands, then stream from Python.
qwencleo install-vllm # installs the vLLM nightly (cu129) — the only build
# with Qwen3-ASR support (not on PyPI; uses uv)
qwencleo serve # launches the server (sets the right flags for you:
# VLLM_USE_FLASHINFER_SAMPLER=0, --gpu-memory-utilization 0.8)
Needs an Ampere-or-newer GPU (L4 / A100 / H100). See
server/vllm_serve.mdfor details and manual flags.
Then stream straight off the model object — deltas arrive as they're generated
(the language X<asr_text> prefix is stripped for you):
from qwencleo_asr import QwenCleoASR
asr = QwenCleoASR()
for delta in asr.stream("clip.wav", port=8000): # talks to the vLLM server
print(delta, end="", flush=True)
Or use the helpers directly:
from qwencleo_asr import stream_vllm, transcribe_vllm, VLLMOffline
for delta in stream_vllm("clip.wav", port=8000, language="Arabic"):
print(delta, end="", flush=True)
print(transcribe_vllm("clip.wav", port=8000)) # one-shot via the server
print(VLLMOffline().transcribe("clip.wav")) # in-process, no server
From the shell:
qwencleo stream-vllm clip.wav --port 8000 # token-by-token to stdout
CLI
qwencleo transcribe clip.wav
qwencleo transcribe a.wav b.wav --language None --normalize
qwencleo stream long_podcast.wav --chunk-s 20 --overlap-s 2
🌐 Serving
FastAPI server
QWENCLEO_MODEL=mohammedaly22/QwenCleo-ASR \
uvicorn server.app:app --host 0.0.0.0 --port 8000
curl -X POST http://localhost:8000/v1/transcribe -F file=@clip.wav -F language=Arabic
Gradio demo
python app/gradio_app.py # http://localhost:7860 (mic + file upload)
vLLM — serving, streaming & OpenAI-compatible API
Full guide in server/vllm_serve.md. In short:
qwencleo install-vllm # vLLM nightly (cu129) — the only build with Qwen3-ASR support
qwencleo serve # OpenAI-compatible server on :8000
OpenAI-compatible transcription:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
print(client.audio.transcriptions.create(
model="mohammedaly22/QwenCleo-ASR", file=open("clip.wav","rb").read()).text)
Streaming mic web demo
Live browser-mic transcription via the upstream Flask demo:
qwen-asr-demo-streaming \
--asr-model-path mohammedaly22/QwenCleo-ASR \
--host 0.0.0.0 --port 8000 --gpu-memory-utilization 0.9
# open http://<your-ip>:8000
📓 Examples (Colab)
Runnable notebooks in examples/ — open one, set the runtime to
GPU (Runtime → Change runtime type → GPU), and run the cells top to bottom.
🔗 Links
- 🤗 Model card:
mohammedaly22/QwenCleo-ASR - 📦 PyPI:
qwencleo-asr - 🧱 Base model:
Qwen/Qwen3-ASR-1.7B· Qwen3-ASR repo - Languages: Egyptian Arabic, Modern Standard Arabic, Arabic↔English code-switching
- Recommended
languagehint:"Arabic"(orNoneto auto-detect)
📜 License & citation
Apache-2.0, inheriting the Qwen3-ASR license terms.
@misc{qwencleo_asr_2026,
title = {QwenCleo-ASR: The Best Open-Source Egyptian Arabic and Code-Switching Speech Recognition Model},
author = {Mohammed Aly},
year = {2026},
howpublished = {\url{https://huggingface.co/mohammedaly22/QwenCleo-ASR}},
note = {Fine-tuned from Qwen3-ASR-1.7B}
}
Release files for qwencleo-asr 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qwencleo_asr-0.2.1.tar.gz | 27.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qwencleo_asr-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.0 kB
Release files / qwencleo_asr-0.2.1.tar.gz
| Download URL | qwencleo_asr-0.2.1.tar.gz |
|---|---|
| Size | 27.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8a04d16fcb148a8494d6916777a9cd63313e9f8aa4652c5ab0e41857a8269e4f
|
|
BLAKE2b-256 checksum How to use checksums |
fc3571b39154c6a7b96f2af489b90356a238662fc6a1ab7493e3523e06666013
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / qwencleo_asr-0.2.1-py3-none-any.whl
| Download URL | qwencleo_asr-0.2.1-py3-none-any.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b2ba72a23a01abad81974c125857e65800062225e94ddb5b29e2b1f0c314d979
|
|
BLAKE2b-256 checksum How to use checksums |
0c1db2be522c7aeaa45bd909e14b0c60d12fb28c0b015c84b42a480cd700aafb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|