Pushkar02-n commited on
Commit
09c1319
·
verified ·
1 Parent(s): cd7fa25

Upload 2 files

Browse files
Files changed (2) hide show
  1. Dockerfile +34 -0
  2. app.py +100 -0
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies required for building and audio processing
6
+ RUN apt-get update && apt-get install -y \
7
+ git bash wget build-essential libsndfile1 \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Force install PyTorch CPU version to save massive amounts of build time and space
11
+ RUN pip install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
12
+
13
+ # Clone and install the custom NeMo fork required by IndicConformerASR
14
+ RUN git clone https://github.com/AI4Bharat/NeMo.git \
15
+ && cd NeMo \
16
+ && git checkout nemo-v2 \
17
+ && bash reinstall.sh \
18
+ && cd ..
19
+
20
+ # Install FastAPI, Piper TTS, and patch the known dependency issues
21
+ RUN pip install --no-cache-dir "numpy<2.0" huggingface_hub==0.23.2 fastapi uvicorn python-multipart "piper-tts==1.2.0"
22
+
23
+ # Fetch the Piper Nepali Chitwan model directly during the Docker build
24
+ RUN wget -q -O chitwan.onnx "https://huggingface.co/rhasspy/piper-voices/resolve/main/ne/ne_NP/chitwan/medium/ne_NP-chitwan-medium.onnx?download=true"
25
+ RUN wget -q -O chitwan.onnx.json "https://huggingface.co/rhasspy/piper-voices/resolve/main/ne/ne_NP/chitwan/medium/ne_NP-chitwan-medium.onnx.json?download=true"
26
+
27
+ # Copy the FastAPI app into the container
28
+ COPY app.py /app/app.py
29
+
30
+ # Hugging Face Spaces route traffic through port 7860
31
+ EXPOSE 7860
32
+
33
+ # Start the asynchronous Uvicorn server
34
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import wave
3
+ import asyncio
4
+ import torch
5
+ from fastapi import FastAPI, Depends, HTTPException, status, UploadFile, File
6
+ from fastapi.security import HTTPBasic, HTTPBasicCredentials
7
+ from fastapi.responses import FileResponse
8
+ import nemo.collections.asr as nemo_asr
9
+ from piper.voice import PiperVoice
10
+
11
+ app = FastAPI(title="ASR & TTS API")
12
+ security = HTTPBasic()
13
+
14
+ # Basic Authentication Configuration
15
+ USERNAME = os.environ.get("API_USERNAME", "admin")
16
+ PASSWORD = os.environ.get("API_PASSWORD", "secret")
17
+
18
+ def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)):
19
+ if not (credentials.username == USERNAME and credentials.password == PASSWORD):
20
+ raise HTTPException(
21
+ status_code=status.HTTP_401_UNAUTHORIZED,
22
+ detail="Incorrect username or password",
23
+ headers={"WWW-Authenticate": "Basic"},
24
+ )
25
+ return credentials
26
+
27
+ # Global references for the models
28
+ asr_model = None
29
+ tts_voice = None
30
+
31
+ @app.on_event("startup")
32
+ async def load_models():
33
+ global asr_model, tts_voice
34
+
35
+ # 1. Load and Quantize NeMo ASR
36
+ # Ensure you have uploaded your downloaded NeMo model to the Space with this filename
37
+ nemo_path = "model.nemo"
38
+ if os.path.exists(nemo_path):
39
+ device = torch.device('cpu')
40
+ model = nemo_asr.models.EncDecCTCModel.restore_from(restore_path=nemo_path, map_location=device)
41
+ model.freeze()
42
+
43
+ # Apply CPU Dynamic Quantization for memory reduction and speed
44
+ model = torch.quantization.quantize_dynamic(
45
+ model, {torch.nn.Linear}, dtype=torch.qint8
46
+ )
47
+
48
+ model.cur_decoder = 'ctc'
49
+ asr_model = model
50
+ print("ASR Model loaded and dynamically quantized.")
51
+ else:
52
+ print("WARNING: model.nemo not found. Please upload it.")
53
+
54
+ # 2. Load Piper TTS (Nepali Chitwan)
55
+ tts_model_path = "chitwan.onnx"
56
+ if os.path.exists(tts_model_path):
57
+ tts_voice = PiperVoice.load(tts_model_path)
58
+ print("TTS Model loaded.")
59
+
60
+ @app.post("/asr")
61
+ async def transcribe(file: UploadFile = File(...), _: str = Depends(verify_credentials)):
62
+ if not asr_model:
63
+ raise HTTPException(status_code=503, detail="ASR model not loaded")
64
+
65
+ # Save the uploaded audio temporarily
66
+ audio_path = f"/tmp/{file.filename}"
67
+ with open(audio_path, "wb") as f:
68
+ f.write(await file.read())
69
+
70
+ # Run the CPU-bound ASR transcription in a thread pool
71
+ loop = asyncio.get_event_loop()
72
+ transcription = await loop.run_in_executor(
73
+ None,
74
+ # Note: If using the AI4Bharat multilingual model, add `language_id='ne'` below
75
+ lambda: asr_model.transcribe(paths2audio_files=[audio_path], batch_size=1)[0]
76
+ )
77
+
78
+ os.remove(audio_path)
79
+ return {"text": transcription}
80
+
81
+ @app.post("/tts")
82
+ async def synthesize(text: str, _: str = Depends(verify_credentials)):
83
+ if not tts_voice:
84
+ raise HTTPException(status_code=503, detail="TTS model not loaded")
85
+
86
+ output_path = "/tmp/output.wav"
87
+
88
+ # Run the CPU-bound TTS synthesis in a thread pool
89
+ loop = asyncio.get_event_loop()
90
+
91
+ def generate_audio():
92
+ with wave.open(output_path, "wb") as wav_file:
93
+ wav_file.setnchannels(1)
94
+ wav_file.setsampwidth(2)
95
+ wav_file.setframerate(tts_voice.config.sample_rate)
96
+ tts_voice.synthesize(text, wav_file)
97
+
98
+ await loop.run_in_executor(None, generate_audio)
99
+
100
+ return FileResponse(output_path, media_type="audio/wav")