🦝 Streamline CLAUDE.md - reference guide not changelog

Remove verbose explanations and code examples. Keep it concise:
- TTS Engine Status: one-liner per engine
- Testing: condensed workflow steps
- Multiprocess: key pattern + implementation reference

CLAUDE.md is a quick reference, not documentation.
This commit is contained in:
Russell Ballestrini 2025-11-09 18:29:24 -05:00
parent 187121558e
commit 3887e9b850

View file

@ -120,11 +120,11 @@ uncloseai-speech/
## TTS Engine Status
### Working (95.9% Hydration Success - 235/245 voices)
- ✅ Piper TTS (tts-1) - 55/55 voices (100%), fast CPU inference, absolute paths working
- ✅ XTTS v2 (tts-1-hd) - 6/8 voices (75%), high quality voice cloning, multilingual
- ✅ Silero TTS (tts-1-silero) - 142/148 voices (95.9%), fast CPU-friendly, 5 languages, auto-downloads from torch.hub
- ✅ Kokoro TTS (tts-1-kokoro) - 32/34 voices (94.1%), lightweight decoder (82M params)
### Production Ready (95.9% success rate across 245 voices)
- ✅ 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)
### High Priority Integration
@ -172,27 +172,15 @@ Container:
## Testing Philosophy
**Always test the full stack:**
1. Clean state (`make clean`)
2. Fresh deploy (`make deploy`)
3. Voice download (`make voices`)
4. API test (`make test`, `make test-xtts`)
5. Hydration test (`make hydrate`) - Tests ALL 245 voices sequentially
6. Load test (`make load-test`) - 100 concurrent random requests
**Full stack testing workflow:**
1. `make clean` - Clean state
2. `make deploy` - Fresh deploy
3. `make voices` - Download voice models
4. `make test` - Basic API test
5. `make hydrate` - Test all 245 voices (sequential, safe)
6. `make load-test` - 100 concurrent requests (stress test)
**Hydration Testing:**
- `make hydrate` - Tests every voice across all 4 engines
- Sequential (one at a time) to avoid overwhelming server
- Reports success/failure with file sizes
- Output saved to `/tmp/hydrate_test/`
**Load Testing:**
- `make load-test` - 100 concurrent requests with random voices
- 10 parallel workers via `xargs -P10`
- Tests multiprocess worker architecture
- Reports timing, throughput, and success rate
**Never assume** - if you changed something, test from scratch.
**Never assume** - always test from scratch after changes.
## Raccoon Mission Values
@ -227,56 +215,15 @@ Container:
4. Check if container was rebuilt (`make deploy` does this)
5. Verify voices downloaded (`ls` in container via `make logs` approach)
## Known Issues and Solutions (2025-11-09)
## Multiprocess Architecture (uvicorn workers=4)
### Issue: Worker Processes Not Loading Caches
**Key pattern:** Worker processes spawn as fresh imports, don't run `__main__` block.
**Symptom:** Voice auto-detection fails with "Voice 'X' not found in any model" despite voices being configured in `voice_to_speaker.yaml`.
**Solutions implemented:**
1. **Caches** - Initialize in `lifespan` context manager (runs per worker)
2. **Args** - Use `DefaultArgs` class at module level, override in `__main__`
**Root Cause:** When using `uvicorn.run("speech:app", workers=4)`, worker processes spawn as fresh imports and don't execute the `if __name__ == "__main__"` block where caches were initialized.
**Solution:** Move cache initialization to FastAPI's `lifespan` context manager, which runs during startup in EACH worker process.
```python
@contextlib.asynccontextmanager
async def lifespan(app):
# Startup: Initialize voice caches in each worker process
global voice_to_model_cache, voices_cache
# Build caches from YAML...
# (see speech.py:23-81 for full implementation)
yield
# Shutdown: Cleanup...
```
### Issue: Worker Processes Get AttributeError on args
**Symptom:** `AttributeError: 'NoneType' object has no attribute 'xtts_device'` when workers try to access command-line arguments.
**Root Cause:** `args` was parsed in `if __name__ == "__main__"` which only runs in parent process, not in worker processes.
**Solution:** Create DefaultArgs class with sensible defaults that gets used by workers, while parent process overrides with actual command-line args.
```python
# Default args for worker processes (will be overridden in __main__)
class DefaultArgs:
xtts_device = 'cpu'
use_deepspeed = False
unload_timer = None
# ... etc
args = DefaultArgs()
```
Then in `__main__`:
```python
if __name__ == "__main__":
parser = argparse.ArgumentParser(...)
args = parser.parse_args() # Overrides DefaultArgs
# ... rest of startup
```
See `speech.py:23-113` for implementation.
## Future Refactoring (Planned)