models-cpu-test / app.py
Pushkar02-n's picture
Update app.py
5bf7405 verified
Raw
History Blame Contribute Delete
3.88 kB
import os
import wave
import asyncio
import torch
from fastapi import FastAPI, Depends, HTTPException, status, UploadFile, File
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.responses import FileResponse
import nemo.collections.asr as nemo_asr
from piper.voice import PiperVoice
from pydantic import BaseModel
app = FastAPI(title="ASR & TTS API")
security = HTTPBasic()
# Basic Authentication Configuration
USERNAME = os.environ.get("API_USERNAME", "admin")
PASSWORD = os.environ.get("API_PASSWORD", "secret")
def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)):
if not (credentials.username == USERNAME and credentials.password == PASSWORD):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials
# Global references for the models
asr_model = None
tts_voice = None
@app.on_event("startup")
async def load_models():
global asr_model, tts_voice
# 1. Load and Quantize NeMo ASR
# Ensure you have uploaded your downloaded NeMo model to the Space with this filename
nemo_path = "model.nemo"
if os.path.exists(nemo_path):
device = torch.device('cpu')
model = nemo_asr.models.EncDecCTCModel.restore_from(restore_path=nemo_path, map_location=device)
model.freeze()
# Apply CPU Dynamic Quantization for memory reduction and speed
# model = torch.quantization.quantize_dynamic(
# model, {torch.nn.Linear}, dtype=torch.qint8
# )
torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8, inplace=True
)
model.cur_decoder = 'ctc'
asr_model = model
print("ASR Model loaded and dynamically quantized.")
else:
print("WARNING: model.nemo not found. Please upload it.")
# 2. Load Piper TTS (Nepali Chitwan)
tts_model_path = "chitwan.onnx"
if os.path.exists(tts_model_path):
tts_voice = PiperVoice.load(tts_model_path)
print("TTS Model loaded.")
@app.post("/asr")
async def transcribe(file: UploadFile = File(...), _: str = Depends(verify_credentials)):
if not asr_model:
raise HTTPException(status_code=503, detail="ASR model not loaded")
# Save the uploaded audio temporarily
audio_path = f"/tmp/{file.filename}"
with open(audio_path, "wb") as f:
f.write(await file.read())
# Run the CPU-bound ASR transcription in a thread pool
loop = asyncio.get_event_loop()
transcription = await loop.run_in_executor(
None,
# Pass the list positionally, and explicitly declare Nepali ('ne')
lambda: asr_model.transcribe([audio_path], batch_size=1, language_id='ne')[0]
)
os.remove(audio_path)
return {"text": transcription}
class TTSRequest(BaseModel):
text: str
@app.post("/tts")
async def synthesize(req: TTSRequest, _: str = Depends(verify_credentials)):
if not tts_voice:
raise HTTPException(status_code=503, detail="TTS model not loaded")
output_path = "/tmp/output.wav"
# Run the CPU-bound TTS synthesis in a thread pool
loop = asyncio.get_event_loop()
def generate_audio():
with wave.open(output_path, "wb") as wav_file:
wav_file.setnchannels(1)
wav_file.setsampwidth(2)
wav_file.setframerate(tts_voice.config.sample_rate)
# Make sure to call req.text here!
tts_voice.synthesize(req.text, wav_file)
await loop.run_in_executor(None, generate_audio)
return FileResponse(
output_path,
media_type="audio/wav",
headers={"Content-Disposition": "attachment; filename=tts_output.wav"}
)