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
2 changes: 2 additions & 0 deletions util/opentelemetry-util-genai/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Avoid import-time warnings when optional audio dependencies for PCM16-to-WAV conversion are not installed.

## Version 0.3b0 (2026-02-20)

- Add `gen_ai.tool_definitions` to completion hook ([#4181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4181))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,6 @@

_logger = logging.getLogger(__name__)

# Log warning if audio libraries are not available
if not _audio_libs_available:
_logger.warning(
"numpy or soundfile not available, PCM16 to WAV conversion will be skipped"
)

# Supported modality types for pre-upload (derived from Modality type)
_SUPPORTED_MODALITIES = get_args(Modality)

Expand Down Expand Up @@ -591,9 +585,6 @@ def _convert_pcm16_to_wav(
Byte data in WAV format, None if conversion fails
"""
if not _audio_libs_available or np is None or sf is None:
_logger.warning(
"Cannot convert PCM16 to WAV: numpy or soundfile not available"
)
return None

try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
and audio format conversion (e.g., PCM16 to WAV)
"""

import os
import subprocess
import sys
import textwrap
from pathlib import Path
from unittest.mock import patch

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

@staticmethod
def test_import_without_audio_libs_does_not_write_to_standard_streams():
"""Missing optional audio libs should not emit import-time output."""
project_root = Path(__file__).parents[4]
util_genai_src = Path(__file__).parents[2] / "src"
instrumentation_src = (
project_root / "opentelemetry-instrumentation" / "src"
)
env = os.environ.copy()
pythonpath_parts = [
str(util_genai_src),
str(instrumentation_src),
env.get("PYTHONPATH", ""),
]
env["PYTHONPATH"] = os.pathsep.join(
part for part in pythonpath_parts if part
)

script = textwrap.dedent(
"""
import importlib.abc
import logging
import sys

class BlockAudioLibs(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path=None, target=None):
if fullname == "numpy" or fullname.startswith("numpy."):
raise ImportError(fullname)
if fullname == "soundfile" or fullname.startswith("soundfile."):
raise ImportError(fullname)
return None

sys.meta_path.insert(0, BlockAudioLibs())
logging.basicConfig(level=logging.WARNING, stream=sys.stdout)
import opentelemetry.util.genai._multimodal_upload.pre_uploader # noqa: F401
"""
)

completed = subprocess.run(
[sys.executable, "-c", script],
env=env,
check=True,
capture_output=True,
text=True,
)

assert completed.stdout == ""
assert completed.stderr == ""

# ========== Edge Case Tests ==========

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

@staticmethod
def test_pcm16_conversion_missing_audio_libs_logs_single_warning(
caplog,
):
"""Missing optional audio libs should only log the actual conversion skip."""
with (
patch(
"opentelemetry.util.genai._multimodal_upload.pre_uploader._audio_libs_available",
False,
),
patch(
"opentelemetry.util.genai._multimodal_upload.pre_uploader.np",
None,
),
patch(
"opentelemetry.util.genai._multimodal_upload.pre_uploader.sf",
None,
),
):
pre_uploader = MultimodalPreUploader(base_path="/tmp/test_upload")
part = Blob(
content=b"\x00\x01" * 1000,
mime_type="audio/pcm16",
modality="audio",
)
input_messages = [InputMessage(role="user", parts=[part])]

with caplog.at_level(
"WARNING",
logger=(
"opentelemetry.util.genai._multimodal_upload.pre_uploader"
),
):
uploads = pre_uploader.pre_upload(
span_context=None,
start_time_utc_nano=1000000000000000000,
input_messages=input_messages,
output_messages=None,
)

assert len(uploads) == 1
assert uploads[0].content_type == "audio/pcm16"
warning_messages = [record.getMessage() for record in caplog.records]
assert warning_messages == [
"Failed to convert PCM16 to WAV, using original format"
]

@staticmethod
def test_pcm16_conversion_disabled_by_default():
"""Test PCM16 conversion stays disabled when env var is unset"""
Expand Down
Loading