A production-grade Python FastAPI microservice that processes voice commands for the Vocallet accessible finance application. Built for Indonesian users including visually impaired individuals, UMKM small businesses, and personal finance management.
⚠️ First run note: On first startup, models (~1.3GB total) will download from Hugging Face. This can take 5–20 minutes depending on your internet connection. The service will not accept requests until downloads complete.
🌍 Environment Variables
Variable
Default
Description
APP_NAME
Vocallet AI Service
Service display name
APP_VERSION
1.0.0
Service version string
DEBUG
false
Enable debug mode + verbose logging
HOST
0.0.0.0
Bind host address
PORT
8001
Bind port
ALLOWED_ORIGINS
http://localhost:3000,...
Comma-separated CORS allowed origins
HF_TOKEN
(empty)
Hugging Face access token (optional for public models)
HF_CACHE_DIR
./models_cache
Local directory for model weight cache
STT_MODEL_NAME
openai/whisper-small
Hugging Face model ID for speech-to-text
INTENT_MODEL_NAME
joeddav/xlm-roberta-large-xnli
Hugging Face model ID for intent classification
DEVICE
auto
Inference device: auto, cpu, cuda, cuda:0, mps
TORCH_DTYPE
float32
float32 (default) or float16 (GPU, faster + less VRAM)
MAX_AUDIO_SIZE_MB
25
Maximum allowed upload file size in MB
MAX_AUDIO_DURATION_SECONDS
60
Maximum allowed audio duration in seconds
INTENT_CONFIDENCE_THRESHOLD
0.45
Scores below this fall back to "tidak diketahui"
INTENT_LABELS
catat pengeluaran,...
Comma-separated intent label strings
TEMP_DIR
./temp_audio
Directory for temporary audio file storage
📡 API Documentation
POST /api/voice-command
Process a voice audio file and return the transcript and intent.
1{2"status":"error",3"error_code":"AUDIO_TOO_LONG",4"message":"Audio duration (75.0s) exceeds maximum allowed (60s).",5"detail":"...",6"timestamp":"2024-08-10T12:34:56.789Z"7}
Error Codes:
Code
HTTP
Description
FILE_TOO_LARGE
400
File size exceeds MAX_AUDIO_SIZE_MB
UNSUPPORTED_FORMAT
400
File format not in allowed list
AUDIO_TOO_LONG
400
Audio duration exceeds MAX_AUDIO_DURATION_SECONDS
SERVICE_UNAVAILABLE
503
Models not loaded yet
TRANSCRIPTION_FAILED
422
Whisper could not transcribe the audio
CLASSIFICATION_FAILED
422
Intent classification failed
INTERNAL_ERROR
500
Unexpected server error
GET /health
Returns service health and model loading status.
curl http://localhost:8001/health
json
1{2"status":"healthy",3"service":"Vocallet AI Service",4"version":"1.0.0",5"uptime_seconds":3600.5,6"models":{7"stt":{8"name":"openai/whisper-small",9"loaded":true,10"load_time_seconds":12.3,11"device":"cpu",12"error":null13},14"intent":{15"name":"joeddav/xlm-roberta-large-xnli",16"loaded":true,17"load_time_seconds":25.7,18"device":"cpu",19"error":null20}21}22}
Status values:healthy (both models loaded) | degraded (one model failed) | unhealthy (both failed)
GET /health/models
Detailed model information including GPU memory usage (if applicable).
curl http://localhost:8001/health/models
GET /health/ping
Simple liveness probe for Docker/Kubernetes healthchecks.
Example: Forwarding audio from Express to the Python microservice.
javascript
1const axios =require('axios');2constFormData=require('form-data');3const multer =require('multer');45const upload =multer({storage: multer.memoryStorage()});67// Express route: POST /voice-command8router.post('/voice-command', upload.single('audio'),async(req, res)=>{9try{10const form =newFormData();11 form.append('file', req.file.buffer,{12filename: req.file.originalname,13contentType: req.file.mimetype,14});1516const response =await axios.post(17'http://localhost:8001/api/voice-command',18 form,19{20headers: form.getHeaders(),21timeout:60000// 60s timeout for model inference22}23);2425 res.json(response.data);26}catch(err){27const detail = err.response?.data?.detail || err.message;28 res.status(err.response?.status ||500).json({29error:'voice_processing_failed',30 detail
31});32}33});
🧪 Running Tests
bash
1# Install dev dependencies2pip install -r requirements-dev.txt
34# Run all tests5pytest tests/ -v
67# Run with coverage report8pytest tests/ -v --cov=app --cov-report=html
910# Open coverage report11open htmlcov/index.html # macOS12xdg-open htmlcov/index.html # Linux
✅ First Run Checklist
When you start the service for the first time, here is what happens:
FastAPI boots up — Uvicorn starts, lifespan context begins
detect_device() runs — Auto-detects CUDA → MPS → CPU
asyncio.gather() kicks off — Both models begin downloading/loading concurrently
Whisper downloads — ~242MB for whisper-small; goes to ./models_cache/
XLM-RoBERTa downloads — ~1.1GB for xlm-roberta-large-xnli; goes to ./models_cache/
Models load into memory — Weights deserialized into PyTorch; moved to target device
"Vocallet AI Service ready!" is logged — Now accepting requests
GET /health returns "status": "healthy" — Both models are live
⏱️ Total startup time on first run (CPU): 5–25 minutes (mostly download)
⏱️ Total startup time on subsequent runs (cache warm): 30–90 seconds (model load only)