A local HTTP service that accepts text via POST and speaks it through system speakers using Piper TTS neural voice synthesis. Features: - POST /notify - Queue text for TTS playback - GET /health - Health check with TTS/audio/queue status - GET /voices - List installed voice models - Async queue processing (no overlapping audio) - Non-blocking audio via sounddevice - 73 tests covering API contract Tech stack: - FastAPI + Uvicorn - Piper TTS (neural voices, offline) - sounddevice (PortAudio) - Pydantic for validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1013 lines
34 KiB
JSON
1013 lines
34 KiB
JSON
{
|
|
"project": {
|
|
"name": "voice-server",
|
|
"description": "Local HTTP service for text-to-speech playback",
|
|
"version": "1.0.0",
|
|
"created": "2025-12-18",
|
|
"last_updated": "2025-12-18"
|
|
},
|
|
"methodology": {
|
|
"approach": "hybrid-tdd",
|
|
"description": "TDD for API contracts, validation, and queue logic. Implementation-first for hardware integrations.",
|
|
"tdd_components": [
|
|
"request_validation",
|
|
"queue_behavior",
|
|
"error_responses",
|
|
"health_check_logic"
|
|
],
|
|
"implementation_first_components": [
|
|
"piper_tts_integration",
|
|
"sounddevice_playback",
|
|
"end_to_end_flow"
|
|
]
|
|
},
|
|
"phases": [
|
|
{
|
|
"id": "phase_1",
|
|
"name": "Core Infrastructure",
|
|
"description": "Project setup, FastAPI skeleton, and configuration management",
|
|
"estimated_days": "1-2"
|
|
},
|
|
{
|
|
"id": "phase_2",
|
|
"name": "TTS Integration",
|
|
"description": "Piper TTS setup, voice model management, and parameter support",
|
|
"estimated_days": "1-2"
|
|
},
|
|
{
|
|
"id": "phase_3",
|
|
"name": "Audio Playback",
|
|
"description": "sounddevice integration and audio error handling",
|
|
"estimated_days": "1"
|
|
},
|
|
{
|
|
"id": "phase_4",
|
|
"name": "Queue Management",
|
|
"description": "Async queue implementation and request processing pipeline",
|
|
"estimated_days": "1"
|
|
},
|
|
{
|
|
"id": "phase_5",
|
|
"name": "Error Handling",
|
|
"description": "Exception handlers, logging infrastructure, and health monitoring",
|
|
"estimated_days": "1"
|
|
},
|
|
{
|
|
"id": "phase_6",
|
|
"name": "Testing",
|
|
"description": "Unit tests, integration tests, performance tests, and system tests",
|
|
"estimated_days": "1-2"
|
|
},
|
|
{
|
|
"id": "phase_7",
|
|
"name": "Documentation & Deployment",
|
|
"description": "README, systemd service, and production hardening",
|
|
"estimated_days": "1"
|
|
}
|
|
],
|
|
"tasks": [
|
|
{
|
|
"id": "1.1.1",
|
|
"phase": "phase_1",
|
|
"name": "Initialize project directory",
|
|
"description": "Create project directory structure at /mnt/NV2/Development/voice-server with app/, tests/, models/ subdirectories",
|
|
"dependencies": [],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Directory already exists from PRD creation"
|
|
},
|
|
{
|
|
"id": "1.1.2",
|
|
"phase": "phase_1",
|
|
"name": "Create Python virtual environment",
|
|
"description": "Initialize virtual environment using uv (uv venv)",
|
|
"dependencies": ["1.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use uv as per user preference"
|
|
},
|
|
{
|
|
"id": "1.1.3",
|
|
"phase": "phase_1",
|
|
"name": "Install core dependencies",
|
|
"description": "Install fastapi, uvicorn[standard], piper-tts, sounddevice, numpy, pydantic, python-dotenv",
|
|
"dependencies": ["1.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Also install pytest, pytest-asyncio, httpx for testing"
|
|
},
|
|
{
|
|
"id": "1.1.4",
|
|
"phase": "phase_1",
|
|
"name": "Create pyproject.toml",
|
|
"description": "Create pyproject.toml with pinned dependency versions and project metadata",
|
|
"dependencies": ["1.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use uv's native pyproject.toml support instead of requirements.txt"
|
|
},
|
|
{
|
|
"id": "1.1.5",
|
|
"phase": "phase_1",
|
|
"name": "Create environment configuration",
|
|
"description": "Create .env.example with all configurable environment variables",
|
|
"dependencies": ["1.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include HOST, PORT, QUEUE_SIZE, LOG_LEVEL, MODEL_DIR, DEFAULT_VOICE"
|
|
},
|
|
{
|
|
"id": "1.1.6",
|
|
"phase": "phase_1",
|
|
"name": "Initialize git repository",
|
|
"description": "Initialize git repo with .gitignore for Python, IDEs, .env, voice models, __pycache__",
|
|
"dependencies": ["1.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Add models/*.onnx and models/*.json to .gitignore (large files)"
|
|
},
|
|
{
|
|
"id": "1.2.1",
|
|
"phase": "phase_1",
|
|
"name": "Write tests for Pydantic request/response models",
|
|
"description": "TDD: Write tests for NotifyRequest, NotifyResponse, HealthResponse, ErrorResponse models with validation rules",
|
|
"dependencies": ["1.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Test message length limits (1-10000), rate range (50-400), voice pattern validation"
|
|
},
|
|
{
|
|
"id": "1.2.2",
|
|
"phase": "phase_1",
|
|
"name": "Implement Pydantic models",
|
|
"description": "Create app/models.py with NotifyRequest, NotifyResponse, HealthResponse, ErrorResponse models",
|
|
"dependencies": ["1.2.1"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Implementation to make tests from 1.2.1 pass"
|
|
},
|
|
{
|
|
"id": "1.2.3",
|
|
"phase": "phase_1",
|
|
"name": "Create FastAPI application skeleton",
|
|
"description": "Create app/main.py with FastAPI app, lifespan handler, and CORS middleware",
|
|
"dependencies": ["1.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use lifespan context manager (not deprecated on_event)"
|
|
},
|
|
{
|
|
"id": "1.2.4",
|
|
"phase": "phase_1",
|
|
"name": "Write tests for /notify endpoint contract",
|
|
"description": "TDD: Write tests for POST /notify - valid requests return 202, invalid return 422, missing fields return 400",
|
|
"dependencies": ["1.2.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Use httpx.AsyncClient for async endpoint testing"
|
|
},
|
|
{
|
|
"id": "1.2.5",
|
|
"phase": "phase_1",
|
|
"name": "Implement /notify endpoint skeleton",
|
|
"description": "Create POST /notify endpoint that validates request and returns 202 (queue integration later)",
|
|
"dependencies": ["1.2.4"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Initially just validate and return success; queue integration in phase 4"
|
|
},
|
|
{
|
|
"id": "1.2.6",
|
|
"phase": "phase_1",
|
|
"name": "Write tests for /health endpoint",
|
|
"description": "TDD: Write tests for GET /health - returns status, uptime, queue info structure",
|
|
"dependencies": ["1.2.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Test both healthy and unhealthy response structures"
|
|
},
|
|
{
|
|
"id": "1.2.7",
|
|
"phase": "phase_1",
|
|
"name": "Implement /health endpoint skeleton",
|
|
"description": "Create GET /health endpoint returning basic health status",
|
|
"dependencies": ["1.2.6"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Full health checks (TTS, audio) added in phase 5"
|
|
},
|
|
{
|
|
"id": "1.2.8",
|
|
"phase": "phase_1",
|
|
"name": "Implement /voices endpoint skeleton",
|
|
"description": "Create GET /voices endpoint returning list of available voice models",
|
|
"dependencies": ["1.2.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Initially return empty list; populate after TTS integration"
|
|
},
|
|
{
|
|
"id": "1.2.9",
|
|
"phase": "phase_1",
|
|
"name": "Configure JSON logging middleware",
|
|
"description": "Add structured JSON logging for all requests with timestamp, request_id, path, status_code",
|
|
"dependencies": ["1.2.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use Python's logging with JSON formatter"
|
|
},
|
|
{
|
|
"id": "1.2.10",
|
|
"phase": "phase_1",
|
|
"name": "Verify server startup",
|
|
"description": "Test server starts successfully with uvicorn app.main:app --reload",
|
|
"dependencies": ["1.2.5", "1.2.7", "1.2.8"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Verify /docs (Swagger UI) is accessible"
|
|
},
|
|
{
|
|
"id": "1.3.1",
|
|
"phase": "phase_1",
|
|
"name": "Write tests for configuration loading",
|
|
"description": "TDD: Write tests for config loading from env vars with defaults, validation of values",
|
|
"dependencies": ["1.1.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Test default values, env var override, invalid value handling"
|
|
},
|
|
{
|
|
"id": "1.3.2",
|
|
"phase": "phase_1",
|
|
"name": "Implement configuration module",
|
|
"description": "Create app/config.py with Settings class using pydantic-settings for env var loading",
|
|
"dependencies": ["1.3.1"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Include host, port, queue_size, log_level, model_dir, default_voice settings"
|
|
},
|
|
{
|
|
"id": "1.3.3",
|
|
"phase": "phase_1",
|
|
"name": "Add CLI argument parsing",
|
|
"description": "Add CLI argument support for --host, --port, --log-level to override env vars",
|
|
"dependencies": ["1.3.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use argparse or typer; CLI args take precedence over env vars"
|
|
},
|
|
{
|
|
"id": "2.1.1",
|
|
"phase": "phase_2",
|
|
"name": "Create TTS engine module structure",
|
|
"description": "Create app/tts_engine.py with TTSEngine abstract base and PiperTTSEngine class skeleton",
|
|
"dependencies": ["1.2.10"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Abstract base allows for future TTS engine swapping"
|
|
},
|
|
{
|
|
"id": "2.1.2",
|
|
"phase": "phase_2",
|
|
"name": "Download default voice model",
|
|
"description": "Download en_US-lessac-medium.onnx and .json to models/ directory",
|
|
"dependencies": ["1.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Can use piper --download-dir or manual download from GitHub releases"
|
|
},
|
|
{
|
|
"id": "2.1.3",
|
|
"phase": "phase_2",
|
|
"name": "Implement Piper TTS voice loading",
|
|
"description": "Implement PiperTTSEngine.load_voice() to load .onnx model with caching",
|
|
"dependencies": ["2.1.1", "2.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Cache loaded models in memory to avoid reload on each request"
|
|
},
|
|
{
|
|
"id": "2.1.4",
|
|
"phase": "phase_2",
|
|
"name": "Implement text-to-audio synthesis",
|
|
"description": "Implement PiperTTSEngine.synthesize() returning NumPy array of audio samples",
|
|
"dependencies": ["2.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Return float32 NumPy array compatible with sounddevice"
|
|
},
|
|
{
|
|
"id": "2.1.5",
|
|
"phase": "phase_2",
|
|
"name": "Write integration tests for TTS synthesis",
|
|
"description": "Write tests verifying TTS generates valid audio array for sample text",
|
|
"dependencies": ["2.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Test output is non-empty NumPy array with expected sample rate"
|
|
},
|
|
{
|
|
"id": "2.1.6",
|
|
"phase": "phase_2",
|
|
"name": "Measure TTS latency benchmarks",
|
|
"description": "Benchmark TTS generation time for various text lengths (10, 100, 500, 1000, 5000 chars)",
|
|
"dependencies": ["2.1.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Document results for performance baseline"
|
|
},
|
|
{
|
|
"id": "2.2.1",
|
|
"phase": "phase_2",
|
|
"name": "Create models directory structure",
|
|
"description": "Create models/ directory for voice model storage with README explaining model installation",
|
|
"dependencies": ["1.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include instructions for downloading additional voices"
|
|
},
|
|
{
|
|
"id": "2.2.2",
|
|
"phase": "phase_2",
|
|
"name": "Implement voice model discovery",
|
|
"description": "Implement function to scan models/ directory and return available voice models",
|
|
"dependencies": ["2.2.1", "2.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Parse .json config files for model metadata (language, quality)"
|
|
},
|
|
{
|
|
"id": "2.2.3",
|
|
"phase": "phase_2",
|
|
"name": "Implement /voices endpoint fully",
|
|
"description": "Update /voices endpoint to return discovered models with metadata",
|
|
"dependencies": ["2.2.2", "1.2.8"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include name, language, quality, size_mb, installed status"
|
|
},
|
|
{
|
|
"id": "2.2.4",
|
|
"phase": "phase_2",
|
|
"name": "Add voice validation to /notify",
|
|
"description": "Validate requested voice exists before queuing; return 422 if not found",
|
|
"dependencies": ["2.2.2", "1.2.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Provide helpful error message listing available voices"
|
|
},
|
|
{
|
|
"id": "2.3.1",
|
|
"phase": "phase_2",
|
|
"name": "Implement speech rate adjustment",
|
|
"description": "Add rate parameter support to TTS synthesis (50-400 WPM range)",
|
|
"dependencies": ["2.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Check if Piper supports rate adjustment natively or needs post-processing"
|
|
},
|
|
{
|
|
"id": "2.3.2",
|
|
"phase": "phase_2",
|
|
"name": "Test rate adjustment across range",
|
|
"description": "Test TTS output at rate=50, 100, 170 (default), 300, 400 WPM",
|
|
"dependencies": ["2.3.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Verify audio sounds correct at extremes"
|
|
},
|
|
{
|
|
"id": "2.3.3",
|
|
"phase": "phase_2",
|
|
"name": "Implement voice_enabled flag",
|
|
"description": "Add voice_enabled parameter to skip TTS for debugging/testing",
|
|
"dependencies": ["2.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "When false, skip TTS and audio playback but still process request"
|
|
},
|
|
{
|
|
"id": "3.1.1",
|
|
"phase": "phase_3",
|
|
"name": "Create audio player module",
|
|
"description": "Create app/audio_player.py with AudioPlayer class skeleton",
|
|
"dependencies": ["1.2.10"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use sounddevice for non-blocking playback"
|
|
},
|
|
{
|
|
"id": "3.1.2",
|
|
"phase": "phase_3",
|
|
"name": "Implement audio device verification",
|
|
"description": "Implement verify_audio_devices() to check for available output devices at startup",
|
|
"dependencies": ["3.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Use sd.query_devices() to enumerate devices"
|
|
},
|
|
{
|
|
"id": "3.1.3",
|
|
"phase": "phase_3",
|
|
"name": "Implement non-blocking playback",
|
|
"description": "Implement AudioPlayer.play() using sd.play() for non-blocking audio output",
|
|
"dependencies": ["3.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "sd.play() returns immediately; audio plays in background thread"
|
|
},
|
|
{
|
|
"id": "3.1.4",
|
|
"phase": "phase_3",
|
|
"name": "Implement async wait method",
|
|
"description": "Implement AudioPlayer.wait_async() for async-friendly waiting on playback completion",
|
|
"dependencies": ["3.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Poll sd.get_stream().active with asyncio.sleep()"
|
|
},
|
|
{
|
|
"id": "3.1.5",
|
|
"phase": "phase_3",
|
|
"name": "Test audio playback with sample data",
|
|
"description": "Test AudioPlayer with synthesized sine wave to verify audio output works",
|
|
"dependencies": ["3.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "implementation_first",
|
|
"notes": "Use numpy to generate test tone; verify sound is heard"
|
|
},
|
|
{
|
|
"id": "3.1.6",
|
|
"phase": "phase_3",
|
|
"name": "Verify non-blocking behavior",
|
|
"description": "Test that play() returns immediately and server can handle requests during playback",
|
|
"dependencies": ["3.1.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Send request, verify 202 returned before audio finishes"
|
|
},
|
|
{
|
|
"id": "3.2.1",
|
|
"phase": "phase_3",
|
|
"name": "Implement retry logic for device failures",
|
|
"description": "Implement RobustAudioPlayer with automatic retry on sd.PortAudioError",
|
|
"dependencies": ["3.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Retry up to 3 times with 0.5s delay between attempts"
|
|
},
|
|
{
|
|
"id": "3.2.2",
|
|
"phase": "phase_3",
|
|
"name": "Handle device disconnection",
|
|
"description": "Gracefully handle audio device disconnection during playback",
|
|
"dependencies": ["3.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Log error, skip playback, continue processing queue"
|
|
},
|
|
{
|
|
"id": "3.2.3",
|
|
"phase": "phase_3",
|
|
"name": "Implement audio diagnostics",
|
|
"description": "Implement get_audio_diagnostics() for health check reporting",
|
|
"dependencies": ["3.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Return device count, default output name, sample rate"
|
|
},
|
|
{
|
|
"id": "3.2.4",
|
|
"phase": "phase_3",
|
|
"name": "Add audio error logging",
|
|
"description": "Add detailed logging for all audio errors with device context",
|
|
"dependencies": ["3.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include device name, error type, retry count in logs"
|
|
},
|
|
{
|
|
"id": "4.1.1",
|
|
"phase": "phase_4",
|
|
"name": "Write tests for queue behavior",
|
|
"description": "TDD: Write tests for queue enqueue, dequeue, overflow, ordering (FIFO)",
|
|
"dependencies": ["1.2.10"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Test max size enforcement, QueueFullError raising"
|
|
},
|
|
{
|
|
"id": "4.1.2",
|
|
"phase": "phase_4",
|
|
"name": "Create queue manager module",
|
|
"description": "Create app/queue_manager.py with TTSQueue class using asyncio.Queue",
|
|
"dependencies": ["4.1.1"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Implement to pass tests from 4.1.1"
|
|
},
|
|
{
|
|
"id": "4.1.3",
|
|
"phase": "phase_4",
|
|
"name": "Implement queue enqueue with timeout",
|
|
"description": "Implement TTSQueue.enqueue() with 1s timeout, raising QueueFullError on timeout",
|
|
"dependencies": ["4.1.2"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Return queue position on success"
|
|
},
|
|
{
|
|
"id": "4.1.4",
|
|
"phase": "phase_4",
|
|
"name": "Implement queue metrics",
|
|
"description": "Add stats tracking: processed count, error count, current size",
|
|
"dependencies": ["4.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Expose via TTSQueue.stats property"
|
|
},
|
|
{
|
|
"id": "4.1.5",
|
|
"phase": "phase_4",
|
|
"name": "Implement graceful queue shutdown",
|
|
"description": "Implement TTSQueue.shutdown() to wait for current item, reject new items",
|
|
"dependencies": ["4.1.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Called during application shutdown via lifespan"
|
|
},
|
|
{
|
|
"id": "4.2.1",
|
|
"phase": "phase_4",
|
|
"name": "Implement background queue processor",
|
|
"description": "Create async background task to process queue items sequentially",
|
|
"dependencies": ["4.1.2", "2.1.4", "3.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Generate TTS, play audio, wait for completion, then next item"
|
|
},
|
|
{
|
|
"id": "4.2.2",
|
|
"phase": "phase_4",
|
|
"name": "Integrate queue with /notify endpoint",
|
|
"description": "Update /notify to enqueue validated requests to TTSQueue",
|
|
"dependencies": ["4.2.1", "1.2.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Return queue_position in response"
|
|
},
|
|
{
|
|
"id": "4.2.3",
|
|
"phase": "phase_4",
|
|
"name": "Add request timeout handling",
|
|
"description": "Add 60s timeout for individual request processing in queue worker",
|
|
"dependencies": ["4.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Cancel TTS generation if exceeds timeout; log and continue"
|
|
},
|
|
{
|
|
"id": "4.2.4",
|
|
"phase": "phase_4",
|
|
"name": "Implement CPU-bound TTS in thread pool",
|
|
"description": "Run TTS synthesis in thread pool executor to avoid blocking event loop",
|
|
"dependencies": ["4.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use loop.run_in_executor() for TTS generation"
|
|
},
|
|
{
|
|
"id": "4.2.5",
|
|
"phase": "phase_4",
|
|
"name": "Test queue with concurrent requests",
|
|
"description": "Test sending 20+ concurrent requests and verify sequential playback",
|
|
"dependencies": ["4.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "All requests should be processed in order received"
|
|
},
|
|
{
|
|
"id": "4.3.1",
|
|
"phase": "phase_4",
|
|
"name": "Add queue status to /health",
|
|
"description": "Update /health to include queue size, capacity, utilization percentage",
|
|
"dependencies": ["4.1.4", "1.2.7"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include processed and error counts"
|
|
},
|
|
{
|
|
"id": "4.3.2",
|
|
"phase": "phase_4",
|
|
"name": "Add queue event logging",
|
|
"description": "Log queue events: enqueue, process start, process complete, errors",
|
|
"dependencies": ["4.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include request_id for correlation"
|
|
},
|
|
{
|
|
"id": "4.3.3",
|
|
"phase": "phase_4",
|
|
"name": "Test queue overflow scenarios",
|
|
"description": "Test behavior when queue reaches max size; verify 503 returned",
|
|
"dependencies": ["4.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Fill queue with slow requests, send additional request"
|
|
},
|
|
{
|
|
"id": "5.1.1",
|
|
"phase": "phase_5",
|
|
"name": "Write tests for error responses",
|
|
"description": "TDD: Write tests for all error response formats (400, 422, 500, 503)",
|
|
"dependencies": ["1.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": "tdd",
|
|
"notes": "Verify error response structure matches API spec"
|
|
},
|
|
{
|
|
"id": "5.1.2",
|
|
"phase": "phase_5",
|
|
"name": "Create custom exception classes",
|
|
"description": "Create app/exceptions.py with QueueFullError, TTSEngineError, AudioPlaybackError",
|
|
"dependencies": ["5.1.1"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Include error codes and details for each exception type"
|
|
},
|
|
{
|
|
"id": "5.1.3",
|
|
"phase": "phase_5",
|
|
"name": "Implement exception handlers",
|
|
"description": "Add FastAPI exception handlers for each custom exception type",
|
|
"dependencies": ["5.1.2"],
|
|
"completed": false,
|
|
"tested": true,
|
|
"test_approach": "tdd",
|
|
"notes": "Map exceptions to appropriate HTTP status codes"
|
|
},
|
|
{
|
|
"id": "5.1.4",
|
|
"phase": "phase_5",
|
|
"name": "Implement generic exception handler",
|
|
"description": "Add catch-all exception handler for unexpected errors (500)",
|
|
"dependencies": ["5.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Log full traceback but return sanitized error to client"
|
|
},
|
|
{
|
|
"id": "5.2.1",
|
|
"phase": "phase_5",
|
|
"name": "Configure structured JSON logging",
|
|
"description": "Set up logging with JSON formatter including timestamp, level, message, context",
|
|
"dependencies": ["1.2.9"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use python-json-logger or custom formatter"
|
|
},
|
|
{
|
|
"id": "5.2.2",
|
|
"phase": "phase_5",
|
|
"name": "Implement rotating file handler",
|
|
"description": "Configure RotatingFileHandler with 10MB max size, 5 backups",
|
|
"dependencies": ["5.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Write to voice-server.log in project directory"
|
|
},
|
|
{
|
|
"id": "5.2.3",
|
|
"phase": "phase_5",
|
|
"name": "Add request ID tracking",
|
|
"description": "Generate unique request_id for each request; include in all related logs",
|
|
"dependencies": ["5.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use UUID4; add to response headers for client correlation"
|
|
},
|
|
{
|
|
"id": "5.2.4",
|
|
"phase": "phase_5",
|
|
"name": "Test log rotation",
|
|
"description": "Verify log files rotate correctly when size limit reached",
|
|
"dependencies": ["5.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Generate enough log entries to trigger rotation"
|
|
},
|
|
{
|
|
"id": "5.3.1",
|
|
"phase": "phase_5",
|
|
"name": "Implement comprehensive /health endpoint",
|
|
"description": "Update /health with TTS engine, audio system, queue, and system resource checks",
|
|
"dependencies": ["4.3.1", "3.2.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Return 200 if all healthy, 503 if any component unhealthy"
|
|
},
|
|
{
|
|
"id": "5.3.2",
|
|
"phase": "phase_5",
|
|
"name": "Add TTS engine health check",
|
|
"description": "Test TTS engine can synthesize short test phrase",
|
|
"dependencies": ["2.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use minimal text to minimize overhead"
|
|
},
|
|
{
|
|
"id": "5.3.3",
|
|
"phase": "phase_5",
|
|
"name": "Add system resource monitoring",
|
|
"description": "Include CPU and memory usage in health check response",
|
|
"dependencies": ["5.3.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use psutil for system metrics"
|
|
},
|
|
{
|
|
"id": "5.3.4",
|
|
"phase": "phase_5",
|
|
"name": "Test health endpoint under load",
|
|
"description": "Verify /health responds correctly while queue is processing",
|
|
"dependencies": ["5.3.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Health check should not block or be blocked by TTS processing"
|
|
},
|
|
{
|
|
"id": "6.1.1",
|
|
"phase": "phase_6",
|
|
"name": "Set up pytest infrastructure",
|
|
"description": "Create tests/ directory with conftest.py, pytest.ini, test fixtures",
|
|
"dependencies": ["1.1.4"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include async fixtures for FastAPI testing"
|
|
},
|
|
{
|
|
"id": "6.1.2",
|
|
"phase": "phase_6",
|
|
"name": "Write remaining unit tests",
|
|
"description": "Complete any missing unit tests to achieve 80%+ coverage",
|
|
"dependencies": ["6.1.1", "5.1.3"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use pytest-cov for coverage reporting"
|
|
},
|
|
{
|
|
"id": "6.2.1",
|
|
"phase": "phase_6",
|
|
"name": "Write end-to-end integration tests",
|
|
"description": "Test complete request flow from HTTP request to audio playback",
|
|
"dependencies": ["4.2.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "May need to mock audio output for CI environments"
|
|
},
|
|
{
|
|
"id": "6.2.2",
|
|
"phase": "phase_6",
|
|
"name": "Write error scenario tests",
|
|
"description": "Test TTS failure, audio failure, queue overflow scenarios end-to-end",
|
|
"dependencies": ["6.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Use dependency injection to simulate failures"
|
|
},
|
|
{
|
|
"id": "6.3.1",
|
|
"phase": "phase_6",
|
|
"name": "Create load testing script",
|
|
"description": "Create load test using locust or wrk for performance benchmarking",
|
|
"dependencies": ["4.2.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Target: 50+ req/s, <50ms API response time"
|
|
},
|
|
{
|
|
"id": "6.3.2",
|
|
"phase": "phase_6",
|
|
"name": "Measure performance benchmarks",
|
|
"description": "Record p50, p95, p99 latency; TTS generation time; memory usage",
|
|
"dependencies": ["6.3.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Document baseline performance for future comparison"
|
|
},
|
|
{
|
|
"id": "6.4.1",
|
|
"phase": "phase_6",
|
|
"name": "Test on target environment",
|
|
"description": "Run full test suite on Nobara/Fedora 42 with real audio hardware",
|
|
"dependencies": ["6.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Test with both PulseAudio and ALSA if possible"
|
|
},
|
|
{
|
|
"id": "6.4.2",
|
|
"phase": "phase_6",
|
|
"name": "Test long-running stability",
|
|
"description": "Run server for 24+ hours with periodic requests; check for memory leaks",
|
|
"dependencies": ["6.4.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Monitor memory usage over time"
|
|
},
|
|
{
|
|
"id": "7.1.1",
|
|
"phase": "phase_7",
|
|
"name": "Create comprehensive README",
|
|
"description": "Write README.md with overview, installation, configuration, usage, API docs, troubleshooting",
|
|
"dependencies": ["6.4.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include example curl commands and Python client code"
|
|
},
|
|
{
|
|
"id": "7.1.2",
|
|
"phase": "phase_7",
|
|
"name": "Document voice model installation",
|
|
"description": "Create guide for downloading and installing additional Piper voice models",
|
|
"dependencies": ["2.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Link to Piper voice model repository"
|
|
},
|
|
{
|
|
"id": "7.1.3",
|
|
"phase": "phase_7",
|
|
"name": "Create example client scripts",
|
|
"description": "Create examples/ directory with curl, Python, and JavaScript client examples",
|
|
"dependencies": ["7.1.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include async Python example using httpx"
|
|
},
|
|
{
|
|
"id": "7.2.1",
|
|
"phase": "phase_7",
|
|
"name": "Create systemd service file",
|
|
"description": "Create voice-server.service for systemd deployment",
|
|
"dependencies": ["6.4.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include restart on failure, proper user/group, working directory"
|
|
},
|
|
{
|
|
"id": "7.2.2",
|
|
"phase": "phase_7",
|
|
"name": "Test systemd service",
|
|
"description": "Test service installation, start, stop, restart, and auto-restart on failure",
|
|
"dependencies": ["7.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Document installation steps in README"
|
|
},
|
|
{
|
|
"id": "7.2.3",
|
|
"phase": "phase_7",
|
|
"name": "Create deployment script",
|
|
"description": "Create deploy.sh script for automated deployment",
|
|
"dependencies": ["7.2.1"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Include venv setup, dependency install, service restart"
|
|
},
|
|
{
|
|
"id": "7.3.1",
|
|
"phase": "phase_7",
|
|
"name": "Configure production logging",
|
|
"description": "Set up production log levels (INFO default, DEBUG disabled)",
|
|
"dependencies": ["5.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Configurable via LOG_LEVEL environment variable"
|
|
},
|
|
{
|
|
"id": "7.3.2",
|
|
"phase": "phase_7",
|
|
"name": "Implement graceful shutdown",
|
|
"description": "Handle SIGTERM/SIGINT for graceful shutdown with queue draining",
|
|
"dependencies": ["4.1.5"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Wait for current playback to complete before exit"
|
|
},
|
|
{
|
|
"id": "7.3.3",
|
|
"phase": "phase_7",
|
|
"name": "Security audit",
|
|
"description": "Review input sanitization, resource limits, error message exposure",
|
|
"dependencies": ["6.2.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Verify no internal details leaked in error responses"
|
|
},
|
|
{
|
|
"id": "7.3.4",
|
|
"phase": "phase_7",
|
|
"name": "Performance tuning",
|
|
"description": "Tune queue size, worker count, timeouts based on benchmark results",
|
|
"dependencies": ["6.3.2"],
|
|
"completed": false,
|
|
"tested": false,
|
|
"test_approach": null,
|
|
"notes": "Document recommended settings for different use cases"
|
|
}
|
|
],
|
|
"summary": {
|
|
"total_tasks": 78,
|
|
"tdd_tasks": 12,
|
|
"implementation_first_tasks": 8,
|
|
"phases": 7,
|
|
"estimated_days": "4-7"
|
|
}
|
|
}
|