uncloseai-speech/CLAUDE.md

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-speech for project references
  • In documentation: Use uncloseai-speech for project name
  • In comments: Use uncloseai-speech consistently
  • 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" -- use tmux send-keys -t 0:1
  • NEVER use rsync or scp -- use git push then tmux send-keys -t 0:1 'git pull' Enter
  • ALWAYS discover the correct window with tmux-hosts first

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: Claude or 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" -- use tmux send-keys -t 0:1
  • DON'T use rsync or scp -- use git push + tmux send-keys -t 0:1 'git pull' Enter
  • DON'T hardcode tmux window numbers -- use tmux-hosts to 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.

  1. Caches - Initialize in lifespan context manager (runs per worker)
  2. Args - Use DefaultArgs class 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.