diff --git a/deploy/runpod/README.md b/deploy/runpod/README.md new file mode 100644 index 0000000..7b57553 --- /dev/null +++ b/deploy/runpod/README.md @@ -0,0 +1,97 @@ +# Deploy OpenClaw Voice on RunPod + +Deploy OpenClaw Voice on RunPod for GPU-accelerated voice inference. + +## Quick Deploy + +### 1. Create a RunPod Account +Sign up at [runpod.io](https://runpod.io) + +### 2. Deploy from Template + +Use our pre-built template: + +``` +Template ID: openclaw-voice +Docker Image: ghcr.io/purple-horizons/openclaw-voice:latest +``` + +Or deploy manually: + +### 3. Manual Deployment + +**Create a new Pod:** +- GPU: RTX 4090 recommended ($0.44/hr) +- Docker Image: `ghcr.io/purple-horizons/openclaw-voice:latest` +- Expose Port: 8765 +- Volume: 20GB (for model cache) + +**Environment Variables:** +``` +OPENCLAW_STT_MODEL=large-v3-turbo +OPENCLAW_STT_DEVICE=cuda +OPENCLAW_REQUIRE_AUTH=true +OPENCLAW_MASTER_KEY= +OPENAI_API_KEY= +``` + +### 4. Connect + +Once deployed, get your Pod's public URL: +``` +wss://-8765.proxy.runpod.net/ws?api_key= +``` + +## Cost Optimization + +### On-Demand (Pay per hour) +- RTX 4090: ~$0.44/hr +- RTX 3090: ~$0.30/hr +- A10: ~$0.50/hr + +### Spot Instances (Up to 80% cheaper) +- Use spot instances for non-critical workloads +- May be interrupted with 30s notice + +### Reserved (Best for production) +- Reserve a GPU for consistent pricing +- No interruptions + +## Scaling + +### Single Pod +- Handles ~10-20 concurrent voice sessions +- Good for testing and small deployments + +### Multiple Pods with Load Balancer +1. Deploy 2+ pods +2. Use RunPod's serverless endpoints +3. Or put behind Cloudflare Load Balancer + +## Monitoring + +### Health Check +```bash +curl https:/// +``` + +### Check Logs +```bash +runpodctl logs +``` + +## Troubleshooting + +### GPU Not Detected +- Ensure NVIDIA drivers are loaded +- Check `nvidia-smi` in pod terminal + +### Out of Memory +- Reduce batch size +- Use smaller Whisper model (base vs large) +- Upgrade to GPU with more VRAM + +### High Latency +- Use larger GPU +- Enable streaming responses +- Check network latency to RunPod region diff --git a/scripts/download_models.py b/scripts/download_models.py new file mode 100644 index 0000000..7b4c9b1 --- /dev/null +++ b/scripts/download_models.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Download Whisper models for offline use. + +Usage: + python scripts/download_models.py [model_name] + +Models: + tiny - 39M params, ~1GB VRAM (fastest) + base - 74M params, ~1GB VRAM + small - 244M params, ~2GB VRAM + medium - 769M params, ~5GB VRAM + large-v3-turbo - 809M params, ~6GB VRAM (best quality/speed) +""" + +import sys +import os + + +def download_model(model_name: str = "base"): + """Download a Whisper model.""" + print(f"Downloading Whisper model: {model_name}") + print("This may take a few minutes...") + + try: + from faster_whisper import WhisperModel + + # This will download the model if not cached + model = WhisperModel(model_name, device="cpu", compute_type="int8") + + print(f"✅ Model '{model_name}' downloaded successfully!") + print(f" Cached at: ~/.cache/huggingface/") + + # Test the model + import numpy as np + audio = np.zeros(16000, dtype=np.float32) + segments, info = model.transcribe(audio) + list(segments) # Consume generator + + print(f"✅ Model tested successfully!") + + except ImportError: + print("❌ faster-whisper not installed. Run:") + print(" pip install faster-whisper") + sys.exit(1) + except Exception as e: + print(f"❌ Error: {e}") + sys.exit(1) + + +def list_models(): + """List available models.""" + models = { + "tiny": "39M params, ~1GB VRAM, fastest", + "base": "74M params, ~1GB VRAM, good balance", + "small": "244M params, ~2GB VRAM", + "medium": "769M params, ~5GB VRAM", + "large-v3": "1.5B params, ~10GB VRAM, best quality", + "large-v3-turbo": "809M params, ~6GB VRAM, best quality/speed ratio", + } + + print("Available Whisper models:") + print() + for name, desc in models.items(): + print(f" {name:20} - {desc}") + print() + print("Recommended: large-v3-turbo (GPU) or base (CPU)") + + +if __name__ == "__main__": + if len(sys.argv) < 2: + list_models() + print() + model = input("Enter model name to download (or 'q' to quit): ").strip() + if model.lower() == 'q': + sys.exit(0) + else: + model = sys.argv[1] + + if model in ["-h", "--help", "help"]: + list_models() + sys.exit(0) + + download_model(model) diff --git a/scripts/generate_master_key.py b/scripts/generate_master_key.py new file mode 100644 index 0000000..74601ea --- /dev/null +++ b/scripts/generate_master_key.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +""" +Generate a secure master API key for OpenClaw Voice. + +Usage: + python scripts/generate_master_key.py + +Add the output to your .env file or deployment environment. +""" + +import secrets + + +def generate_master_key() -> str: + """Generate a secure master key.""" + return f"ocv_master_{secrets.token_urlsafe(32)}" + + +if __name__ == "__main__": + key = generate_master_key() + + print("=" * 60) + print("OpenClaw Voice Master Key") + print("=" * 60) + print() + print(f"OPENCLAW_MASTER_KEY={key}") + print() + print("Add this to your .env file or deployment environment.") + print("Keep this key SECRET - it has full admin access.") + print("=" * 60)