Skip to content

My inference setup on windows is 1GB despite the model being >100MB #113

@BoQsc

Description

@BoQsc

The model is great, but I'm not aware of a good way to run this model in portable way, I ended up with 1GB of installation bloat to get inference going.

import os
import sys
import subprocess
import urllib.request
import zipfile
import time

# --- Configuration & Paths ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
PORTABLE_DIR = os.path.join(BASE_DIR, "portable_python")
PYTHON_EXE = os.path.join(PORTABLE_DIR, "python.exe")

# Force models to download into a local folder, NOT C:\Users\...
os.environ["HF_HOME"] = os.path.join(BASE_DIR, "model_cache")

# URLs
PYTHON_ZIP_URL = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-embed-amd64.zip"
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
KITTEN_WHEEL = "https://github.com/KittenML/KittenTTS/releases/download/0.8.1/kittentts-0.8.1-py3-none-any.whl"

def build_minimal_sandbox():
    """Builds the portable Python environment with aggressive space-saving."""
    print("⬇️ Downloading minimal Windows Embeddable Python 3.11...")
    os.makedirs(PORTABLE_DIR, exist_ok=True)
    zip_path = os.path.join(PORTABLE_DIR, "python.zip")
    urllib.request.urlretrieve(PYTHON_ZIP_URL, zip_path)
    
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(PORTABLE_DIR)
    os.remove(zip_path) # Clean up

    print("🔧 Unlocking environment for pip...")
    pth_file = os.path.join(PORTABLE_DIR, "python311._pth")
    with open(pth_file, 'r') as f:
        content = f.read()
    with open(pth_file, 'w') as f:
        f.write(content.replace('#import site', 'import site'))

    print("⬇️ Installing pip...")
    pip_script = os.path.join(PORTABLE_DIR, "get-pip.py")
    urllib.request.urlretrieve(GET_PIP_URL, pip_script)
    subprocess.run([PYTHON_EXE, pip_script], check=True)
    os.remove(pip_script) # Clean up

    print("🚀 Installing dependencies (Optimized for minimal footprint)...")
    # 1. Install CPU-only PyTorch explicitly without caching to save ~150MB+
    subprocess.run([
        PYTHON_EXE, "-m", "pip", "install", "--no-cache-dir", 
        "torch", "--index-url", "https://download.pytorch.org/whl/cpu"
    ], check=True)
    
    # 2. Install KittenTTS and Soundfile without caching
    subprocess.run([
        PYTHON_EXE, "-m", "pip", "install", "--no-cache-dir", 
        "soundfile", KITTEN_WHEEL
    ], check=True)
    
    print("✅ Sandbox build complete!\n")

def interactive_tts_loop():
    """Runs the actual TTS interface (Only executes inside the sandbox)."""
    from kittentts import KittenTTS
    
    print("🧠 Loading KittenTTS Nano model (INT8)...")
    model = KittenTTS("KittenML/kitten-tts-nano-0.8-int8")
    
    print("\n" + "="*50)
    print("🎙️ PORTABLE TTS ENGINE ACTIVE")
    print("Type your text below to generate audio. Type 'exit' to quit.")
    print("="*50)
    
    file_counter = 1
    while True:
        try:
            text = input("\n📝 Text to speak: ")
            if text.strip().lower() in ['exit', 'quit']:
                break
            if not text.strip():
                continue
                
            output_file = f"output_{file_counter}.wav"
            start_time = time.time()
            
            model.generate_to_file(text=text, output_path=output_file, voice="Luna", speed=1.0)
            
            elapsed = time.time() - start_time
            print(f"🎉 Done! Saved '{output_file}' in {elapsed:.2f} seconds.")
            file_counter += 1
            
        except KeyboardInterrupt:
            break

if __name__ == "__main__":
    # 1. Check if the portable Python exists. If not, build it.
    if not os.path.exists(PYTHON_EXE):
        build_minimal_sandbox()

    # 2. Check WHICH Python is currently running this script.
    current_python = os.path.abspath(sys.executable).lower()
    portable_python = os.path.abspath(PYTHON_EXE).lower()
    
    if current_python != portable_python:
        # We are running via your system Python. 
        # Relaunch THIS script using the portable Python instead.
        print("🔄 Switching to portable sandbox environment...")
        subprocess.run([PYTHON_EXE, __file__])
    else:
        # We are safely inside the portable Python! Run the AI.
        interactive_tts_loop()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions