Skip to content
Closed
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
21 changes: 10 additions & 11 deletions lib/crewai-tools/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ authors = [
]
requires-python = ">=3.10, <3.14"
dependencies = [
"lancedb~=0.5.4",
"pytube~=15.0.0",
"requests~=2.32.5",
"docker~=7.1.0",
"crewai==1.8.0",
"lancedb~=0.5.4",
"tiktoken~=0.8.0",
"beautifulsoup4~=4.13.4",
"python-docx~=1.2.0",
"youtube-transcript-api~=1.2.2",
"pymupdf~=1.26.6",
"lancedb>=0.5.4",
"pytube>=15.0.0",
"requests>=2.32.5",
"docker>=7.1.0",
"crewai>=1.8.0",
"tiktoken>=0.8.0",
"beautifulsoup4>=4.12.3",
"python-docx>=1.1.0",
"youtube-transcript-api>=0.6.3",
"pymupdf>=1.25.0",
]


Expand Down
88 changes: 44 additions & 44 deletions lib/crewai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,36 @@ authors = [
requires-python = ">=3.10, <3.14"
dependencies = [
# Core Dependencies
"pydantic~=2.11.9",
"openai~=1.83.0",
"pydantic>=2.11.9,<3.0.0",
"openai>=1.13.3",
"instructor>=1.3.3",
# Text Processing
"pdfplumber~=0.11.4",
"regex~=2024.9.11",
"pdfplumber>=0.11.4",
"regex>=2024.9.11",
# Telemetry and Monitoring
"opentelemetry-api~=1.34.0",
"opentelemetry-sdk~=1.34.0",
"opentelemetry-exporter-otlp-proto-http~=1.34.0",
"opentelemetry-api>=1.30.0",
"opentelemetry-sdk>=1.30.0",
"opentelemetry-exporter-otlp-proto-http>=1.30.0",
# Data Handling
"chromadb~=1.1.0",
"tokenizers~=0.20.3",
"openpyxl~=3.1.5",
"chromadb>=1.0.0",
"tokenizers>=0.20.3",
"openpyxl>=3.1.5",
# Authentication and Security
"python-dotenv~=1.1.1",
"pyjwt~=2.9.0",
"python-dotenv>=1.0.0",
"pyjwt>=2.9.0",
# Configuration and Utils
"click~=8.1.7",
"appdirs~=1.4.4",
"jsonref~=1.1.0",
"json-repair~=0.25.2",
"tomli-w~=1.1.0",
"tomli~=2.0.2",
"json5~=0.10.0",
"portalocker~=2.7.0",
"pydantic-settings~=2.10.1",
"mcp~=1.16.0",
"uv~=0.9.13",
"aiosqlite~=0.21.0",
"click>=8.1.7",
"appdirs>=1.4.4",
"jsonref>=1.1.0",
"json-repair>=0.25.2",
"tomli-w>=1.1.0",
"tomli>=2.0.2",
"json5>=0.10.0",
"portalocker>=2.7.0",
"pydantic-settings>=2.10.1,<3.0.0",
"mcp>=1.16.0",
"uv>=0.4.25",
"aiosqlite>=0.21.0",
]

[project.urls]
Expand All @@ -49,54 +49,54 @@ Repository = "https://github.com/crewAIInc/crewAI"

[project.optional-dependencies]
tools = [
"crewai-tools==1.8.0",
"crewai-tools>=1.8.0",
]
embeddings = [
"tiktoken~=0.8.0"
"tiktoken>=0.8.0"
]
pandas = [
"pandas~=2.2.3",
"pandas>=2.2.3",
]
openpyxl = [
"openpyxl~=3.1.5",
"openpyxl>=3.1.5",
]
mem0 = ["mem0ai~=0.1.94"]
mem0 = ["mem0ai>=0.1.94"]
docling = [
"docling~=2.63.0",
"docling>=2.12.0",
]
qdrant = [
"qdrant-client[fastembed]~=1.14.3",
"qdrant-client[fastembed]>=1.14.3",
]
aws = [
"boto3~=1.40.38",
"aiobotocore~=2.25.2",
"boto3>=1.40.38",
"aiobotocore>=2.25.2",
]
watson = [
"ibm-watsonx-ai~=1.3.39",
"ibm-watsonx-ai>=1.3.39",
]
voyageai = [
"voyageai~=0.3.5",
"voyageai>=0.3.5",
]
litellm = [
"litellm~=1.74.9",
"litellm>=1.74.9",
]
bedrock = [
"boto3~=1.40.45",
"boto3>=1.40.45",
]
google-genai = [
"google-genai~=1.49.0",
"google-genai>=1.2.0",
]
azure-ai-inference = [
"azure-ai-inference~=1.0.0b9",
"azure-ai-inference>=1.0.0b9",
]
anthropic = [
"anthropic~=0.71.0",
"anthropic>=0.69.0",
]
a2a = [
"a2a-sdk~=0.3.10",
"httpx-auth~=0.23.1",
"httpx-sse~=0.4.0",
"aiocache[redis,memcached]~=0.12.3",
"a2a-sdk>=0.3.10",
"httpx-auth>=0.23.1",
"httpx-sse>=0.4.0",
"aiocache[redis,memcached]>=0.12.3",
]


Expand Down
171 changes: 171 additions & 0 deletions lib/crewai/tests/test_dependency_compatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"""Test that crewai dependencies are compatible with common integrations.

This test module verifies that crewai's dependency constraints are flexible enough
to allow installation alongside common third-party packages like opik for monitoring.

Related issue: https://github.com/crewAIInc/crewAI/issues/4201
"""

import re
from pathlib import Path

import tomli


def get_pyproject_path() -> Path:
"""Get the path to the crewai pyproject.toml file."""
return Path(__file__).parent.parent / "pyproject.toml"


def parse_pyproject() -> dict:
"""Parse the pyproject.toml file."""
pyproject_path = get_pyproject_path()
with open(pyproject_path, "rb") as f:
return tomli.load(f)


def test_openai_dependency_is_flexible():
"""Test that openai dependency uses >= instead of ~= to allow version flexibility.

The ~= operator (compatible release) is too restrictive and can cause dependency
conflicts with packages like opik that also depend on openai.

For example, openai~=1.83.0 means >=1.83.0,<1.84.0 which is very restrictive.
Using openai>=1.13.3 allows any version >= 1.13.3 which is more flexible.
"""
pyproject = parse_pyproject()
dependencies = pyproject["project"]["dependencies"]

openai_dep = None
for dep in dependencies:
if dep.startswith("openai"):
openai_dep = dep
break

assert openai_dep is not None, "openai dependency not found in pyproject.toml"

# Check that it uses >= instead of ~=
assert "~=" not in openai_dep, (
f"openai dependency should use >= instead of ~= for flexibility. "
f"Found: {openai_dep}"
)
assert ">=" in openai_dep, (
f"openai dependency should use >= for minimum version. Found: {openai_dep}"
)


def test_pydantic_dependency_allows_minor_updates():
"""Test that pydantic dependency allows minor version updates within v2.

Using pydantic>=2.x.x,<3.0.0 allows minor updates while staying within v2.
"""
pyproject = parse_pyproject()
dependencies = pyproject["project"]["dependencies"]

pydantic_dep = None
for dep in dependencies:
if dep.startswith("pydantic") and not dep.startswith("pydantic-settings"):
pydantic_dep = dep
break

assert pydantic_dep is not None, "pydantic dependency not found in pyproject.toml"

# Check that it uses >= and <3.0.0 instead of ~=
assert "~=" not in pydantic_dep, (
f"pydantic dependency should use >= instead of ~= for flexibility. "
f"Found: {pydantic_dep}"
)
assert ">=" in pydantic_dep, (
f"pydantic dependency should use >= for minimum version. Found: {pydantic_dep}"
)
assert "<3.0.0" in pydantic_dep, (
f"pydantic dependency should have <3.0.0 upper bound. Found: {pydantic_dep}"
)


def test_pydantic_settings_dependency_allows_minor_updates():
"""Test that pydantic-settings dependency allows minor version updates within v2."""
pyproject = parse_pyproject()
dependencies = pyproject["project"]["dependencies"]

pydantic_settings_dep = None
for dep in dependencies:
if dep.startswith("pydantic-settings"):
pydantic_settings_dep = dep
break

assert (
pydantic_settings_dep is not None
), "pydantic-settings dependency not found in pyproject.toml"

# Check that it uses >= and <3.0.0 instead of ~=
assert "~=" not in pydantic_settings_dep, (
f"pydantic-settings dependency should use >= instead of ~= for flexibility. "
f"Found: {pydantic_settings_dep}"
)
assert ">=" in pydantic_settings_dep, (
f"pydantic-settings dependency should use >= for minimum version. "
f"Found: {pydantic_settings_dep}"
)


def test_core_dependencies_use_flexible_constraints():
"""Test that core dependencies use >= instead of ~= for flexibility.

The ~= operator is too restrictive for most dependencies and can cause
conflicts with third-party packages.
"""
pyproject = parse_pyproject()
dependencies = pyproject["project"]["dependencies"]

# These are core dependencies that should use flexible constraints
core_deps = [
"openai",
"pydantic",
"opentelemetry-api",
"opentelemetry-sdk",
"click",
]

for core_dep in core_deps:
matching_dep = None
for dep in dependencies:
if dep.startswith(core_dep):
matching_dep = dep
break

if matching_dep:
assert "~=" not in matching_dep, (
f"{core_dep} dependency should use >= instead of ~= for flexibility. "
f"Found: {matching_dep}"
)


def test_no_overly_restrictive_pinning():
"""Test that dependencies don't use overly restrictive pinning.

Dependencies should not use == (exact version) or ~= (compatible release)
unless there's a specific reason documented.
"""
pyproject = parse_pyproject()
dependencies = pyproject["project"]["dependencies"]

for dep in dependencies:
# Skip comments
if dep.strip().startswith("#"):
continue

# Check for exact version pinning (==)
# Allow == only if there's a known reason
if "==" in dep:
# Currently no dependencies should use ==
assert False, (
f"Dependency uses exact version pinning (==) which is too restrictive: {dep}"
)

# Check for compatible release (~=)
if "~=" in dep:
assert False, (
f"Dependency uses compatible release (~=) which can be too restrictive: {dep}. "
f"Consider using >= instead."
)
Loading
Loading