@@ -2,9 +2,10 @@ use anyhow::{Context, Result};
22use async_trait:: async_trait;
33use futures:: { Stream , StreamExt } ;
44use googleapis_tonic_google_cloud_speech_v2:: google:: cloud:: speech:: v2:: {
5- StreamingRecognizeResponse , streaming_recognize_response:: SpeechEventType ,
5+ StreamingRecognizeResponse , WordInfo , streaming_recognize_response:: SpeechEventType ,
66} ;
77use serde:: Deserialize ;
8+ use std:: collections:: HashMap ;
89use tokio:: sync:: mpsc:: UnboundedReceiver ;
910use tonic:: Code ;
1011
@@ -23,6 +24,8 @@ pub struct Params {
2324 pub model : String ,
2425 pub language : String ,
2526 #[ serde( default ) ]
27+ pub diarization : bool ,
28+ #[ serde( default ) ]
2629 pub region : Region ,
2730}
2831
@@ -154,6 +157,7 @@ async fn transcribe_and_process_stream_session(
154157 . transcribe (
155158 & params. model ,
156159 languages,
160+ params. diarization ,
157161 interim_results,
158162 audio_format,
159163 audio_receiver,
@@ -221,7 +225,12 @@ where
221225 let language = include_detected_language
222226 . then ( || one. language_code . trim ( ) . to_owned ( ) )
223227 . filter ( |x| !x. is_empty ( ) ) ;
224- text_output. final_text ( alternative. transcript . trim ( ) . to_owned ( ) , language) ?;
228+ let speaker = speaker_with_max_assigned_characters ( & alternative. words ) ;
229+ text_output. final_text (
230+ alternative. transcript . trim ( ) . to_owned ( ) ,
231+ language,
232+ speaker,
233+ ) ?;
225234 }
226235 [ _, ..] => {
227236 let interim_text = response
@@ -321,6 +330,25 @@ fn should_restart_for_stream_limit(code: Code, message: &str) -> bool {
321330 code == Code :: Aborted && message. contains ( "max duration of 5 minutes reached for stream" )
322331}
323332
333+ fn speaker_with_max_assigned_characters ( words : & [ WordInfo ] ) -> Option < String > {
334+ let mut char_count_by_speaker = HashMap :: < & str , usize > :: new ( ) ;
335+
336+ for word in words {
337+ let speaker = word. speaker_label . trim ( ) ;
338+ if speaker. is_empty ( ) {
339+ continue ;
340+ }
341+
342+ let spoken_chars = word. word . chars ( ) . count ( ) ;
343+ * char_count_by_speaker. entry ( speaker) . or_default ( ) += spoken_chars;
344+ }
345+
346+ char_count_by_speaker
347+ . into_iter ( )
348+ . max_by_key ( |( _, char_count) | * char_count)
349+ . map ( |( speaker, _) | speaker. to_owned ( ) )
350+ }
351+
324352// Keeps interim/final emission behavior in one place across all exit paths.
325353// If a session ends without any final result, we promote the last interim text
326354// to final in Drop so callers still receive a terminal text event.
@@ -338,8 +366,13 @@ impl<'a> StreamTextOutput<'a> {
338366 }
339367 }
340368
341- fn final_text ( & mut self , text : String , language : Option < String > ) -> Result < ( ) > {
342- self . output . text ( true , text, language, None ) ?;
369+ fn final_text (
370+ & mut self ,
371+ text : String ,
372+ language : Option < String > ,
373+ speaker : Option < String > ,
374+ ) -> Result < ( ) > {
375+ self . output . text ( true , text, language, speaker) ?;
343376 self . pending_interim_text = None ;
344377 Ok ( ( ) )
345378 }
0 commit comments