Remostart commited on
Commit
fff63e7
·
verified ·
1 Parent(s): 67268a8

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +38 -20
main.py CHANGED
@@ -1,15 +1,17 @@
1
  # main.py
2
- import io
3
  import json
4
  import torch
5
  import torchaudio
6
  import requests
7
  import numpy as np
 
 
8
 
9
- from fastapi import FastAPI, UploadFile, File, HTTPException
10
  from transformers import AutoProcessor, AutoModelForCTC
11
  from pydantic import BaseModel
12
 
 
13
  with open("config.json", "r") as f:
14
  CONFIG = json.load(f)
15
 
@@ -26,7 +28,7 @@ OVERLAP_SECONDS = 2
26
  MAX_SAMPLES = SAMPLE_RATE * MAX_AUDIO_SECONDS
27
 
28
 
29
- app = FastAPI(title="Universal Audio STT", version="1.3.0")
30
 
31
 
32
  processor = AutoProcessor.from_pretrained(MODEL_NAME)
@@ -37,18 +39,28 @@ model.eval()
37
  class STTResponse(BaseModel):
38
  transcript: str
39
  downstream_response: dict | None = None
 
40
 
41
 
 
 
 
42
 
43
- def load_audio(file_bytes: bytes) -> np.ndarray:
44
- buffer = io.BytesIO(file_bytes)
 
45
 
46
  try:
47
- waveform, sr = torchaudio.load(buffer)
48
  except Exception:
49
- raise ValueError("Unsupported or corrupted audio file")
 
 
 
 
 
50
 
51
- waveform = waveform.mean(dim=0) # why: force mono
52
 
53
  if sr != SAMPLE_RATE:
54
  waveform = torchaudio.functional.resample(
@@ -56,9 +68,9 @@ def load_audio(file_bytes: bytes) -> np.ndarray:
56
  )
57
 
58
  if waveform.numel() > MAX_SAMPLES:
59
- raise ValueError("Audio exceeds 5 minute limit")
60
 
61
- return waveform.numpy()
62
 
63
 
64
  def chunk_audio(audio: np.ndarray):
@@ -67,7 +79,7 @@ def chunk_audio(audio: np.ndarray):
67
  step = chunk_size - overlap
68
 
69
  for start in range(0, len(audio), step):
70
- chunk = audio[start : start + chunk_size]
71
  if len(chunk) < SAMPLE_RATE:
72
  break
73
  yield chunk
@@ -82,9 +94,7 @@ def transcribe_chunk(chunk: np.ndarray) -> str:
82
  )
83
 
84
  with torch.no_grad():
85
- logits = model(
86
- inputs.input_values.to(DEVICE)
87
- ).logits
88
 
89
  predicted_ids = torch.argmax(logits, dim=-1)
90
  return processor.batch_decode(predicted_ids)[0].strip()
@@ -112,12 +122,18 @@ def forward_to_text_model(text: str):
112
 
113
  @app.post("/stt", response_model=STTResponse)
114
  async def stt(audio: UploadFile = File(...)):
115
- try:
116
- audio_bytes = await audio.read()
117
- audio_data = load_audio(audio_bytes)
118
- transcript = transcribe_long(audio_data)
119
- except Exception as e:
120
- raise HTTPException(status_code=400, detail=str(e))
 
 
 
 
 
 
121
 
122
  downstream = None
123
  try:
@@ -128,5 +144,7 @@ async def stt(audio: UploadFile = File(...)):
128
  return STTResponse(
129
  transcript=transcript,
130
  downstream_response=downstream,
 
131
  )
132
 
 
 
1
  # main.py
 
2
  import json
3
  import torch
4
  import torchaudio
5
  import requests
6
  import numpy as np
7
+ import tempfile
8
+ import os
9
 
10
+ from fastapi import FastAPI, UploadFile, File
11
  from transformers import AutoProcessor, AutoModelForCTC
12
  from pydantic import BaseModel
13
 
14
+
15
  with open("config.json", "r") as f:
16
  CONFIG = json.load(f)
17
 
 
28
  MAX_SAMPLES = SAMPLE_RATE * MAX_AUDIO_SECONDS
29
 
30
 
31
+ app = FastAPI(title="Universal Audio STT", version="1.4.0")
32
 
33
 
34
  processor = AutoProcessor.from_pretrained(MODEL_NAME)
 
39
  class STTResponse(BaseModel):
40
  transcript: str
41
  downstream_response: dict | None = None
42
+ error: str | None = None
43
 
44
 
45
+ def load_audio_safe(file_bytes: bytes) -> tuple[np.ndarray | None, str | None]:
46
+ if not file_bytes:
47
+ return None, "Empty audio file"
48
 
49
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
50
+ tmp.write(file_bytes)
51
+ tmp_path = tmp.name
52
 
53
  try:
54
+ waveform, sr = torchaudio.load(tmp_path)
55
  except Exception:
56
+ return None, "Unsupported or corrupted audio format"
57
+ finally:
58
+ os.unlink(tmp_path)
59
+
60
+ if waveform.numel() == 0:
61
+ return None, "Audio contains no samples"
62
 
63
+ waveform = waveform.mean(dim=0)
64
 
65
  if sr != SAMPLE_RATE:
66
  waveform = torchaudio.functional.resample(
 
68
  )
69
 
70
  if waveform.numel() > MAX_SAMPLES:
71
+ return None, "Audio exceeds 5 minute limit"
72
 
73
+ return waveform.numpy(), None
74
 
75
 
76
  def chunk_audio(audio: np.ndarray):
 
79
  step = chunk_size - overlap
80
 
81
  for start in range(0, len(audio), step):
82
+ chunk = audio[start:start + chunk_size]
83
  if len(chunk) < SAMPLE_RATE:
84
  break
85
  yield chunk
 
94
  )
95
 
96
  with torch.no_grad():
97
+ logits = model(inputs.input_values.to(DEVICE)).logits
 
 
98
 
99
  predicted_ids = torch.argmax(logits, dim=-1)
100
  return processor.batch_decode(predicted_ids)[0].strip()
 
122
 
123
  @app.post("/stt", response_model=STTResponse)
124
  async def stt(audio: UploadFile = File(...)):
125
+ audio_bytes = await audio.read()
126
+
127
+ audio_data, error = load_audio_safe(audio_bytes)
128
+
129
+ if error:
130
+ return STTResponse(
131
+ transcript="",
132
+ downstream_response=None,
133
+ error=error,
134
+ )
135
+
136
+ transcript = transcribe_long(audio_data)
137
 
138
  downstream = None
139
  try:
 
144
  return STTResponse(
145
  transcript=transcript,
146
  downstream_response=downstream,
147
+ error=None,
148
  )
149
 
150
+