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
113 changes: 108 additions & 5 deletions src/blaxel/core/common/sentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,40 @@
# Optional dependencies that may not be installed - import errors for these are expected
_OPTIONAL_DEPENDENCIES = ("opentelemetry",)

# Optional blaxel framework integration subpackages. Importing any of these
# requires installing the matching extra (e.g. ``pip install blaxel[openai]``).
# When the extra -- or one of its transitive dependencies -- is missing, or when
# the integration files are absent from a stripped/partial install, importing the
# integration raises an ImportError. That is an expected environment issue, not an
# SDK bug, so it must not be reported to Sentry.
_OPTIONAL_INTEGRATION_PACKAGES = (
"blaxel.langgraph",
"blaxel.llamaindex",
"blaxel.openai",
"blaxel.crewai",
"blaxel.googleadk",
"blaxel.livekit",
"blaxel.pydantic",
"blaxel.telemetry",
)

_OPTIONAL_INTEGRATION_ENTRYPOINT_MODULES = {
"blaxel.langgraph": ("model", "tools"),
"blaxel.llamaindex": ("model", "tools"),
"blaxel.openai": ("model", "tools"),
"blaxel.crewai": ("model", "tools"),
"blaxel.googleadk": ("model", "tools"),
"blaxel.livekit": ("model", "tools"),
"blaxel.pydantic": ("model", "tools"),
"blaxel.telemetry": ("exporters", "instrumentation", "log", "manager", "span"),
}

# Filesystem fragments used to detect an import error raised while loading one of
# the optional integration packages above (covers both POSIX and Windows paths).
_OPTIONAL_INTEGRATION_PATHS = tuple(
f"blaxel/{pkg.rsplit('.', 1)[-1]}/" for pkg in _OPTIONAL_INTEGRATION_PACKAGES
) + tuple(f"blaxel\\{pkg.rsplit('.', 1)[-1]}\\" for pkg in _OPTIONAL_INTEGRATION_PACKAGES)

# SDK path patterns to identify errors originating from our SDK
_SDK_PATTERNS = [
"blaxel/",
Expand Down Expand Up @@ -190,11 +224,80 @@ def _get_exception_key(exc_type, exc_value, frame) -> str:
return f"{exc_name}:{exc_msg}:{origin}"


def _is_optional_dependency_error(exc_type, exc_value) -> bool:
"""Check if the exception is an import error for an optional dependency."""
if exc_type and issubclass(exc_type, ImportError):
msg = str(exc_value).lower()
return any(dep in msg for dep in _OPTIONAL_DEPENDENCIES)
def _has_optional_integration_frame(exc_value) -> bool:
"""Check whether an exception traceback passed through an integration module."""
tb = getattr(exc_value, "__traceback__", None)
while tb is not None:
filename = tb.tb_frame.f_code.co_filename
if any(path in filename for path in _OPTIONAL_INTEGRATION_PATHS):
return True
tb = tb.tb_next
return False


def _is_optional_integration_entrypoint_missing(missing: str) -> bool:
"""Check whether the missing module is a public optional integration entrypoint."""
if missing in _OPTIONAL_INTEGRATION_PACKAGES:
return True

for package, entrypoints in _OPTIONAL_INTEGRATION_ENTRYPOINT_MODULES.items():
if any(missing == f"{package}.{entrypoint}" for entrypoint in entrypoints):
return True
return False


def _is_optional_dependency_error(exc_type, exc_value, seen: set[int] | None = None) -> bool:
"""Check if the exception is an import error that is expected when an optional
integration extra is not installed.

These are environment issues (the user imported, e.g., ``blaxel.openai``
without ``pip install blaxel[openai]``, or runs a stripped/partial install
that is missing the integration's modules) rather than SDK defects, so they
should not be reported to Sentry.
"""
if not (exc_type and issubclass(exc_type, ImportError)):
return False

if seen is None:
seen = set()
exc_id = id(exc_value)
if exc_id in seen:
return False
seen.add(exc_id)

# Name of the module that could not be imported, when available
# (e.g. "blaxel.openai.model", "agents", "opentelemetry.exporter.otlp").
missing = getattr(exc_value, "name", None) or ""

# 1) A public optional integration entrypoint itself is unavailable -- e.g.
# a stripped/partial install missing ``blaxel/openai/model.py``,
# surfacing as ModuleNotFoundError("No module named 'blaxel.openai.model'").
# Do not suppress deeper ``blaxel.<integration>.*`` misses: those may be
# real SDK packaging or internal import bugs and should still reach Sentry.
if _is_optional_integration_entrypoint_missing(missing):
return True

# 2) A known optional third-party dependency could not be imported.
msg = str(exc_value).lower()
if any(dep in missing for dep in _OPTIONAL_DEPENDENCIES) or any(
dep in msg for dep in _OPTIONAL_DEPENDENCIES
):
return True

# 3) Optional integration import guards wrap the original import failure in
# a friendly ImportError with no module name. Suppress that wrapper when
# its explicit cause is already known optional-import noise.
cause = getattr(exc_value, "__cause__", None)
if isinstance(cause, ImportError) and _is_optional_dependency_error(type(cause), cause, seen):
return True

# 4) A non-blaxel (third-party) import failed while loading an optional
# integration package -- i.e. the matching extra is not installed. Only
# treat non-blaxel modules this way so that genuine SDK import bugs (which
# fail on a "blaxel.*" module) are still captured.
if missing and not missing.startswith("blaxel") and _has_optional_integration_frame(exc_value):
return True

return False


Expand Down
116 changes: 116 additions & 0 deletions tests/core/test_sentry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Tests for the lightweight Sentry error filter.

The SDK installs a trace function (``sys.settrace``) that forwards exceptions
originating from ``site-packages/blaxel`` to Sentry. Import errors that happen
because an optional integration extra is not installed (or because a
stripped/partial install is missing the integration's modules) are environment
issues, not SDK defects, and must be filtered out before reaching Sentry.
"""

from blaxel.core.common.sentry import (
_OPTIONAL_INTEGRATION_ENTRYPOINT_MODULES,
_is_optional_dependency_error,
)


def _raise_in_file(filename: str, code: str) -> Exception:
"""Execute ``code`` as if it lived in ``filename`` and return the exception."""
try:
exec(compile(code, filename, "exec"), {})
except Exception as e: # noqa: BLE001 - we want the raised exception object
return e
raise AssertionError("code did not raise")


class TestIsOptionalDependencyError:
"""Cover the import-error classification used to suppress Sentry noise."""

def test_missing_integration_submodule_is_optional(self):
"""The exact production symptom: a stripped install missing model.py.

``from .model import *`` in ``blaxel/openai/__init__.py`` raises
``ModuleNotFoundError: No module named 'blaxel.openai.model'``.
"""
exc = ModuleNotFoundError(
"No module named 'blaxel.openai.model'", name="blaxel.openai.model"
)
assert _is_optional_dependency_error(type(exc), exc) is True

def test_missing_livekit_submodule_is_optional(self):
exc = ModuleNotFoundError(
"No module named 'blaxel.livekit.model'", name="blaxel.livekit.model"
)
assert _is_optional_dependency_error(type(exc), exc) is True

def test_each_optional_integration_package_is_covered(self):
for pkg in _OPTIONAL_INTEGRATION_ENTRYPOINT_MODULES:
exc = ModuleNotFoundError(f"No module named '{pkg}'", name=pkg)
assert _is_optional_dependency_error(type(exc), exc) is True, pkg

def test_each_optional_integration_entrypoint_module_is_covered(self):
for pkg, entrypoints in _OPTIONAL_INTEGRATION_ENTRYPOINT_MODULES.items():
for entrypoint in entrypoints:
missing = f"{pkg}.{entrypoint}"
exc = ModuleNotFoundError(f"No module named '{missing}'", name=missing)
assert _is_optional_dependency_error(type(exc), exc) is True, missing

def test_opentelemetry_dependency_is_optional(self):
"""Existing behavior: opentelemetry import errors are still suppressed."""
exc = ModuleNotFoundError("No module named 'opentelemetry'", name="opentelemetry")
assert _is_optional_dependency_error(type(exc), exc) is True

def test_missing_third_party_dep_while_loading_integration_is_optional(self):
"""A missing extra dep (e.g. ``agents`` for blaxel[openai]) is expected."""
exc = _raise_in_file(
"/usr/lib/python3.12/site-packages/blaxel/openai/model.py",
"raise ModuleNotFoundError(\"No module named 'agents'\", name='agents')",
)
assert _is_optional_dependency_error(type(exc), exc) is True

def test_missing_nested_blaxel_module_inside_integration_is_not_optional(self):
"""Internal integration packaging/import bugs must still reach Sentry."""
exc = ModuleNotFoundError(
"No module named 'blaxel.pydantic.custom.gemni'",
name="blaxel.pydantic.custom.gemni",
)
assert _is_optional_dependency_error(ModuleNotFoundError, exc) is False

def test_wrapped_integration_import_guard_error_is_optional(self):
"""The friendly optional-extra guard from blaxel.openai stays quiet."""
cause = ModuleNotFoundError(
"No module named 'blaxel.openai.model'",
name="blaxel.openai.model",
)
exc = ImportError(
"The openai extra dependencies are required to use the OpenAI Agents integration. "
"Install them with: pip install blaxel[openai]"
)
exc.__cause__ = cause

assert _is_optional_dependency_error(type(exc), exc) is True

def test_missing_third_party_dep_outside_integration_is_not_optional(self):
"""A third-party import failure outside any optional integration (e.g. a
genuine missing core dependency) must still be reported."""
exc = _raise_in_file(
"/usr/lib/python3.12/site-packages/blaxel/core/common/settings.py",
"raise ModuleNotFoundError(\"No module named 'httpx'\", name='httpx')",
)
assert _is_optional_dependency_error(type(exc), exc) is False

def test_core_module_import_error_is_not_optional(self):
"""A genuine SDK bug failing on a ``blaxel.*`` core module is captured."""
exc = ModuleNotFoundError(
"No module named 'blaxel.core.missing'", name="blaxel.core.missing"
)
assert _is_optional_dependency_error(type(exc), exc) is False

def test_non_import_error_is_not_optional(self):
exc = ValueError("not an import error")
assert _is_optional_dependency_error(type(exc), exc) is False

def test_circular_import_error_cause_is_not_optional(self):
exc = ImportError("wrapped import failed")
exc.__cause__ = exc

assert _is_optional_dependency_error(type(exc), exc) is False
Loading