From 5844839639766f05da239024fa144d0ab4cd1c6c Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:59:38 -0700 Subject: [PATCH] refactor(stt): fold no-provider error tail, drop _normalize_local_command_model alias, compact dispatcher call sites --- tools/transcription_audio.py | 8 ----- tools/transcription_command.py | 6 ---- tools/transcription_local.py | 9 +----- tools/transcription_tools.py | 53 ++++++++++++---------------------- 4 files changed, 20 insertions(+), 56 deletions(-) diff --git a/tools/transcription_audio.py b/tools/transcription_audio.py index de446f7803..8c385cc9a9 100644 --- a/tools/transcription_audio.py +++ b/tools/transcription_audio.py @@ -254,17 +254,9 @@ def _convert_caf_to_wav(file_path: str) -> Optional[str]: # they may wrap local CLIs that want the original bytes. _CLOUD_TRIM_THRESHOLD_DB_DEFAULT = -40 # audio below this level counts as silence - - _CLOUD_TRIM_KEEP_MS_DEFAULT = 300 # how much of each pause survives the trim - - _CLOUD_TRIM_MIN_SAVING = 0.10 # use the trimmed file only when >=10% shorter - - _CLOUD_TRIM_MIN_RESULT_SECONDS = 0.3 # all-silence guard floor: never upload ~empty audio - - # Below this the trim can't pay for itself (several providers bill a 10s # minimum per request) and the encode would sit on the synchronous voice-note path. _CLOUD_TRIM_MIN_INPUT_SECONDS = 12.0 diff --git a/tools/transcription_command.py b/tools/transcription_command.py index 6049ca222b..d4137e9b7a 100644 --- a/tools/transcription_command.py +++ b/tools/transcription_command.py @@ -40,14 +40,8 @@ logger = logging.getLogger("tools.transcription_tools") # The single-env-var HERMES_LOCAL_STT_COMMAND escape hatch stays untouched via # the built-in ``local_command`` path. DEFAULT_COMMAND_STT_TIMEOUT_SECONDS = 300 - - DEFAULT_COMMAND_STT_LANGUAGE = "en" - - DEFAULT_COMMAND_STT_OUTPUT_FORMAT = "txt" - - COMMAND_STT_OUTPUT_FORMATS = frozenset({"txt", "json", "srt", "vtt"}) diff --git a/tools/transcription_local.py b/tools/transcription_local.py index eda2a75312..ab6a42859a 100644 --- a/tools/transcription_local.py +++ b/tools/transcription_local.py @@ -68,9 +68,6 @@ def _normalize_local_model(model_name: Optional[str]) -> str: return model_name -_normalize_local_command_model = _normalize_local_model - - def _try_lazy_install_stt() -> bool: """Lazy-install faster-whisper and re-check dynamically so it's usable without a restart.""" try: @@ -188,11 +185,7 @@ def _load_local_whisper_model(model_name: str, device: str = "auto", compute_typ # condition_on_previous_text=False so one hallucinated token can't seed a run; # and the segment confidence gate in _is_hallucinated_segment. _VAD_MIN_SILENCE_MS_DEFAULT = 500 - - _NO_SPEECH_PROB_THRESHOLD_DEFAULT = 0.6 - - _LOGPROB_THRESHOLD_DEFAULT = -1.0 @@ -308,7 +301,7 @@ def _transcribe_local_command( # Language: hook override > stt.local.language > stt.language > env > "en". language = language or _resolve_stt_language("local") or DEFAULT_LOCAL_STT_LANGUAGE - normalized_model = _normalize_local_command_model(model_name) + normalized_model = _normalize_local_model(model_name) try: with tempfile.TemporaryDirectory(prefix="hermes-local-stt-") as output_dir: diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index ded56eceff..f2d0c78560 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -76,7 +76,6 @@ from tools.transcription_local import ( # noqa: F401 (re-exported; tests patch _join_confident_segments, _load_local_whisper_model, _looks_like_cuda_lib_error, - _normalize_local_command_model, _normalize_local_model, _transcribe_local_command, _try_lazy_install_stt, @@ -625,10 +624,9 @@ def _transcribe_prepared_audio( # Convert CAF (iMessage voice notes) to WAV for cloud STT providers. if Path(file_path).suffix.lower() == ".caf" and provider not in ("local", "local_command"): - converted = _convert_caf_to_wav(file_path) - if not converted: + file_path = _convert_caf_to_wav(file_path) + if not file_path: return _error_result("CAF audio could not be converted to WAV.") - file_path = converted # Best-effort pre-upload silence trim for built-in cloud providers. trim_cleanup_dir: Optional[str] = None @@ -696,12 +694,9 @@ def _dispatch_stt_provider( # The hook fires after provider resolution and BEFORE any backend is # invoked; ``language`` stays None unless a hook overrides it. model, language, prompt = _apply_pre_transcription_hook( - file_path=file_path, - provider=provider, - model=model, + file_path=file_path, provider=provider, model=model, language=_get_stt_section(stt_config, provider).get("language"), - prompt=prompt, - source=source, + prompt=prompt, source=source, ) prompt = _enforce_prompt_length_limit(prompt, provider) @@ -718,36 +713,28 @@ def _dispatch_stt_provider( command_provider_config = _resolve_command_stt_provider_config(provider, stt_config) if command_provider_config is not None: return _transcribe_command_stt( - file_path, - provider, - command_provider_config, - stt_config, - model_override=model, - language_override=language, - prompt=prompt, + file_path, provider, command_provider_config, stt_config, + model_override=model, language_override=language, prompt=prompt, ) # Plugin-registered backend. Plugins read per-provider config under # ``stt.`` like built-ins; the ``model`` argument overrides it. plugin_cfg = _get_stt_section(stt_config, provider) plugin_result = _dispatch_to_plugin_provider( - file_path, - provider, - stt_config, + file_path, provider, stt_config, model=model or plugin_cfg.get("model"), language=language or _resolve_stt_language(provider, stt_config), prompt=prompt, ) if plugin_result is not None: return plugin_result + return _no_provider_error(provider, stt_config) + +def _no_provider_error(provider: str, stt_config: Dict[str, Any]) -> Dict[str, Any]: + """Error envelope when nothing claimed *provider*: unregistered name > openai selection reason > generic hint.""" provider_key = str(provider or "").strip().lower() - if ( - "provider" in stt_config - and provider_key - and provider_key not in BUILTIN_STT_PROVIDERS - and provider_key != "none" - ): + if "provider" in stt_config and provider_key and provider_key not in BUILTIN_STT_PROVIDERS and provider_key != "none": return _unregistered_stt_provider_error(provider_key) # An explicit openai selection flattened to "none" carries a @@ -794,16 +781,14 @@ def transcribe_audio( return source_error prepared_path, cleanup_dir, prep_error = _prepare_audio_for_transcription(file_path) - if prep_error: - return prep_error - if prepared_path is None: - return _error_result("Audio preprocessing did not produce a file for transcription.") + if prep_error or prepared_path is None: + return prep_error or _error_result("Audio preprocessing did not produce a file for transcription.") try: - prepared_error = _validate_audio_file(prepared_path, enforce_size_limit=False) - if prepared_error: - return prepared_error - return _transcribe_prepared_audio(prepared_path, model, source) + return ( + _validate_audio_file(prepared_path, enforce_size_limit=False) + or _transcribe_prepared_audio(prepared_path, model, source) + ) finally: if cleanup_dir: shutil.rmtree(cleanup_dir, ignore_errors=True) @@ -828,7 +813,7 @@ def transcribe_audio_local_fallback( if _HAS_FASTER_WHISPER: return _transcribe_local(file_path, _normalize_local_model(local_model)) if _has_local_command(): - return _transcribe_local_command(file_path, _normalize_local_command_model(local_model)) + return _transcribe_local_command(file_path, _normalize_local_model(local_model)) return _error_result("No installed local STT backend is available.", provider="local")