← Whispering

fix: make flash-attn required for Qwen3-TTS, not optional

53f692abc9581564b64b5b10615bb333e28d2652 · Claude · Feb 10, 2026 18:08

- flash-attn is now auto-installed when installing Qwen3-TTS (no prompt)
- install.sh uses --no-build-isolation (required since flash-attn needs
  torch at build time but doesn't declare it as a build dep)
- get_available_backends() checks for flash_attn alongside qwen_tts
- Qwen3TTSProvider._ensure_initialized() raises clear ImportError if
  flash-attn is missing instead of silently falling back
- INSTALL_TTS.md documents flash-attn as required, warns against uv

https://claude.ai/code/session_01965m1v3f1cK6gDV1JHioA8

+42 −30 · 4 files changed

  • INSTALL_TTS.md +8 −4
  • requirements.txt +2 −0
  • scripts/install.sh +15 −12
  • src/tts_provider.py +17 −14
diff --git a/INSTALL_TTS.md b/INSTALL_TTS.md
index 50f24fe..1d15697 100644
--- a/INSTALL_TTS.md
+++ b/INSTALL_TTS.md
@@ -26,14 +26,18 @@ Supports 10 languages (English, Chinese, Japanese, Korean, German, French, Russi
 # Activate your virtual environment first
 source .venv/bin/activate
 
-# Install Qwen3-TTS
+# Step 1: Install Qwen3-TTS
 pip install qwen-tts
 
-# Optional: faster inference with flash attention
-MAX_JOBS=4 pip install -U flash-attn --no-build-isolation
+# Step 2: Install flash-attn (REQUIRED - must install AFTER torch)
+# --no-build-isolation is needed because flash-attn needs torch at build time
+MAX_JOBS=4 pip install flash-attn --no-build-isolation
+
+# NOTE: Do NOT use "uv pip install flash-attn" - it will fail.
+# Use regular pip with --no-build-isolation instead.
 
 # Verify
-python -c "from qwen_tts import Qwen3TTSModel; print('Qwen3-TTS OK')"
+python -c "from qwen_tts import Qwen3TTSModel; import flash_attn; print('Qwen3-TTS OK')"
 ```
 
 ### Option B: Chatterbox TTS (ResembleAI)
diff --git a/requirements.txt b/requirements.txt
index 2f1d421..9291ff3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -27,6 +27,8 @@ resemble-perth>=1.0.1
 conformer>=0.3.2
 
 # Qwen3-TTS: pip install qwen-tts (installs its own deps)
+# flash-attn is required for Qwen3-TTS. Install AFTER torch:
+#   pip install flash-attn --no-build-isolation
 
 # Optional: for auto-type feature
 pyautogui
diff --git a/scripts/install.sh b/scripts/install.sh
index 438cbe6..7dfbe77 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -246,23 +246,26 @@ install_qwen3() {
         rm -rf "$TEMP_DIR"
     }
 
-    # Optional: flash attention for faster inference
-    echo -e "${YELLOW}Do you want to install flash-attn for faster Qwen3-TTS inference? (y/N)${NC}"
-    echo -e "${BLUE}(Requires a compatible GPU and may take a while to compile)${NC}"
-    read -r flash_response
-    if [[ "$flash_response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
-        echo -e "${YELLOW}Installing flash-attn...${NC}"
-        MAX_JOBS=4 $PYTHON_CMD -m pip install -U flash-attn --no-build-isolation 2>/dev/null && {
-            echo -e "${GREEN}✓ flash-attn installed${NC}"
-        } || {
-            echo -e "${YELLOW}  flash-attn installation failed (optional - TTS will still work)${NC}"
-        }
-    fi
+    # Install flash-attn (required for Qwen3-TTS performance)
+    # Must use --no-build-isolation because flash-attn needs torch at build time
+    # but doesn't declare it as a build dependency
+    echo -e "${YELLOW}Installing flash-attn (required for Qwen3-TTS)...${NC}"
+    echo -e "${BLUE}This may take a few minutes to compile...${NC}"
+    MAX_JOBS=4 $PYTHON_CMD -m pip install -U flash-attn --no-build-isolation 2>/dev/null && {
+        echo -e "${GREEN}✓ flash-attn installed${NC}"
+    } || {
+        echo -e "${RED}✗ flash-attn installation failed${NC}"
+        echo -e "${BLUE}  Try manually: pip install flash-attn --no-build-isolation${NC}"
+        echo -e "${BLUE}  Make sure torch is installed first and you have a compatible GPU + CUDA toolkit.${NC}"
+    }
 
     # Verify installation
     $PYTHON_CMD -c "from qwen_tts import Qwen3TTSModel; print('  ✓ Qwen3-TTS import successful')" 2>/dev/null || {
         echo -e "${YELLOW}  Note: Qwen3-TTS import test failed. It may still work at runtime.${NC}"
     }
+    $PYTHON_CMD -c "import flash_attn; print('  ✓ flash-attn import successful')" 2>/dev/null || {
+        echo -e "${YELLOW}  Warning: flash-attn not available. Qwen3-TTS will be slower.${NC}"
+    }
     echo
 }
 
diff --git a/src/tts_provider.py b/src/tts_provider.py
index 8a73c64..4564ba8 100644
--- a/src/tts_provider.py
+++ b/src/tts_provider.py
@@ -36,9 +36,10 @@ def get_available_backends() -> dict[str, bool]:
     except (ImportError, ModuleNotFoundError, Exception):
         backends["chatterbox"] = False
 
-    # Check Qwen3-TTS - verify the model class is importable
+    # Check Qwen3-TTS - verify both the model class and flash-attn are importable
     try:
         from qwen_tts import Qwen3TTSModel  # noqa: F401
+        import flash_attn  # noqa: F401
         backends["qwen3"] = True
     except (ImportError, ModuleNotFoundError, Exception):
         backends["qwen3"] = False
@@ -239,23 +240,24 @@ class Qwen3TTSProvider(BaseTTSProvider):
             kwargs = {"device_map": f"{self.device}:0" if self.device == "cuda" else self.device}
             kwargs["dtype"] = dtype
 
-            # Try flash attention if available
+            # flash-attn is required for Qwen3-TTS
             try:
                 import flash_attn  # noqa: F401
                 kwargs["attn_implementation"] = "flash_attention_2"
             except ImportError:
-                pass
+                raise ImportError(
+                    "flash-attn is required for Qwen3-TTS but is not installed.\n"
+                    "Install with: pip install flash-attn --no-build-isolation\n"
+                    "(torch must be installed first)\n"
+                    "Or run: ./scripts/install.sh --tts=qwen3"
+                )
 
             self.model = Qwen3TTSModel.from_pretrained(model_name, **kwargs)
             self._initialized = True
             print(f"Qwen3-TTS loaded on {self.device}")
 
         except ImportError as e:
-            raise ImportError(
-                "Qwen3-TTS is not installed.\n"
-                "Install with: pip install qwen-tts\n"
-                "Or run: ./scripts/install.sh --tts"
-            ) from e
+            raise ImportError(str(e)) from e
         except Exception as e:
             raise RuntimeError(f"Failed to load Qwen3-TTS: {e}") from e
 
@@ -266,6 +268,7 @@ class Qwen3TTSProvider(BaseTTSProvider):
 
         try:
             from qwen_tts import Qwen3TTSModel
+            import flash_attn  # noqa: F401
 
             model_name = self._get_model_name(clone=True)
             print(f"Loading Qwen3-TTS clone model ({model_name})...")
@@ -273,16 +276,16 @@ class Qwen3TTSProvider(BaseTTSProvider):
             dtype = torch.bfloat16 if self.device == "cuda" else torch.float32
             kwargs = {"device_map": f"{self.device}:0" if self.device == "cuda" else self.device}
             kwargs["dtype"] = dtype
-
-            try:
-                import flash_attn  # noqa: F401
-                kwargs["attn_implementation"] = "flash_attention_2"
-            except ImportError:
-                pass
+            kwargs["attn_implementation"] = "flash_attention_2"
 
             self._clone_model = Qwen3TTSModel.from_pretrained(model_name, **kwargs)
             print("Qwen3-TTS clone model loaded")
 
+        except ImportError as e:
+            raise ImportError(
+                "flash-attn is required for Qwen3-TTS.\n"
+                "Install with: pip install flash-attn --no-build-isolation"
+            ) from e
         except Exception as e:
             raise RuntimeError(f"Failed to load Qwen3-TTS clone model: {e}") from e