fix: relax opentelemetry version constraints to avoid conflicts with openlit

The opentelemetry-api, opentelemetry-sdk, and opentelemetry-exporter-otlp-proto-http
packages were pinned to ~=1.34.0 (>=1.34.0, <1.35.0) in both crewai and crewai-core.
This caused dependency conflicts with packages like openlit (>=1.42.0) which require
opentelemetry-sdk >=1.38.0.

Relaxed the constraints to >=1.34.0,<2.0.0 in both pyproject.toml files.
The code only uses stable OpenTelemetry APIs so this broader range is safe.

Added regression tests that verify:
- OTel constraints allow versions >=1.38.0 (openlit compatibility)
- OTel constraints maintain lower bound >=1.34.0
- OTel constraints cap at <2.0.0
- All OTel imports used by crewai remain functional

Fixes #5845

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2026-05-18 08:23:39 +00:00
parent a95d26763f
commit 31033c9c00
3 changed files with 153 additions and 6 deletions

View File

@@ -16,9 +16,9 @@ dependencies = [
"pyjwt>=2.9.0,<3",
"pydantic>=2.11.9,<2.13",
"rich>=13.7.1",
"opentelemetry-api~=1.34.0",
"opentelemetry-sdk~=1.34.0",
"opentelemetry-exporter-otlp-proto-http~=1.34.0",
"opentelemetry-api>=1.34.0,<2.0.0",
"opentelemetry-sdk>=1.34.0,<2.0.0",
"opentelemetry-exporter-otlp-proto-http>=1.34.0,<2.0.0",
"tomli~=2.0.2",
]

View File

@@ -18,9 +18,9 @@ dependencies = [
"pdfplumber~=0.11.4",
"regex~=2026.1.15",
# Telemetry and Monitoring
"opentelemetry-api~=1.34.0",
"opentelemetry-sdk~=1.34.0",
"opentelemetry-exporter-otlp-proto-http~=1.34.0",
"opentelemetry-api>=1.34.0,<2.0.0",
"opentelemetry-sdk>=1.34.0,<2.0.0",
"opentelemetry-exporter-otlp-proto-http>=1.34.0,<2.0.0",
# Data Handling
"chromadb~=1.1.0",
"tokenizers>=0.21,<1",

View File

@@ -0,0 +1,147 @@
"""Tests for OpenTelemetry dependency compatibility.
Ensures the opentelemetry version constraints in pyproject.toml are relaxed
enough to avoid conflicts with third-party observability packages (e.g. openlit)
that require newer OpenTelemetry versions.
Regression test for https://github.com/crewAIInc/crewAI/issues/5845
"""
from __future__ import annotations
import importlib
from pathlib import Path
from typing import Any
import pytest
try:
import tomllib
except ModuleNotFoundError: # Python < 3.11
import tomli as tomllib # type: ignore[no-redef]
from packaging.requirements import Requirement
from packaging.version import Version
OTEL_PACKAGES = (
"opentelemetry-api",
"opentelemetry-sdk",
"opentelemetry-exporter-otlp-proto-http",
)
REPO_ROOT = Path(__file__).resolve().parents[4]
CREWAI_PYPROJECT = REPO_ROOT / "lib" / "crewai" / "pyproject.toml"
CREWAI_CORE_PYPROJECT = REPO_ROOT / "lib" / "crewai-core" / "pyproject.toml"
def _parse_otel_deps(pyproject_path: Path) -> dict[str, Requirement]:
"""Parse OpenTelemetry dependencies from a pyproject.toml file."""
with open(pyproject_path, "rb") as f:
data: dict[str, Any] = tomllib.load(f)
deps = data.get("project", {}).get("dependencies", [])
result: dict[str, Requirement] = {}
for dep_str in deps:
req = Requirement(dep_str)
if req.name in OTEL_PACKAGES:
result[req.name] = req
return result
class TestOpenTelemetryVersionConstraints:
"""Verify that OTel dependency constraints are not overly restrictive."""
@pytest.mark.parametrize("pyproject_path", [CREWAI_PYPROJECT, CREWAI_CORE_PYPROJECT])
def test_otel_packages_present(self, pyproject_path: Path) -> None:
"""All three OpenTelemetry packages must be declared as dependencies."""
deps = _parse_otel_deps(pyproject_path)
for pkg in OTEL_PACKAGES:
assert pkg in deps, (
f"{pkg} missing from {pyproject_path.relative_to(REPO_ROOT)}"
)
@pytest.mark.parametrize("pyproject_path", [CREWAI_PYPROJECT, CREWAI_CORE_PYPROJECT])
def test_otel_upper_bound_allows_newer_versions(self, pyproject_path: Path) -> None:
"""OTel constraints must allow versions >= 1.38.0 (required by openlit).
Regression test for https://github.com/crewAIInc/crewAI/issues/5845
"""
deps = _parse_otel_deps(pyproject_path)
for pkg in OTEL_PACKAGES:
req = deps[pkg]
# Version 1.38.0 must be within the allowed range
assert req.specifier.contains(Version("1.38.0")), (
f"{pkg} in {pyproject_path.relative_to(REPO_ROOT)} does not allow "
f"version 1.38.0 (specifier: {req.specifier}). This causes dependency "
f"conflicts with openlit and similar observability packages."
)
@pytest.mark.parametrize("pyproject_path", [CREWAI_PYPROJECT, CREWAI_CORE_PYPROJECT])
def test_otel_lower_bound_is_at_least_1_34(self, pyproject_path: Path) -> None:
"""OTel constraints must have a lower bound >= 1.34.0 for API stability."""
deps = _parse_otel_deps(pyproject_path)
for pkg in OTEL_PACKAGES:
req = deps[pkg]
assert req.specifier.contains(Version("1.34.0")), (
f"{pkg} lower bound excludes 1.34.0 (specifier: {req.specifier})"
)
@pytest.mark.parametrize("pyproject_path", [CREWAI_PYPROJECT, CREWAI_CORE_PYPROJECT])
def test_otel_upper_bound_below_2(self, pyproject_path: Path) -> None:
"""OTel constraints must cap at < 2.0.0 to guard against breaking changes."""
deps = _parse_otel_deps(pyproject_path)
for pkg in OTEL_PACKAGES:
req = deps[pkg]
assert not req.specifier.contains(Version("2.0.0")), (
f"{pkg} allows version 2.0.0 (specifier: {req.specifier}). "
f"The upper bound should be < 2.0.0."
)
class TestOpenTelemetryImports:
"""Verify that the OpenTelemetry APIs used by CrewAI are importable."""
def test_trace_module_importable(self) -> None:
mod = importlib.import_module("opentelemetry.trace")
assert hasattr(mod, "get_tracer")
assert hasattr(mod, "set_tracer_provider")
def test_sdk_tracer_provider_importable(self) -> None:
mod = importlib.import_module("opentelemetry.sdk.trace")
assert hasattr(mod, "TracerProvider")
def test_otlp_exporter_importable(self) -> None:
mod = importlib.import_module(
"opentelemetry.exporter.otlp.proto.http.trace_exporter"
)
assert hasattr(mod, "OTLPSpanExporter")
def test_batch_span_processor_importable(self) -> None:
mod = importlib.import_module("opentelemetry.sdk.trace.export")
assert hasattr(mod, "BatchSpanProcessor")
assert hasattr(mod, "SpanExportResult")
def test_resource_importable(self) -> None:
mod = importlib.import_module("opentelemetry.sdk.resources")
assert hasattr(mod, "SERVICE_NAME")
assert hasattr(mod, "Resource")
def test_span_status_importable(self) -> None:
mod = importlib.import_module("opentelemetry.trace")
assert hasattr(mod, "Span")
assert hasattr(mod, "Status")
assert hasattr(mod, "StatusCode")
class TestCrewaiTelemetryModuleIntegrity:
"""Verify that crewai's telemetry modules import without errors."""
def test_crewai_core_telemetry_importable(self) -> None:
mod = importlib.import_module("crewai_core.telemetry")
assert hasattr(mod, "Telemetry")
def test_crewai_telemetry_importable(self) -> None:
mod = importlib.import_module("crewai.telemetry.telemetry")
assert hasattr(mod, "Telemetry")
assert hasattr(mod, "SafeOTLPSpanExporter")