Simplify Makefile to run locally, remove all remote SSH/rsync

Remove REMOTE_HOST, REMOTE_USER, REMOTE_PATH, vars.sh loading.
All docker commands use sudo locally. Tests hit localhost:8000.
Delete old non-gendered voice WAV files (alloy, echo, fable, etc).
This commit is contained in:
russell@unturf.com 2026-01-27 12:51:08 -05:00
parent ebfa0319d1
commit c56b8cb24e
21 changed files with 191 additions and 246 deletions

437
Makefile
View file

@ -1,39 +1,26 @@
# Raccoon Mission: uncloseai-speech Development Makefile
# Deploy to remote server with ease
# Configuration is loaded from vars.sh (copy vars.sh.example to vars.sh)
# Raccoon Mission: uncloseai-speech Makefile
# All commands run locally. Use git push/pull to sync between machines.
# Load configuration from vars.sh if it exists
ifneq (,$(wildcard vars.sh))
include vars.sh
export
endif
# Fallback defaults if vars.sh is not found
REMOTE_HOST ?= localhost
REMOTE_USER ?= $(USER)
REMOTE_PATH ?= ~/uncloseai-speech
CONTAINER_NAME ?= uncloseai-speech-server-1
.PHONY: help deploy sync restart logs test clean stop start voices voices-qwen voices-piper voices-xtts voices-kokoro test-kokoro voices-silero test-silero voices-chatterbox test-chatterbox push-all hydrate load-test test-qwen venv venv-run local local-cpu
.PHONY: help deploy restart logs test clean stop start voices voices-qwen voices-piper voices-xtts voices-kokoro test-kokoro voices-silero test-silero voices-chatterbox test-chatterbox push-all hydrate load-test test-qwen venv venv-run local local-cpu
help:
@echo "🦝 Raccoon TTS Mission - Development Commands"
@echo "Raccoon TTS Mission - Development Commands"
@echo ""
@echo "Quick Start (Docker with GPU):"
@echo " make local - Build and run locally with GPU support"
@echo " make local-cpu - Build and run locally (CPU only, slower)"
@echo " make test - Test Qwen3-TTS endpoint"
@echo "Docker (GPU):"
@echo " make deploy - Build and start container (GPU)"
@echo " make deploy-cpu - Build and start container (CPU only)"
@echo " make restart - Rebuild and restart container"
@echo " make stop - Stop container"
@echo " make start - Start container (no rebuild)"
@echo " make clean - Stop and remove container + volumes"
@echo " make logs - Tail container logs"
@echo ""
@echo "Quick Start (No Docker):"
@echo "No Docker:"
@echo " make venv - Create Python virtual environment"
@echo " make venv-run - Run server in virtual environment"
@echo ""
@echo "Remote Deployment:"
@echo " make deploy - Full deploy: sync files, restart container"
@echo " make sync - Sync local files to remote server"
@echo " make restart - Restart the Docker container"
@echo ""
@echo "Testing:"
@echo " make test - Test TTS endpoint (Qwen3-TTS)"
@echo " make test-qwen - Test Qwen3-TTS voice cloning"
@ -49,268 +36,227 @@ help:
@echo " make voices-kokoro - Download Kokoro models"
@echo " make voices-silero - Download Silero models"
@echo ""
@echo "Container Management:"
@echo " make start - Start Docker container"
@echo " make stop - Stop Docker container"
@echo " make clean - Stop and remove container"
@echo ""
@echo "Git:"
@echo " make push-all - Push to all git remotes"
sync:
@echo "📦 Syncing files to $(REMOTE_HOST)..."
rsync -avz --exclude '.git' --exclude '__pycache__' --exclude '*.pyc' \
--exclude 'voices/*' --exclude 'config/voice_to_speaker.yaml' \
./ $(REMOTE_USER)@$(REMOTE_HOST):$(REMOTE_PATH)/
@echo "📝 Ensuring speech.env exists..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && [ -f speech.env ] || cp sample.env speech.env"
deploy: sync restart
@echo "✅ Deployment complete!"
# ============================================================================
# Local Development (Docker)
# Docker
# ============================================================================
local:
@echo "🐳 Building and running with GPU support..."
deploy:
@[ -f speech.env ] || cp sample.env speech.env
docker compose up -d --build
@echo ""
@echo "✅ Container started! Qwen3-TTS model will download on first request (~3.4GB)"
@echo " Test with: make test"
@echo " View logs: make logs"
sudo docker compose up -d --build
local-cpu:
@echo "🐳 Building and running (CPU only - slower inference)..."
deploy-cpu:
@[ -f speech.env ] || cp sample.env speech.env
docker compose -f docker-compose.cpu.yml up -d --build
@echo ""
@echo "✅ Container started (CPU mode)!"
@echo " Note: Qwen3-TTS is ~10x slower on CPU"
@echo " Test with: make test"
sudo docker compose -f docker-compose.cpu.yml up -d --build
restart:
sudo docker compose up -d --build
stop:
sudo docker compose down
start:
sudo docker compose up -d
clean: stop
sudo docker compose down -v
logs:
sudo docker logs -f $(CONTAINER_NAME)
# ============================================================================
# Local Development (Python venv - no Docker)
# Python venv (no Docker)
# ============================================================================
VENV_DIR := .venv
PYTHON := python3
venv:
@echo "🐍 Creating Python virtual environment..."
@echo "Creating Python virtual environment..."
@if [ ! -d "$(VENV_DIR)" ]; then \
$(PYTHON) -m venv $(VENV_DIR); \
echo "Virtual environment created at $(VENV_DIR)"; \
echo "Virtual environment created at $(VENV_DIR)"; \
else \
echo " Virtual environment already exists at $(VENV_DIR)"; \
echo "Virtual environment already exists at $(VENV_DIR)"; \
fi
@echo ""
@echo "📦 Installing dependencies..."
@echo "Installing dependencies..."
$(VENV_DIR)/bin/pip install --upgrade pip
$(VENV_DIR)/bin/pip install -r requirements.txt
@echo ""
@echo "✅ Setup complete!"
@echo ""
@echo "To activate manually:"
@echo " source $(VENV_DIR)/bin/activate"
@echo ""
@echo "To run the server:"
@echo " make venv-run"
@echo ""
@echo "Or run directly:"
@echo " $(VENV_DIR)/bin/python speech.py"
@echo "Done. Run: make venv-run"
venv-run:
@echo "🚀 Starting uncloseai-speech server..."
@if [ ! -d "$(VENV_DIR)" ]; then \
echo "Virtual environment not found. Run 'make venv' first."; \
echo "Virtual environment not found. Run 'make venv' first."; \
exit 1; \
fi
@[ -d "config" ] || mkdir -p config
@[ -d "voices" ] || mkdir -p voices
@echo ""
@echo "Server starting on http://localhost:8000"
@echo "Qwen3-TTS model will download on first request (~3.4GB)"
@echo ""
$(VENV_DIR)/bin/python speech.py
venv-clean:
@echo "🧹 Removing virtual environment..."
rm -rf $(VENV_DIR)
@echo "✅ Virtual environment removed"
restart:
@echo "🔄 Rebuilding and restarting container on $(REMOTE_HOST)..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && docker compose up -d --build"
stop:
@echo "🛑 Stopping container on $(REMOTE_HOST)..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && docker compose down"
start:
@echo "▶️ Starting container on $(REMOTE_HOST)..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && docker compose up -d"
clean: stop
@echo "🧹 Removing container..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "cd $(REMOTE_PATH) && docker compose down -v"
logs:
@echo "📋 Tailing logs from $(REMOTE_HOST)..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker logs -f $(CONTAINER_NAME)"
# ============================================================================
# Testing
# ============================================================================
test: test-qwen
test-qwen:
@echo "🧪 Testing Qwen3-TTS endpoint (default model)..."
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
@echo "Testing Qwen3-TTS endpoint..."
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-qwen","voice":"alloy","input":"Raccoon mission TTS test with Qwen three"}' \
-d '{"model":"tts-1-qwen","voice":"aria","input":"Raccoon mission TTS test with Qwen three"}' \
-o /tmp/qwen_test.mp3
@echo "✅ Test complete! Playing audio..."
@firefox /tmp/qwen_test.mp3 || mpv /tmp/qwen_test.mp3 || echo "Install firefox or mpv to play audio"
voices: voices-qwen
@echo "✅ Qwen3-TTS ready (model downloads automatically on first use)"
voices-qwen:
@echo "🎤 Qwen3-TTS models download automatically on first use"
@echo " Model: Qwen/Qwen3-TTS-12Hz-1.7B-Base (~3.4GB)"
@echo " The model will be cached in /app/voices/hub/"
@echo ""
@echo "To pre-download, run: make test-qwen"
voices-all: voices-qwen voices-piper voices-xtts voices-silero
@echo "✅ All voices downloaded (Qwen, Piper, XTTS, Silero)!"
voices-piper:
@echo "🎤 Downloading all Piper voices..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker exec $(CONTAINER_NAME) bash -c '\
set -e; \
BASE_URL=\"https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0\"; \
download_voice() { \
local path=\"\$$1\"; \
local name=\"\$$2\"; \
mkdir -p \"/app/voices/\$$path\"; \
cd \"/app/voices/\$$path\"; \
echo \"Downloading \$$name...\"; \
curl -f -L \"\$$BASE_URL/\$$path/\$$name.onnx\" -o \"\$$name.onnx\" || echo \"Failed to download \$$name.onnx\"; \
curl -f -L \"\$$BASE_URL/\$$path/\$$name.onnx.json\" -o \"\$$name.onnx.json\" || echo \"Failed to download \$$name.onnx.json\"; \
}; \
echo \"=== Downloading English US voices ===\"; \
download_voice \"en/en_US/libritts_r/medium\" \"en_US-libritts_r-medium\"; \
download_voice \"en/en_US/amy/medium\" \"en_US-amy-medium\"; \
download_voice \"en/en_US/arctic/medium\" \"en_US-arctic-medium\"; \
download_voice \"en/en_US/bryce/medium\" \"en_US-bryce-medium\"; \
download_voice \"en/en_US/danny/low\" \"en_US-danny-low\"; \
download_voice \"en/en_US/hfc_female/medium\" \"en_US-hfc_female-medium\"; \
download_voice \"en/en_US/hfc_male/medium\" \"en_US-hfc_male-medium\"; \
download_voice \"en/en_US/joe/medium\" \"en_US-joe-medium\"; \
download_voice \"en/en_US/john/medium\" \"en_US-john-medium\"; \
download_voice \"en/en_US/kathleen/low\" \"en_US-kathleen-low\"; \
download_voice \"en/en_US/kristin/medium\" \"en_US-kristin-medium\"; \
download_voice \"en/en_US/kusal/medium\" \"en_US-kusal-medium\"; \
download_voice \"en/en_US/l2arctic/medium\" \"en_US-l2arctic-medium\"; \
download_voice \"en/en_US/lessac/medium\" \"en_US-lessac-medium\"; \
download_voice \"en/en_US/libritts/high\" \"en_US-libritts-high\"; \
download_voice \"en/en_US/ljspeech/medium\" \"en_US-ljspeech-medium\"; \
download_voice \"en/en_US/norman/medium\" \"en_US-norman-medium\"; \
download_voice \"en/en_US/reza_ibrahim/medium\" \"en_US-reza_ibrahim-medium\"; \
download_voice \"en/en_US/ryan/high\" \"en_US-ryan-high\"; \
download_voice \"en/en_US/sam/medium\" \"en_US-sam-medium\"; \
echo \"=== Downloading English GB voices ===\"; \
download_voice \"en/en_GB/northern_english_male/medium\" \"en_GB-northern_english_male-medium\"; \
download_voice \"en/en_GB/alan/medium\" \"en_GB-alan-medium\"; \
download_voice \"en/en_GB/alba/medium\" \"en_GB-alba-medium\"; \
download_voice \"en/en_GB/aru/medium\" \"en_GB-aru-medium\"; \
download_voice \"en/en_GB/cori/medium\" \"en_GB-cori-medium\"; \
download_voice \"en/en_GB/jenny_dioco/medium\" \"en_GB-jenny_dioco-medium\"; \
download_voice \"en/en_GB/semaine/medium\" \"en_GB-semaine-medium\"; \
download_voice \"en/en_GB/southern_english_female/low\" \"en_GB-southern_english_female-low\"; \
download_voice \"en/en_GB/vctk/medium\" \"en_GB-vctk-medium\"; \
echo \"=== All Piper voices downloaded! ===\"'"
@echo "✅ All Piper voices downloaded!"
voices-xtts:
@echo "🎤 Downloading XTTS speaker samples..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker exec $(CONTAINER_NAME) bash -c 'cd /app && ./scripts/download_samples.sh'"
@echo "✅ XTTS speaker samples downloaded!"
voices-kokoro:
@echo "🎤 Downloading Kokoro TTS models..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker exec $(CONTAINER_NAME) bash -c '\
mkdir -p /app/voices/kokoro && \
cd /app/voices/kokoro && \
huggingface-cli download hexgrad/kokoro-82m --local-dir .'"
@echo "✅ Kokoro models downloaded!"
@echo "Saved to /tmp/qwen_test.mp3"
test-kokoro:
@echo "🧪 Testing Kokoro fast synthesis..."
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
@echo "Testing Kokoro fast synthesis..."
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-kokoro","voice":"alloy","input":"Testing Kokoro fast decoder synthesis"}' \
-d '{"model":"tts-1-kokoro","voice":"aria","input":"Testing Kokoro fast decoder synthesis"}' \
-o /tmp/kokoro_test.mp3
@echo "✅ Test complete! Playing audio..."
@firefox /tmp/kokoro_test.mp3 || mpv /tmp/kokoro_test.mp3 || echo "Install firefox or mpv to play audio"
@echo ""
@echo "Saved to /tmp/kokoro_test.mp3"
test-xtts:
@echo "🧪 Testing XTTS HD endpoint (this may take 1-2 minutes on first run)..."
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
@echo "Testing XTTS HD endpoint..."
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-hd","voice":"alloy","input":"Testing XTTS high definition"}' \
-d '{"model":"tts-1-hd","voice":"aria","input":"Testing XTTS high definition"}' \
-o /tmp/xtts_test.mp3
@echo "✅ Test complete! Playing audio..."
@firefox /tmp/xtts_test.mp3 || mpv /tmp/xtts_test.mp3 || echo "Install firefox or mpv to play audio"
push-all:
@echo "🚀 Pushing to all remotes..."
git push origin main
git push github main
@echo "✅ Pushed to origin and github!"
voices-silero:
@echo "🎤 Downloading Silero TTS models..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker exec $(CONTAINER_NAME) bash -c '\
cd /app/voices && \
python3 -c \"import torch; \
for lang in [\"\"en\"\", \"\"ru\"\", \"\"de\"\", \"\"es\"\", \"\"fr\"\"]: \
model, *_ = torch.hub.load(repo_or_dir=\"\"snakers4/silero-models\"\", model=\"\"silero_tts\"\", language=lang, speaker=\"\"v4_\"\"+lang if lang==\"\"en\"\" else \"\"v3_\"\"+lang); \
print(f\"\"Downloaded Silero {lang}\"\")\"'"
@echo "✅ Silero models downloaded!"
@echo ""
@echo "Saved to /tmp/xtts_test.mp3"
test-silero:
@echo "🧪 Testing Silero endpoint..."
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
@echo "Testing Silero endpoint..."
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-silero","voice":"alloy","input":"Testing Silero fast synthesis"}' \
-d '{"model":"tts-1-silero","voice":"aria","input":"Testing Silero fast synthesis"}' \
-o /tmp/silero_test.mp3
@echo "✅ Test complete! Playing audio..."
@firefox /tmp/silero_test.mp3 || mpv /tmp/silero_test.mp3 || echo "Install firefox or mpv to play audio"
voices-chatterbox:
@echo "🎤 Downloading Chatterbox models..."
ssh $(REMOTE_USER)@$(REMOTE_HOST) "docker exec $(CONTAINER_NAME) bash -c '\
mkdir -p /app/voices/chatterbox && \
cd /app/voices/chatterbox && \
huggingface-cli download resemble-ai/chatterbox --local-dir .'"
@echo "✅ Chatterbox models downloaded!"
@echo ""
@echo "Saved to /tmp/silero_test.mp3"
test-chatterbox:
@echo "🧪 Testing Chatterbox with emotion control..."
curl -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
@echo "Testing Chatterbox with emotion control..."
curl -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-chatter","voice":"alloy","input":"Testing emotional speech synthesis"}' \
-d '{"model":"tts-1-chatter","voice":"aria","input":"Testing emotional speech synthesis"}' \
-o /tmp/chatterbox_test.mp3
@echo "✅ Test complete! Playing audio..."
@firefox /tmp/chatterbox_test.mp3 || mpv /tmp/chatterbox_test.mp3 || echo "Install firefox or mpv to play audio"
@echo ""
@echo "Saved to /tmp/chatterbox_test.mp3"
# ============================================================================
# Voice Downloads
# ============================================================================
voices: voices-qwen
@echo "Qwen3-TTS ready (model downloads automatically on first use)"
voices-qwen:
@echo "Qwen3-TTS models download automatically on first use"
@echo " Model: Qwen/Qwen3-TTS-12Hz-1.7B-Base (~3.4GB)"
@echo " To pre-download, run: make test-qwen"
voices-all: voices-qwen voices-piper voices-xtts voices-silero
@echo "All voices downloaded!"
voices-piper:
@echo "Downloading all Piper voices..."
sudo docker exec $(CONTAINER_NAME) bash -c '\
set -e; \
BASE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0"; \
download_voice() { \
local path="$$1"; \
local name="$$2"; \
mkdir -p "/app/voices/$$path"; \
cd "/app/voices/$$path"; \
echo "Downloading $$name..."; \
curl -f -L "$$BASE_URL/$$path/$$name.onnx" -o "$$name.onnx" || echo "Failed: $$name.onnx"; \
curl -f -L "$$BASE_URL/$$path/$$name.onnx.json" -o "$$name.onnx.json" || echo "Failed: $$name.onnx.json"; \
}; \
echo "=== English US voices ==="; \
download_voice "en/en_US/libritts_r/medium" "en_US-libritts_r-medium"; \
download_voice "en/en_US/amy/medium" "en_US-amy-medium"; \
download_voice "en/en_US/arctic/medium" "en_US-arctic-medium"; \
download_voice "en/en_US/bryce/medium" "en_US-bryce-medium"; \
download_voice "en/en_US/danny/low" "en_US-danny-low"; \
download_voice "en/en_US/hfc_female/medium" "en_US-hfc_female-medium"; \
download_voice "en/en_US/hfc_male/medium" "en_US-hfc_male-medium"; \
download_voice "en/en_US/joe/medium" "en_US-joe-medium"; \
download_voice "en/en_US/john/medium" "en_US-john-medium"; \
download_voice "en/en_US/kathleen/low" "en_US-kathleen-low"; \
download_voice "en/en_US/kristin/medium" "en_US-kristin-medium"; \
download_voice "en/en_US/kusal/medium" "en_US-kusal-medium"; \
download_voice "en/en_US/l2arctic/medium" "en_US-l2arctic-medium"; \
download_voice "en/en_US/lessac/medium" "en_US-lessac-medium"; \
download_voice "en/en_US/libritts/high" "en_US-libritts-high"; \
download_voice "en/en_US/ljspeech/medium" "en_US-ljspeech-medium"; \
download_voice "en/en_US/norman/medium" "en_US-norman-medium"; \
download_voice "en/en_US/reza_ibrahim/medium" "en_US-reza_ibrahim-medium"; \
download_voice "en/en_US/ryan/high" "en_US-ryan-high"; \
download_voice "en/en_US/sam/medium" "en_US-sam-medium"; \
echo "=== English GB voices ==="; \
download_voice "en/en_GB/northern_english_male/medium" "en_GB-northern_english_male-medium"; \
download_voice "en/en_GB/alan/medium" "en_GB-alan-medium"; \
download_voice "en/en_GB/alba/medium" "en_GB-alba-medium"; \
download_voice "en/en_GB/aru/medium" "en_GB-aru-medium"; \
download_voice "en/en_GB/cori/medium" "en_GB-cori-medium"; \
download_voice "en/en_GB/jenny_dioco/medium" "en_GB-jenny_dioco-medium"; \
download_voice "en/en_GB/semaine/medium" "en_GB-semaine-medium"; \
download_voice "en/en_GB/southern_english_female/low" "en_GB-southern_english_female-low"; \
download_voice "en/en_GB/vctk/medium" "en_GB-vctk-medium"; \
echo "=== Done ==="'
voices-xtts:
@echo "Downloading XTTS speaker samples..."
sudo docker exec $(CONTAINER_NAME) bash -c 'cd /app && ./scripts/download_samples.sh'
voices-kokoro:
@echo "Downloading Kokoro TTS models..."
sudo docker exec $(CONTAINER_NAME) bash -c '\
mkdir -p /app/voices/kokoro && \
cd /app/voices/kokoro && \
huggingface-cli download hexgrad/kokoro-82m --local-dir .'
voices-silero:
@echo "Downloading Silero TTS models..."
sudo docker exec $(CONTAINER_NAME) bash -c '\
cd /app/voices && \
python3 -c "import torch; \
for lang in [\"en\", \"ru\", \"de\", \"es\", \"fr\"]: \
model, *_ = torch.hub.load(repo_or_dir=\"snakers4/silero-models\", model=\"silero_tts\", language=lang, speaker=\"v4_\"+lang if lang==\"en\" else \"v3_\"+lang); \
print(f\"Downloaded Silero {lang}\")"'
voices-chatterbox:
@echo "Downloading Chatterbox models..."
sudo docker exec $(CONTAINER_NAME) bash -c '\
mkdir -p /app/voices/chatterbox && \
cd /app/voices/chatterbox && \
huggingface-cli download resemble-ai/chatterbox --local-dir .'
# ============================================================================
# Git
# ============================================================================
push-all:
git push origin main
git push github main
# ============================================================================
# Stress Testing
# ============================================================================
hydrate:
@echo "🦝 Hydrating all TTS models by testing ALL voices..."
@echo "This will test all 227 voices across 4 engines (Piper, XTTS, Silero, Kokoro)"
@echo "Hydrating all TTS models by testing ALL voices..."
@echo ""
@mkdir -p /tmp/hydrate_test
@curl -s http://$(REMOTE_HOST):8000/v1/voices | jq -r '.data[] as $$model | $$model.voices[] | "\($$model.id):\(.)"' > /tmp/hydrate_voices.txt
@curl -s http://localhost:8000/v1/voices | jq -r '.data[] as $$model | $$model.voices[] | "\($$model.id):\(.)"' > /tmp/hydrate_voices.txt
@TOTAL=$$(wc -l < /tmp/hydrate_voices.txt); \
COUNT=0; \
FAILED=0; \
@ -318,18 +264,18 @@ hydrate:
while IFS=: read -r MODEL VOICE; do \
COUNT=$$((COUNT + 1)); \
printf "[%3d/%3d] Testing %-20s %-30s ... " "$$COUNT" "$$TOTAL" "$$MODEL" "$$VOICE"; \
if curl -s -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
if curl -s -X POST http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d "{\"voice\":\"$$VOICE\",\"input\":\"Hydration test\"}" \
-o /tmp/hydrate_test/$${MODEL}_$${VOICE}.mp3 2>&1 | grep -q "error"; then \
echo "FAILED"; \
echo "FAILED"; \
FAILED=$$((FAILED + 1)); \
else \
SIZE=$$(stat -c%s /tmp/hydrate_test/$${MODEL}_$${VOICE}.mp3 2>/dev/null || echo 0); \
if [ "$$SIZE" -gt 1000 ]; then \
echo "OK ($${SIZE} bytes)"; \
echo "OK ($${SIZE} bytes)"; \
else \
echo "⚠️ SMALL ($${SIZE} bytes)"; \
echo "SMALL ($${SIZE} bytes)"; \
FAILED=$$((FAILED + 1)); \
fi; \
fi; \
@ -337,19 +283,18 @@ hydrate:
END_TIME=$$(date +%s); \
DURATION=$$((END_TIME - START_TIME)); \
echo ""; \
echo "🎉 Hydration complete!"; \
echo " Total voices: $$TOTAL"; \
echo " Successful: $$((TOTAL - FAILED))"; \
echo " Failed: $$FAILED"; \
echo " Duration: $${DURATION}s"; \
echo " Output: /tmp/hydrate_test/"
echo "Hydration complete!"; \
echo " Total voices: $$TOTAL"; \
echo " Successful: $$((TOTAL - FAILED))"; \
echo " Failed: $$FAILED"; \
echo " Duration: $${DURATION}s"; \
echo " Output: /tmp/hydrate_test/"
load-test:
@echo "🚀 Load testing TTS service with random concurrent requests..."
@echo "This will send 100 concurrent requests with random voices across all models"
@echo "Load testing TTS service with random concurrent requests..."
@echo ""
@mkdir -p /tmp/load_test
@curl -s http://$(REMOTE_HOST):8000/v1/voices | jq -r '.data[] as $$model | $$model.voices[] | "\($$model.id):\(.)"' > /tmp/load_test_voices.txt
@curl -s http://localhost:8000/v1/voices | jq -r '.data[] as $$model | $$model.voices[] | "\($$model.id):\(.)"' > /tmp/load_test_voices.txt
@TOTAL_VOICES=$$(wc -l < /tmp/load_test_voices.txt); \
REQUESTS=100; \
CONCURRENT=10; \
@ -366,7 +311,7 @@ load-test:
VOICE=\$$(echo \$$VOICE_SPEC | cut -d: -f2); \
NUM={}; \
START=\$$(date +%s%3N); \
if curl -s -X POST http://$(REMOTE_HOST):8000/v1/audio/speech \
if curl -s -X POST http://localhost:8000/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{\"model\":\"'\$$MODEL'\",\"voice\":\"'\$$VOICE'\",\"input\":\"Load test number '\$$NUM'\"}' \
-o /tmp/load_test/request_\$${NUM}.mp3 2>&1; then \
@ -383,12 +328,12 @@ load-test:
SUCCESS=$$(ls /tmp/load_test/*.mp3 2>/dev/null | wc -l); \
VALID=$$(find /tmp/load_test -name '*.mp3' -size +1000c 2>/dev/null | wc -l); \
echo ""; \
echo "🎉 Load test complete!"; \
echo " Total requests: $$REQUESTS"; \
echo " Files created: $$SUCCESS"; \
echo " Valid audio (>1KB): $$VALID"; \
echo " Failed: $$((REQUESTS - VALID))"; \
echo " Duration: $${DURATION}s"; \
echo " Avg: $$((DURATION * 1000 / REQUESTS))ms per request"; \
echo " Throughput: $$((REQUESTS / DURATION)) req/s"; \
echo " Output: /tmp/load_test/"
echo "Load test complete!"; \
echo " Total requests: $$REQUESTS"; \
echo " Files created: $$SUCCESS"; \
echo " Valid audio (>1KB): $$VALID"; \
echo " Failed: $$((REQUESTS - VALID))"; \
echo " Duration: $${DURATION}s"; \
echo " Avg: $$((DURATION * 1000 / REQUESTS))ms per request"; \
echo " Throughput: $$((REQUESTS / DURATION)) req/s"; \
echo " Output: /tmp/load_test/"