CoolFace
Apppublic

helo-ayush/Diarization_VoiceFingerprinted

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
deepgram_processor.py146 linesDownload Raw Back to utils
1# ==============================================================================
2# DEEPGRAM PROCESSOR (LEGACY / UTILITIES)
3# Originally used for STT, now primarily holds `build_raw_transcript` helper
4# to group word-level data cleanly by speaker segments.
5# ==============================================================================
6import os
7import time
8import asyncio
9from deepgram import DeepgramClient, PrerecordedOptions
10
11
12deepgram = DeepgramClient(os.getenv("DEEPGRAM_API_KEY"))
13
14
15async def transcribe_with_diarization(audio_bytes: bytes) -> dict:
16    """
17    Transcribe audio with speaker diarization using Deepgram Nova-3.
18    Returns word-level data with speaker assignments.
19    """
20    size_kb = len(audio_bytes) / 1024
21    print(f"๐ŸŽ™๏ธ Sending {size_kb:.1f}KB to Deepgram...")
22
23    start_time = time.time()
24
25    options = PrerecordedOptions(
26        model="nova-3",
27        language="multi",
28        diarize=True,
29        smart_format=True,
30        punctuate=True,
31        utterances=True,
32        multichannel=False,
33    )
34
35    source = {"buffer": audio_bytes}
36
37    # Run with 120s timeout
38    try:
39        response = await asyncio.wait_for(
40            asyncio.to_thread(
41                deepgram.listen.rest.v("1").transcribe_file,
42                source,
43                options,
44            ),
45            timeout=120
46        )
47    except asyncio.TimeoutError:
48        raise TimeoutError("Deepgram timed out after 120s")
49
50    processing_time = int((time.time() - start_time) * 1000)
51    print(f"โœ… Deepgram completed in {processing_time}ms")
52
53    utterances = response.results.utterances
54    channel = response.results.channels[0] if response.results.channels else None
55    words_raw = channel.alternatives[0].words if channel and channel.alternatives else []
56
57    # No speech detected
58    if not utterances or len(utterances) == 0:
59        transcript = channel.alternatives[0].transcript if channel and channel.alternatives else ""
60        return {
61            "words": [],
62            "transcript": transcript,
63            "has_speech": False,
64            "processing_time": processing_time
65        }
66
67    # Build word-level array with speaker info
68    words = [
69        {
70            "word": w.punctuated_word or w.word,
71            "speaker": w.speaker,
72            "confidence": getattr(w, "speaker_confidence", None) or w.confidence or 0,
73            "start": w.start,
74            "end": w.end,
75        }
76        for w in words_raw
77    ]
78
79    return {"words": words, "has_speech": True, "processing_time": processing_time}
80
81
82def build_raw_transcript(words: list, agent_map: dict = None) -> dict:
83    """
84    Filter out background noise speakers (< 5% word share)
85    and build a raw transcript string from valid words.
86    """
87    total_words = len(words)
88
89    # Count words per speaker
90    speaker_counts = {}
91    for w in words:
92        speaker_counts[w["speaker"]] = speaker_counts.get(w["speaker"], 0) + 1
93
94    # Filter speakers with < 5% word share
95    valid_speakers = [
96        int(s) for s, count in speaker_counts.items()
97        if (count / total_words) > 0.05
98    ]
99
100    print(f"๐Ÿ‘ฅ Speakers detected: {len(speaker_counts)}, valid: {len(valid_speakers)}")
101    for spk, count in speaker_counts.items():
102        pct = (count / total_words) * 100
103        valid = int(spk) in valid_speakers
104        print(f"   Speaker {spk}: {count} words ({pct:.1f}%) {'โœ…' if valid else 'โŒ filtered'}")
105
106    # Filter words and group into speaker segments
107    valid_words = [w for w in words if w["speaker"] in valid_speakers]
108    segments = []
109    current_seg = None
110
111    for w in valid_words:
112        if current_seg is None or current_seg["speaker"] != w["speaker"]:
113            if current_seg:
114                segments.append(current_seg)
115            current_seg = {"speaker": w["speaker"], "words": [w["word"]], "start": w.get("start", 0), "end": w.get("end", 0)}
116        else:
117            current_seg["words"].append(w["word"])
118            current_seg["end"] = w.get("end", current_seg["end"])
119    if current_seg:
120        segments.append(current_seg)
121
122    agent_map = agent_map or {}
123    
124    formatted_segments = []
125    for s in segments:
126        spk_id = s.get("speaker")
127        speaker_name = agent_map.get(spk_id, f"Speaker {spk_id}")
128        words_text = " ".join(s["words"])
129        formatted_segments.append(f"{speaker_name}: {words_text}")
130        
131    raw_transcript = "\n".join(formatted_segments)
132
133    # Confidence stats
134    avg_confidence = sum(w["confidence"] for w in valid_words) / len(valid_words) if valid_words else 0
135    low_conf_words = sum(1 for w in valid_words if w["confidence"] < 0.7)
136    print(f"๐Ÿ“Š Deepgram confidence: avg={avg_confidence:.3f}, low-conf words={low_conf_words}/{len(valid_words)}")
137
138    return {
139        "raw_transcript": raw_transcript,
140        "valid_words": valid_words,
141        "valid_speakers": valid_speakers,
142        "segments": segments,
143        "avg_confidence": avg_confidence,
144        "low_conf_words": low_conf_words,
145    }
146