fix: make flash-attn required for Qwen3-TTS, not optional
- 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
@@ -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) @@ -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 @@ -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 } @@ -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