6.7 KiB
Instructions for Claude Code
Project: uncloseai-speech - Raccoon Mission TTS System License: AGPL v3 (must provide source code to network service users)
Brand Identity
CRITICAL: Always use consistent naming across all files.
Project Name
- Correct:
uncloseai-speech(lowercase, hyphenated) - Wrong: "UncloseAI Speech", "Uncloseai Speech", "UncloseAI-Speech"
Organization Name
- Correct:
uncloseai(lowercase, one word) - Wrong: "UncloseAI", "Unclose AI", "UnClose AI"
Usage Guidelines
- In code: Use
uncloseai-speechfor project references - In documentation: Use
uncloseai-speechfor project name - In comments: Use
uncloseai-speechconsistently - Repository URLs:
uncloseai-speech(lowercase, hyphenated) - Docker images:
uncloseai-speech(lowercase, hyphenated) - API responses: Use
"owned_by": "uncloseai"(lowercase, one word)
Core Principles
1. Makefile-First Development
ALWAYS prefer Makefile targets over manual commands.
- DO:
make deploy,make voices,make test - DON'T: Manual docker commands, curl commands
Makefile is the source of truth for all deployment and development tasks.
2. Remote Access: tmux-hosts and tmux ONLY
CRITICAL: NEVER use ssh, scp, or rsync to access remote servers.
Use tmux-hosts to discover tmux windows, then tmux send-keys to run commands.
# Discover available hosts
tmux-hosts
# Output:
# 0:0 ai.foxhop.net
# 0:1 3090-ai.foxhop.net
# Run a command on 3090-ai
tmux send-keys -t 0:1 'command here' Enter
# Read output
tmux capture-pane -t 0:1 -p | tail -20
- NEVER use
ssh user@host "command"-- usetmux send-keys -t 0:1 - NEVER use
rsyncorscp-- usegit pushthentmux send-keys -t 0:1 'git pull' Enter - ALWAYS discover the correct window with
tmux-hostsfirst
3. Git-Based Deployment
We use git, not rsync/scp. All code syncs via git push/pull.
# Deploy workflow:
# 1. Commit and push locally
git add files && git commit -m "message" && git push
# 2. Pull and rebuild on server via tmux
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && git pull && sudo docker compose up -d --build' Enter
4. All Commands Run Locally
The Makefile assumes it runs on the server directly. No remote execution. When you need to run make targets on the server, use tmux:
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && make deploy' Enter
5. Configuration Management
sample.env- Default environment (commit this)speech.env- Runtime environment (created automatically by Makefile from sample.env)
6. Git Commit Guidelines
- Never add AI attribution - Do not use
Co-Authored-By: Claudeor similar in commit messages - Write clear, concise commit messages describing what changed and why
- Use imperative mood ("Add feature" not "Added feature")
Production Deployment
Production Server: 3090-ai.foxhop.net (tmux window 0:1)
- URL: https://speech.ai.unturf.com
- Repo Location:
/home/fox/git/uncloseai-speech - Container:
uncloseai-speech-server-1 - Git remote:
ssh://git@git.unturf.com:2222/engineering/unturf/uncloseai-speech.git - Git user on server:
timehexon <timehexon@unturf.com>
Deployment Workflow
Local:
/home/fox/git/uncloseai-speech/
git commit && git push
Remote (3090-ai, tmux 0:1):
/home/fox/git/uncloseai-speech/
git pull && sudo docker compose up -d --build
Container:
/app/
├── speech.py
├── cloned-voices/ (mounted from host)
├── voices/ (mounted from host)
└── config/
└── voice_to_speaker.yaml
Quick Production Commands
# Discover tmux windows
tmux-hosts
# Deploy changes
git push
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && git pull && sudo docker compose up -d --build' Enter
# Check container status
tmux send-keys -t 0:1 'sudo docker ps | grep speech' Enter
# View logs
tmux send-keys -t 0:1 'sudo docker logs --tail 50 uncloseai-speech-server-1' Enter
# Restart container
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && sudo docker compose restart' Enter
# Read output from tmux
tmux capture-pane -t 0:1 -p | tail -20
Voice Configuration
21 distinct gendered voices from LibriSpeech test-clean (public domain):
- Female (11): aria, clara, elena, grace, hazel, iris, luna, maya, ruby, sage, sofia
- Male (10): atlas, caleb, felix, hugo, jasper, kai, leo, marcus, owen, theo
Voice WAV files are in cloned-voices/, mounted into the container.
Config is in voice_to_speaker.default.yaml.
TTS Engine Status
Default Model (Qwen3-TTS)
- Qwen3-TTS (tts-1-qwen) - DEFAULT - 1.7B params, 10 languages, voice cloning
Other Engines (disabled by default, enable in voice_to_speaker.yaml)
- Piper TTS (tts-1) - 55 voices, fast CPU inference
- XTTS v2 (tts-1-hd) - Voice cloning, multilingual
- Silero TTS (tts-1-silero) - 142 voices, 5 languages, auto-downloads
- Kokoro TTS (tts-1-kokoro) - 32 voices, lightweight (82M params)
See docs/MODELS.md for complete roadmap.
Testing
# Run on server via tmux
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && make test' Enter
# Or test from any machine with curl
curl -X POST http://3090-ai.foxhop.net:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1-qwen","voice":"aria","input":"Test"}' \
-o /tmp/test.mp3
When Things Break
# Check logs
tmux send-keys -t 0:1 'sudo docker logs --tail 50 uncloseai-speech-server-1' Enter
# Check GPU memory
tmux send-keys -t 0:1 'nvidia-smi' Enter
# Restart container
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && sudo docker compose restart' Enter
# Full rebuild
tmux send-keys -t 0:1 'cd ~/git/uncloseai-speech && git pull && sudo docker compose up -d --build' Enter
# Read tmux output
tmux capture-pane -t 0:1 -p | tail -20
Common Mistakes to Avoid
- DON'T use
ssh user@host "command"-- usetmux send-keys -t 0:1 - DON'T use
rsyncorscp-- usegit push+tmux send-keys -t 0:1 'git pull' Enter - DON'T hardcode tmux window numbers -- use
tmux-hoststo discover them - DON'T put scripts in docs/ -- put them in scripts/
- DON'T forget to push before deploying
AGPL v3 Compliance
This project is AGPL v3 licensed. Anyone using this TTS service over a network must be able to access the source code.
Multiprocess Architecture
Worker processes spawn as fresh imports, don't run __main__ block.
- Caches - Initialize in
lifespancontext manager (runs per worker) - Args - Use
DefaultArgsclass at module level, override in__main__
See speech.py:23-113 for implementation.
Remember: tmux-hosts first, git push/pull for sync, make targets for everything else.