From 257418cbd8cb45ad63f8e39a167a8fb42f5ced60 Mon Sep 17 00:00:00 2001 From: joshua-berri Date: Thu, 12 Nov 2026 23:26:55 -0800 Subject: [PATCH] fix(proxy): import soundfile lazily in the transcribe logging handler (#51480) * fix(proxy): import soundfile lazily in the transcribe logging handler The messages streaming path imports the pass-through success handler, which pulled in soundfile at module load. Installs without soundfile failed on every Anthropic and Bedrock messages stream. Only media_file_seconds needs it Fixes #45379 * test(vector_store): pin set_verbose off in the SDK fallback warning test An ambient litellm.set_verbose=True adds a deprecation warning to the captured records, so the exact one-warning assertion failed depending on test order --------- Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com> --- .../transcribe_passthrough_logging_handler.py | 3 ++- ...st_transcribe_passthrough_logging_handler.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/transcribe_passthrough_logging_handler.py b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/transcribe_passthrough_logging_handler.py index ec189b83195..d7dd2fb2921 100644 --- a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/transcribe_passthrough_logging_handler.py +++ b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/transcribe_passthrough_logging_handler.py @@ -13,7 +13,6 @@ from typing import IO, ClassVar, Final, Protocol, TypeAlias from urllib.parse import quote import httpx -import soundfile from pydantic import ConfigDict, TypeAdapter, ValidationError from typing_extensions import ReadOnly, TypedDict @@ -522,6 +521,8 @@ async def write_media_within_limit(response: httpx.Response, media_file: IO[byte def media_file_seconds(path: Path) -> float | None: + import soundfile + try: with soundfile.SoundFile(str(path)) as audio: frames: Final = len(audio) diff --git a/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_transcribe_passthrough_logging_handler.py b/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_transcribe_passthrough_logging_handler.py index 067e2cfc25f..a25a1dfe5c9 100644 --- a/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_transcribe_passthrough_logging_handler.py +++ b/tests/unit/proxy/pass_through_endpoints/llm_provider_handlers/test_transcribe_passthrough_logging_handler.py @@ -1,7 +1,9 @@ import asyncio +import importlib import io import json import struct +import sys import wave from datetime import datetime from pathlib import Path @@ -471,6 +473,21 @@ class TestMediaFileSeconds: assert media_file_seconds(media) is None +def test_passthrough_logging_imports_without_soundfile_installed(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setitem(sys.modules, "soundfile", None) + for name in ( + "litellm.proxy.pass_through_endpoints.llm_provider_handlers.transcribe_passthrough_logging_handler", + "litellm.proxy.pass_through_endpoints.success_handler", + ): + package, _, attribute = name.rpartition(".") + monkeypatch.setattr(sys.modules[package], attribute, sys.modules[name]) + monkeypatch.delitem(sys.modules, name) + + success_handler: Final = importlib.import_module("litellm.proxy.pass_through_endpoints.success_handler") + + assert success_handler.PassThroughEndpointLogging is not PassThroughEndpointLogging + + class TestStartedTranscriptionJob: def test_reads_the_media_and_creation_time_from_the_start_response(self): started = started_transcription_job(