FastAPI backend for diarization-only selective speaker transcription
An always-on transcription system that captures only the primary user's speech by using enrollment-anchored speaker diarization. Built for frontline workers who need accurate, privacy-respecting conversation records.
This backend implements a sophisticated selective speaker pipeline:
- Enrollment: User records a ~30-second voice sample
- Continuous Recording: Android app sends audio chunks via VAD
- Audio Concatenation: Backend concatenates
[enrollment] + [silence] + [chunk] - Diarization: Upload to AssemblyAI for transcription with speaker labels
- Selective Filtering: Keep only segments matching the enrolled speaker
- Storage: Save transcripts with timestamps and GPS locations
- β Privacy-First: Only the enrolled user's speech is transcribed
- β High Accuracy: Combines diarization with enrollment anchoring
- β Fully Integrated: Real AssemblyAI API integration with webhooks
- β Audio Processing: Automatic audio concatenation and validation
- β Scalable: Built on FastAPI with async support and background tasks
- β Production-Ready: Database models, webhook handling, and comprehensive testing
βββββββββββββββ
β Android β Records audio chunks (VAD-triggered)
β App β
ββββββββ¬βββββββ
β POST /chunks/submit
β
βββββββββββββββββββ
β FastAPI β Stores chunk metadata, enqueues job
β Backend β
ββββββββββ¬βββββββββ
β Concatenate [enroll + silence + chunk]
β
βββββββββββββββββββ
β AssemblyAI β Transcribe with diarization
β STT Service β
ββββββββββ¬βββββββββ
β Webhook: transcription complete
β
βββββββββββββββββββ
β Diarization β Map speakers, keep only user segments
β Mapper β
ββββββββββ¬βββββββββ
β
β
βββββββββββββββββββ
β PostgreSQL β Store segments, locations
β Database β
βββββββββββββββββββ
- Python 3.11+
- Docker & Docker Compose (for PostgreSQL)
- PostgreSQL 16 (or use Docker)
cd selective-speaker
python -m venv .venv
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
pip install -e .Create a .env file in the project root:
cat > .env << EOF
DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/selective
ENV=dev
STORAGE_ROOT=./data
# Diarization settings
PAD_MS=1000
ENROLL_DOMINANCE=0.8
SEGMENT_GAP_MS=500
SEGMENT_MIN_MS=1000
SEGMENT_MIN_CHARS=6
# AssemblyAI integration (get your API key from https://www.assemblyai.com/)
ASSEMBLYAI_API_KEY=your_api_key_here
ASSEMBLYAI_WEBHOOK_SECRET=your_webhook_secret_here
WEBHOOK_BASE_URL=http://localhost:8000
# Audio settings
AUDIO_SAMPLE_RATE=16000
AUDIO_CHANNELS=1
EOFNote: For development without real API calls, you can leave the default values. For production, get your API key from AssemblyAI.
docker compose up -d dbVerify database is running:
docker compose psQuick setup (use Alembic for production):
python -c "from app.db import Base, engine; import app.models; Base.metadata.create_all(engine)"uvicorn app.main:app --reload --host 0.0.0.0 --port 8000The API will be available at:
- API: http://localhost:8000
- Docs: http://localhost:8000/docs (Swagger UI)
- Redoc: http://localhost:8000/redoc
users: User accounts (linked to Firebase Auth)
id,uid(Firebase),display_name,email,created_at
enrollments: Voice enrollment records
id,user_id,audio_url,duration_ms,phrase_text,edit_distance,created_at
chunks: Recorded audio chunks
id,user_id,audio_url,device_id,start_ts,end_ts,gps_lat,gps_lon,created_at
segments: Transcribed speech segments (filtered for user)
id,chunk_id,speaker_label,start_ms,end_ms,text,confidence,kept
locations: Reverse-geocoded addresses
id,chunk_id,address,source
pip install pytest
pytest tests/Use the test script with sample data:
python scripts/local_map_segments.py \
--enroll-ms 3000 \
--stt tests/fixtures/sample_stt.jsonExpected output:
{
"status": "ok",
"user_label": "SPEAKER_00",
"kept": [
{
"start_ms": 100,
"end_ms": 2600,
"text": "Hello I said hello world",
"avg_conf": 0.89
}
]
}- POST
/enrollment/complete- Complete voice enrollment - POST
/enrollment/reset- Reset enrollment (re-record) - GET
/enrollment/status- Check enrollment status
- POST
/chunks/submit- Submit audio chunk for transcription- Triggers background processing: concatenation β upload β transcription
- GET
/chunks/{chunk_id}- Get chunk details and segments
- GET
/utterances- List utterances (paginated timeline)- Query params:
limit,before_id
- Query params:
- GET
/utterances/search?q=query- Search transcripts
- POST
/webhooks/assemblyai- AssemblyAI transcription callback- Automatically called by AssemblyAI when transcription completes
- Processes diarization and stores user segments
Configure via .env file or environment variables:
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql+psycopg://... |
PostgreSQL connection string |
ENV |
dev |
Environment (dev/prod) |
STORAGE_ROOT |
./data |
Local file storage path |
PAD_MS |
1000 |
Silence padding between enrollment and chunk (ms) |
ENROLL_DOMINANCE |
0.8 |
Min % of enrollment that must be user's voice |
SEGMENT_GAP_MS |
500 |
Max gap to merge adjacent words into segment |
SEGMENT_MIN_MS |
1000 |
Minimum segment duration to keep |
SEGMENT_MIN_CHARS |
6 |
Minimum text length to keep |
ASSEMBLYAI_WEBHOOK_SECRET |
devsecret |
Webhook signature verification secret |
-
Enrollment:
curl -X POST http://localhost:8000/enrollment/complete \ -H "Content-Type: application/json" \ -d '{ "audio_url": "enrollments/user123.wav", "duration_ms": 30000, "phrase_text": "This is my enrollment phrase" }'
-
Submit Chunk:
curl -X POST http://localhost:8000/chunks/submit \ -H "Content-Type: application/json" \ -d '{ "audio_url": "chunks/chunk001.wav", "device_id": "android-device-1", "gps_lat": 37.7749, "gps_lon": -122.4194 }'
-
Webhook Callback (simulated):
curl -X POST http://localhost:8000/webhooks/assemblyai \ -H "Content-Type: application/json" \ -d @tests/fixtures/webhook_payload.json -
List Utterances:
curl http://localhost:8000/utterances?limit=20
-
AssemblyAI Integration: β Fully implemented
- Real API calls with upload and transcription
- Webhook signature verification
- Custom metadata passing
- Background task processing
-
Audio Processing: β Implemented
- Audio concatenation with silence padding
- Format validation and compatibility checking
- WAV file handling
-
Logging: β Configured
- Loguru for structured logging
- Request/response tracking
- Error logging with stack traces
-
Background Tasks: β Implemented
- FastAPI BackgroundTasks for async processing
- Audio upload and transcription queueing
-
Authentication:
- Implement Firebase Auth token verification
- Add JWT middleware for protected routes
-
Storage:
- Replace local storage with S3/GCS
- Implement presigned URL generation for uploads
-
Job Queue (Optional - FastAPI BackgroundTasks may be sufficient):
- Add Celery + Redis for heavy processing
- Or use Cloud Tasks for serverless scaling
-
Geocoding:
- Integrate Google Geocoding API or Mapbox
- Cache results to reduce API calls
-
Monitoring:
- Add Sentry for error tracking
- Set up centralized logging (CloudWatch/Stackdriver)
- Implement health checks and metrics
-
Database:
- Set up Alembic migrations properly
- Add indexes for performance
- Enable connection pooling
-
Security:
- Enable HTTPS/TLS
- Implement rate limiting
- Add CORS configuration
- Use secrets manager for credentials
-
Cost Optimization:
- Monitor AssemblyAI usage
- Implement caching strategies
- Optimize chunk sizes
selective-speaker/
βββ app/
β βββ main.py # FastAPI app entry point
β βββ config.py # Configuration settings
β βββ db.py # Database session management
β βββ models.py # SQLAlchemy ORM models
β βββ schemas.py # Pydantic request/response schemas
β βββ storage.py # File storage abstraction
β βββ routes/
β β βββ enrollment.py # Enrollment endpoints
β β βββ chunks.py # Chunk submission endpoints
β β βββ utterances.py # Timeline/search endpoints
β β βββ webhooks.py # Webhook handlers
β βββ services/
β β βββ diarization_mapper.py # Core enrollment-anchored logic
β β βββ assemblyai_client.py # STT service integration
β β βββ geocoding.py # GPS β address conversion
β βββ utils/
β βββ audio.py # Audio file utilities
βββ tests/
β βββ test_diarization_mapper.py
β βββ fixtures/
β βββ sample_stt.json
βββ scripts/
β βββ local_map_segments.py # Test harness for mapper
βββ docker-compose.yml
βββ pyproject.toml
βββ README.md
- Create feature branch
- Make changes
- Run tests:
pytest tests/ - Check linting (if configured)
- Submit PR
This project is part of Frontier Audio's Always-On Selective Speaker system.
# Check if PostgreSQL is running
docker compose ps
# View logs
docker compose logs db
# Restart database
docker compose restart dbMake sure you've installed the package in development mode:
pip install -e .Change the port in uvicorn command:
uvicorn app.main:app --reload --port 8001- ASSEMBLYAI_INTEGRATION.md - Complete guide to AssemblyAI integration
- PROJECT_SUMMARY.md - Architecture overview and design decisions
- GETTING_STARTED.md - 5-minute quick start guide
Built with β€οΈ for Frontier Audio