Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions services/WhisperLive/whisper_live/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
26 changes: 3 additions & 23 deletions services/WhisperLive/whisper_live/transcriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading