File size: 2,092 Bytes
e561008
889e86b
94dd7b2
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
889e86b
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94dd7b2
e561008
 
 
020705e
e561008
 
 
 
 
 
 
 
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
from fastapi import APIRouter, UploadFile, File, Depends, HTTPException
from utils.rate_limiter import with_rate_limit
from utils.groq_client import call_groq_transcribe

router = APIRouter()

SUPPORTED_AUDIO_TYPES = {
    "audio/webm", "audio/mp4", "audio/mpeg", "audio/wav",
    "audio/ogg", "audio/flac", "audio/x-m4a",
}

MAX_AUDIO_BYTES = 25 * 1024 * 1024  # 25 MB — Groq Whisper limit


@router.post("/transcribe")
async def transcribe_audio(
    audio: UploadFile = File(...),
    user: dict = Depends(with_rate_limit("transcription")),
):
    """
    Accepts a browser-recorded audio blob, sends it to Groq Whisper,
    and returns the transcript text.

    Voice input is NEVER stored — the audio bytes are discarded after
    transcription returns. Only the resulting text is saved by the caller.
    See VUL-019 (no audio persistence).
    """
    # Basic content-type guard — not a hard security boundary since the
    # browser may send 'application/octet-stream'. Real validation happens
    # when Groq rejects unsupported formats.
    content_type = audio.content_type or "audio/webm"
    if content_type not in SUPPORTED_AUDIO_TYPES and content_type != "application/octet-stream":
        raise HTTPException(
            status_code=415,
            detail=f"Unsupported audio format: {content_type}",
        )

    audio_bytes = await audio.read()

    if len(audio_bytes) > MAX_AUDIO_BYTES:
        raise HTTPException(
            status_code=413,
            detail="Audio file exceeds 25 MB limit.",
        )

    if len(audio_bytes) == 0:
        raise HTTPException(status_code=400, detail="Audio file is empty.")

    try:
        transcription = call_groq_transcribe(
            file=(audio.filename or "recording.webm", audio_bytes, content_type),
            response_format="json",
            language="en",
            fixture="transcript",
        )
        return {"transcript": transcription.text}

    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"Transcription failed: {str(e)}",
        )