# app.py from app.gradio_app import build_demo from models.tts_router import cleanup_old_audio import os import shutil import stat import huggingface_hub from shutil import which # ----------------------------- # Model: ensure GGUF is present # ----------------------------- def ensure_model() -> str: """ Download the GGUF model into ./models if not already present. Requires in environment: - LLAMACPP_MODEL_PATH (target path including filename) - HF_MODEL_REPO (e.g. Qwen/Qwen2.5-1.5B-Instruct-GGUF) - HF_MODEL_FILE (e.g. qwen2.5-1.5b-instruct-q4_k_m.gguf) """ model_path = os.getenv("LLAMACPP_MODEL_PATH") repo_id = os.getenv("HF_MODEL_REPO") filename = os.getenv("HF_MODEL_FILE") or (os.path.basename(model_path) if model_path else None) if not model_path or not repo_id or not filename: raise RuntimeError("Missing config: set LLAMACPP_MODEL_PATH and HF_MODEL_REPO (optionally HF_MODEL_FILE).") if os.path.exists(model_path): print(f"[MODEL] Found existing model at {model_path}") return model_path os.makedirs(os.path.dirname(model_path), exist_ok=True) print(f"[MODEL] Downloading {filename} from {repo_id} …") local_path = huggingface_hub.hf_hub_download( repo_id=repo_id, filename=filename, local_dir=os.path.dirname(model_path), local_dir_use_symlinks=False, ) # If hf_hub_download wrote elsewhere (cache), move/rename into desired path if os.path.abspath(local_path) != os.path.abspath(model_path): shutil.copyfile(local_path, model_path) print(f"[MODEL] Ready at {model_path}") return model_path # --------------------------------------- # Piper: rely on the PyPI wheel (piper-tts) # --------------------------------------- # --- keep your existing imports --- import os from huggingface_hub import hf_hub_download def ensure_piper_voice(): """ Make sure the Piper voice model exists at PIPER_MODEL. Defaults to Amy-medium English if none specified. """ target_path = os.getenv("PIPER_MODEL", "models/piper/en_US-amy-medium.onnx") repo_id = os.getenv("PIPER_VOICE_REPO", "rhasspy/piper-voices") hf_file = os.getenv("PIPER_VOICE_FILE", "en/en_US/en_US-amy-medium.onnx") # path inside the repo # If already present, we're done. if os.path.exists(target_path): print(f"[PIPER] Voice model present: {target_path}") return target_path os.makedirs(os.path.dirname(target_path), exist_ok=True) print(f"[PIPER] Downloading voice {hf_file} from {repo_id} …") local = hf_hub_download( repo_id=repo_id, filename=hf_file, local_dir=os.path.dirname(target_path), local_dir_use_symlinks=False, ) # Move/rename to the exact target filename if the name differs if os.path.abspath(local) != os.path.abspath(target_path): import shutil shutil.copyfile(local, target_path) print(f"[PIPER] Voice ready at: {target_path}") return target_path # No PIPER_BIN provided → try PATH resolved = which("piper") if resolved: print(f"[PIPER] Found in PATH: {resolved}") return resolved print("[PIPER] Not found. Install via 'piper-tts' wheel in requirements.txt.") return None def log_env_for_audio(): print("[ENV] TTS_ENGINE =", os.getenv("TTS_ENGINE", "(unset)")) print("[ENV] PIPER_MODEL=", os.getenv("PIPER_MODEL", "(unset)")) print("[ENV] PIPER_BIN =", os.getenv("PIPER_BIN", "(unset)")) # ------------- # Application # ------------- def main(): # Log a few envs to help debug Spaces log_env_for_audio() # Clean runtime/audio on boot cleanup_old_audio(keep_latest=None) # Ensure model exists locally ensure_model() ensure_piper_voice() # Launch Gradio demo = build_demo() # On Spaces, share=True is ignored (safe locally) demo.launch(share=True) if __name__ == "__main__": main()