Skip to content

Commit 01bba83

Browse files
authored
fix: silence GenAI audio optional dependency import warning (#223)
2 parents de65b32 + f5c7c56 commit 01bba83

3 files changed

Lines changed: 102 additions & 9 deletions

File tree

util/opentelemetry-util-genai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
- Avoid import-time warnings when optional audio dependencies for PCM16-to-WAV conversion are not installed.
11+
1012
## Version 0.3b0 (2026-02-20)
1113

1214
- Add `gen_ai.tool_definitions` to completion hook ([#4181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4181))

util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/pre_uploader.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@
7878

7979
_logger = logging.getLogger(__name__)
8080

81-
# Log warning if audio libraries are not available
82-
if not _audio_libs_available:
83-
_logger.warning(
84-
"numpy or soundfile not available, PCM16 to WAV conversion will be skipped"
85-
)
86-
8781
# Supported modality types for pre-upload (derived from Modality type)
8882
_SUPPORTED_MODALITIES = get_args(Modality)
8983

@@ -591,9 +585,6 @@ def _convert_pcm16_to_wav(
591585
Byte data in WAV format, None if conversion fails
592586
"""
593587
if not _audio_libs_available or np is None or sf is None:
594-
_logger.warning(
595-
"Cannot convert PCM16 to WAV: numpy or soundfile not available"
596-
)
597588
return None
598589

599590
try:

util/opentelemetry-util-genai/tests/_multimodal_upload/test_pre_uploader_audio.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
and audio format conversion (e.g., PCM16 to WAV)
1919
"""
2020

21+
import os
22+
import subprocess
23+
import sys
24+
import textwrap
2125
from pathlib import Path
2226
from unittest.mock import patch
2327

@@ -65,6 +69,55 @@ def _read_audio_file(filename: str) -> bytes:
6569
with open(filepath, "rb") as file_obj:
6670
return file_obj.read()
6771

72+
@staticmethod
73+
def test_import_without_audio_libs_does_not_write_to_standard_streams():
74+
"""Missing optional audio libs should not emit import-time output."""
75+
project_root = Path(__file__).parents[4]
76+
util_genai_src = Path(__file__).parents[2] / "src"
77+
instrumentation_src = (
78+
project_root / "opentelemetry-instrumentation" / "src"
79+
)
80+
env = os.environ.copy()
81+
pythonpath_parts = [
82+
str(util_genai_src),
83+
str(instrumentation_src),
84+
env.get("PYTHONPATH", ""),
85+
]
86+
env["PYTHONPATH"] = os.pathsep.join(
87+
part for part in pythonpath_parts if part
88+
)
89+
90+
script = textwrap.dedent(
91+
"""
92+
import importlib.abc
93+
import logging
94+
import sys
95+
96+
class BlockAudioLibs(importlib.abc.MetaPathFinder):
97+
def find_spec(self, fullname, path=None, target=None):
98+
if fullname == "numpy" or fullname.startswith("numpy."):
99+
raise ImportError(fullname)
100+
if fullname == "soundfile" or fullname.startswith("soundfile."):
101+
raise ImportError(fullname)
102+
return None
103+
104+
sys.meta_path.insert(0, BlockAudioLibs())
105+
logging.basicConfig(level=logging.WARNING, stream=sys.stdout)
106+
import opentelemetry.util.genai._multimodal_upload.pre_uploader # noqa: F401
107+
"""
108+
)
109+
110+
completed = subprocess.run(
111+
[sys.executable, "-c", script],
112+
env=env,
113+
check=True,
114+
capture_output=True,
115+
text=True,
116+
)
117+
118+
assert completed.stdout == ""
119+
assert completed.stderr == ""
120+
68121
# ========== Edge Case Tests ==========
69122

70123
@staticmethod
@@ -174,6 +227,53 @@ def test_pcm16_to_wav_conversion(pre_uploader, pcm_mime_type):
174227
# If library unavailable, should keep original format
175228
assert uploads[0].content_type == pcm_mime_type
176229

230+
@staticmethod
231+
def test_pcm16_conversion_missing_audio_libs_logs_single_warning(
232+
caplog,
233+
):
234+
"""Missing optional audio libs should only log the actual conversion skip."""
235+
with (
236+
patch(
237+
"opentelemetry.util.genai._multimodal_upload.pre_uploader._audio_libs_available",
238+
False,
239+
),
240+
patch(
241+
"opentelemetry.util.genai._multimodal_upload.pre_uploader.np",
242+
None,
243+
),
244+
patch(
245+
"opentelemetry.util.genai._multimodal_upload.pre_uploader.sf",
246+
None,
247+
),
248+
):
249+
pre_uploader = MultimodalPreUploader(base_path="/tmp/test_upload")
250+
part = Blob(
251+
content=b"\x00\x01" * 1000,
252+
mime_type="audio/pcm16",
253+
modality="audio",
254+
)
255+
input_messages = [InputMessage(role="user", parts=[part])]
256+
257+
with caplog.at_level(
258+
"WARNING",
259+
logger=(
260+
"opentelemetry.util.genai._multimodal_upload.pre_uploader"
261+
),
262+
):
263+
uploads = pre_uploader.pre_upload(
264+
span_context=None,
265+
start_time_utc_nano=1000000000000000000,
266+
input_messages=input_messages,
267+
output_messages=None,
268+
)
269+
270+
assert len(uploads) == 1
271+
assert uploads[0].content_type == "audio/pcm16"
272+
warning_messages = [record.getMessage() for record in caplog.records]
273+
assert warning_messages == [
274+
"Failed to convert PCM16 to WAV, using original format"
275+
]
276+
177277
@staticmethod
178278
def test_pcm16_conversion_disabled_by_default():
179279
"""Test PCM16 conversion stays disabled when env var is unset"""

0 commit comments

Comments
 (0)