File size: 4,436 Bytes
95e502b
8c12a1f
8a968d8
 
8c12a1f
57ff1be
95e502b
 
452486a
feef241
 
8a968d8
 
 
 
 
 
 
 
 
 
 
dbb52ad
e3a60f0
dbb52ad
feef241
 
 
8a968d8
feef241
 
95e502b
 
 
dbb52ad
95e502b
 
dbb52ad
95e502b
dbb52ad
 
95e502b
ace55a6
8c12a1f
9ff04de
8c12a1f
8a968d8
 
8c12a1f
8a968d8
d23c998
95e502b
dbb52ad
8a968d8
ace55a6
8c12a1f
8a968d8
8c12a1f
 
8a968d8
8c12a1f
 
 
8a968d8
8c12a1f
 
 
8a968d8
8c12a1f
8a968d8
8c12a1f
8a968d8
 
d23c998
8c12a1f
 
8a968d8
8c12a1f
ad8f362
8c12a1f
 
8a968d8
 
8c12a1f
 
8a968d8
8c12a1f
 
8a968d8
 
8c12a1f
 
452486a
8a968d8
 
 
 
 
8c12a1f
8a968d8
8c12a1f
8a968d8
8c12a1f
70a342b
8a968d8
8c12a1f
0915cb0
95e502b
dbb52ad
8a968d8
95e502b
0915cb0
95e502b
dbb52ad
8a968d8
95e502b
 
 
 
 
ace55a6
95e502b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import os
import io
import time
import logging
import torch
import torchaudio
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from transformers import pipeline
from huggingface_hub import login

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('transcription.log')
    ]
)
logger = logging.getLogger(__name__)

# Set HF_HOME to a writable directory
os.environ['HF_HOME'] = '/app/.cache/huggingface'

# Handle authentication if token is provided
hf_token = os.getenv("HF_TOKEN")
if hf_token:
    logger.info("Logging in to Hugging Face with provided token")
    login(token=hf_token)

# Initialize FastAPI app
app = FastAPI()

# Enable CORS for all origins (adjust for production)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Load the pipeline with device set to GPU if available
MODEL_NAME = "STT-Darija-ORG/wav2vec2-xlsr-300m-darija-augmented"
device = 0 if torch.cuda.is_available() else -1
logger.info(f"Loading model {MODEL_NAME} on device: {'GPU' if device == 0 else 'CPU'}")
start_time = time.time()
pipe = pipeline("automatic-speech-recognition", model=MODEL_NAME, device=device)
logger.info(f"Model loaded in {time.time() - start_time:.2f} seconds")

@app.post("/transcribe")
async def transcribe_audio(file: UploadFile = File(...)):
    start_time = time.time()
    try:
        # Read the file content
        logger.info(f"Received file: {file.filename}, size: {file.size} bytes")
        content = await file.read()
        if len(content) == 0:
            logger.error("Uploaded file is empty")
            raise HTTPException(status_code=400, detail="Uploaded file is empty")

        # Load audio from bytes
        load_start = time.time()
        file_like = io.BytesIO(content)
        try:
            waveform, sample_rate = torchaudio.load(file_like)
            logger.info(f"Audio loaded, sample rate: {sample_rate} Hz, channels: {waveform.shape[0]}, duration: {waveform.shape[1]/sample_rate:.2f} seconds")
        except Exception as e:
            logger.error(f"Failed to load audio: {str(e)}")
            raise HTTPException(status_code=400, detail=f"Invalid audio file: {str(e)}")
        load_time = time.time() - load_start
        logger.info(f"Audio loading took {load_time:.2f} seconds")

        # Ensure mono audio
        if waveform.shape[0] > 1:
            logger.info("Converting multi-channel audio to mono")
            waveform = waveform[0:1, :]  # Take the first channel

        # Resample if necessary
        if sample_rate != 16000:
            logger.info(f"Resampling audio from {sample_rate} Hz to 16000 Hz")
            resample_start = time.time()
            transform = torchaudio.transforms.Resample(sample_rate, 16000)
            waveform = transform(waveform)
            logger.info(f"Resampling took {time.time() - resample_start:.2f} seconds")

        # Pass to pipeline
        logger.info("Starting transcription")
        transcribe_start = time.time()
        audio_np = waveform.numpy().flatten()
        result = pipe(audio_np)
        transcription = result["text"]
        transcribe_time = time.time() - transcribe_start
        logger.info(f"Transcription completed in {transcribe_time:.2f} seconds, result: {transcription}")

        total_time = time.time() - start_time
        logger.info(f"Total request processing time: {total_time:.2f} seconds")

        return {"transcription": transcription, "processing_time_seconds": round(total_time, 2)}
    except HTTPException as e:
        logger.error(f"HTTP error: {str(e)}")
        raise e
    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")

@app.get("/health")
async def health_check():
    logger.info("Health check requested")
    return {"status": "healthy", "model": MODEL_NAME}

@app.get("/")
async def home():
    logger.info("Home endpoint accessed")
    return {
        "message": "Speech Recognition API",
        "endpoints": {
            "/transcribe": "POST - Upload audio file for transcription",
            "/health": "GET - Check API health"
        }
    }