call-qa-processing / ml-services /src /transcriber.py
aniketqxp's picture
feat: add processing service runtime
f1ef7e2
Raw
History Blame Contribute Delete
3.58 kB
import os
import json
import time
import requests
import soundfile as sf
import numpy as np
from dotenv import load_dotenv
os.environ["HF_HUB_OFFLINE"] = "1"
import whisperx
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
TARGET_SR = 16000
def load_audio_sf(path):
data, sr = sf.read(path, dtype="float32")
if data.ndim > 1:
data = data.mean(axis=1)
if sr != TARGET_SR:
n = int(len(data) * TARGET_SR / sr)
data = np.interp(np.linspace(0, len(data) - 1, n), np.arange(len(data)), data)
return data
def get_api_segments(api_key, file_path, label):
print(f"[{label}] Transcribing via OpenAI API...")
headers = {"Authorization": f"Bearer {api_key}"}
with open(file_path, "rb") as f:
response = requests.post(
"https://api.openai.com/v1/audio/transcriptions",
headers=headers,
files={"file": (os.path.basename(file_path), f, "audio/wav")},
data={"model": "whisper-1", "response_format": "verbose_json"},
)
if response.status_code != 200:
raise RuntimeError(f"OpenAI API failed ({response.status_code}): {response.text}")
segments = [
{"text": s["text"].strip(), "start": s["start"], "end": s["end"]}
for s in response.json().get("segments", [])
]
print(f"[{label}] Obtained {len(segments)} segments from API")
return segments
def align_locally(segments, audio_path, label, align_model, metadata):
print(f"[{label}] Loading audio...")
audio = load_audio_sf(audio_path)
print(f"[{label}] Aligning with wav2vec2 (forced-alignment)...")
t0 = time.time()
result = whisperx.align(
segments, align_model, metadata, audio, device="cpu", return_char_alignments=False
)
print(f"[{label}] Aligned in {time.time() - t0:.2f}s")
words = [
{"word": w["word"], "start": round(w["start"], 3), "end": round(w["end"], 3)}
for seg in result.get("segments", [])
for w in seg.get("words", [])
if "start" in w and "end" in w
]
print(f"[{label}] Extracted {len(words)} aligned words")
return words
def run(agent_audio, customer_audio, output_path, call_id="call_1"):
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("OPENAI_API_KEY not set in .env")
agent_segs = get_api_segments(api_key, agent_audio, "AGENT")
customer_segs = get_api_segments(api_key, customer_audio, "CUSTOMER")
print("\nLoading WhisperX wav2vec2 alignment model from cache...")
align_model, metadata = whisperx.load_align_model(language_code="en", device="cpu")
agent_words = align_locally(agent_segs, agent_audio, "AGENT", align_model, metadata)
customer_words = align_locally(customer_segs, customer_audio, "CUSTOMER", align_model, metadata)
out = {
"model": "Hybrid (OpenAI API + Local WhisperX Forced-Alignment)",
"call": call_id,
"agent": agent_words,
"customer": customer_words,
}
with open(output_path, "w", encoding="utf-8") as f:
json.dump(out, f, indent=2)
print(f"\nSaved to {output_path}")
print(f"Agent: {len(agent_words)} words | Customer: {len(customer_words)} words")
return out
if __name__ == "__main__":
APPTEK = r"d:\Desktop\ai-ml-capstone\data\apptek"
OUT = r"d:\Desktop\ai-ml-capstone\frontend\transcript_data.json"
run(
agent_audio=os.path.join(APPTEK, "call_1_agent.wav"),
customer_audio=os.path.join(APPTEK, "call_1_customer.wav"),
output_path=OUT,
)