diff --git a/services/WhisperLive/whisper_live/server.py b/services/WhisperLive/whisper_live/server.py index e74c27038..896d3188a 100644 --- a/services/WhisperLive/whisper_live/server.py +++ b/services/WhisperLive/whisper_live/server.py @@ -872,6 +872,8 @@ def get_audio_from_websocket(self, websocket): self.handle_audio_chunk_metadata(websocket, control_message) elif message_type == "session_control": self.handle_session_control(websocket, control_message) + elif message_type == "change_language": + self.handle_change_language(websocket, control_message) else: logging.warning(f"Unknown control message type: {message_type}") @@ -936,6 +938,51 @@ def handle_session_control(self, websocket, control_message): except Exception as e: logging.error(f"Error processing session control: {e}") + def handle_change_language(self, websocket, control_message): + """ + Handle language change requests during an active session. + + Args: + websocket: The websocket connection + control_message: The parsed control message with language change request + Expected format: {"type": "change_language", "language": "ru", "uid": "client_uid"} + """ + try: + client = self.client_manager.get_client(websocket) + if not client: + logging.warning("Language change requested but no client found for websocket") + return + + new_language = control_message.get("language") + if not new_language: + logging.warning(f"Language change requested but no language specified for client {client.client_uid}") + return + + # Validate language code (basic check) + if not isinstance(new_language, str) or len(new_language) != 2: + logging.warning(f"Invalid language code '{new_language}' for client {client.client_uid}") + return + + old_language = client.language + client.language = new_language + + logging.info(f"Language changed for client {client.client_uid}: {old_language} -> {new_language}") + + # Send confirmation to client + language_data = { + "uid": client.client_uid, + "language": new_language, + "language_prob": 1.0, # Manual change, so confidence is 1.0 + "changed": True, + "previous_language": old_language + } + client.websocket.send(json.dumps(language_data)) + + logger.info(f"LANGUAGE_CHANGED: client={client.client_uid}, old={old_language}, new={new_language}") + + except Exception as e: + logging.error(f"Error processing language change: {e}") + def handle_speaker_activity_update(self, websocket, control_message): """ Handle speaker activity update messages from the bot. @@ -1561,6 +1608,8 @@ def handle_control_message(self, websocket, message): self.handle_speaker_activity_update(websocket, control_message) elif message_type == "audio_chunk_metadata": self.handle_audio_chunk_metadata(websocket, control_message) + elif message_type == "change_language": + self.handle_change_language(websocket, control_message) else: logging.warning(f"Unknown control message type: {message_type} from UID {client.uid if client else 'N/A'}") except json.JSONDecodeError: diff --git a/services/WhisperLive/whisper_live/transcriber.py b/services/WhisperLive/whisper_live/transcriber.py index 1ad12c4a9..1c0fb50d5 100644 --- a/services/WhisperLive/whisper_live/transcriber.py +++ b/services/WhisperLive/whisper_live/transcriber.py @@ -1842,33 +1842,23 @@ def detect_language( if not segment_language_probs: continue - # Get the maximum probability in this segment - max_prob = max(prob for _, prob in segment_language_probs) + # Get top language and its probability (list is sorted by probability descending) + top_lang, top_prob = segment_language_probs[0] # Skip segments with very low confidence - these are likely noise or silence - # Also check if probabilities are too evenly distributed (high entropy = uncertain) - if max_prob < min_segment_confidence: + if top_prob < min_segment_confidence: # This segment is likely noise/silence, skip it - if self.logger: - self.logger.debug( - f"Skipping segment with low confidence (max_prob={max_prob:.3f} < {min_segment_confidence})" - ) continue # Additional check: if top language probability is too close to second place, # it might indicate uncertainty/noise. Require at least 0.15 difference for confidence. # Also check if top probability is below a reasonable threshold even if it passed min_segment_confidence if len(segment_language_probs) >= 2: - top_prob = segment_language_probs[0][1] second_prob = segment_language_probs[1][1] prob_diff = top_prob - second_prob # Skip if: (uncertainty AND low confidence) OR (very low top probability) if (prob_diff < 0.15 and top_prob < 0.5) or top_prob < 0.35: # Too uncertain or too low confidence, likely noise/silence - if self.logger: - self.logger.debug( - f"Skipping uncertain/low-confidence segment (top_prob={top_prob:.3f}, diff={prob_diff:.3f})" - ) continue # Aggregate probabilities for all languages in this segment @@ -1920,11 +1910,6 @@ def detect_language( else: # Very low confidence, return "en" as fallback with low probability # This allows transcription to proceed, but the system knows it's uncertain - if self.logger: - self.logger.info( - f"All segments filtered out, last segment has low confidence ({top_prob:.3f} < 0.6). " - f"Returning 'en' with probability 0.0" - ) language, language_probability = "en", 0.0 else: # No language probabilities available, use "en" as fallback @@ -1952,11 +1937,6 @@ def detect_language( # Confidence too low, likely noise/silence - return "en" as fallback with low probability # This allows transcription to proceed, but the system knows it's uncertain # and won't set the language (set_language requires > 0.5) - if self.logger: - self.logger.info( - f"Language detection confidence too low ({language_probability:.3f} < 0.6), " - f"likely noise/silence. Returning 'en' with probability 0.0" - ) language, language_probability = "en", 0.0 return language, language_probability, all_language_probs