mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-03-19 10:18:15 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b262f5a6d | ||
|
|
6a6adaf2da | ||
|
|
116182f708 | ||
|
|
9eed13b8a2 |
1356
docs/docs.json
1356
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,22 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Mar 18, 2026">
|
||||
## v1.11.0
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.11.0)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.11.0rc2
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Mar 17, 2026">
|
||||
## v1.11.0rc2
|
||||
|
||||
|
||||
@@ -4,6 +4,22 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 3월 18일">
|
||||
## v1.11.0
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.11.0)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 문서
|
||||
- v1.11.0rc2에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 3월 17일">
|
||||
## v1.11.0rc2
|
||||
|
||||
|
||||
@@ -4,6 +4,22 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="18 mar 2026">
|
||||
## v1.11.0
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.11.0)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.11.0rc2
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="17 mar 2026">
|
||||
## v1.11.0rc2
|
||||
|
||||
|
||||
@@ -152,4 +152,4 @@ __all__ = [
|
||||
"wrap_file_source",
|
||||
]
|
||||
|
||||
__version__ = "1.11.0rc2"
|
||||
__version__ = "1.11.0"
|
||||
|
||||
@@ -11,7 +11,7 @@ dependencies = [
|
||||
"pytube~=15.0.0",
|
||||
"requests~=2.32.5",
|
||||
"docker~=7.1.0",
|
||||
"crewai==1.11.0rc2",
|
||||
"crewai==1.11.0",
|
||||
"tiktoken~=0.8.0",
|
||||
"beautifulsoup4~=4.13.4",
|
||||
"python-docx~=1.2.0",
|
||||
|
||||
@@ -309,4 +309,4 @@ __all__ = [
|
||||
"ZapierActionTools",
|
||||
]
|
||||
|
||||
__version__ = "1.11.0rc2"
|
||||
__version__ = "1.11.0"
|
||||
|
||||
@@ -53,7 +53,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = [
|
||||
"crewai-tools==1.11.0rc2",
|
||||
"crewai-tools==1.11.0",
|
||||
]
|
||||
embeddings = [
|
||||
"tiktoken~=0.8.0"
|
||||
|
||||
@@ -42,7 +42,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
|
||||
|
||||
_suppress_pydantic_deprecation_warnings()
|
||||
|
||||
__version__ = "1.11.0rc2"
|
||||
__version__ = "1.11.0"
|
||||
_telemetry_submitted = False
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.11.0rc2"
|
||||
"crewai[tools]==1.11.0"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.11.0rc2"
|
||||
"crewai[tools]==1.11.0"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.11.0rc2"
|
||||
"crewai[tools]==1.11.0"
|
||||
]
|
||||
|
||||
[tool.crewai]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Centralised lock factory.
|
||||
|
||||
If ``REDIS_URL`` is set, locks are distributed via ``portalocker.RedisLock``. Otherwise, falls
|
||||
back to the standard ``portalocker.Lock``.
|
||||
If ``REDIS_URL`` is set and the ``redis`` package is installed, locks are distributed via
|
||||
``portalocker.RedisLock``. Otherwise, falls back to the standard ``portalocker.Lock``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -30,6 +30,18 @@ _REDIS_URL: str | None = os.environ.get("REDIS_URL")
|
||||
_DEFAULT_TIMEOUT: Final[int] = 120
|
||||
|
||||
|
||||
def _redis_available() -> bool:
|
||||
"""Return True if redis is installed and REDIS_URL is set."""
|
||||
if not _REDIS_URL:
|
||||
return False
|
||||
try:
|
||||
import redis # noqa: F401
|
||||
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _redis_connection() -> redis.Redis:
|
||||
"""Return a cached Redis connection, creating one on first call."""
|
||||
@@ -51,7 +63,7 @@ def lock(name: str, *, timeout: float = _DEFAULT_TIMEOUT) -> Iterator[None]:
|
||||
"""
|
||||
channel = f"crewai:{md5(name.encode(), usedforsecurity=False).hexdigest()}"
|
||||
|
||||
if _REDIS_URL:
|
||||
if _redis_available():
|
||||
with portalocker.RedisLock(
|
||||
channel=channel,
|
||||
connection=_redis_connection(),
|
||||
|
||||
70
lib/crewai/tests/utilities/test_lock_store.py
Normal file
70
lib/crewai/tests/utilities/test_lock_store.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Tests for lock_store.
|
||||
|
||||
We verify our own logic: the _redis_available guard and which portalocker
|
||||
backend is selected. We trust portalocker to handle actual locking mechanics.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import crewai.utilities.lock_store as lock_store
|
||||
from crewai.utilities.lock_store import lock
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def no_redis_url(monkeypatch):
|
||||
monkeypatch.setattr(lock_store, "_REDIS_URL", None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _redis_available
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_redis_not_available_without_url():
|
||||
assert lock_store._redis_available() is False
|
||||
|
||||
|
||||
def test_redis_not_available_when_package_missing(monkeypatch):
|
||||
monkeypatch.setattr(lock_store, "_REDIS_URL", "redis://localhost:6379")
|
||||
monkeypatch.setitem(sys.modules, "redis", None) # None → ImportError on import
|
||||
assert lock_store._redis_available() is False
|
||||
|
||||
|
||||
def test_redis_available_with_url_and_package(monkeypatch):
|
||||
monkeypatch.setattr(lock_store, "_REDIS_URL", "redis://localhost:6379")
|
||||
monkeypatch.setitem(sys.modules, "redis", mock.MagicMock())
|
||||
assert lock_store._redis_available() is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# lock strategy selection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_uses_file_lock_when_redis_unavailable():
|
||||
with mock.patch("portalocker.Lock") as mock_lock:
|
||||
with lock("file_test"):
|
||||
pass
|
||||
|
||||
mock_lock.assert_called_once()
|
||||
assert "crewai:" in mock_lock.call_args.args[0]
|
||||
|
||||
|
||||
def test_uses_redis_lock_when_redis_available(monkeypatch):
|
||||
fake_conn = mock.MagicMock()
|
||||
monkeypatch.setattr(lock_store, "_redis_available", mock.Mock(return_value=True))
|
||||
monkeypatch.setattr(lock_store, "_redis_connection", mock.Mock(return_value=fake_conn))
|
||||
|
||||
with mock.patch("portalocker.RedisLock") as mock_redis_lock:
|
||||
with lock("redis_test"):
|
||||
pass
|
||||
|
||||
mock_redis_lock.assert_called_once()
|
||||
kwargs = mock_redis_lock.call_args.kwargs
|
||||
assert kwargs["channel"].startswith("crewai:")
|
||||
assert kwargs["connection"] is fake_conn
|
||||
@@ -1,3 +1,3 @@
|
||||
"""CrewAI development tools."""
|
||||
|
||||
__version__ = "1.11.0rc2"
|
||||
__version__ = "1.11.0"
|
||||
|
||||
6
uv.lock
generated
6
uv.lock
generated
@@ -5556,11 +5556,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user