diff --git a/.env.test b/.env.test new file mode 100644 index 000000000..4ef1b503c --- /dev/null +++ b/.env.test @@ -0,0 +1,161 @@ +# ============================================================================= +# Test Environment Variables +# ============================================================================= +# This file contains all environment variables needed to run tests locally +# in a way that mimics the GitHub Actions CI environment. + +# ============================================================================= + +# ----------------------------------------------------------------------------- +# LLM Provider API Keys +# ----------------------------------------------------------------------------- +OPENAI_API_KEY=fake-api-key +ANTHROPIC_API_KEY=fake-anthropic-key +GEMINI_API_KEY=fake-gemini-key +AZURE_API_KEY=fake-azure-key +OPENROUTER_API_KEY=fake-openrouter-key + +# ----------------------------------------------------------------------------- +# AWS Credentials +# ----------------------------------------------------------------------------- +AWS_ACCESS_KEY_ID=fake-aws-access-key +AWS_SECRET_ACCESS_KEY=fake-aws-secret-key +AWS_DEFAULT_REGION=us-east-1 +AWS_REGION_NAME=us-east-1 + +# ----------------------------------------------------------------------------- +# Azure OpenAI Configuration +# ----------------------------------------------------------------------------- +AZURE_ENDPOINT=https://fake-azure-endpoint.openai.azure.com +AZURE_OPENAI_ENDPOINT=https://fake-azure-endpoint.openai.azure.com +AZURE_OPENAI_API_KEY=fake-azure-openai-key +AZURE_API_VERSION=2024-02-15-preview +OPENAI_API_VERSION=2024-02-15-preview + +# ----------------------------------------------------------------------------- +# Google Cloud Configuration +# ----------------------------------------------------------------------------- +#GOOGLE_CLOUD_PROJECT=fake-gcp-project +#GOOGLE_CLOUD_LOCATION=us-central1 + +# ----------------------------------------------------------------------------- +# OpenAI Configuration +# ----------------------------------------------------------------------------- +OPENAI_BASE_URL=https://api.openai.com/v1 +OPENAI_API_BASE=https://api.openai.com/v1 + +# ----------------------------------------------------------------------------- +# Search & Scraping Tool API Keys +# ----------------------------------------------------------------------------- +SERPER_API_KEY=fake-serper-key +EXA_API_KEY=fake-exa-key +BRAVE_API_KEY=fake-brave-key +FIRECRAWL_API_KEY=fake-firecrawl-key +TAVILY_API_KEY=fake-tavily-key +SERPAPI_API_KEY=fake-serpapi-key +SERPLY_API_KEY=fake-serply-key +LINKUP_API_KEY=fake-linkup-key +PARALLEL_API_KEY=fake-parallel-key + +# ----------------------------------------------------------------------------- +# Exa Configuration +# ----------------------------------------------------------------------------- +EXA_BASE_URL=https://api.exa.ai + +# ----------------------------------------------------------------------------- +# Web Scraping & Automation +# ----------------------------------------------------------------------------- +BRIGHT_DATA_API_KEY=fake-brightdata-key +BRIGHT_DATA_ZONE=fake-zone +BRIGHTDATA_API_URL=https://api.brightdata.com +BRIGHTDATA_DEFAULT_TIMEOUT=600 +BRIGHTDATA_DEFAULT_POLLING_INTERVAL=1 + +OXYLABS_USERNAME=fake-oxylabs-user +OXYLABS_PASSWORD=fake-oxylabs-pass + +SCRAPFLY_API_KEY=fake-scrapfly-key +SCRAPEGRAPH_API_KEY=fake-scrapegraph-key + +BROWSERBASE_API_KEY=fake-browserbase-key +BROWSERBASE_PROJECT_ID=fake-browserbase-project + +HYPERBROWSER_API_KEY=fake-hyperbrowser-key +MULTION_API_KEY=fake-multion-key +APIFY_API_TOKEN=fake-apify-token + +# ----------------------------------------------------------------------------- +# Database & Vector Store Credentials +# ----------------------------------------------------------------------------- +SINGLESTOREDB_URL=mysql://fake:fake@localhost:3306/fake +SINGLESTOREDB_HOST=localhost +SINGLESTOREDB_PORT=3306 +SINGLESTOREDB_USER=fake-user +SINGLESTOREDB_PASSWORD=fake-password +SINGLESTOREDB_DATABASE=fake-database +SINGLESTOREDB_CONNECT_TIMEOUT=30 + +SNOWFLAKE_USER=fake-snowflake-user +SNOWFLAKE_PASSWORD=fake-snowflake-password +SNOWFLAKE_ACCOUNT=fake-snowflake-account +SNOWFLAKE_WAREHOUSE=fake-snowflake-warehouse +SNOWFLAKE_DATABASE=fake-snowflake-database +SNOWFLAKE_SCHEMA=fake-snowflake-schema + +WEAVIATE_URL=http://localhost:8080 +WEAVIATE_API_KEY=fake-weaviate-key + +EMBEDCHAIN_DB_URI=sqlite:///test.db + +# Databricks Credentials +DATABRICKS_HOST=https://fake-databricks.cloud.databricks.com +DATABRICKS_TOKEN=fake-databricks-token +DATABRICKS_CONFIG_PROFILE=fake-profile + +# MongoDB Credentials +MONGODB_URI=mongodb://fake:fake@localhost:27017/fake + +# ----------------------------------------------------------------------------- +# CrewAI Platform & Enterprise +# ----------------------------------------------------------------------------- +# setting CREWAI_PLATFORM_INTEGRATION_TOKEN causes these test to fail: +#=========================== short test summary info ============================ +#FAILED tests/test_context.py::TestPlatformIntegrationToken::test_platform_context_manager_basic_usage - AssertionError: assert 'fake-platform-token' is None +# + where 'fake-platform-token' = get_platform_integration_token() +#FAILED tests/test_context.py::TestPlatformIntegrationToken::test_context_var_isolation_between_tests - AssertionError: assert 'fake-platform-token' is None +# + where 'fake-platform-token' = get_platform_integration_token() +#FAILED tests/test_context.py::TestPlatformIntegrationToken::test_multiple_sequential_context_managers - AssertionError: assert 'fake-platform-token' is None +# + where 'fake-platform-token' = get_platform_integration_token() +#CREWAI_PLATFORM_INTEGRATION_TOKEN=fake-platform-token +CREWAI_PERSONAL_ACCESS_TOKEN=fake-personal-token +CREWAI_PLUS_URL=https://fake.crewai.com + +# ----------------------------------------------------------------------------- +# Other Service API Keys +# ----------------------------------------------------------------------------- +ZAPIER_API_KEY=fake-zapier-key +PATRONUS_API_KEY=fake-patronus-key +MINDS_API_KEY=fake-minds-key +HF_TOKEN=fake-hf-token + +# ----------------------------------------------------------------------------- +# Feature Flags/Testing Modes +# ----------------------------------------------------------------------------- +CREWAI_DISABLE_TELEMETRY=true +OTEL_SDK_DISABLED=true +CREWAI_TESTING=true +CREWAI_TRACING_ENABLED=false + +# ----------------------------------------------------------------------------- +# Testing/CI Configuration +# ----------------------------------------------------------------------------- +# VCR recording mode: "none" (default), "new_episodes", "all", "once" +PYTEST_VCR_RECORD_MODE=none + +# Set to "true" by GitHub when running in GitHub Actions +# GITHUB_ACTIONS=false + +# ----------------------------------------------------------------------------- +# Python Configuration +# ----------------------------------------------------------------------------- +PYTHONUNBUFFERED=1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0189d1364..6d8054ff4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,18 +5,6 @@ on: [pull_request] permissions: contents: read -env: - OPENAI_API_KEY: fake-api-key - PYTHONUNBUFFERED: 1 - BRAVE_API_KEY: fake-brave-key - SNOWFLAKE_USER: fake-snowflake-user - SNOWFLAKE_PASSWORD: fake-snowflake-password - SNOWFLAKE_ACCOUNT: fake-snowflake-account - SNOWFLAKE_WAREHOUSE: fake-snowflake-warehouse - SNOWFLAKE_DATABASE: fake-snowflake-database - SNOWFLAKE_SCHEMA: fake-snowflake-schema - EMBEDCHAIN_DB_URI: sqlite:///test.db - jobs: tests: name: tests (${{ matrix.python-version }}) @@ -84,26 +72,20 @@ jobs: # fi cd lib/crewai && uv run pytest \ - --block-network \ - --timeout=30 \ -vv \ --splits 8 \ --group ${{ matrix.group }} \ $DURATIONS_ARG \ --durations=10 \ - -n auto \ --maxfail=3 - name: Run tool tests (group ${{ matrix.group }} of 8) run: | cd lib/crewai-tools && uv run pytest \ - --block-network \ - --timeout=30 \ -vv \ --splits 8 \ --group ${{ matrix.group }} \ --durations=10 \ - -n auto \ --maxfail=3 diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..a64092570 --- /dev/null +++ b/conftest.py @@ -0,0 +1,166 @@ +"""Pytest configuration for crewAI workspace.""" + +from collections.abc import Generator +import os +from pathlib import Path +import tempfile +from typing import Any + +from dotenv import load_dotenv +import pytest +from vcr.request import Request # type: ignore[import-untyped] + + +env_test_path = Path(__file__).parent / ".env.test" +load_dotenv(env_test_path, override=True) +load_dotenv(override=True) + + +@pytest.fixture(autouse=True, scope="function") +def cleanup_event_handlers() -> Generator[None, Any, None]: + """Clean up event bus handlers after each test to prevent test pollution.""" + yield + + try: + from crewai.events.event_bus import crewai_event_bus + + with crewai_event_bus._rwlock.w_locked(): + crewai_event_bus._sync_handlers.clear() + crewai_event_bus._async_handlers.clear() + except Exception: # noqa: S110 + pass + + +@pytest.fixture(autouse=True, scope="function") +def setup_test_environment() -> Generator[None, Any, None]: + """Setup test environment for crewAI workspace.""" + with tempfile.TemporaryDirectory() as temp_dir: + storage_dir = Path(temp_dir) / "crewai_test_storage" + storage_dir.mkdir(parents=True, exist_ok=True) + + if not storage_dir.exists() or not storage_dir.is_dir(): + raise RuntimeError( + f"Failed to create test storage directory: {storage_dir}" + ) + + try: + test_file = storage_dir / ".permissions_test" + test_file.touch() + test_file.unlink() + except (OSError, IOError) as e: + raise RuntimeError( + f"Test storage directory {storage_dir} is not writable: {e}" + ) from e + + os.environ["CREWAI_STORAGE_DIR"] = str(storage_dir) + os.environ["CREWAI_TESTING"] = "true" + + try: + yield + finally: + os.environ.pop("CREWAI_TESTING", "true") + os.environ.pop("CREWAI_STORAGE_DIR", None) + os.environ.pop("CREWAI_DISABLE_TELEMETRY", "true") + os.environ.pop("OTEL_SDK_DISABLED", "true") + os.environ.pop("OPENAI_BASE_URL", "https://api.openai.com/v1") + os.environ.pop("OPENAI_API_BASE", "https://api.openai.com/v1") + + +HEADERS_TO_FILTER = { + "authorization": "AUTHORIZATION-XXX", + "content-security-policy": "CSP-FILTERED", + "cookie": "COOKIE-XXX", + "set-cookie": "SET-COOKIE-XXX", + "permissions-policy": "PERMISSIONS-POLICY-XXX", + "referrer-policy": "REFERRER-POLICY-XXX", + "strict-transport-security": "STS-XXX", + "x-content-type-options": "X-CONTENT-TYPE-XXX", + "x-frame-options": "X-FRAME-OPTIONS-XXX", + "x-permitted-cross-domain-policies": "X-PERMITTED-XXX", + "x-request-id": "X-REQUEST-ID-XXX", + "x-runtime": "X-RUNTIME-XXX", + "x-xss-protection": "X-XSS-PROTECTION-XXX", + "x-stainless-arch": "X-STAINLESS-ARCH-XXX", + "x-stainless-os": "X-STAINLESS-OS-XXX", + "x-stainless-read-timeout": "X-STAINLESS-READ-TIMEOUT-XXX", + "cf-ray": "CF-RAY-XXX", + "etag": "ETAG-XXX", + "Strict-Transport-Security": "STS-XXX", + "access-control-expose-headers": "ACCESS-CONTROL-XXX", + "openai-organization": "OPENAI-ORG-XXX", + "openai-project": "OPENAI-PROJECT-XXX", + "x-ratelimit-limit-requests": "X-RATELIMIT-LIMIT-REQUESTS-XXX", + "x-ratelimit-limit-tokens": "X-RATELIMIT-LIMIT-TOKENS-XXX", + "x-ratelimit-remaining-requests": "X-RATELIMIT-REMAINING-REQUESTS-XXX", + "x-ratelimit-remaining-tokens": "X-RATELIMIT-REMAINING-TOKENS-XXX", + "x-ratelimit-reset-requests": "X-RATELIMIT-RESET-REQUESTS-XXX", + "x-ratelimit-reset-tokens": "X-RATELIMIT-RESET-TOKENS-XXX", + "x-goog-api-key": "X-GOOG-API-KEY-XXX", +} + + +def _filter_request_headers(request: Request) -> Request: # type: ignore[no-any-unimported] + """Filter sensitive headers from request before recording.""" + for header_name, replacement in HEADERS_TO_FILTER.items(): + for variant in [header_name, header_name.upper(), header_name.title()]: + if variant in request.headers: + request.headers[variant] = [replacement] + return request + + +def _filter_response_headers(response: dict[str, Any]) -> dict[str, Any]: + """Filter sensitive headers from response before recording.""" + for header_name, replacement in HEADERS_TO_FILTER.items(): + for variant in [header_name, header_name.upper(), header_name.title()]: + if variant in response["headers"]: + response["headers"][variant] = [replacement] + return response + + +@pytest.fixture(scope="module") +def vcr_cassette_dir(request: Any) -> str: + """Generate cassette directory path based on test module location. + + Organizes cassettes to mirror test directory structure within each package: + lib/crewai/tests/llms/google/test_google.py -> lib/crewai/tests/cassettes/llms/google/ + lib/crewai-tools/tests/tools/test_search.py -> lib/crewai-tools/tests/cassettes/tools/ + """ + test_file = Path(request.fspath) + + for parent in test_file.parents: + if parent.name in ("crewai", "crewai-tools") and parent.parent.name == "lib": + package_root = parent + break + else: + package_root = test_file.parent + + tests_root = package_root / "tests" + test_dir = test_file.parent + + if test_dir != tests_root: + relative_path = test_dir.relative_to(tests_root) + cassette_dir = tests_root / "cassettes" / relative_path + else: + cassette_dir = tests_root / "cassettes" + + cassette_dir.mkdir(parents=True, exist_ok=True) + + return str(cassette_dir) + + +@pytest.fixture(scope="module") +def vcr_config(vcr_cassette_dir: str) -> dict[str, Any]: + """Configure VCR with organized cassette storage.""" + config = { + "cassette_library_dir": vcr_cassette_dir, + "record_mode": os.getenv("PYTEST_VCR_RECORD_MODE", "once"), + "filter_headers": [(k, v) for k, v in HEADERS_TO_FILTER.items()], + "before_record_request": _filter_request_headers, + "before_record_response": _filter_response_headers, + "filter_query_parameters": ["key"], + } + + if os.getenv("GITHUB_ACTIONS") == "true": + config["record_mode"] = "none" + + return config diff --git a/lib/crewai-tools/BUILDING_TOOLS.md b/lib/crewai-tools/BUILDING_TOOLS.md index 2994b918e..0b860660b 100644 --- a/lib/crewai-tools/BUILDING_TOOLS.md +++ b/lib/crewai-tools/BUILDING_TOOLS.md @@ -218,7 +218,7 @@ Update the root `README.md` only if the tool introduces a new category or notabl ## Discovery and specs -Our internal tooling discovers classes whose names end with `Tool`. Keep your class exported from the module path under `crewai_tools/tools/...` to be picked up by scripts like `generate_tool_specs.py`. +Our internal tooling discovers classes whose names end with `Tool`. Keep your class exported from the module path under `crewai_tools/tools/...` to be picked up by scripts like `crewai_tools.generate_tool_specs.py`. --- diff --git a/lib/crewai-tools/generate_tool_specs.py b/lib/crewai-tools/src/crewai_tools/generate_tool_specs.py similarity index 90% rename from lib/crewai-tools/generate_tool_specs.py rename to lib/crewai-tools/src/crewai_tools/generate_tool_specs.py index af97191c4..ebf10bdbf 100644 --- a/lib/crewai-tools/generate_tool_specs.py +++ b/lib/crewai-tools/src/crewai_tools/generate_tool_specs.py @@ -4,17 +4,20 @@ from collections.abc import Mapping import inspect import json from pathlib import Path -from typing import Any, cast +from typing import Any from crewai.tools.base_tool import BaseTool, EnvVar -from crewai_tools import tools from pydantic import BaseModel from pydantic.json_schema import GenerateJsonSchema from pydantic_core import PydanticOmit +from crewai_tools import tools + class SchemaGenerator(GenerateJsonSchema): - def handle_invalid_for_json_schema(self, schema, error_info): + def handle_invalid_for_json_schema( + self, schema: Any, error_info: Any + ) -> dict[str, Any]: raise PydanticOmit @@ -73,7 +76,7 @@ class ToolSpecExtractor: @staticmethod def _extract_field_default( - field: dict | None, fallback: str | list[Any] = "" + field: dict[str, Any] | None, fallback: str | list[Any] = "" ) -> str | list[Any] | int: if not field: return fallback @@ -83,7 +86,7 @@ class ToolSpecExtractor: return default if isinstance(default, (list, str, int)) else fallback @staticmethod - def _extract_params(args_schema_field: dict | None) -> dict[str, Any]: + def _extract_params(args_schema_field: dict[str, Any] | None) -> dict[str, Any]: if not args_schema_field: return {} @@ -94,15 +97,15 @@ class ToolSpecExtractor: ): return {} - # Cast to type[BaseModel] after runtime check - schema_class = cast(type[BaseModel], args_schema_class) try: - return schema_class.model_json_schema(schema_generator=SchemaGenerator) + return args_schema_class.model_json_schema(schema_generator=SchemaGenerator) except Exception: return {} @staticmethod - def _extract_env_vars(env_vars_field: dict | None) -> list[dict[str, Any]]: + def _extract_env_vars( + env_vars_field: dict[str, Any] | None, + ) -> list[dict[str, Any]]: if not env_vars_field: return [] diff --git a/lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_csv_search_tool.yaml b/lib/crewai-tools/tests/cassettes/tools/test_csv_search_tool.yaml similarity index 100% rename from lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_csv_search_tool.yaml rename to lib/crewai-tools/tests/cassettes/tools/test_csv_search_tool.yaml diff --git a/lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_directory_search_tool.yaml b/lib/crewai-tools/tests/cassettes/tools/test_directory_search_tool.yaml similarity index 100% rename from lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_directory_search_tool.yaml rename to lib/crewai-tools/tests/cassettes/tools/test_directory_search_tool.yaml diff --git a/lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_json_search_tool.yaml b/lib/crewai-tools/tests/cassettes/tools/test_json_search_tool.yaml similarity index 100% rename from lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_json_search_tool.yaml rename to lib/crewai-tools/tests/cassettes/tools/test_json_search_tool.yaml diff --git a/lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_mdx_search_tool.yaml b/lib/crewai-tools/tests/cassettes/tools/test_mdx_search_tool.yaml similarity index 100% rename from lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_mdx_search_tool.yaml rename to lib/crewai-tools/tests/cassettes/tools/test_mdx_search_tool.yaml diff --git a/lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_txt_search_tool.yaml b/lib/crewai-tools/tests/cassettes/tools/test_txt_search_tool.yaml similarity index 100% rename from lib/crewai-tools/tests/tools/cassettes/test_search_tools/test_txt_search_tool.yaml rename to lib/crewai-tools/tests/cassettes/tools/test_txt_search_tool.yaml diff --git a/lib/crewai-tools/tests/it/tools/__init__.py b/lib/crewai-tools/tests/it/tools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/crewai-tools/tests/it/tools/conftest.py b/lib/crewai-tools/tests/it/tools/conftest.py deleted file mode 100644 index a633c22c7..000000000 --- a/lib/crewai-tools/tests/it/tools/conftest.py +++ /dev/null @@ -1,21 +0,0 @@ -import pytest - - -def pytest_configure(config): - """Register custom markers.""" - config.addinivalue_line("markers", "integration: mark test as an integration test") - config.addinivalue_line("markers", "asyncio: mark test as an async test") - - # Set the asyncio loop scope through ini configuration - config.inicfg["asyncio_mode"] = "auto" - - -@pytest.fixture(scope="function") -def event_loop(): - """Create an instance of the default event loop for each test case.""" - import asyncio - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - yield loop - loop.close() diff --git a/lib/crewai-tools/tests/test_generate_tool_specs.py b/lib/crewai-tools/tests/test_generate_tool_specs.py index 18c2dfe8d..290d099b9 100644 --- a/lib/crewai-tools/tests/test_generate_tool_specs.py +++ b/lib/crewai-tools/tests/test_generate_tool_specs.py @@ -2,7 +2,7 @@ import json from unittest import mock from crewai.tools.base_tool import BaseTool, EnvVar -from generate_tool_specs import ToolSpecExtractor +from crewai_tools.generate_tool_specs import ToolSpecExtractor from pydantic import BaseModel, Field import pytest @@ -61,8 +61,8 @@ def test_unwrap_schema(extractor): @pytest.fixture def mock_tool_extractor(extractor): with ( - mock.patch("generate_tool_specs.dir", return_value=["MockTool"]), - mock.patch("generate_tool_specs.getattr", return_value=MockTool), + mock.patch("crewai_tools.generate_tool_specs.dir", return_value=["MockTool"]), + mock.patch("crewai_tools.generate_tool_specs.getattr", return_value=MockTool), ): extractor.extract_all_tools() assert len(extractor.tools_spec) == 1 diff --git a/lib/crewai-tools/tests/tools/firecrawl_crawl_website_tool_test.py b/lib/crewai-tools/tests/tools/firecrawl_crawl_website_tool_test.py index 1590a4a52..41417874e 100644 --- a/lib/crewai-tools/tests/tools/firecrawl_crawl_website_tool_test.py +++ b/lib/crewai-tools/tests/tools/firecrawl_crawl_website_tool_test.py @@ -4,7 +4,7 @@ from crewai_tools.tools.firecrawl_crawl_website_tool.firecrawl_crawl_website_too FirecrawlCrawlWebsiteTool, ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_firecrawl_crawl_tool_integration(): tool = FirecrawlCrawlWebsiteTool(config={ "limit": 2, diff --git a/lib/crewai-tools/tests/tools/firecrawl_scrape_website_tool_test.py b/lib/crewai-tools/tests/tools/firecrawl_scrape_website_tool_test.py index 70f1cf2e1..6c0a05825 100644 --- a/lib/crewai-tools/tests/tools/firecrawl_scrape_website_tool_test.py +++ b/lib/crewai-tools/tests/tools/firecrawl_scrape_website_tool_test.py @@ -4,7 +4,7 @@ from crewai_tools.tools.firecrawl_scrape_website_tool.firecrawl_scrape_website_t FirecrawlScrapeWebsiteTool, ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_firecrawl_scrape_tool_integration(): tool = FirecrawlScrapeWebsiteTool() result = tool.run(url="https://firecrawl.dev") diff --git a/lib/crewai-tools/tests/tools/firecrawl_search_tool_test.py b/lib/crewai-tools/tests/tools/firecrawl_search_tool_test.py index e6294c084..217faa10b 100644 --- a/lib/crewai-tools/tests/tools/firecrawl_search_tool_test.py +++ b/lib/crewai-tools/tests/tools/firecrawl_search_tool_test.py @@ -3,7 +3,7 @@ import pytest from crewai_tools.tools.firecrawl_search_tool.firecrawl_search_tool import FirecrawlSearchTool -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_firecrawl_search_tool_integration(): tool = FirecrawlSearchTool() result = tool.run(query="firecrawl") diff --git a/lib/crewai-tools/tests/tools/test_search_tools.py b/lib/crewai-tools/tests/tools/test_search_tools.py index 298ecf62f..52c08633f 100644 --- a/lib/crewai-tools/tests/tools/test_search_tools.py +++ b/lib/crewai-tools/tests/tools/test_search_tools.py @@ -23,15 +23,13 @@ from crewai_tools.tools.rag.rag_tool import Adapter import pytest -pytestmark = [pytest.mark.vcr(filter_headers=["authorization"])] - - @pytest.fixture def mock_adapter(): mock_adapter = MagicMock(spec=Adapter) return mock_adapter +@pytest.mark.vcr() def test_directory_search_tool(): with tempfile.TemporaryDirectory() as temp_dir: test_file = Path(temp_dir) / "test.txt" @@ -65,6 +63,7 @@ def test_pdf_search_tool(mock_adapter): ) +@pytest.mark.vcr() def test_txt_search_tool(): with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as temp_file: temp_file.write(b"This is a test file for txt search") @@ -102,6 +101,7 @@ def test_docx_search_tool(mock_adapter): ) +@pytest.mark.vcr() def test_json_search_tool(): with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as temp_file: temp_file.write(b'{"test": "This is a test JSON file"}') @@ -127,6 +127,7 @@ def test_xml_search_tool(mock_adapter): ) +@pytest.mark.vcr() def test_csv_search_tool(): with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as temp_file: temp_file.write(b"name,description\ntest,This is a test CSV file") @@ -141,6 +142,7 @@ def test_csv_search_tool(): os.unlink(temp_file_path) +@pytest.mark.vcr() def test_mdx_search_tool(): with tempfile.NamedTemporaryFile(suffix=".mdx", delete=False) as temp_file: temp_file.write(b"# Test MDX\nThis is a test MDX file") diff --git a/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py b/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py index bffa0d032..6c2b1c916 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py @@ -76,7 +76,7 @@ class TraceBatchManager: use_ephemeral: bool = False, ) -> TraceBatch: """Initialize a new trace batch (thread-safe)""" - with self._init_lock: + with self._batch_ready_cv: if self.current_batch is not None: logger.debug( "Batch already initialized, skipping duplicate initialization" @@ -99,7 +99,6 @@ class TraceBatchManager: self.backend_initialized = True self._batch_ready_cv.notify_all() - return self.current_batch def _initialize_backend_batch( @@ -107,7 +106,7 @@ class TraceBatchManager: user_context: dict[str, str], execution_metadata: dict[str, Any], use_ephemeral: bool = False, - ): + ) -> None: """Send batch initialization to backend""" if not is_tracing_enabled_in_context(): @@ -204,7 +203,7 @@ class TraceBatchManager: return False return True - def add_event(self, trace_event: TraceEvent): + def add_event(self, trace_event: TraceEvent) -> None: """Add event to buffer""" self.event_buffer.append(trace_event) @@ -300,7 +299,7 @@ class TraceBatchManager: return finalized_batch - def _finalize_backend_batch(self, events_count: int = 0): + def _finalize_backend_batch(self, events_count: int = 0) -> None: """Send batch finalization to backend Args: @@ -366,7 +365,7 @@ class TraceBatchManager: logger.error(f"❌ Error finalizing trace batch: {e}") self.plus_api.mark_trace_batch_as_failed(self.trace_batch_id, str(e)) - def _cleanup_batch_data(self): + def _cleanup_batch_data(self) -> None: """Clean up batch data after successful finalization to free memory""" try: if hasattr(self, "event_buffer") and self.event_buffer: @@ -411,7 +410,7 @@ class TraceBatchManager: lambda: self.current_batch is not None, timeout=timeout ) - def record_start_time(self, key: str): + def record_start_time(self, key: str) -> None: """Record start time for duration calculation""" self.execution_start_times[key] = datetime.now(timezone.utc) diff --git a/lib/crewai/src/crewai/llms/constants.py b/lib/crewai/src/crewai/llms/constants.py index 02a138297..9552efada 100644 --- a/lib/crewai/src/crewai/llms/constants.py +++ b/lib/crewai/src/crewai/llms/constants.py @@ -256,6 +256,7 @@ GeminiModels: TypeAlias = Literal[ "gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts", "gemini-2.5-computer-use-preview-10-2025", + "gemini-2.5-pro-exp-03-25", "gemini-2.0-flash", "gemini-2.0-flash-001", "gemini-2.0-flash-exp", @@ -309,6 +310,7 @@ GEMINI_MODELS: list[GeminiModels] = [ "gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts", "gemini-2.5-computer-use-preview-10-2025", + "gemini-2.5-pro-exp-03-25", "gemini-2.0-flash", "gemini-2.0-flash-001", "gemini-2.0-flash-exp", diff --git a/lib/crewai/tests/agents/test_agent.py b/lib/crewai/tests/agents/test_agent.py index 977db03db..55ddf3256 100644 --- a/lib/crewai/tests/agents/test_agent.py +++ b/lib/crewai/tests/agents/test_agent.py @@ -147,7 +147,7 @@ def test_custom_llm(): assert agent.llm.model == "gpt-4" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execution(): agent = Agent( role="test role", @@ -166,7 +166,7 @@ def test_agent_execution(): assert output == "1 + 1 is 2" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execution_with_tools(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -211,7 +211,7 @@ def test_agent_execution_with_tools(): assert received_events[0].tool_args == {"first_number": 3, "second_number": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_logging_tool_usage(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -245,7 +245,7 @@ def test_logging_tool_usage(): assert agent.tools_handler.last_used_tool.arguments == tool_usage.arguments -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_cache_hitting(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -325,7 +325,7 @@ def test_cache_hitting(): assert received_events[0].output == "12" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_disabling_cache_for_agent(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -389,7 +389,7 @@ def test_disabling_cache_for_agent(): read.assert_not_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execution_with_specific_tools(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -412,7 +412,7 @@ def test_agent_execution_with_specific_tools(): assert output == "The result of the multiplication is 12." -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_powered_by_new_o_model_family_that_allows_skipping_tool(): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -438,7 +438,7 @@ def test_agent_powered_by_new_o_model_family_that_allows_skipping_tool(): assert output == "12" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_powered_by_new_o_model_family_that_uses_tool(): @tool def comapny_customer_data() -> str: @@ -464,7 +464,7 @@ def test_agent_powered_by_new_o_model_family_that_uses_tool(): assert output == "42" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_custom_max_iterations(): @tool def get_final_answer() -> float: @@ -509,7 +509,7 @@ def test_agent_custom_max_iterations(): assert call_count == 2 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.timeout(30) def test_agent_max_iterations_stops_loop(): """Test that agent execution terminates when max_iter is reached.""" @@ -546,7 +546,7 @@ def test_agent_max_iterations_stops_loop(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_repeated_tool_usage(capsys): """Test that agents handle repeated tool usage appropriately. @@ -595,7 +595,7 @@ def test_agent_repeated_tool_usage(capsys): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys): @tool def get_final_answer(anything: str) -> float: @@ -638,7 +638,7 @@ def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_moved_on_after_max_iterations(): @tool def get_final_answer() -> float: @@ -665,7 +665,7 @@ def test_agent_moved_on_after_max_iterations(): assert output == "42" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_respect_the_max_rpm_set(capsys): @tool def get_final_answer() -> float: @@ -699,7 +699,7 @@ def test_agent_respect_the_max_rpm_set(capsys): moveon.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys): from unittest.mock import patch @@ -737,7 +737,7 @@ def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys): moveon.assert_not_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_without_max_rpm_respects_crew_rpm(capsys): from unittest.mock import patch @@ -797,7 +797,7 @@ def test_agent_without_max_rpm_respects_crew_rpm(capsys): moveon.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_error_on_parsing_tool(capsys): from unittest.mock import patch @@ -840,7 +840,7 @@ def test_agent_error_on_parsing_tool(capsys): assert "Error on parsing tool." in captured.out -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_remembers_output_format_after_using_tools_too_many_times(): from unittest.mock import patch @@ -875,7 +875,7 @@ def test_agent_remembers_output_format_after_using_tools_too_many_times(): remember_format.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_use_specific_tasks_output_as_context(capsys): agent1 = Agent(role="test role", goal="test goal", backstory="test backstory") agent2 = Agent(role="test role2", goal="test goal2", backstory="test backstory2") @@ -902,7 +902,7 @@ def test_agent_use_specific_tasks_output_as_context(capsys): assert "hi" in result.raw.lower() or "hello" in result.raw.lower() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_step_callback(): class StepCallback: def callback(self, step): @@ -936,7 +936,7 @@ def test_agent_step_callback(): callback.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_function_calling_llm(): from crewai.llm import LLM llm = LLM(model="gpt-4o", is_litellm=True) @@ -983,7 +983,7 @@ def test_agent_function_calling_llm(): mock_original_tool_calling.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_tool_result_as_answer_is_the_final_answer_for_the_agent(): from crewai.tools import BaseTool @@ -1013,7 +1013,7 @@ def test_tool_result_as_answer_is_the_final_answer_for_the_agent(): assert result.raw == "Howdy!" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_tool_usage_information_is_appended_to_agent(): from crewai.tools import BaseTool @@ -1068,7 +1068,7 @@ def test_agent_definition_based_on_dict(): # test for human input -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_human_input(): # Agent configuration config = { @@ -1216,7 +1216,7 @@ Thought:<|eot_id|> assert mock_format_prompt.return_value == expected_prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_allow_crewai_trigger_context(): from crewai import Crew @@ -1237,7 +1237,7 @@ def test_task_allow_crewai_trigger_context(): assert "Trigger Payload: Important context data" in prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_without_allow_crewai_trigger_context(): from crewai import Crew @@ -1260,7 +1260,7 @@ def test_task_without_allow_crewai_trigger_context(): assert "Important context data" not in prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_allow_crewai_trigger_context_no_payload(): from crewai import Crew @@ -1282,7 +1282,7 @@ def test_task_allow_crewai_trigger_context_no_payload(): assert "Trigger Payload:" not in prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical(): from crewai import Crew @@ -1311,7 +1311,7 @@ def test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical(): assert "Trigger Payload: Initial context data" not in first_prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_first_task_auto_inject_trigger(): from crewai import Crew @@ -1344,7 +1344,7 @@ def test_first_task_auto_inject_trigger(): assert "Trigger Payload:" not in second_prompt -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_ensure_first_task_allow_crewai_trigger_context_is_false_does_not_inject(): from crewai import Crew @@ -1549,7 +1549,7 @@ def test_agent_with_additional_kwargs(): assert agent.llm.frequency_penalty == 0.1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call(): llm = LLM(model="gpt-3.5-turbo") messages = [{"role": "user", "content": "Say 'Hello, World!'"}] @@ -1558,7 +1558,7 @@ def test_llm_call(): assert "Hello, World!" in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_error(): llm = LLM(model="non-existent-model") messages = [{"role": "user", "content": "This should fail"}] @@ -1567,7 +1567,7 @@ def test_llm_call_with_error(): llm.call(messages) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_context_length_exceeds_limit(): # Import necessary modules from crewai.utilities.agent_utils import handle_context_length @@ -1620,7 +1620,7 @@ def test_handle_context_length_exceeds_limit(): mock_summarize.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_context_length_exceeds_limit_cli_no(): agent = Agent( role="test role", @@ -1695,7 +1695,7 @@ def test_agent_with_all_llm_attributes(): assert agent.llm.api_key == "sk-your-api-key-here" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_all_attributes(): llm = LLM( model="gpt-3.5-turbo", @@ -1712,7 +1712,7 @@ def test_llm_call_with_all_attributes(): assert "STOP" not in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_ollama_llama3(): agent = Agent( role="test role", @@ -1733,7 +1733,7 @@ def test_agent_with_ollama_llama3(): assert "Llama3" in response or "AI" in response or "language model" in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_ollama_llama3(): llm = LLM( model="ollama/llama3.2:3b", @@ -1752,7 +1752,7 @@ def test_llm_call_with_ollama_llama3(): assert "Llama3" in response or "AI" in response or "language model" in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execute_task_basic(): agent = Agent( role="test role", @@ -1771,7 +1771,7 @@ def test_agent_execute_task_basic(): assert "4" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execute_task_with_context(): agent = Agent( role="test role", @@ -1793,7 +1793,7 @@ def test_agent_execute_task_with_context(): assert "fox" in result.lower() and "dog" in result.lower() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execute_task_with_tool(): @tool def dummy_tool(query: str) -> str: @@ -1818,7 +1818,7 @@ def test_agent_execute_task_with_tool(): assert "Dummy result for: test query" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execute_task_with_custom_llm(): agent = Agent( role="test role", @@ -1839,7 +1839,7 @@ def test_agent_execute_task_with_custom_llm(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_execute_task_with_ollama(): agent = Agent( role="test role", @@ -1859,7 +1859,7 @@ def test_agent_execute_task_with_ollama(): assert "AI" in result or "artificial intelligence" in result.lower() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -1891,7 +1891,7 @@ def test_agent_with_knowledge_sources(): assert "red" in result.raw.lower() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources_with_query_limit_and_score_threshold(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -1939,7 +1939,7 @@ def test_agent_with_knowledge_sources_with_query_limit_and_score_threshold(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_default(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -1988,7 +1988,7 @@ def test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_defau ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources_extensive_role(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -2024,7 +2024,7 @@ def test_agent_with_knowledge_sources_extensive_role(): assert "red" in result.raw.lower() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources_works_with_copy(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -2063,7 +2063,7 @@ def test_agent_with_knowledge_sources_works_with_copy(): assert isinstance(agent_copy.llm, BaseLLM) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_sources_generate_search_query(): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -2116,7 +2116,7 @@ def test_agent_with_knowledge_sources_generate_search_query(): assert "red" in result.raw.lower() -@pytest.mark.vcr(record_mode="none", filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_knowledge_with_no_crewai_knowledge(): mock_knowledge = MagicMock(spec=Knowledge) @@ -2143,7 +2143,7 @@ def test_agent_with_knowledge_with_no_crewai_knowledge(): mock_knowledge.query.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_with_only_crewai_knowledge(): mock_knowledge = MagicMock(spec=Knowledge) @@ -2168,7 +2168,7 @@ def test_agent_with_only_crewai_knowledge(): mock_knowledge.query.assert_called_once() -@pytest.mark.vcr(record_mode="none", filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_knowledege_with_crewai_knowledge(): crew_knowledge = MagicMock(spec=Knowledge) agent_knowledge = MagicMock(spec=Knowledge) @@ -2197,7 +2197,7 @@ def test_agent_knowledege_with_crewai_knowledge(): crew_knowledge.query.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_litellm_auth_error_handling(): """Test that LiteLLM authentication errors are handled correctly and not retried.""" from litellm import AuthenticationError as LiteLLMAuthenticationError @@ -2326,7 +2326,7 @@ def test_litellm_anthropic_error_handling(): mock_llm_call.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_get_knowledge_search_query(): """Test that _get_knowledge_search_query calls the LLM with the correct prompts.""" from crewai.utilities.i18n import I18N diff --git a/lib/crewai/tests/agents/test_lite_agent.py b/lib/crewai/tests/agents/test_lite_agent.py index f2fa4b2e6..5eac82f19 100644 --- a/lib/crewai/tests/agents/test_lite_agent.py +++ b/lib/crewai/tests/agents/test_lite_agent.py @@ -70,7 +70,7 @@ class ResearchResult(BaseModel): sources: list[str] = Field(description="List of sources used") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.parametrize("verbose", [True, False]) def test_lite_agent_created_with_correct_parameters(monkeypatch, verbose): """Test that LiteAgent is created with the correct parameters when Agent.kickoff() is called.""" @@ -130,7 +130,7 @@ def test_lite_agent_created_with_correct_parameters(monkeypatch, verbose): assert created_lite_agent["response_format"] == TestResponse -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_lite_agent_with_tools(): """Test that Agent can use tools.""" # Create a LiteAgent with tools @@ -174,7 +174,7 @@ def test_lite_agent_with_tools(): assert event.tool_name == "search_web" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_lite_agent_structured_output(): """Test that Agent can return a simple structured output.""" @@ -217,7 +217,7 @@ def test_lite_agent_structured_output(): return result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_lite_agent_returns_usage_metrics(): """Test that LiteAgent returns usage metrics.""" llm = LLM(model="gpt-4o-mini") @@ -238,7 +238,7 @@ def test_lite_agent_returns_usage_metrics(): assert result.usage_metrics["total_tokens"] > 0 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_lite_agent_output_includes_messages(): """Test that LiteAgentOutput includes messages from agent execution.""" llm = LLM(model="gpt-4o-mini") @@ -259,7 +259,7 @@ def test_lite_agent_output_includes_messages(): assert len(result.messages) > 0 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.asyncio async def test_lite_agent_returns_usage_metrics_async(): """Test that LiteAgent returns usage metrics when run asynchronously.""" @@ -354,9 +354,9 @@ def test_sets_parent_flow_when_inside_flow(): assert captured_agent.parent_flow is flow -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_guardrail_is_called_using_string(): - guardrail_events = defaultdict(list) + guardrail_events: dict[str, list] = defaultdict(list) from crewai.events.event_types import ( LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent, @@ -369,35 +369,33 @@ def test_guardrail_is_called_using_string(): guardrail="""Only include Brazilian players, both women and men""", ) - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(LLMGuardrailStartedEvent) def capture_guardrail_started(source, event): assert isinstance(source, LiteAgent) assert source.original_agent == agent - guardrail_events["started"].append(event) - if ( - len(guardrail_events["started"]) == 2 - and len(guardrail_events["completed"]) == 2 - ): - all_events_received.set() + with condition: + guardrail_events["started"].append(event) + condition.notify() @crewai_event_bus.on(LLMGuardrailCompletedEvent) def capture_guardrail_completed(source, event): assert isinstance(source, LiteAgent) assert source.original_agent == agent - guardrail_events["completed"].append(event) - if ( - len(guardrail_events["started"]) == 2 - and len(guardrail_events["completed"]) == 2 - ): - all_events_received.set() + with condition: + guardrail_events["completed"].append(event) + condition.notify() result = agent.kickoff(messages="Top 10 best players in the world?") - assert all_events_received.wait(timeout=10), ( - "Timeout waiting for all guardrail events" - ) + with condition: + success = condition.wait_for( + lambda: len(guardrail_events["started"]) >= 2 + and len(guardrail_events["completed"]) >= 2, + timeout=10, + ) + assert success, "Timeout waiting for all guardrail events" assert len(guardrail_events["started"]) == 2 assert len(guardrail_events["completed"]) == 2 assert not guardrail_events["completed"][0].success @@ -408,33 +406,27 @@ def test_guardrail_is_called_using_string(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_guardrail_is_called_using_callable(): - guardrail_events = defaultdict(list) + guardrail_events: dict[str, list] = defaultdict(list) from crewai.events.event_types import ( LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent, ) - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(LLMGuardrailStartedEvent) def capture_guardrail_started(source, event): - guardrail_events["started"].append(event) - if ( - len(guardrail_events["started"]) == 1 - and len(guardrail_events["completed"]) == 1 - ): - all_events_received.set() + with condition: + guardrail_events["started"].append(event) + condition.notify() @crewai_event_bus.on(LLMGuardrailCompletedEvent) def capture_guardrail_completed(source, event): - guardrail_events["completed"].append(event) - if ( - len(guardrail_events["started"]) == 1 - and len(guardrail_events["completed"]) == 1 - ): - all_events_received.set() + with condition: + guardrail_events["completed"].append(event) + condition.notify() agent = Agent( role="Sports Analyst", @@ -445,42 +437,40 @@ def test_guardrail_is_called_using_callable(): result = agent.kickoff(messages="Top 1 best players in the world?") - assert all_events_received.wait(timeout=10), ( - "Timeout waiting for all guardrail events" - ) + with condition: + success = condition.wait_for( + lambda: len(guardrail_events["started"]) >= 1 + and len(guardrail_events["completed"]) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for all guardrail events" assert len(guardrail_events["started"]) == 1 assert len(guardrail_events["completed"]) == 1 assert guardrail_events["completed"][0].success assert "Pelé - Santos, 1958" in result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_guardrail_reached_attempt_limit(): - guardrail_events = defaultdict(list) + guardrail_events: dict[str, list] = defaultdict(list) from crewai.events.event_types import ( LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent, ) - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(LLMGuardrailStartedEvent) def capture_guardrail_started(source, event): - guardrail_events["started"].append(event) - if ( - len(guardrail_events["started"]) == 3 - and len(guardrail_events["completed"]) == 3 - ): - all_events_received.set() + with condition: + guardrail_events["started"].append(event) + condition.notify() @crewai_event_bus.on(LLMGuardrailCompletedEvent) def capture_guardrail_completed(source, event): - guardrail_events["completed"].append(event) - if ( - len(guardrail_events["started"]) == 3 - and len(guardrail_events["completed"]) == 3 - ): - all_events_received.set() + with condition: + guardrail_events["completed"].append(event) + condition.notify() agent = Agent( role="Sports Analyst", @@ -498,9 +488,13 @@ def test_guardrail_reached_attempt_limit(): ): agent.kickoff(messages="Top 10 best players in the world?") - assert all_events_received.wait(timeout=10), ( - "Timeout waiting for all guardrail events" - ) + with condition: + success = condition.wait_for( + lambda: len(guardrail_events["started"]) >= 3 + and len(guardrail_events["completed"]) >= 3, + timeout=10, + ) + assert success, "Timeout waiting for all guardrail events" assert len(guardrail_events["started"]) == 3 # 2 retries + 1 initial call assert len(guardrail_events["completed"]) == 3 # 2 retries + 1 initial call assert not guardrail_events["completed"][0].success @@ -508,7 +502,7 @@ def test_guardrail_reached_attempt_limit(): assert not guardrail_events["completed"][2].success -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_output_when_guardrail_returns_base_model(): class Player(BaseModel): name: str @@ -599,7 +593,7 @@ def test_lite_agent_with_custom_llm_and_guardrails(): assert result2.raw == "Modified by guardrail" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_lite_agent_with_invalid_llm(): """Test that LiteAgent raises proper error when create_llm returns None.""" with patch("crewai.lite_agent.create_llm", return_value=None): @@ -615,7 +609,7 @@ def test_lite_agent_with_invalid_llm(): @patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"}) @patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_kickoff_with_platform_tools(mock_get): """Test that Agent.kickoff() properly integrates platform tools with LiteAgent""" mock_response = Mock() @@ -657,7 +651,7 @@ def test_agent_kickoff_with_platform_tools(mock_get): @patch.dict("os.environ", {"EXA_API_KEY": "test_exa_key"}) @patch("crewai.agent.Agent._get_external_mcp_tools") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_kickoff_with_mcp_tools(mock_get_mcp_tools): """Test that Agent.kickoff() properly integrates MCP tools with LiteAgent""" # Setup mock MCP tools - create a proper BaseTool instance diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml b/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml deleted file mode 100644 index 49b349b5d..000000000 --- a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml +++ /dev/null @@ -1,126 +0,0 @@ -interactions: -- request: - body: '{"messages": [{"role": "system", "content": "You are Test Agent. Test backstory\nYour - personal goal is: Test goal\nTo give my best complete final answer to the task - respond using the exact following format:\n\nThought: I now can give a great - answer\nFinal Answer: Your final answer must be the great and the most complete - as possible, it must be outcome described.\n\nI MUST use these formats, my job - depends on it!"}, {"role": "user", "content": "\nCurrent Task: Say hello to - the world\n\nThis is the expected criteria for your final answer: hello world\nyou - MUST return the actual complete content as the final answer, not a summary.\n\nBegin! - This is VERY important to you, use the tools available and give your best Final - Answer, your job depends on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop": - ["\nObservation:"]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate, zstd - connection: - - keep-alive - content-length: - - '825' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.93.0 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.93.0 - x-stainless-raw-response: - - 'true' - x-stainless-read-timeout: - - '600.0' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.12.9 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//jFJdi9swEHz3r1j0HBc7ZydXvx1HC0evhT6UUtrDKNLa1lXWqpJ8aTjy - 34vsXOz0A/pi8M7OaGZ3nxMApiSrgImOB9Fbnd4Why/v79HeePeD37/Zfdx8evf5+rY4fNjdPbJV - ZNDuEUV4Yb0S1FuNQZGZYOGQB4yq+bYsr7J1nhcj0JNEHWmtDWlBaa+MStfZukizbZpfn9gdKYGe - VfA1AQB4Hr/Rp5H4k1WQrV4qPXrPW2TVuQmAOdKxwrj3ygduAlvNoCAT0IzW78DQHgQ30KonBA5t - tA3c+D06gG/mrTJcw834X0GHWhPsyWm5FHTYDJ7HUGbQegFwYyjwOJQxysMJOZ7Na2qto53/jcoa - ZZTvaofck4lGfSDLRvSYADyMQxoucjPrqLehDvQdx+fycjvpsXk3C/TqBAYKXC/q29NoL/VqiYEr - 7RdjZoKLDuVMnXfCB6loASSL1H+6+Zv2lFyZ9n/kZ0AItAFlbR1KJS4Tz20O4+n+q+085dEw8+ie - lMA6KHRxExIbPujpoJg/+IB93SjTorNOTVfV2LrcZLzZYFm+Zskx+QUAAP//AwB1vYZ+YwMAAA== - headers: - CF-RAY: - - 96fc9f29dea3cf1f-SJC - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Fri, 15 Aug 2025 23:55:15 GMT - Server: - - cloudflare - Set-Cookie: - - __cf_bm=oA9oTa3cE0ZaEUDRf0hCpnarSAQKzrVUhl6qDS4j09w-1755302115-1.0.1.1-gUUDl4ZqvBQkg7244DTwOmSiDUT2z_AiQu0P1xUaABjaufSpZuIlI5G0H7OSnW.ldypvpxjj45NGWesJ62M_2U7r20tHz_gMmDFw6D5ZiNc; - path=/; expires=Sat, 16-Aug-25 00:25:15 GMT; domain=.api.openai.com; HttpOnly; - Secure; SameSite=None - - _cfuvid=ICenEGMmOE5jaOjwD30bAOwrF8.XRbSIKTBl1EyWs0o-1755302115700-0.0.1.1-604800000; - path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - cf-cache-status: - - DYNAMIC - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '735' - openai-project: - - proj_xitITlrFeen7zjNSzML82h9x - openai-version: - - '2020-10-01' - x-envoy-upstream-service-time: - - '753' - x-ratelimit-limit-project-tokens: - - '150000000' - x-ratelimit-limit-requests: - - '30000' - x-ratelimit-limit-tokens: - - '150000000' - x-ratelimit-remaining-project-tokens: - - '149999830' - x-ratelimit-remaining-requests: - - '29999' - x-ratelimit-remaining-tokens: - - '149999827' - x-ratelimit-reset-project-tokens: - - 0s - x-ratelimit-reset-requests: - - 2ms - x-ratelimit-reset-tokens: - - 0s - x-request-id: - - req_212fde9d945a462ba0d89ea856131dce - status: - code: 200 - message: OK -version: 1 diff --git a/lib/crewai/tests/cassettes/test_agent_custom_max_iterations.yaml b/lib/crewai/tests/cassettes/agents/test_agent_custom_max_iterations.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_custom_max_iterations.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_custom_max_iterations.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_error_on_parsing_tool.yaml b/lib/crewai/tests/cassettes/agents/test_agent_error_on_parsing_tool.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_error_on_parsing_tool.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_error_on_parsing_tool.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execute_task_basic.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execute_task_basic.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execute_task_basic.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execute_task_basic.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execute_task_with_context.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_context.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execute_task_with_context.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_context.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execute_task_with_custom_llm.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_custom_llm.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execute_task_with_custom_llm.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_custom_llm.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execute_task_with_ollama.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_ollama.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execute_task_with_ollama.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_ollama.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execute_task_with_tool.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_tool.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execute_task_with_tool.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execute_task_with_tool.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execution.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execution.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execution.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execution.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execution_with_specific_tools.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execution_with_specific_tools.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execution_with_specific_tools.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execution_with_specific_tools.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_execution_with_tools.yaml b/lib/crewai/tests/cassettes/agents/test_agent_execution_with_tools.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_execution_with_tools.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_execution_with_tools.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_function_calling_llm.yaml b/lib/crewai/tests/cassettes/agents/test_agent_function_calling_llm.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_function_calling_llm.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_function_calling_llm.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_kickoff_with_mcp_tools.yaml b/lib/crewai/tests/cassettes/agents/test_agent_kickoff_with_mcp_tools.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_kickoff_with_mcp_tools.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_kickoff_with_mcp_tools.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_kickoff_with_platform_tools.yaml b/lib/crewai/tests/cassettes/agents/test_agent_kickoff_with_platform_tools.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_kickoff_with_platform_tools.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_kickoff_with_platform_tools.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_knowledege_with_crewai_knowledge.yaml b/lib/crewai/tests/cassettes/agents/test_agent_knowledege_with_crewai_knowledge.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_knowledege_with_crewai_knowledge.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_knowledege_with_crewai_knowledge.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_max_iterations_stops_loop.yaml b/lib/crewai/tests/cassettes/agents/test_agent_max_iterations_stops_loop.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_max_iterations_stops_loop.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_max_iterations_stops_loop.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_moved_on_after_max_iterations.yaml b/lib/crewai/tests/cassettes/agents/test_agent_moved_on_after_max_iterations.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_moved_on_after_max_iterations.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_moved_on_after_max_iterations.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_output_when_guardrail_returns_base_model.yaml b/lib/crewai/tests/cassettes/agents/test_agent_output_when_guardrail_returns_base_model.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_output_when_guardrail_returns_base_model.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_output_when_guardrail_returns_base_model.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_powered_by_new_o_model_family_that_allows_skipping_tool.yaml b/lib/crewai/tests/cassettes/agents/test_agent_powered_by_new_o_model_family_that_allows_skipping_tool.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_powered_by_new_o_model_family_that_allows_skipping_tool.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_powered_by_new_o_model_family_that_allows_skipping_tool.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_powered_by_new_o_model_family_that_uses_tool.yaml b/lib/crewai/tests/cassettes/agents/test_agent_powered_by_new_o_model_family_that_uses_tool.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_powered_by_new_o_model_family_that_uses_tool.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_powered_by_new_o_model_family_that_uses_tool.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_remembers_output_format_after_using_tools_too_many_times.yaml b/lib/crewai/tests/cassettes/agents/test_agent_remembers_output_format_after_using_tools_too_many_times.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_remembers_output_format_after_using_tools_too_many_times.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_remembers_output_format_after_using_tools_too_many_times.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_repeated_tool_usage.yaml b/lib/crewai/tests/cassettes/agents/test_agent_repeated_tool_usage.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_repeated_tool_usage.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_repeated_tool_usage.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_repeated_tool_usage_check_even_with_disabled_cache.yaml b/lib/crewai/tests/cassettes/agents/test_agent_repeated_tool_usage_check_even_with_disabled_cache.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_repeated_tool_usage_check_even_with_disabled_cache.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_repeated_tool_usage_check_even_with_disabled_cache.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_respect_the_max_rpm_set.yaml b/lib/crewai/tests/cassettes/agents/test_agent_respect_the_max_rpm_set.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_respect_the_max_rpm_set.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_respect_the_max_rpm_set.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_respect_the_max_rpm_set_over_crew_rpm.yaml b/lib/crewai/tests/cassettes/agents/test_agent_respect_the_max_rpm_set_over_crew_rpm.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_respect_the_max_rpm_set_over_crew_rpm.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_respect_the_max_rpm_set_over_crew_rpm.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_step_callback.yaml b/lib/crewai/tests/cassettes/agents/test_agent_step_callback.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_step_callback.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_step_callback.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_use_specific_tasks_output_as_context.yaml b/lib/crewai/tests/cassettes/agents/test_agent_use_specific_tasks_output_as_context.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_use_specific_tasks_output_as_context.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_use_specific_tasks_output_as_context.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_extensive_role.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_extensive_role.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_extensive_role.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_extensive_role.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_generate_search_query.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_generate_search_query.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_generate_search_query.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_generate_search_query.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_default.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_default.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_default.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_with_query_limit_and_score_threshold_default.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_works_with_copy.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_sources_works_with_copy.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_knowledge_with_no_crewai_knowledge.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_with_no_crewai_knowledge.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_knowledge_with_no_crewai_knowledge.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_knowledge_with_no_crewai_knowledge.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_ollama_llama3.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_ollama_llama3.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_ollama_llama3.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_ollama_llama3.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_with_only_crewai_knowledge.yaml b/lib/crewai/tests/cassettes/agents/test_agent_with_only_crewai_knowledge.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_with_only_crewai_knowledge.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_with_only_crewai_knowledge.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_without_max_rpm_respects_crew_rpm.yaml b/lib/crewai/tests/cassettes/agents/test_agent_without_max_rpm_respects_crew_rpm.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_without_max_rpm_respects_crew_rpm.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_without_max_rpm_respects_crew_rpm.yaml diff --git a/lib/crewai/tests/cassettes/test_agent_without_max_rpm_respet_crew_rpm.yaml b/lib/crewai/tests/cassettes/agents/test_agent_without_max_rpm_respet_crew_rpm.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_agent_without_max_rpm_respet_crew_rpm.yaml rename to lib/crewai/tests/cassettes/agents/test_agent_without_max_rpm_respet_crew_rpm.yaml diff --git a/lib/crewai/tests/cassettes/test_cache_hitting.yaml b/lib/crewai/tests/cassettes/agents/test_cache_hitting.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_cache_hitting.yaml rename to lib/crewai/tests/cassettes/agents/test_cache_hitting.yaml diff --git a/lib/crewai/tests/cassettes/test_disabling_cache_for_agent.yaml b/lib/crewai/tests/cassettes/agents/test_disabling_cache_for_agent.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_disabling_cache_for_agent.yaml rename to lib/crewai/tests/cassettes/agents/test_disabling_cache_for_agent.yaml diff --git a/lib/crewai/tests/cassettes/test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical.yaml b/lib/crewai/tests/cassettes/agents/test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical.yaml rename to lib/crewai/tests/cassettes/agents/test_do_not_allow_crewai_trigger_context_for_first_task_hierarchical.yaml diff --git a/lib/crewai/tests/cassettes/test_ensure_first_task_allow_crewai_trigger_context_is_false_does_not_inject.yaml b/lib/crewai/tests/cassettes/agents/test_ensure_first_task_allow_crewai_trigger_context_is_false_does_not_inject.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_ensure_first_task_allow_crewai_trigger_context_is_false_does_not_inject.yaml rename to lib/crewai/tests/cassettes/agents/test_ensure_first_task_allow_crewai_trigger_context_is_false_does_not_inject.yaml diff --git a/lib/crewai/tests/cassettes/test_first_task_auto_inject_trigger.yaml b/lib/crewai/tests/cassettes/agents/test_first_task_auto_inject_trigger.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_first_task_auto_inject_trigger.yaml rename to lib/crewai/tests/cassettes/agents/test_first_task_auto_inject_trigger.yaml diff --git a/lib/crewai/tests/cassettes/test_get_knowledge_search_query.yaml b/lib/crewai/tests/cassettes/agents/test_get_knowledge_search_query.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_get_knowledge_search_query.yaml rename to lib/crewai/tests/cassettes/agents/test_get_knowledge_search_query.yaml diff --git a/lib/crewai/tests/cassettes/test_guardrail_is_called_using_callable.yaml b/lib/crewai/tests/cassettes/agents/test_guardrail_is_called_using_callable.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_guardrail_is_called_using_callable.yaml rename to lib/crewai/tests/cassettes/agents/test_guardrail_is_called_using_callable.yaml diff --git a/lib/crewai/tests/cassettes/test_guardrail_is_called_using_string.yaml b/lib/crewai/tests/cassettes/agents/test_guardrail_is_called_using_string.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_guardrail_is_called_using_string.yaml rename to lib/crewai/tests/cassettes/agents/test_guardrail_is_called_using_string.yaml diff --git a/lib/crewai/tests/cassettes/test_guardrail_reached_attempt_limit.yaml b/lib/crewai/tests/cassettes/agents/test_guardrail_reached_attempt_limit.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_guardrail_reached_attempt_limit.yaml rename to lib/crewai/tests/cassettes/agents/test_guardrail_reached_attempt_limit.yaml diff --git a/lib/crewai/tests/cassettes/test_handle_context_length_exceeds_limit_cli_no.yaml b/lib/crewai/tests/cassettes/agents/test_handle_context_length_exceeds_limit_cli_no.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_handle_context_length_exceeds_limit_cli_no.yaml rename to lib/crewai/tests/cassettes/agents/test_handle_context_length_exceeds_limit_cli_no.yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_created_with_correct_parameters[False].yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_created_with_correct_parameters[False].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_created_with_correct_parameters[False].yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_created_with_correct_parameters[False].yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_created_with_correct_parameters[True].yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_created_with_correct_parameters[True].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_created_with_correct_parameters[True].yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_created_with_correct_parameters[True].yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_output_includes_messages.yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_output_includes_messages.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_output_includes_messages.yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_output_includes_messages.yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_returns_usage_metrics.yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_returns_usage_metrics.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_returns_usage_metrics.yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_returns_usage_metrics.yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_returns_usage_metrics_async.yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_returns_usage_metrics_async.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_returns_usage_metrics_async.yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_returns_usage_metrics_async.yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_structured_output.yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_structured_output.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_structured_output.yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_structured_output.yaml diff --git a/lib/crewai/tests/cassettes/test_lite_agent_with_tools.yaml b/lib/crewai/tests/cassettes/agents/test_lite_agent_with_tools.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_lite_agent_with_tools.yaml rename to lib/crewai/tests/cassettes/agents/test_lite_agent_with_tools.yaml diff --git a/lib/crewai/tests/cassettes/test_llm_call.yaml b/lib/crewai/tests/cassettes/agents/test_llm_call.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_llm_call.yaml rename to lib/crewai/tests/cassettes/agents/test_llm_call.yaml diff --git a/lib/crewai/tests/cassettes/test_llm_call_with_all_attributes.yaml b/lib/crewai/tests/cassettes/agents/test_llm_call_with_all_attributes.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_llm_call_with_all_attributes.yaml rename to lib/crewai/tests/cassettes/agents/test_llm_call_with_all_attributes.yaml diff --git a/lib/crewai/tests/cassettes/test_llm_call_with_ollama_llama3.yaml b/lib/crewai/tests/cassettes/agents/test_llm_call_with_ollama_llama3.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_llm_call_with_ollama_llama3.yaml rename to lib/crewai/tests/cassettes/agents/test_llm_call_with_ollama_llama3.yaml diff --git a/lib/crewai/tests/cassettes/test_logging_tool_usage.yaml b/lib/crewai/tests/cassettes/agents/test_logging_tool_usage.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_logging_tool_usage.yaml rename to lib/crewai/tests/cassettes/agents/test_logging_tool_usage.yaml diff --git a/lib/crewai/tests/cassettes/test_task_allow_crewai_trigger_context.yaml b/lib/crewai/tests/cassettes/agents/test_task_allow_crewai_trigger_context.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_task_allow_crewai_trigger_context.yaml rename to lib/crewai/tests/cassettes/agents/test_task_allow_crewai_trigger_context.yaml diff --git a/lib/crewai/tests/cassettes/agents/test_task_allow_crewai_trigger_context_no_payload.yaml b/lib/crewai/tests/cassettes/agents/test_task_allow_crewai_trigger_context_no_payload.yaml new file mode 100644 index 000000000..f98ef8f04 --- /dev/null +++ b/lib/crewai/tests/cassettes/agents/test_task_allow_crewai_trigger_context_no_payload.yaml @@ -0,0 +1,211 @@ +interactions: +- request: + body: '{"trace_id": "4d0d2b51-d83a-4054-b41e-8c2d17baa88f", "execution_type": + "crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null, + "crew_name": "crew", "flow_name": null, "crewai_version": "1.6.0", "privacy_level": + "standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count": + 0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-11-29T02:50:39.376314+00:00"}, + "ephemeral_trace_id": "4d0d2b51-d83a-4054-b41e-8c2d17baa88f"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '488' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.6.0 + X-Crewai-Version: + - 1.6.0 + authorization: + - AUTHORIZATION-XXX + method: POST + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches + response: + body: + string: '{"id":"71726285-2e63-4d2a-b4c4-4bbd0ff6a9f1","ephemeral_trace_id":"4d0d2b51-d83a-4054-b41e-8c2d17baa88f","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.6.0","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"1.6.0","privacy_level":"standard"},"created_at":"2025-11-29T02:50:39.931Z","updated_at":"2025-11-29T02:50:39.931Z","access_code":"TRACE-bf7f3f49b3","user_identifier":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '515' + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 29 Nov 2025 02:50:39 GMT + cache-control: + - no-store + content-security-policy: + - CSP-FILTERED + etag: + - ETAG-XXX + expires: + - '0' + permissions-policy: + - PERMISSIONS-POLICY-XXX + pragma: + - no-cache + referrer-policy: + - REFERRER-POLICY-XXX + strict-transport-security: + - STS-XXX + vary: + - Accept + x-content-type-options: + - X-CONTENT-TYPE-XXX + x-frame-options: + - X-FRAME-OPTIONS-XXX + x-permitted-cross-domain-policies: + - X-PERMITTED-XXX + x-request-id: + - X-REQUEST-ID-XXX + x-runtime: + - X-RUNTIME-XXX + x-xss-protection: + - X-XSS-PROTECTION-XXX + status: + code: 201 + message: Created +- request: + body: '{"messages":[{"role":"system","content":"You are test role. test backstory\nYour + personal goal is: test goal\nTo give my best complete final answer to the task + respond using the exact following format:\n\nThought: I now can give a great + answer\nFinal Answer: Your final answer must be the great and the most complete + as possible, it must be outcome described.\n\nI MUST use these formats, my job + depends on it!"},{"role":"user","content":"\nCurrent Task: Analyze the data\n\nThis + is the expected criteria for your final answer: Analysis report\nyou MUST return + the actual complete content as the final answer, not a summary.\n\nBegin! This + is VERY important to you, use the tools available and give your best Final Answer, + your job depends on it!\n\nThought:"}],"model":"gpt-4.1-mini"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - AUTHORIZATION-XXX + connection: + - keep-alive + content-length: + - '785' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - X-STAINLESS-ARCH-XXX + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - X-STAINLESS-OS-XXX + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - X-STAINLESS-READ-TIMEOUT-XXX + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.10 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: !!binary | + H4sIAAAAAAAAAwAAAP//jFbbbhtHDH33VxD71AKyECeynegtdW9uiiZI3AJpXRj0DHeXzSxnO5yV + ohT594Kzq5WcpEBfdBkOycPDy/CfE4CKfbWGyrWYXdeH06v2/EX7li7T5uenL364rH/6NUn+/Ze/ + 3nP99mW1MI14/xe5vNdautj1gTJHGcUuEWYyq2eXF6snT1cXT54VQRc9BVNr+ny6Wp6ddix8+vjR + 4/PTR6vTs9Wk3kZ2pNUa/jgBAPinfBpQ8fS+WsOjxf6kI1VsqFrPlwCqFIOdVKjKmlFytTgIXZRM + UrDftHFo2ryGa5C4BYcCDW8IEBoLAFB0S+lWvmfBAM/LvzXcyq08Fww7ZYXX1MeU7ehsCdeSU/SD + MyJu5aYlyKjvINHfAydSQMhtTOYTcG8g1pBbKn4FPGZcwk2EPsUNe0Ni1CZqSdSQpeJuUVTsMrSo + cE8koDvN1GFmhyHsINGGaUt+AVvOLWC2mDkK5AieMnIAFA+JAm1QHNl5/gRwR5J1abE9XsK35u3l + hpLZvZU3bEoSQXtyXLMb4WxR99g9sBSTfYpdXzCzTgEAqg5dYaQhobRXV8p7SHmPyMCQZqhjmlkz + qgEF0OUBA6gjwcRxMVrpI0tW0MG1gAoydOYBA2wwDKQLcJipiYntd+aOQGn8ExPE3FKCDSbG+0AK + 2zgEb867guYej5I2BlMYejIx9CpFR6osza2cjkdXgVBYmjV8JzokluaQPlaoExHUKXbA4qJYxZK4 + AqfjYmnGbRmjlGKyrEzWX6YGhT+gJXcNV1NkH0zNrmtOg8uj1+LRaKS6JpdLpe8Jne39xjpgmA3+ + WgC4FlPWYrBJ2LdqyWFvJVXvICcSP0p7K7QkCl9xDdj3gZ3R+HXhaLWEuW9uyLXCltnimdQl7guk + Nxkza2anFk5wQyhQjPOOUBbQkefyHT0twPrbY/LgacO4L/FBPKUiAkeSEwbIJJ7E7QpOz9pTUo5S + Ir+xCGZwa7geQ2M3ux76rTmJCXzcSvk9hR03lMYqsnrnjk7Hahqb2axfxZRoiuLg46ol987I3cu0 + 5d6aOW+tnz3XNSWSfKjFQuL5Er5n8SxNYe4F7eDVRPr6wOMIWrmREoXkQ2YsfJTYYTCQU4/OWK9F + uWmzcSCZUp8ozxyU+jlK9rbFbNo74K4Pu6L/KpZBgwGucFDSNfy4662nlBSijIkJO4u7RpdjMgh1 + GKzkjxqjhHqxhKsoLgyWpxLtm6HrMO1KLSAL1BMTI/SulFuhspS5GauZQknb/aAspApl/r/PI+3k + 92NmZuA1udh1JP7IUj2kMhbQjVwkYNmQZm7KpYL2cgk/c8cjXQXtc9mZN80Jy0AicXEwVsnPwymY + Cvlp/LnY02hdh7pmx5b/aVxz15v7PUnU59Z4OOrgW3m6hOd9T+Lt+Sw9+NlU/rpUZOnnBeRSV+Ng + Gd0YtH0DYoA45H6YHoFvyFlKzX0im1yfTf+Hk5/14eifn7zpDYhDDpaSEk9HuY0+htjsHswtOsz9 + D/MMm+dX2C3hut4/A/uJihvkYJEtygjaTZwpgWbqFbYcAuxKYeAh7NIXJb+m+inawsB34o3y6eR4 + qUhUD4q22cgQwpEAReJUErbO/DlJPs4LTIhNn+K9fqJa1Sys7V0i1Ci2rGiOfVWkH08A/iyL0vBg + 96lGuu9yfEfF3dn5+WivOixoB+mTp88maY4Zw0FwvlotvmDwbqRKj3atyqFryR9UD4sZDp7jkeDk + KOzP4XzJ9hg6S/N/zB8EzlGfyd/1iTy7hyEfriWyBfa/rs00F8CV2tbj6C4zJUuFpxqHMG6V1bh4 + 3dUsjc1LHlfLur97dnlxQeerZ/ePq5OPJ/8CAAD//wMAwDPj9GkLAAA= + headers: + CF-RAY: + - CF-RAY-XXX + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Sat, 29 Nov 2025 02:50:45 GMT + Server: + - cloudflare + Set-Cookie: + - SET-COOKIE-XXX + Strict-Transport-Security: + - STS-XXX + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - X-CONTENT-TYPE-XXX + access-control-expose-headers: + - ACCESS-CONTROL-XXX + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - OPENAI-ORG-XXX + openai-processing-ms: + - '5125' + openai-project: + - OPENAI-PROJECT-XXX + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '5227' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-project-tokens: + - '150000000' + x-ratelimit-limit-requests: + - X-RATELIMIT-LIMIT-REQUESTS-XXX + x-ratelimit-limit-tokens: + - X-RATELIMIT-LIMIT-TOKENS-XXX + x-ratelimit-remaining-project-tokens: + - '149999830' + x-ratelimit-remaining-requests: + - X-RATELIMIT-REMAINING-REQUESTS-XXX + x-ratelimit-remaining-tokens: + - X-RATELIMIT-REMAINING-TOKENS-XXX + x-ratelimit-reset-project-tokens: + - 0s + x-ratelimit-reset-requests: + - X-RATELIMIT-RESET-REQUESTS-XXX + x-ratelimit-reset-tokens: + - X-RATELIMIT-RESET-TOKENS-XXX + x-request-id: + - X-REQUEST-ID-XXX + status: + code: 200 + message: OK +version: 1 diff --git a/lib/crewai/tests/cassettes/test_task_without_allow_crewai_trigger_context.yaml b/lib/crewai/tests/cassettes/agents/test_task_without_allow_crewai_trigger_context.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_task_without_allow_crewai_trigger_context.yaml rename to lib/crewai/tests/cassettes/agents/test_task_without_allow_crewai_trigger_context.yaml diff --git a/lib/crewai/tests/cassettes/test_tool_result_as_answer_is_the_final_answer_for_the_agent.yaml b/lib/crewai/tests/cassettes/agents/test_tool_result_as_answer_is_the_final_answer_for_the_agent.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_tool_result_as_answer_is_the_final_answer_for_the_agent.yaml rename to lib/crewai/tests/cassettes/agents/test_tool_result_as_answer_is_the_final_answer_for_the_agent.yaml diff --git a/lib/crewai/tests/cassettes/test_tool_usage_information_is_appended_to_agent.yaml b/lib/crewai/tests/cassettes/agents/test_tool_usage_information_is_appended_to_agent.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_tool_usage_information_is_appended_to_agent.yaml rename to lib/crewai/tests/cassettes/agents/test_tool_usage_information_is_appended_to_agent.yaml diff --git a/lib/crewai/tests/cassettes/TestAgentEvaluator.test_eval_lite_agent.yaml b/lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_eval_lite_agent.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAgentEvaluator.test_eval_lite_agent.yaml rename to lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_eval_lite_agent.yaml diff --git a/lib/crewai/tests/cassettes/TestAgentEvaluator.test_eval_specific_agents_from_crew.yaml b/lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_eval_specific_agents_from_crew.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAgentEvaluator.test_eval_specific_agents_from_crew.yaml rename to lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_eval_specific_agents_from_crew.yaml diff --git a/lib/crewai/tests/cassettes/TestAgentEvaluator.test_evaluate_current_iteration.yaml b/lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_evaluate_current_iteration.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAgentEvaluator.test_evaluate_current_iteration.yaml rename to lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_evaluate_current_iteration.yaml diff --git a/lib/crewai/tests/cassettes/TestAgentEvaluator.test_failed_evaluation.yaml b/lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_failed_evaluation.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAgentEvaluator.test_failed_evaluation.yaml rename to lib/crewai/tests/cassettes/experimental/evaluation/TestAgentEvaluator.test_failed_evaluation.yaml diff --git a/lib/crewai/tests/cassettes/test_docling_source.yaml b/lib/crewai/tests/cassettes/knowledge/test_docling_source.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_docling_source.yaml rename to lib/crewai/tests/cassettes/knowledge/test_docling_source.yaml diff --git a/lib/crewai/tests/cassettes/test_multiple_docling_sources.yaml b/lib/crewai/tests/cassettes/knowledge/test_multiple_docling_sources.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_multiple_docling_sources.yaml rename to lib/crewai/tests/cassettes/knowledge/test_multiple_docling_sources.yaml diff --git a/lib/crewai/tests/cassettes/test_anthropic_stop_sequences_sent_to_api.yaml b/lib/crewai/tests/cassettes/llms/anthropic/test_anthropic_stop_sequences_sent_to_api.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_anthropic_stop_sequences_sent_to_api.yaml rename to lib/crewai/tests/cassettes/llms/anthropic/test_anthropic_stop_sequences_sent_to_api.yaml diff --git a/lib/crewai/tests/cassettes/TestAnthropicHeaderInterceptor.test_header_interceptor_with_real_call.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestAnthropicHeaderInterceptor.test_header_interceptor_with_real_call.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAnthropicHeaderInterceptor.test_header_interceptor_with_real_call.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestAnthropicHeaderInterceptor.test_header_interceptor_with_real_call.yaml diff --git a/lib/crewai/tests/cassettes/TestAnthropicInterceptorIntegration.test_anthropic_call_with_interceptor_tracks_requests.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestAnthropicInterceptorIntegration.test_anthropic_call_with_interceptor_tracks_requests.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAnthropicInterceptorIntegration.test_anthropic_call_with_interceptor_tracks_requests.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestAnthropicInterceptorIntegration.test_anthropic_call_with_interceptor_tracks_requests.yaml diff --git a/lib/crewai/tests/cassettes/TestAnthropicLoggingInterceptor.test_logging_interceptor_tracks_details.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestAnthropicLoggingInterceptor.test_logging_interceptor_tracks_details.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestAnthropicLoggingInterceptor.test_logging_interceptor_tracks_details.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestAnthropicLoggingInterceptor.test_logging_interceptor_tracks_details.yaml diff --git a/lib/crewai/tests/cassettes/TestOpenAIAuthInterceptor.test_auth_interceptor_with_real_call.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestOpenAIAuthInterceptor.test_auth_interceptor_with_real_call.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestOpenAIAuthInterceptor.test_auth_interceptor_with_real_call.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestOpenAIAuthInterceptor.test_auth_interceptor_with_real_call.yaml diff --git a/lib/crewai/tests/cassettes/TestOpenAIInterceptorIntegration.test_openai_call_with_interceptor_tracks_requests.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestOpenAIInterceptorIntegration.test_openai_call_with_interceptor_tracks_requests.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestOpenAIInterceptorIntegration.test_openai_call_with_interceptor_tracks_requests.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestOpenAIInterceptorIntegration.test_openai_call_with_interceptor_tracks_requests.yaml diff --git a/lib/crewai/tests/cassettes/TestOpenAILoggingInterceptor.test_logging_interceptor_tracks_details.yaml b/lib/crewai/tests/cassettes/llms/hooks/TestOpenAILoggingInterceptor.test_logging_interceptor_tracks_details.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestOpenAILoggingInterceptor.test_logging_interceptor_tracks_details.yaml rename to lib/crewai/tests/cassettes/llms/hooks/TestOpenAILoggingInterceptor.test_logging_interceptor_tracks_details.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_completion_call.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_completion_call.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_completion_call.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_completion_call.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_completion_call_returns_usage_metrics.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_completion_call_returns_usage_metrics.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_completion_call_returns_usage_metrics.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_completion_call_returns_usage_metrics.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_is_default_provider_without_explicit_llm_set_on_agent.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_is_default_provider_without_explicit_llm_set_on_agent.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_is_default_provider_without_explicit_llm_set_on_agent.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_is_default_provider_without_explicit_llm_set_on_agent.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_response_format_none.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_none.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_response_format_none.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_none.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_response_format_with_dict.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_with_dict.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_response_format_with_dict.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_with_dict.yaml diff --git a/lib/crewai/tests/cassettes/test_openai_response_format_with_pydantic_model.yaml b/lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_with_pydantic_model.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_openai_response_format_with_pydantic_model.yaml rename to lib/crewai/tests/cassettes/llms/openai/test_openai_response_format_with_pydantic_model.yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_save.yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_save.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_save.yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_save.yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_save_using_crew_without_memory_flag[save].yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_using_crew_without_memory_flag[save].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_save_using_crew_without_memory_flag[save].yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_using_crew_without_memory_flag[save].yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_save_using_crew_without_memory_flag[search].yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_using_crew_without_memory_flag[search].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_save_using_crew_without_memory_flag[search].yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_using_crew_without_memory_flag[search].yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_save_with_memory_flag[save].yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_with_memory_flag[save].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_save_with_memory_flag[save].yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_with_memory_flag[save].yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_save_with_memory_flag[search].yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_with_memory_flag[search].yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_save_with_memory_flag[search].yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_save_with_memory_flag[search].yaml diff --git a/lib/crewai/tests/cassettes/test_crew_external_memory_search.yaml b/lib/crewai/tests/cassettes/memory/test_crew_external_memory_search.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_crew_external_memory_search.yaml rename to lib/crewai/tests/cassettes/memory/test_crew_external_memory_search.yaml diff --git a/lib/crewai/tests/pipeline/cassettes/test_router_with_empty_input.yaml b/lib/crewai/tests/cassettes/pipeline/test_router_with_empty_input.yaml similarity index 100% rename from lib/crewai/tests/pipeline/cassettes/test_router_with_empty_input.yaml rename to lib/crewai/tests/cassettes/pipeline/test_router_with_empty_input.yaml diff --git a/lib/crewai/tests/cassettes/test_telemetry_fails_due_connect_timeout.yaml b/lib/crewai/tests/cassettes/telemetry/test_telemetry_fails_due_connect_timeout.yaml similarity index 95% rename from lib/crewai/tests/cassettes/test_telemetry_fails_due_connect_timeout.yaml rename to lib/crewai/tests/cassettes/telemetry/test_telemetry_fails_due_connect_timeout.yaml index 6caf38bb2..47c9862c6 100644 --- a/lib/crewai/tests/cassettes/test_telemetry_fails_due_connect_timeout.yaml +++ b/lib/crewai/tests/cassettes/telemetry/test_telemetry_fails_due_connect_timeout.yaml @@ -50,9 +50,9 @@ interactions: Date: - Mon, 05 May 2025 18:37:34 GMT Permissions-Policy: - - publickey-credentials-create=(self),publickey-credentials-get=(self),accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),execution-while-not-rendered=(),execution-while-out-of-viewport=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),identity-credentials-get=(),idle-detection=(),local-fonts=(),magnetometer=(),microphone=(),midi=(),otp-credentials=(),payment=(),picture-in-picture=(),screen-wake-lock=(),serial=(),speaker-selection=(),storage-access=(),usb=(),web-share=(),xr-spatial-tracking=() + - PERMISSIONS-POLICY-XXX Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload + - STS-XXX Vary: - Accept-Encoding X-Cache: @@ -60,11 +60,11 @@ interactions: X-Cache-Hits: - 0, 34, 0 X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - deny + - X-FRAME-OPTIONS-XXX X-Permitted-Cross-Domain-Policies: - - none + - X-PERMITTED-XXX X-Served-By: - cache-iad-kjyo7100160-IAD, cache-iad-kjyo7100044-IAD, cache-gru-sbsp2090079-GRU X-Timer: @@ -78,32 +78,19 @@ interactions: access-control-allow-origin: - '*' access-control-expose-headers: - - X-PyPI-Last-Serial + - ACCESS-CONTROL-XXX access-control-max-age: - '86400' cache-control: - max-age=900, public content-security-policy: - - base-uri 'self'; connect-src 'self' https://api.github.com/repos/ https://api.github.com/search/issues - https://gitlab.com/api/ https://analytics.python.org fastly-insights.com *.fastly-insights.com - *.ethicalads.io https://api.pwnedpasswords.com https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/sre/mathmaps/ - https://2p66nmmycsj3.statuspage.io; default-src 'none'; font-src 'self' fonts.gstatic.com; - form-action 'self' https://checkout.stripe.com; frame-ancestors 'none'; frame-src - 'none'; img-src 'self' https://pypi-camo.freetls.fastly.net/ *.fastly-insights.com - *.ethicalads.io ethicalads.blob.core.windows.net; script-src 'self' https://analytics.python.org - *.fastly-insights.com *.ethicalads.io 'sha256-U3hKDidudIaxBDEzwGJApJgPEf2mWk6cfMWghrAa6i0=' - https://cdn.jsdelivr.net/npm/mathjax@3.2.2/ 'sha256-1CldwzdEg2k1wTmf7s5RWVd7NMXI/7nxxjJM2C4DqII=' - 'sha256-0POaN8stWYQxhzjKS+/eOfbbJ/u4YHO5ZagJvLpMypo='; style-src 'self' fonts.googleapis.com - *.ethicalads.io 'sha256-2YHqZokjiizkHi1Zt+6ar0XJ0OeEy/egBnlm+MDMtrM=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' - 'sha256-JLEjeN9e5dGsz5475WyRaoA4eQOdNPxDIeUhclnJDCE=' 'sha256-mQyxHEuwZJqpxCw3SLmc4YOySNKXunyu2Oiz1r3/wAE=' - 'sha256-OCf+kv5Asiwp++8PIevKBYSgnNLNUZvxAp4a7wMLuKA=' 'sha256-h5LOiLhk6wiJrGsG5ItM0KimwzWQH/yAcmoJDJL//bY='; - worker-src *.fastly-insights.com + - CSP-FILTERED content-type: - application/json etag: - - '"ggQSbqfOWm/rak+86bKvfA"' + - ETAG-XXX referrer-policy: - - origin-when-cross-origin + - REFERRER-POLICY-XXX x-pypi-last-serial: - '28851779' status: @@ -137,19 +124,19 @@ interactions: user-agent: - OpenAI/Python 1.75.0 x-stainless-arch: - - arm64 + - X-STAINLESS-ARCH-XXX x-stainless-async: - 'false' x-stainless-lang: - python x-stainless-os: - - MacOS + - X-STAINLESS-OS-XXX x-stainless-package-version: - 1.75.0 x-stainless-raw-response: - 'true' x-stainless-read-timeout: - - '600.0' + - X-STAINLESS-READ-TIMEOUT-XXX x-stainless-retry-count: - '0' x-stainless-runtime: @@ -173,7 +160,7 @@ interactions: \"default\",\n \"system_fingerprint\": \"fp_dbaca60df0\"\n}\n" headers: CF-RAY: - - 93b259955a50a166-GRU + - CF-RAY-XXX Connection: - keep-alive Content-Encoding: @@ -187,35 +174,229 @@ interactions: Transfer-Encoding: - chunked X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX access-control-expose-headers: - - X-Request-ID + - ACCESS-CONTROL-XXX alt-svc: - h3=":443"; ma=86400 cf-cache-status: - DYNAMIC openai-organization: - - crewai-iuxna1 + - OPENAI-ORG-XXX openai-processing-ms: - '333' openai-version: - '2020-10-01' strict-transport-security: - - max-age=31536000; includeSubDomains; preload + - STS-XXX x-ratelimit-limit-requests: - - '30000' + - X-RATELIMIT-LIMIT-REQUESTS-XXX x-ratelimit-limit-tokens: - - '150000000' + - X-RATELIMIT-LIMIT-TOKENS-XXX x-ratelimit-remaining-requests: - - '29999' + - X-RATELIMIT-REMAINING-REQUESTS-XXX x-ratelimit-remaining-tokens: - - '149999826' + - X-RATELIMIT-REMAINING-TOKENS-XXX x-ratelimit-reset-requests: - - 2ms + - X-RATELIMIT-RESET-REQUESTS-XXX x-ratelimit-reset-tokens: - - 0s + - X-RATELIMIT-RESET-TOKENS-XXX x-request-id: - - req_1821906e2f17a712ceb213bd130355ee + - X-REQUEST-ID-XXX http_version: HTTP/1.1 status_code: 200 +- request: + body: '{"trace_id": "1b96b2cd-2b52-4507-b7ac-4ba5452e3614", "execution_type": + "crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null, + "crew_name": "TestCrew", "flow_name": null, "crewai_version": "1.6.0", "privacy_level": + "standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count": + 0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-11-28T22:38:09.909251+00:00"}, + "ephemeral_trace_id": "1b96b2cd-2b52-4507-b7ac-4ba5452e3614"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '492' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.6.0 + X-Crewai-Organization-Id: + - 73c2b193-f579-422c-84c7-76a39a1da77f + X-Crewai-Version: + - 1.6.0 + authorization: + - AUTHORIZATION-XXX + method: POST + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches + response: + body: + string: '{"id":"adab193e-2496-4b41-83df-da4901c39502","ephemeral_trace_id":"1b96b2cd-2b52-4507-b7ac-4ba5452e3614","execution_type":"crew","crew_name":"TestCrew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.6.0","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"TestCrew","flow_name":null,"crewai_version":"1.6.0","privacy_level":"standard"},"created_at":"2025-11-28T22:38:10.222Z","updated_at":"2025-11-28T22:38:10.222Z","access_code":"TRACE-ab608edfd9","user_identifier":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '523' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 28 Nov 2025 22:38:10 GMT + cache-control: + - no-store + content-security-policy: + - CSP-FILTERED + etag: + - ETAG-XXX + expires: + - '0' + permissions-policy: + - PERMISSIONS-POLICY-XXX + pragma: + - no-cache + referrer-policy: + - REFERRER-POLICY-XXX + strict-transport-security: + - STS-XXX + vary: + - Accept + x-content-type-options: + - X-CONTENT-TYPE-XXX + x-frame-options: + - X-FRAME-OPTIONS-XXX + x-permitted-cross-domain-policies: + - X-PERMITTED-XXX + x-request-id: + - X-REQUEST-ID-XXX + x-runtime: + - X-RUNTIME-XXX + x-xss-protection: + - X-XSS-PROTECTION-XXX + status: + code: 201 + message: Created +- request: + body: '{"messages":[{"role":"system","content":"You are agent. You are a helpful + assistant that just says hi\nYour personal goal is: Just say hi\nTo give my + best complete final answer to the task respond using the exact following format:\n\nThought: + I now can give a great answer\nFinal Answer: Your final answer must be the great + and the most complete as possible, it must be outcome described.\n\nI MUST use + these formats, my job depends on it!"},{"role":"user","content":"\nCurrent Task: + Just say hi\n\nThis is the expected criteria for your final answer: hi\nyou + MUST return the actual complete content as the final answer, not a summary.\n\nBegin! + This is VERY important to you, use the tools available and give your best Final + Answer, your job depends on it!\n\nThought:"}],"model":"gpt-4o-mini"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - AUTHORIZATION-XXX + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - X-STAINLESS-ARCH-XXX + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - X-STAINLESS-OS-XXX + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - X-STAINLESS-READ-TIMEOUT-XXX + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.10 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: !!binary | + H4sIAAAAAAAAAwAAAP//jFJNb9swDL37VxA614OdeUnj21BgH9iKHXfYCoORaVubLAkS3XQo8t8H + KWnsdCvQiwHz8T29R/IxAxCqFTUIOSDL0en8Zii/3X5/uP00VviF/WZ/89HpXTVeT6X7Kq4iw+5+ + keQn1htpR6eJlTVHWHpCpqhabtbV2/W22hYJGG1LOtJ6x3ll81EZla+KVZUXm7y8PrEHqyQFUcOP + DADgMX2jT9PSg6ghaaXKSCFgT6I+NwEIb3WsCAxBBUbD4moGpTVMJln/DMbuQaKBXt0TIPTRNqAJ + e/IAP80HZVDD+/Rfw6CWOp66KWDMYiatFwAaYxnjLFKCuxNyOHvWtnfe7sIzquiUUWFoPGGwJvoL + bJ1I6CEDuEuzmS7iCuft6Lhh+5vSc+X6NBsxr2SBrk4gW0a9qG+egAu9piVGpcNiukKiHKidqfMq + cGqVXQDZIvW/bv6nfUyuTP8a+RmQkhxT2zhPrZKXiec2T/FiX2o7TzkZFoH8vZLUsCIfN9FSh5M+ + 3pEIfwLT2HTK9OSdV8dj6lzzrtgW626FKEV2yP4CAAD//wMAQ/5zVVoDAAA= + headers: + CF-RAY: + - CF-RAY-XXX + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Fri, 28 Nov 2025 22:38:11 GMT + Server: + - cloudflare + Set-Cookie: + - SET-COOKIE-XXX + Strict-Transport-Security: + - STS-XXX + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - X-CONTENT-TYPE-XXX + access-control-expose-headers: + - ACCESS-CONTROL-XXX + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - OPENAI-ORG-XXX + openai-processing-ms: + - '520' + openai-project: + - OPENAI-PROJECT-XXX + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '690' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-project-tokens: + - '150000000' + x-ratelimit-limit-requests: + - X-RATELIMIT-LIMIT-REQUESTS-XXX + x-ratelimit-limit-tokens: + - X-RATELIMIT-LIMIT-TOKENS-XXX + x-ratelimit-remaining-project-tokens: + - '149999825' + x-ratelimit-remaining-requests: + - X-RATELIMIT-REMAINING-REQUESTS-XXX + x-ratelimit-remaining-tokens: + - X-RATELIMIT-REMAINING-TOKENS-XXX + x-ratelimit-reset-project-tokens: + - 0s + x-ratelimit-reset-requests: + - X-RATELIMIT-RESET-REQUESTS-XXX + x-ratelimit-reset-tokens: + - X-RATELIMIT-RESET-TOKENS-XXX + x-request-id: + - X-REQUEST-ID-XXX + status: + code: 200 + message: OK version: 1 diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-001].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-001].yaml index 0cb8df6a3..9ee004f59 100644 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-001].yaml +++ b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-001].yaml @@ -1,33 +1,38 @@ interactions: - request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' + body: '{"contents": [{"parts": [{"text": "What is the capital of France?"}], "role": + "user"}], "generationConfig": {}}' headers: - accept: + Accept: - '*/*' - accept-encoding: - - gzip, deflate - connection: + Accept-Encoding: + - gzip, deflate, zstd + Connection: - keep-alive - content-length: - - '131' - content-type: + Content-Length: + - '111' + Content-Type: - application/json - host: - - generativelanguage.googleapis.com user-agent: - - litellm/1.60.2 + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-client: + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-key: + - X-GOOG-API-KEY-XXX method: POST uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-001:generateContent response: body: - string: !!binary | - H4sIAAAAAAAC/62RTU+EMBCG7/0VTY9kIQUT1vXqx0njRokxUQ8jDNAILaFdoyH8dwssbNGrTdo0 - 807nnT7TEUpZCjITGRjU7IK+2Ail3XgOmpIGpbHCHLLBBlpzyp1W59xtisGv4RFLSqQpNMJARVVO - b1qQKVKhqeftoRXa84JXyZy3/XJ/25wcW1XhUK5WGVZzej8nsFxIocsHBK3kkPaY3O/ZosJncauK - plXvQ9M+D3gUxiE/tzs6i+JtyHdkth5N2UFDgXdowFKB5e/Mlqgbk6gPlJfqMFLZTi4Ow5W8O8pG - WQArJYw3f4rqK2spKhetQ93+HSphvkes188Jc/iYVU8zH+Jg/N3hP3nt1l7kOJVpUE/YajFNpMDa - zsiPAu7nFejS5zxkpCc/6so6tIECAAA= + string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\": + [\n {\n \"text\": \"The capital of France is **Paris**.\\n\"\n + \ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\": + \"STOP\",\n \"avgLogprobs\": -0.021610861023267109\n }\n ],\n \"usageMetadata\": + {\n \"promptTokenCount\": 7,\n \"candidatesTokenCount\": 9,\n \"totalTokenCount\": + 16,\n \"promptTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n + \ \"tokenCount\": 7\n }\n ],\n \"candidatesTokensDetails\": + [\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 9\n }\n + \ ]\n },\n \"modelVersion\": \"gemini-2.0-flash-001\",\n \"responseId\": + \"wVIradyDDo_h_uMPlNSt4Qw\"\n}\n" headers: Alt-Svc: - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 @@ -36,11 +41,11 @@ interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Tue, 22 Apr 2025 14:25:05 GMT + - Sat, 29 Nov 2025 20:08:33 GMT Server: - scaffolding on HTTPServer2 Server-Timing: - - gfet4t7; dur=1219 + - gfet4t7; dur=337 Transfer-Encoding: - chunked Vary: @@ -48,9 +53,9 @@ interactions: - X-Origin - Referer X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - SAMEORIGIN + - X-FRAME-OPTIONS-XXX X-XSS-Protection: - '0' status: diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-lite-001].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-lite-001].yaml index 883142b68..648c2773d 100644 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-lite-001].yaml +++ b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-lite-001].yaml @@ -1,33 +1,38 @@ interactions: - request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' + body: '{"contents": [{"parts": [{"text": "What is the capital of France?"}], "role": + "user"}], "generationConfig": {}}' headers: - accept: + Accept: - '*/*' - accept-encoding: - - gzip, deflate - connection: + Accept-Encoding: + - gzip, deflate, zstd + Connection: - keep-alive - content-length: - - '131' - content-type: + Content-Length: + - '111' + Content-Type: - application/json - host: - - generativelanguage.googleapis.com user-agent: - - litellm/1.60.2 + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-client: + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-key: + - X-GOOG-API-KEY-XXX method: POST uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite-001:generateContent response: body: - string: !!binary | - H4sIAAAAAAAC/61RTU+EMBC98yuanhdSiMjq1Y+Txo0SY6J7GGGARmhJ2zUawn+3wMIWvdpDM5n3 - Zt7Mm84jhGYgcp6DQU0vyavNENKN/4BJYVAYC8wpm2xBmRN3ep0TW4rBr6GIphWSDFpuoCayILcK - RIaEa7IDxXXwJqhT1y/xfnNSU7LGoVUjc6xnej8TaMEF19UjgpZioD2lDzu6oPBZ3smyVfJ9GNhn - AQuTMIpjFl/EZyxKwuTcm6VHUXrQUOI9GrCOwLI3tS2a1qTyA8WVPIyOJJOK498K3h5hI+3yKySM - N3+a6msryWvXVsdxuzvU3HyPlt68pNTxx6xmmv3xHBt/T/hPWtu1lne8ynSoZ1SaTxcpsbE38qOA - +UUNuvJtd/QZC6nXez+t5iqFggIAAA== + string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\": + [\n {\n \"text\": \"The capital of France is Paris.\\n\"\n + \ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\": + \"STOP\",\n \"avgLogprobs\": -0.13051052391529083\n }\n ],\n \"usageMetadata\": + {\n \"promptTokenCount\": 7,\n \"candidatesTokenCount\": 8,\n \"totalTokenCount\": + 15,\n \"promptTokensDetails\": [\n {\n \"modality\": \"TEXT\",\n + \ \"tokenCount\": 7\n }\n ],\n \"candidatesTokensDetails\": + [\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 8\n }\n + \ ]\n },\n \"modelVersion\": \"gemini-2.0-flash-lite-001\",\n \"responseId\": + \"xVIradGLA4SajrEPj4CmyQg\"\n}\n" headers: Alt-Svc: - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 @@ -36,11 +41,11 @@ interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Tue, 22 Apr 2025 14:25:06 GMT + - Sat, 29 Nov 2025 20:08:37 GMT Server: - scaffolding on HTTPServer2 Server-Timing: - - gfet4t7; dur=1090 + - gfet4t7; dur=301 Transfer-Encoding: - chunked Vary: @@ -48,9 +53,9 @@ interactions: - X-Origin - Referer X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - SAMEORIGIN + - X-FRAME-OPTIONS-XXX X-XSS-Protection: - '0' status: diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-thinking-exp-01-21].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-thinking-exp-01-21].yaml index 343498108..a1f268629 100644 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-thinking-exp-01-21].yaml +++ b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.0-flash-thinking-exp-01-21].yaml @@ -1,32 +1,36 @@ interactions: - request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' + body: '{"contents": [{"parts": [{"text": "What is the capital of France?"}], "role": + "user"}], "generationConfig": {}}' headers: - accept: + Accept: - '*/*' - accept-encoding: - - gzip, deflate - connection: + Accept-Encoding: + - gzip, deflate, zstd + Connection: - keep-alive - content-length: - - '131' - content-type: + Content-Length: + - '111' + Content-Type: - application/json - host: - - generativelanguage.googleapis.com user-agent: - - litellm/1.60.2 + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-client: + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-key: + - X-GOOG-API-KEY-XXX method: POST uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-thinking-exp-01-21:generateContent response: body: - string: !!binary | - H4sIAAAAAAAC/22QQWuEMBCF7/6KkKNsFt1DKb2221vp0koplD0M66jDxkSSWbCI/71Rq+vS5pCE - eW9meF8XCSFPYHLKgdHLB/EVKkJ04z1o1jAaDsJcCsUGHF+90+lW/2BhbIcmmVUoTtAQgxa2EM8O - zAkFeRHHB3Dk43grV5398j9urvuc1TgMq22Oerb3s0EWZMhXbwjemsH2nr0e5KKSybEN5SSaF4yj - 5cVDiS/IEJLDkk82ztYNZ/aM5tFexuT306wVp39ltiHkjZLebf4M9U9hJek1vhXZkBA08feIbv+Z - yRUFvlk6UxjfY/TLY0L0gc7TxKLEOtBRu22iCg2+UlyROZMpFbaNSlK1S2XURz/Fy1P+CAIAAA== + string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\": + [\n {\n \"text\": \"The capital of France is **Paris**.\"\n + \ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\": + \"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": + 8,\n \"candidatesTokenCount\": 8,\n \"totalTokenCount\": 41,\n \"promptTokensDetails\": + [\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 8\n }\n + \ ],\n \"thoughtsTokenCount\": 25\n },\n \"modelVersion\": \"models/gemini-2.5-flash-preview-05-20\",\n + \ \"responseId\": \"xFIrafO6NLO2jrEPrPTzwAY\"\n}\n" headers: Alt-Svc: - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 @@ -35,11 +39,11 @@ interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Tue, 22 Apr 2025 14:25:04 GMT + - Sat, 29 Nov 2025 20:08:36 GMT Server: - scaffolding on HTTPServer2 Server-Timing: - - gfet4t7; dur=764 + - gfet4t7; dur=509 Transfer-Encoding: - chunked Vary: @@ -47,9 +51,9 @@ interactions: - X-Origin - Referer X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - SAMEORIGIN + - X-FRAME-OPTIONS-XXX X-XSS-Protection: - '0' status: diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-flash-preview-04-17].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-flash-preview-04-17].yaml deleted file mode 100644 index 347fc505b..000000000 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-flash-preview-04-17].yaml +++ /dev/null @@ -1,59 +0,0 @@ -interactions: -- request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '131' - content-type: - - application/json - host: - - generativelanguage.googleapis.com - user-agent: - - litellm/1.60.2 - method: POST - uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-04-17:generateContent - response: - body: - string: !!binary | - H4sIAAAAAAAC/2WQT0+EMBDF7/0UTY9k2ewaDerVPzfjRokxMR4mMEBjaUk76BrCd7fAslvcHppm - 5s2bvl/HOBcZ6FzmQOjELf/wFc678R56RhNq8o255IsNWDppp9MFby8h3A9DIq2QZ9BIAsVNwR8t - 6Ay5dDyKdmCli6K1CCb74/tzddpnjcLBrDY5qlnezwJRSC1d9YLgjB5kr+nzThy7Uue49+UNmxeM - 1qJ1UOITEvjkcMwnGmvqhlLzhfrOtGPy68kr4LRoJzeHPhmfcjmZrM5c3b3fKVXIL0DrI4KS9Duy - e3hPRYCBFtYzBhbQElSZtqzo3we37IBrIviG1skJVYm1hxdfrK/iQoGr4sbit8SfeHMZbxPBevYH - O2bXiSICAAA= - headers: - Alt-Svc: - - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 - Content-Encoding: - - gzip - Content-Type: - - application/json; charset=UTF-8 - Date: - - Tue, 22 Apr 2025 14:25:28 GMT - Server: - - scaffolding on HTTPServer2 - Server-Timing: - - gfet4t7; dur=20971 - Transfer-Encoding: - - chunked - Vary: - - Origin - - X-Origin - - Referer - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - SAMEORIGIN - X-XSS-Protection: - - '0' - status: - code: 200 - message: OK -version: 1 diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-pro-exp-03-25].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-pro-exp-03-25].yaml deleted file mode 100644 index 3b45a5cc6..000000000 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-2.5-pro-exp-03-25].yaml +++ /dev/null @@ -1,59 +0,0 @@ -interactions: -- request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '131' - content-type: - - application/json - host: - - generativelanguage.googleapis.com - user-agent: - - litellm/1.60.2 - method: POST - uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro-exp-03-25:generateContent - response: - body: - string: !!binary | - H4sIAAAAAAAC/12QT2uEMBDF7/kUIUepi2tZaHttu7fSpZVSKD0MOquhmkgygkX87o26cWM9SJj3 - 5s/7DYxzkYMqZAGEVjzwL1fhfJj/k6YVoSIn+JIrtmDo6l2+IXg7C2E/NYmsQp5DKwlqrs/8aEDl - yKXlUXQCI20U7UTQOa7v75vrPqNrnIY1usDa20dvEGeppK3eEKxWk+09ez2JVZWqwN6VE+YXzKNF - Z6HEFyRwyWHNJ1qjm5Yy/YPqUXdz8rtlVsBpI++T5GIg7WL+03xzMNc+ua2yDgkGcF1IqCX9zvSe - PzMRgKDNWR4EC3gJqnRXVrQ98T5lF2ALww80Vi6wSmwcvjjdHWJ3Yox9Gye3cXoQbGR/TedYqx4C - AAA= - headers: - Alt-Svc: - - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 - Content-Encoding: - - gzip - Content-Type: - - application/json; charset=UTF-8 - Date: - - Tue, 22 Apr 2025 14:25:30 GMT - Server: - - scaffolding on HTTPServer2 - Server-Timing: - - gfet4t7; dur=2418 - Transfer-Encoding: - - chunked - Vary: - - Origin - - X-Origin - - Referer - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - SAMEORIGIN - X-XSS-Protection: - - '0' - status: - code: 200 - message: OK -version: 1 diff --git a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-3-pro-preview].yaml b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-3-pro-preview].yaml index eb47f39b7..21534966c 100644 --- a/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-3-pro-preview].yaml +++ b/lib/crewai/tests/cassettes/test_gemini_models[gemini-gemini-3-pro-preview].yaml @@ -1,43 +1,37 @@ interactions: - request: - body: '{"contents":[{"role":"user","parts":[{"text":"What is the capital of France?"}]}],"generationConfig":{"stop_sequences":[]}}' + body: '{"contents": [{"parts": [{"text": "What is the capital of France?"}], "role": + "user"}], "generationConfig": {}}' headers: - accept: + Accept: - '*/*' - accept-encoding: - - gzip, deflate - connection: + Accept-Encoding: + - gzip, deflate, zstd + Connection: - keep-alive - content-length: - - '123' - content-type: + Content-Length: + - '111' + Content-Type: - application/json - host: - - generativelanguage.googleapis.com user-agent: - - litellm/1.78.5 + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-client: + - google-genai-sdk/1.2.0 gl-python/3.12.9 + x-goog-api-key: + - X-GOOG-API-KEY-XXX method: POST uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:generateContent response: body: - string: !!binary | - H4sIAAAAAAAC/21UW4+iSBh9719heGxmBgFvbDIPgKAgNwUV3OxDCSWU3KFApdP/fWl77XF2l6RI - 5ftOnVN1ku+8vQwGhA+yAAUAw5r4Y/BnXxkM3u7/j16eYZjhvvEo9cUCVPgX9vN7e9r3EAyvH4cI - J4IDHxQIg2SQnwZyBTIfDlA9eH21QIXq19cfxLd/HY3yJoywjcIM4KaCHzRSvZbEWpL4YIlRytG8 - a3eoGiukHPHm3jH2FNvMTC1qLlgS05RL42PVyPMdz1uFHpQuytZSBqcHf7PexMHK3mjJQjWKIbM+ - MxFL6cvWMMfQFsOJ3UQk5j1hWmoxK1DrLqncyrpcQ+UY0uZog2oqkTmXiQ2f27ZBpS58MXBTxRbX - qdfsl25Vn5tswrUHeVhVxenW7kaG0cKdt2hjjxPUBYY26BAUvbqqw30AoG0eTMmzdImnIrI51+VY - xeqUl/HKs8ZgfBPF0bbtMDjMzxZSkv3KNuJgwTlYMkw9YEyKMcfkRvUmkiPpBqL486niJEuQKtE7 - XibhpJy1AltrXSrjq+iEucKfK5z43Ci6bTu+VIVuRNecmwRN2gnbqQHH6lQ06eNM5ttpwEjZVOI3 - umesM9qbcxMySprtbDYXaboQdioPMpuEy3U4VZrM6njN0rAk8Fh3/ON+E58FJPDtxD8upIWTbI/D - MrqM7RWj7VWo6kMFUgaj5Dpzsg8bE6GoIc+rJEcnau8qGNnZygGNcRO61nD5sXgyWbUQ+Z4XQhrX - 3C6UyS2OTHAp2cUJVp0eSZqtyTuTy48XjmW0xLJVYRqYYmSZhatQ45ROKPZiXTZTxiq2ceDPIhii - 7tBurqtSL7ylp5NRw5FUzJXsLkiRJs1BIi05Oxit51ToBF2oTGOvYTXjfJptR62SVdTB7W5aaJzq - nb9adAVFIii3gZE5Qz87C+ViVKa3eJ2f4pyiSzasywoHJA2klNL01IIYX6o55V8n3BUc8vKagLIp - d/pRZoatSfor/yx4bAYp/udP4mlc3r/2f/2aIqLKk/vUpHkAkwf8/QEgTihDdbSBoM6zD5jtmNbX - EBIoC+C1Lw9fHgJ3aqKpQQh1iEGfFOArD4iiytMCO3kMMzFv7kkx++R6ypX/beO8D4XfOvSI/vYf - 1nrea6LkOW+eoqh/IkgQvt2zRnKdpzDpBZ5VHza8PLn1yJrfL0gz45d//Pq0cAerGn16FcK0d+87 - +72/Yb9gi+DlrklUsC7yrIZK8IHbeV4/2Sy/LL9r50a3aquVZ2uPeHl/+RvdmjG6dAUAAA== + string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\": + [\n {\n \"text\": \"The capital of France is **Paris**.\",\n + \ \"thoughtSignature\": \"ErYDCrMDAXLI2nxqP91JUT+ukOUiDfsRxJMh6wlKuN3qt7oHYxuFdQN2p6cbogULRi6Ucri5UsdVdc8y3/RaOMRs1gWnh8SGeb81k1PcI+vSZZ3Cl6pyD8Ajn39AZ1YaFFrLan4cpIbliO1Goj8FjW2TF2z4zOXdg52a0hrvi6V4cjfNLCLQazKJ7r4J/P8LOHEQQMdsDte7eD+0p1bPZfNNlTKLeHV9PowZbiGl1UtSVX6Cy7wow6VYMJ4es+PfuOISZe77ksiY8GTkx6fICce9hyIlFNRvU9xwaxwliaK6EXLIcGdXLkfjyGN11+3YwXMxmIQ8g62l0s/GwzmLmjz42aihAHP33YMgmP1L5rY+o0pStwQwavFh9XVKWHrZEJfVCWE615N0dikgWNN/bpICELBPajzlZ5BSR0e9Hm0unbFOwQt54FWPCHfAD1UT+g5pDrqM28mG94BjCmwS0OmsSaI1gl612zk3Q+ZL8UYfNzeiPVFkgTRmbgLXOJqMiC0aBLrLR4cvIiS/Kc5CDttfu6EXMWoXqRGCt1FI7pNjfG9I/QGRVk/KqIauTLauGcEEeGPsB7h5\"\n + \ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\": + \"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": + 8,\n \"candidatesTokenCount\": 8,\n \"totalTokenCount\": 106,\n \"promptTokensDetails\": + [\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 8\n }\n + \ ],\n \"thoughtsTokenCount\": 90\n },\n \"modelVersion\": \"gemini-3-pro-preview\",\n + \ \"responseId\": \"xFIrafrxEOag_uMP_ayUwA0\"\n}\n" headers: Alt-Svc: - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 @@ -46,11 +40,11 @@ interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Wed, 19 Nov 2025 08:56:53 GMT + - Sat, 29 Nov 2025 20:08:36 GMT Server: - scaffolding on HTTPServer2 Server-Timing: - - gfet4t7; dur=2508 + - gfet4t7; dur=2675 Transfer-Encoding: - chunked Vary: @@ -58,9 +52,9 @@ interactions: - X-Origin - Referer X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - SAMEORIGIN + - X-FRAME-OPTIONS-XXX X-XSS-Protection: - '0' status: diff --git a/lib/crewai/tests/cassettes/test_gemma3[gemini-gemma-3-27b-it].yaml b/lib/crewai/tests/cassettes/test_gemma3[gemini-gemma-3-27b-it].yaml index 496a3a055..bcca09ce4 100644 --- a/lib/crewai/tests/cassettes/test_gemma3[gemini-gemma-3-27b-it].yaml +++ b/lib/crewai/tests/cassettes/test_gemma3[gemini-gemma-3-27b-it].yaml @@ -1,7 +1,6 @@ interactions: - request: - body: '{"contents": [{"role": "user", "parts": [{"text": "What is the capital - of France?"}]}], "generationConfig": {"stop_sequences": []}}' + body: '{"contents":[{"role":"user","parts":[{"text":"What is the capital of France?"}]}],"generationConfig":{"stop_sequences":[]}}' headers: accept: - '*/*' @@ -10,25 +9,25 @@ interactions: connection: - keep-alive content-length: - - '131' + - '123' content-type: - application/json host: - generativelanguage.googleapis.com user-agent: - - litellm/1.74.9 + - litellm/1.80.7 method: POST uri: https://generativelanguage.googleapis.com/v1beta/models/gemma-3-27b-it:generateContent response: body: string: !!binary | - H4sIAAAAAAAC/21RwWrbQBC96yuGvRSMFYp7aOktxAnY1MQ0ahtIfJhII3vwalfZGdUpxv/elRQ5 - CkQL2uXNm7f75h0TAJOjK7hAJTHf4SEiAMfu39a8U3IaCwMUwRqDvnH77zg6R4rSS9tksh1BjjUr - WvAl3AR0OQELTCZrDCyTyQU8uke30E8Ce+cPDkofgOO9nIONL6sw7AUs7wk0il1zWZKFzB8oTDvk - h2/+BoJVI9RUU4gtHXwZcigIssC+qncUCwIHsrbdWQVKlB17N4W8YWFHfWfeWG0CXbRvapcZ2Tqd - z5vp2zCCt9Q6rXxBdqCfBoIp2bHsfhKKdy3tLrtdm3OVXUEvEf6cDBd00qYR3NKKFGMseB6+qUP0 - opnfk7vyTRfLt17LqI8j/rAyapJ5lGQ7zm4Ua3SAlvVfl9v1fWZGLvWd8uCy2zfJq99+BL8pCPde - t1RVmH5JZ1+fUtZOzgSS2juhRdEylvgnw+VTMU/T5bPKmos7nV3+Mskp+Q+x/LCbmwIAAA== + H4sIAAAAAAAC/21R70vDMBD93r/iyBehbOIU8ce3oRMmDocWUVQkttf1XJrUXOKUsf/dtLWzgg00 + 4d27l7x76whApFJnlEmHLE7hMSAA6+Zf14x2qF0odFAAK2ndL7f91r1zoDj8rJtEUiCksiInFZgc + LqzUKQIxxPFcWuI43oUn/aSnbodhqc1KQ24sULiXUlDhZaW0SwZFSwQXxCaU56ggMSu0gwa5Mv7D + Isw8oy8HEFoaeGxTyBASS6asCgwFhhUqVe/kGHLJBRk9gNQTk8a2M/XKeYu79ZvqJXq2Ntvz8+B3 + GNYorJ2WJkPV0TcdQeSkiYsblGx0TbtNrudiWyWd4WeA96LugkZaeJYLnKGTIRa5Hb6obPDiErNE + fWZ8E8txqyWcCSP+t9Jr4vMgSaqfXS/W4EAqcl9NbpP7RPRcuj/Knctmf45+/LYjuEPL1HpdYFnK + 4cFw/+h1SK6RExa5MppxmtWMy9GxlZkeTQ/frl/8bH6SHZ29jx9EtIm+AdW7QZKbAgAA headers: Alt-Svc: - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 @@ -37,11 +36,11 @@ interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Wed, 06 Aug 2025 18:55:33 GMT + - Sat, 29 Nov 2025 21:01:27 GMT Server: - scaffolding on HTTPServer2 Server-Timing: - - gfet4t7; dur=1529 + - gfet4t7; dur=1410 Transfer-Encoding: - chunked Vary: @@ -49,9 +48,9 @@ interactions: - X-Origin - Referer X-Content-Type-Options: - - nosniff + - X-CONTENT-TYPE-XXX X-Frame-Options: - - SAMEORIGIN + - X-FRAME-OPTIONS-XXX X-XSS-Protection: - '0' status: diff --git a/lib/crewai/tests/cassettes/test_task_allow_crewai_trigger_context_no_payload.yaml b/lib/crewai/tests/cassettes/test_task_allow_crewai_trigger_context_no_payload.yaml deleted file mode 100644 index 564295b89..000000000 --- a/lib/crewai/tests/cassettes/test_task_allow_crewai_trigger_context_no_payload.yaml +++ /dev/null @@ -1,570 +0,0 @@ -interactions: -- request: - body: '{"messages": [{"role": "system", "content": "You are test role. test backstory\nYour - personal goal is: test goal\nTo give my best complete final answer to the task - respond using the exact following format:\n\nThought: I now can give a great - answer\nFinal Answer: Your final answer must be the great and the most complete - as possible, it must be outcome described.\n\nI MUST use these formats, my job - depends on it!"}, {"role": "user", "content": "\nCurrent Task: Analyze the data\n\nThis - is the expected criteria for your final answer: Analysis report\nyou MUST return - the actual complete content as the final answer, not a summary.\n\nBegin! This - is VERY important to you, use the tools available and give your best Final Answer, - your job depends on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate, zstd - connection: - - keep-alive - content-length: - - '822' - content-type: - - application/json - cookie: - - _cfuvid=wu1mwFBixM_Cn8wLLh.nRacWi8OMVBrEyBNuF_Htz6I-1743463498282-0.0.1.1-604800000 - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.93.0 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.93.0 - x-stainless-raw-response: - - 'true' - x-stainless-read-timeout: - - '600.0' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.11.12 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//fFfbjhtHDn33VxAC5mXQEmY8lh3Mm9e3DHa9NpzZC3YdBFQ31V2Z6mKn - WCVZCfLvC7JKLY2d7IsAdXfxcnh4yPrtCcDCdYtbWLQDpnac/PLV8+vv/n1P7+7yx+f/eeOHXQ7/ - /P6uf0fxYfqwaPQEb36mNh1PrVoeJ0/JcSiv20iYSK1ev1iv1+urmxc39mLkjrwe66e0fMbL0QW3 - fHr19Nny6sXy+rt6emDXkixu4b9PAAB+s1+NM3T0ZXELV83xyUgi2NPidv4IYBHZ65MFijhJGNKi - Ob1sOSQKFvodBN5DiwF6tyNA6DVswCB7igCfw1sX0MNL+38Ln8PncHn5MqA/iBP4RBPHdHlZHl+v - 4C6kyF1uFYbLSz1/PziBaN/BFEkoJAEERSvSQEHM7dEgbyENBB0m/cR7ahN1wDuK9tyjJPglY0wU - V3A/EPSMvpxycjLjBBJDR9GMWzS48QQuiOuHJA24jkJy2wOkSKGTBjB04MKW4wgdtU4ch+WIDy70 - MEVuSYQEthxhm1OOBJIiJuodyapk/3QFrzXsDzuKO0f7Y/o1G6FUknZqaMw+uckTTBhxpERRwIXW - 504dCnp15vocSRposyQeKUJHI/cRp8G10mhYijS0GgdHRzWLno4foYfOSYpukxWCFfyVDrDD6BSM - Ctev1FXPdKuJLOHy8gfz/7b4v7y8hXtO6GtYkXYUMjWAO4rYE6SIQQrIsEOvr3JwSUDYd6ti8dUx - hddnKajllz010FPoKDbguUU1U/KYcmwHFAVkQwPuHMdq7WPN/NWcuZr6SFHLh6HVmkcWgc5ttxQp - PAbpBPTIksyXJ2XWxFP2GI/ISnX37hzQ12eAqteC1fStb8WZs+LVOw5HltyUIrx1QQOQQpIT5vfG - RrX7OQAsq3UXVEyEOtgc4Hp9YUTCSJ2yXPtiirQzZ7U3Gti7NABC4GTEl8k9EHQ5atZ6YmDvOjyA - EAqHVfGmZP3TqgJ6YYgsZFFcXTSAqUBRAhlcP1A8sRX3GCmQiCGcMPakX44YHyhpHC2OE7q+YvP/ - aHIKb4puxHg4edmgELQcVOZMPxTZnesyegHsqYOn6+XNswawbTkHc6xt/OzqwnTDmF2pZr2snt7S - iJ5mJwKc04StRf/o+eYAT68uwAXTKPQeZCIrbbX0IXgXCGTgabLWzrGnrtG+T65VtvkD4Mihhxw3 - GE62G8tEGyL0gCCD2yZIvMfYKbF7p4FT6LGnkUKaMfyqOQ7nfTFD+UaVNXJwrQCNpDEBStFYwq5U - p56vVLq5uqi1AxkwqphW9EwXar7f80hFhTB2FOZWAsE9oEqr4eFCVZPNAdYXpmXjVDNVLtNei3Oi - ykltLRx1rYIv1NfcrVmMy1rOo9NvCS8grg9u61oMyR9gwyzKykeSa2VdX8yQPhKAuzpDZizf488c - j9WjkKx2JBO1Dr0/GE4Dwd85psFkRuH5F0mCV4ySGiXHXltIo9QeknQUWVVGiqaJNcdPOaIHVDlo - YD847W1r8cbOoKcOckg4TdTBxDrmHfrGRkcwFBUQDso6WF/M0jJX5CvaBaLO+mXuX84pErZDFbRn - K/hELY8jhc4CPdO093P9fij1O6iyvQmD6eSRw6cq03bLMUn1pU8OnEP/1eQrhJwoWrGdDrC60JSx - EXlkC2QF/0hOPwBhrQWM1DmEyWPShijj/NRBj4pmiqILRKXPWQDzFgE9poFUhreRx3lx+aYT33Kb - TdM/EeoaIZWQf9gpwDqydDaopmoTxqPK0479riwkZGOtJVM8jTWrZKzgVQ0bNjl0vi4vpRc4gq2k - 1k5nnVLBUieRtDeSkiFRJEl/3AFvvkwYpM6/17Qjz5MqVO3RVhEOsNWsC9kgzqyFiQKlSmm4Cy45 - THQaBZDQea6jTfcB/yhbK/CR1bqv+awvooVPlVibWHigy2ZLp+HCqv5Zx6Qtau85uMQKteZxp9v7 - aCTSkW17gC1uVY7qEqh+HogmnY/tg6E/YOhNMOeRVLeVEm7VzLJnruBvVAesbV9J+ZyYvRnGnHjE - RGcMm/u/NKEbyR++XlBrJ66t/K3PMi/fH8pQaow+p+247L4qpmdiCH3kvTbWDK9Gb0oDkX7JznrB - jdZEiXT463daxr8cADsu+q2e4lEQVFhn5S5RcOwxuF9LTnrvOFbtOEDPVqnmJE8zuILJybYsJmVP - 1FVaUVNgbOEfWffzs8xmNbUMK1zPV/BysmH95ahYRot0XI47La3KkfM+z9JZBkWx9KeredWh8/X8 - cJ7Yqvh7T2ngjj33B8hShfb87qMYWRVq6Sz08xuVUNxpsHql2nKuEozeOGXmsq7WegPsjtVpc7SV - 2GPopMWpqlB29kW93hSACyvbwdGOSvVy0vndwUZ7W/uh3ILdjmR1fsWMtM2Ces0N2fuzFxh0JzXj - ern9sb75fb7Oeu6nyBv56uhi64KT4ado0qVXV0k8Lezt708AfrRrc350E16UteKnxA9k7q7X62Jv - UW7r/wMAAP//jFi9DoIhDNx5DGaHbxDi9zSEtEVr/CHANzj47gYwFiOD85XLHSXAtcoQ1Jr9G23/ - GgEOy7qbEDqkelvlIXlr8HAilKUS0/2GfB8ANdj+lTPj7tb5dvyHXgAAioXQxUTI8G1ZyhKdW9ie - l322uQnW9dgxkCtMqbYCKfjt0mcMOj9yoasLXF/umLgPGkJ0xi4+WDJm1eqpXgAAAP//AwCGkEKG - dhEAAA== - headers: - CF-RAY: - - 97144c27cad01abc-GRU - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Mon, 18 Aug 2025 20:53:07 GMT - Server: - - cloudflare - Set-Cookie: - - __cf_bm=gumItH7ZRtD4GgE2NL8KJd5b0g0ukzMySphsV0ru1LE-1755550387-1.0.1.1-iwCn2q9kDpJVTaZu1Swtv1kYCiM39NBeviV1R9awG4XHHMKnojkbu6T7jh_Z3UxfNbluVCsI6RMKj.2rEPp1IcH63gHUQdJfHF71CdCZ3Uc; - path=/; expires=Mon, 18-Aug-25 21:23:07 GMT; domain=.api.openai.com; HttpOnly; - Secure; SameSite=None - - _cfuvid=d7iU8FXLKWOoICtn52jYIApBpBp20kALP6yQjOvXHvQ-1755550387858-0.0.1.1-604800000; - path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - cf-cache-status: - - DYNAMIC - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '14516' - openai-project: - - proj_xitITlrFeen7zjNSzML82h9x - openai-version: - - '2020-10-01' - x-envoy-upstream-service-time: - - '14596' - x-ratelimit-limit-project-tokens: - - '150000000' - x-ratelimit-limit-requests: - - '30000' - x-ratelimit-limit-tokens: - - '150000000' - x-ratelimit-remaining-project-tokens: - - '149999830' - x-ratelimit-remaining-requests: - - '29999' - x-ratelimit-remaining-tokens: - - '149999827' - x-ratelimit-reset-project-tokens: - - 0s - x-ratelimit-reset-requests: - - 2ms - x-ratelimit-reset-tokens: - - 0s - x-request-id: - - req_3c1af5f5590a4b76b33f3fbf7d3a3288 - status: - code: 200 - message: OK -- request: - body: '{"trace_id": "ebe3e255-33a6-4b40-8c73-acc782e2cb2e", "execution_type": - "crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null, - "crew_name": "crew", "flow_name": null, "crewai_version": "0.193.2", "privacy_level": - "standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count": - 0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-09-23T20:22:48.851064+00:00"}, - "ephemeral_trace_id": "ebe3e255-33a6-4b40-8c73-acc782e2cb2e"}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '490' - Content-Type: - - application/json - User-Agent: - - CrewAI-CLI/0.193.2 - X-Crewai-Version: - - 0.193.2 - method: POST - uri: http://localhost:3000/crewai_plus/api/v1/tracing/ephemeral/batches - response: - body: - string: '{"id":"dcf37266-a30f-4a61-9084-293f108becab","ephemeral_trace_id":"ebe3e255-33a6-4b40-8c73-acc782e2cb2e","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"0.193.2","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"0.193.2","privacy_level":"standard"},"created_at":"2025-09-23T20:22:48.921Z","updated_at":"2025-09-23T20:22:48.921Z","access_code":"TRACE-20af0f540e","user_identifier":null}' - headers: - Content-Length: - - '519' - cache-control: - - max-age=0, private, must-revalidate - content-security-policy: - - 'default-src ''self'' *.crewai.com crewai.com; script-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts https://www.gstatic.com - https://run.pstmn.io https://share.descript.com/; style-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts; img-src ''self'' - data: *.crewai.com crewai.com https://zeus.tools.crewai.com https://dashboard.tools.crewai.com - https://cdn.jsdelivr.net; font-src ''self'' data: *.crewai.com crewai.com; - connect-src ''self'' *.crewai.com crewai.com https://zeus.tools.crewai.com - https://connect.useparagon.com/ https://zeus.useparagon.com/* https://*.useparagon.com/* - https://run.pstmn.io https://connect.tools.crewai.com/ ws://localhost:3036 - wss://localhost:3036; frame-src ''self'' *.crewai.com crewai.com https://connect.useparagon.com/ - https://zeus.tools.crewai.com https://zeus.useparagon.com/* https://connect.tools.crewai.com/ - https://www.youtube.com https://share.descript.com' - content-type: - - application/json; charset=utf-8 - etag: - - W/"e3802608dd0afa467b9006ae28a09ac0" - permissions-policy: - - camera=(), microphone=(self), geolocation=() - referrer-policy: - - strict-origin-when-cross-origin - server-timing: - - cache_read.active_support;dur=0.08, sql.active_record;dur=17.40, cache_generate.active_support;dur=5.00, - cache_write.active_support;dur=0.23, cache_read_multi.active_support;dur=0.23, - start_processing.action_controller;dur=0.00, start_transaction.active_record;dur=0.00, - transaction.active_record;dur=10.40, process_action.action_controller;dur=15.72 - vary: - - Accept - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-permitted-cross-domain-policies: - - none - x-request-id: - - 86297c99-3a4e-4797-8ce9-79442128fefd - x-runtime: - - '0.072605' - x-xss-protection: - - 1; mode=block - status: - code: 201 - message: Created -- request: - body: '{"events": [{"event_id": "fcb0a361-b236-47a2-8ae5-613d404a433a", "timestamp": - "2025-09-23T20:22:48.928654+00:00", "type": "crew_kickoff_started", "event_data": - {"timestamp": "2025-09-23T20:22:48.850336+00:00", "type": "crew_kickoff_started", - "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, - "task_id": null, "task_name": null, "agent_id": null, "agent_role": null, "crew_name": - "crew", "crew": null, "inputs": {"other_input": "other data"}}}, {"event_id": - "0850c159-2cf7-40d7-af41-dbafc4ec361d", "timestamp": "2025-09-23T20:22:48.930041+00:00", - "type": "task_started", "event_data": {"task_description": "Analyze the data", - "expected_output": "Analysis report", "task_name": "Analyze the data", "context": - "", "agent_role": "test role", "task_id": "7ef853e5-b583-450e-85f4-14f773feab58"}}, - {"event_id": "c06bbca6-f2d9-4f66-a696-f0c201bb3587", "timestamp": "2025-09-23T20:22:48.930693+00:00", - "type": "agent_execution_started", "event_data": {"agent_role": "test role", - "agent_goal": "test goal", "agent_backstory": "test backstory"}}, {"event_id": - "a2f3bd4a-f298-4aec-90c7-fce24533c211", "timestamp": "2025-09-23T20:22:48.930847+00:00", - "type": "llm_call_started", "event_data": {"timestamp": "2025-09-23T20:22:48.930805+00:00", - "type": "llm_call_started", "source_fingerprint": null, "source_type": null, - "fingerprint_metadata": null, "task_id": "7ef853e5-b583-450e-85f4-14f773feab58", - "task_name": "Analyze the data", "agent_id": null, "agent_role": null, "from_task": - null, "from_agent": null, "model": "gpt-4o-mini", "messages": [{"role": "system", - "content": "You are test role. test backstory\nYour personal goal is: test goal\nTo - give my best complete final answer to the task respond using the exact following - format:\n\nThought: I now can give a great answer\nFinal Answer: Your final - answer must be the great and the most complete as possible, it must be outcome - described.\n\nI MUST use these formats, my job depends on it!"}, {"role": "user", - "content": "\nCurrent Task: Analyze the data\n\nThis is the expected criteria - for your final answer: Analysis report\nyou MUST return the actual complete - content as the final answer, not a summary.\n\nBegin! This is VERY important - to you, use the tools available and give your best Final Answer, your job depends - on it!\n\nThought:"}], "tools": null, "callbacks": [""], "available_functions": null}}, {"event_id": "37cccb0f-facb-4b5b-a28d-31820381e77c", - "timestamp": "2025-09-23T20:22:49.029070+00:00", "type": "llm_call_completed", - "event_data": {"timestamp": "2025-09-23T20:22:49.028732+00:00", "type": "llm_call_completed", - "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, - "task_id": "7ef853e5-b583-450e-85f4-14f773feab58", "task_name": "Analyze the - data", "agent_id": null, "agent_role": null, "from_task": null, "from_agent": - null, "messages": [{"role": "system", "content": "You are test role. test backstory\nYour - personal goal is: test goal\nTo give my best complete final answer to the task - respond using the exact following format:\n\nThought: I now can give a great - answer\nFinal Answer: Your final answer must be the great and the most complete - as possible, it must be outcome described.\n\nI MUST use these formats, my job - depends on it!"}, {"role": "user", "content": "\nCurrent Task: Analyze the data\n\nThis - is the expected criteria for your final answer: Analysis report\nyou MUST return - the actual complete content as the final answer, not a summary.\n\nBegin! This - is VERY important to you, use the tools available and give your best Final Answer, - your job depends on it!\n\nThought:"}], "response": "I now can give a great - answer \nFinal Answer: \n\n**Analysis Report**\n\n**1. Introduction** \nThis - report presents a comprehensive analysis of the data collected over the last - quarter. The goal of this analysis is to derive actionable insights, identify - trends, and inform decision-making processes for future strategies.\n\n**2. - Data Overview** \nThe data set comprises multiple parameters including sales - figures, customer demographics, product categories, and geographical distribution. - Key variables analyzed include:\n\n- **Sales Figures**: Total sales revenue, - average transaction value, units sold.\n- **Customer Demographics**: Age, gender, - location, and purchasing behavior.\n- **Product Categories**: Performance across - different categories, including most and least popular products.\n- **Geographical - Distribution**: Sales performance across various regions.\n\n**3. Key Findings** \n- - **Sales Trends**: \n - Sales increased by 15% compared to the previous quarter, - with a notable spike during the holiday season.\n - The average transaction - value also rose by 10%, attributed to higher customer awareness and targeted - marketing campaigns.\n\n- **Customer Demographics**:\n - The primary customer - base consists of individuals aged 25-34, accounting for 40% of total purchases.\n - - Female customers outpaced male customers by 20% in overall spending.\n - Online - shopping surged, particularly among urban customers, indicating a shift towards - digital engagement.\n\n- **Product Category Performance**:\n - Electronics - emerged as the leading category with a 30% market share in total sales.\n - - Home and garden products saw a decline in sales by 5%, prompting a review of - marketing strategies within this segment.\n - Seasonal products during the - holidays significantly boosted sales figures by 25%.\n\n- **Geographical Insights**:\n - - Major urban centers, especially in the Northeast and West Coast, showed the - highest revenue generation.\n - Rural areas, while stable, revealed untapped - potential, demonstrating only a 5% increase in sales, indicating a need for - targeted outreach.\n\n**4. Recommendations** \n- **Marketing Strategy**: Enhance - digital marketing efforts targeting younger demographics with personalized content - and promotions. Utilize social media platforms for engagement, especially considering - the demographic insights gathered from the data.\n\n- **Product Focus**: Reassess - the home and garden product offerings to cater to the evolving preferences of - consumers. Consider bundling products or creating seasonal promotions to reignite - interest.\n\n- **Geographical Expansion**: Develop a strategic plan focusing - on rural area penetration. Initiate campaigns tailored to local preferences - and potential influencers to enhance brand presence.\n\n- **Continuous Data - Monitoring**: Implement a regular data review process to keep track of changing - customer behaviors and market trends. Leverage analytics tools to automate insights - generation for timely decision-making.\n\n**5. Conclusion** \nOverall, the - analysis identifies significant growth potential and areas requiring immediate - attention. By adopting the recommended strategies, the organization can enhance - overall performance, increase customer satisfaction, and ultimately drive more - significant revenue growth.\n\n**6. Appendix** \n- Data tables and charts illustrating - sales growth, customer demographics, and product category performance. \n- - Methodology used for data collection and analysis.\n\nThis report serves as - a foundational tool for understanding the current landscape and guiding future - actions to achieve the outlined business objectives.", "call_type": "", "model": "gpt-4o-mini"}}, {"event_id": "d25a6a5f-f75f-42c4-b3be-fe540479d514", - "timestamp": "2025-09-23T20:22:49.029404+00:00", "type": "agent_execution_completed", - "event_data": {"agent_role": "test role", "agent_goal": "test goal", "agent_backstory": - "test backstory"}}, {"event_id": "bd4ec3c9-b8e9-45da-bf46-d15de6e7d0a7", "timestamp": - "2025-09-23T20:22:49.029547+00:00", "type": "task_completed", "event_data": - {"task_description": "Analyze the data", "task_name": "Analyze the data", "task_id": - "7ef853e5-b583-450e-85f4-14f773feab58", "output_raw": "**Analysis Report**\n\n**1. - Introduction** \nThis report presents a comprehensive analysis of the data - collected over the last quarter. The goal of this analysis is to derive actionable - insights, identify trends, and inform decision-making processes for future strategies.\n\n**2. - Data Overview** \nThe data set comprises multiple parameters including sales - figures, customer demographics, product categories, and geographical distribution. - Key variables analyzed include:\n\n- **Sales Figures**: Total sales revenue, - average transaction value, units sold.\n- **Customer Demographics**: Age, gender, - location, and purchasing behavior.\n- **Product Categories**: Performance across - different categories, including most and least popular products.\n- **Geographical - Distribution**: Sales performance across various regions.\n\n**3. Key Findings** \n- - **Sales Trends**: \n - Sales increased by 15% compared to the previous quarter, - with a notable spike during the holiday season.\n - The average transaction - value also rose by 10%, attributed to higher customer awareness and targeted - marketing campaigns.\n\n- **Customer Demographics**:\n - The primary customer - base consists of individuals aged 25-34, accounting for 40% of total purchases.\n - - Female customers outpaced male customers by 20% in overall spending.\n - Online - shopping surged, particularly among urban customers, indicating a shift towards - digital engagement.\n\n- **Product Category Performance**:\n - Electronics - emerged as the leading category with a 30% market share in total sales.\n - - Home and garden products saw a decline in sales by 5%, prompting a review of - marketing strategies within this segment.\n - Seasonal products during the - holidays significantly boosted sales figures by 25%.\n\n- **Geographical Insights**:\n - - Major urban centers, especially in the Northeast and West Coast, showed the - highest revenue generation.\n - Rural areas, while stable, revealed untapped - potential, demonstrating only a 5% increase in sales, indicating a need for - targeted outreach.\n\n**4. Recommendations** \n- **Marketing Strategy**: Enhance - digital marketing efforts targeting younger demographics with personalized content - and promotions. Utilize social media platforms for engagement, especially considering - the demographic insights gathered from the data.\n\n- **Product Focus**: Reassess - the home and garden product offerings to cater to the evolving preferences of - consumers. Consider bundling products or creating seasonal promotions to reignite - interest.\n\n- **Geographical Expansion**: Develop a strategic plan focusing - on rural area penetration. Initiate campaigns tailored to local preferences - and potential influencers to enhance brand presence.\n\n- **Continuous Data - Monitoring**: Implement a regular data review process to keep track of changing - customer behaviors and market trends. Leverage analytics tools to automate insights - generation for timely decision-making.\n\n**5. Conclusion** \nOverall, the - analysis identifies significant growth potential and areas requiring immediate - attention. By adopting the recommended strategies, the organization can enhance - overall performance, increase customer satisfaction, and ultimately drive more - significant revenue growth.\n\n**6. Appendix** \n- Data tables and charts illustrating - sales growth, customer demographics, and product category performance. \n- - Methodology used for data collection and analysis.\n\nThis report serves as - a foundational tool for understanding the current landscape and guiding future - actions to achieve the outlined business objectives.", "output_format": "OutputFormat.RAW", - "agent_role": "test role"}}, {"event_id": "af918c94-ee6a-4699-9519-d01f6314cb87", - "timestamp": "2025-09-23T20:22:49.030535+00:00", "type": "crew_kickoff_completed", - "event_data": {"timestamp": "2025-09-23T20:22:49.030516+00:00", "type": "crew_kickoff_completed", - "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, - "task_id": null, "task_name": null, "agent_id": null, "agent_role": null, "crew_name": - "crew", "crew": null, "output": {"description": "Analyze the data", "name": - "Analyze the data", "expected_output": "Analysis report", "summary": "Analyze - the data...", "raw": "**Analysis Report**\n\n**1. Introduction** \nThis report - presents a comprehensive analysis of the data collected over the last quarter. - The goal of this analysis is to derive actionable insights, identify trends, - and inform decision-making processes for future strategies.\n\n**2. Data Overview** \nThe - data set comprises multiple parameters including sales figures, customer demographics, - product categories, and geographical distribution. Key variables analyzed include:\n\n- - **Sales Figures**: Total sales revenue, average transaction value, units sold.\n- - **Customer Demographics**: Age, gender, location, and purchasing behavior.\n- - **Product Categories**: Performance across different categories, including most - and least popular products.\n- **Geographical Distribution**: Sales performance - across various regions.\n\n**3. Key Findings** \n- **Sales Trends**: \n - - Sales increased by 15% compared to the previous quarter, with a notable spike - during the holiday season.\n - The average transaction value also rose by 10%, - attributed to higher customer awareness and targeted marketing campaigns.\n\n- - **Customer Demographics**:\n - The primary customer base consists of individuals - aged 25-34, accounting for 40% of total purchases.\n - Female customers outpaced - male customers by 20% in overall spending.\n - Online shopping surged, particularly - among urban customers, indicating a shift towards digital engagement.\n\n- **Product - Category Performance**:\n - Electronics emerged as the leading category with - a 30% market share in total sales.\n - Home and garden products saw a decline - in sales by 5%, prompting a review of marketing strategies within this segment.\n - - Seasonal products during the holidays significantly boosted sales figures by - 25%.\n\n- **Geographical Insights**:\n - Major urban centers, especially in - the Northeast and West Coast, showed the highest revenue generation.\n - Rural - areas, while stable, revealed untapped potential, demonstrating only a 5% increase - in sales, indicating a need for targeted outreach.\n\n**4. Recommendations** \n- - **Marketing Strategy**: Enhance digital marketing efforts targeting younger - demographics with personalized content and promotions. Utilize social media - platforms for engagement, especially considering the demographic insights gathered - from the data.\n\n- **Product Focus**: Reassess the home and garden product - offerings to cater to the evolving preferences of consumers. Consider bundling - products or creating seasonal promotions to reignite interest.\n\n- **Geographical - Expansion**: Develop a strategic plan focusing on rural area penetration. Initiate - campaigns tailored to local preferences and potential influencers to enhance - brand presence.\n\n- **Continuous Data Monitoring**: Implement a regular data - review process to keep track of changing customer behaviors and market trends. - Leverage analytics tools to automate insights generation for timely decision-making.\n\n**5. - Conclusion** \nOverall, the analysis identifies significant growth potential - and areas requiring immediate attention. By adopting the recommended strategies, - the organization can enhance overall performance, increase customer satisfaction, - and ultimately drive more significant revenue growth.\n\n**6. Appendix** \n- - Data tables and charts illustrating sales growth, customer demographics, and - product category performance. \n- Methodology used for data collection and - analysis.\n\nThis report serves as a foundational tool for understanding the - current landscape and guiding future actions to achieve the outlined business - objectives.", "pydantic": null, "json_dict": null, "agent": "test role", "output_format": - "raw"}, "total_tokens": 809}}], "batch_metadata": {"events_count": 8, "batch_sequence": - 1, "is_final_batch": false}}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '16042' - Content-Type: - - application/json - User-Agent: - - CrewAI-CLI/0.193.2 - X-Crewai-Version: - - 0.193.2 - method: POST - uri: http://localhost:3000/crewai_plus/api/v1/tracing/ephemeral/batches/ebe3e255-33a6-4b40-8c73-acc782e2cb2e/events - response: - body: - string: '{"events_created":8,"ephemeral_trace_batch_id":"dcf37266-a30f-4a61-9084-293f108becab"}' - headers: - Content-Length: - - '86' - cache-control: - - max-age=0, private, must-revalidate - content-security-policy: - - 'default-src ''self'' *.crewai.com crewai.com; script-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts https://www.gstatic.com - https://run.pstmn.io https://share.descript.com/; style-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts; img-src ''self'' - data: *.crewai.com crewai.com https://zeus.tools.crewai.com https://dashboard.tools.crewai.com - https://cdn.jsdelivr.net; font-src ''self'' data: *.crewai.com crewai.com; - connect-src ''self'' *.crewai.com crewai.com https://zeus.tools.crewai.com - https://connect.useparagon.com/ https://zeus.useparagon.com/* https://*.useparagon.com/* - https://run.pstmn.io https://connect.tools.crewai.com/ ws://localhost:3036 - wss://localhost:3036; frame-src ''self'' *.crewai.com crewai.com https://connect.useparagon.com/ - https://zeus.tools.crewai.com https://zeus.useparagon.com/* https://connect.tools.crewai.com/ - https://www.youtube.com https://share.descript.com' - content-type: - - application/json; charset=utf-8 - etag: - - W/"5365b7d51712464f7429104b4339a428" - permissions-policy: - - camera=(), microphone=(self), geolocation=() - referrer-policy: - - strict-origin-when-cross-origin - server-timing: - - cache_read.active_support;dur=0.06, sql.active_record;dur=34.08, cache_generate.active_support;dur=2.20, - cache_write.active_support;dur=0.16, cache_read_multi.active_support;dur=0.10, - start_processing.action_controller;dur=0.00, instantiation.active_record;dur=0.06, - start_transaction.active_record;dur=0.00, transaction.active_record;dur=48.40, - process_action.action_controller;dur=55.37 - vary: - - Accept - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-permitted-cross-domain-policies: - - none - x-request-id: - - dd950cf1-62f1-4126-b8b0-9e4629b5f5b6 - x-runtime: - - '0.100871' - x-xss-protection: - - 1; mode=block - status: - code: 200 - message: OK -- request: - body: '{"status": "completed", "duration_ms": 291, "final_event_count": 8}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '67' - Content-Type: - - application/json - User-Agent: - - CrewAI-CLI/0.193.2 - X-Crewai-Version: - - 0.193.2 - method: PATCH - uri: http://localhost:3000/crewai_plus/api/v1/tracing/ephemeral/batches/ebe3e255-33a6-4b40-8c73-acc782e2cb2e/finalize - response: - body: - string: '{"id":"dcf37266-a30f-4a61-9084-293f108becab","ephemeral_trace_id":"ebe3e255-33a6-4b40-8c73-acc782e2cb2e","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"completed","duration_ms":291,"crewai_version":"0.193.2","total_events":8,"execution_context":{"crew_name":"crew","flow_name":null,"privacy_level":"standard","crewai_version":"0.193.2","crew_fingerprint":null},"created_at":"2025-09-23T20:22:48.921Z","updated_at":"2025-09-23T20:22:49.192Z","access_code":"TRACE-20af0f540e","user_identifier":null}' - headers: - Content-Length: - - '520' - cache-control: - - max-age=0, private, must-revalidate - content-security-policy: - - 'default-src ''self'' *.crewai.com crewai.com; script-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts https://www.gstatic.com - https://run.pstmn.io https://share.descript.com/; style-src ''self'' ''unsafe-inline'' - *.crewai.com crewai.com https://cdn.jsdelivr.net/npm/apexcharts; img-src ''self'' - data: *.crewai.com crewai.com https://zeus.tools.crewai.com https://dashboard.tools.crewai.com - https://cdn.jsdelivr.net; font-src ''self'' data: *.crewai.com crewai.com; - connect-src ''self'' *.crewai.com crewai.com https://zeus.tools.crewai.com - https://connect.useparagon.com/ https://zeus.useparagon.com/* https://*.useparagon.com/* - https://run.pstmn.io https://connect.tools.crewai.com/ ws://localhost:3036 - wss://localhost:3036; frame-src ''self'' *.crewai.com crewai.com https://connect.useparagon.com/ - https://zeus.tools.crewai.com https://zeus.useparagon.com/* https://connect.tools.crewai.com/ - https://www.youtube.com https://share.descript.com' - content-type: - - application/json; charset=utf-8 - etag: - - W/"c260c7a5c5e94132d69ede0da4a3cc45" - permissions-policy: - - camera=(), microphone=(self), geolocation=() - referrer-policy: - - strict-origin-when-cross-origin - server-timing: - - cache_read.active_support;dur=0.07, sql.active_record;dur=10.48, cache_generate.active_support;dur=2.79, - cache_write.active_support;dur=0.14, cache_read_multi.active_support;dur=0.10, - start_processing.action_controller;dur=0.00, instantiation.active_record;dur=0.04, - unpermitted_parameters.action_controller;dur=0.00, start_transaction.active_record;dur=0.00, - transaction.active_record;dur=4.50, process_action.action_controller;dur=10.46 - vary: - - Accept - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-permitted-cross-domain-policies: - - none - x-request-id: - - b38e7096-bfc4-46ea-ab8a-cecd09f0444b - x-runtime: - - '0.048311' - x-xss-protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question.yaml diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question_with_coworker_as_array.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question_with_coworker_as_array.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question_with_coworker_as_array.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question_with_coworker_as_array.yaml diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question_with_wrong_co_worker_variable.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question_with_wrong_co_worker_variable.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_ask_question_with_wrong_co_worker_variable.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_ask_question_with_wrong_co_worker_variable.yaml diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work.yaml diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work_with_wrong_co_worker_variable.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work_with_wrong_co_worker_variable.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work_with_wrong_co_worker_variable.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work_with_wrong_co_worker_variable.yaml diff --git a/lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work_withwith_coworker_as_array.yaml b/lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work_withwith_coworker_as_array.yaml similarity index 100% rename from lib/crewai/tests/tools/agent_tools/cassettes/test_delegate_work_withwith_coworker_as_array.yaml rename to lib/crewai/tests/cassettes/tools/agent_tools/test_delegate_work_withwith_coworker_as_array.yaml diff --git a/lib/crewai/tests/cassettes/test_async_tool_using_decorator_within_flow.yaml b/lib/crewai/tests/cassettes/tools/test_async_tool_using_decorator_within_flow.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_async_tool_using_decorator_within_flow.yaml rename to lib/crewai/tests/cassettes/tools/test_async_tool_using_decorator_within_flow.yaml diff --git a/lib/crewai/tests/cassettes/test_async_tool_using_decorator_within_isolated_crew.yaml b/lib/crewai/tests/cassettes/tools/test_async_tool_using_decorator_within_isolated_crew.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_async_tool_using_decorator_within_isolated_crew.yaml rename to lib/crewai/tests/cassettes/tools/test_async_tool_using_decorator_within_isolated_crew.yaml diff --git a/lib/crewai/tests/cassettes/test_async_tool_using_within_isolated_crew.yaml b/lib/crewai/tests/cassettes/tools/test_async_tool_using_within_isolated_crew.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_async_tool_using_within_isolated_crew.yaml rename to lib/crewai/tests/cassettes/tools/test_async_tool_using_within_isolated_crew.yaml diff --git a/lib/crewai/tests/cassettes/test_async_tool_within_flow.yaml b/lib/crewai/tests/cassettes/tools/test_async_tool_within_flow.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_async_tool_within_flow.yaml rename to lib/crewai/tests/cassettes/tools/test_async_tool_within_flow.yaml diff --git a/lib/crewai/tests/cassettes/test_max_usage_count_is_respected.yaml b/lib/crewai/tests/cassettes/tools/test_max_usage_count_is_respected.yaml similarity index 100% rename from lib/crewai/tests/cassettes/test_max_usage_count_is_respected.yaml rename to lib/crewai/tests/cassettes/tools/test_max_usage_count_is_respected.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_env.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_env.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_env.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_env.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_tracing_false.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_tracing_false.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_tracing_false.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_no_http_calls_when_disabled_via_tracing_false.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceEnableDisable.test_trace_calls_when_enabled_via_env.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_trace_calls_when_enabled_via_env.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceEnableDisable.test_trace_calls_when_enabled_via_env.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_trace_calls_when_enabled_via_env.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceEnableDisable.test_trace_calls_when_enabled_via_tracing_true.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_trace_calls_when_enabled_via_tracing_true.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceEnableDisable.test_trace_calls_when_enabled_via_tracing_true.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceEnableDisable.test_trace_calls_when_enabled_via_tracing_true.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_batch_manager_finalizes_batch_clears_buffer.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_batch_manager_finalizes_batch_clears_buffer.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_batch_manager_finalizes_batch_clears_buffer.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_batch_manager_finalizes_batch_clears_buffer.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_events_collection_batch_manager.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_events_collection_batch_manager.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_events_collection_batch_manager.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_events_collection_batch_manager.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_collection_user_accepts.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_collection_user_accepts.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_collection_user_accepts.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_collection_user_accepts.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_collection_with_timeout.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_collection_with_timeout.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_collection_with_timeout.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_collection_with_timeout.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_consolidation_logic.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_consolidation_logic.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_first_time_user_trace_consolidation_logic.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_first_time_user_trace_consolidation_logic.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_batch_marked_as_failed_on_finalize_error.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_batch_marked_as_failed_on_finalize_error.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_batch_marked_as_failed_on_finalize_error.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_batch_marked_as_failed_on_finalize_error.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_collects_crew_events.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_collects_crew_events.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_collects_crew_events.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_collects_crew_events.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_disabled_when_env_false.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_disabled_when_env_false.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_disabled_when_env_false.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_disabled_when_env_false.yaml diff --git a/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml new file mode 100644 index 000000000..13c4fcc25 --- /dev/null +++ b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_ephemeral_batch.yaml @@ -0,0 +1,399 @@ +interactions: +- request: + body: '{"trace_id": "8356baf7-abbe-446a-9668-a63d501765d0", "execution_type": + "crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null, + "crew_name": "crew", "flow_name": null, "crewai_version": "1.6.0", "privacy_level": + "standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count": + 0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-11-29T03:12:24.775214+00:00"}, + "ephemeral_trace_id": "8356baf7-abbe-446a-9668-a63d501765d0"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '488' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.6.0 + X-Crewai-Version: + - 1.6.0 + authorization: + - AUTHORIZATION-XXX + method: POST + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches + response: + body: + string: '{"id":"f8dfcffd-d0ef-4ca2-afad-9eff37f8c9d9","ephemeral_trace_id":"8356baf7-abbe-446a-9668-a63d501765d0","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.6.0","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"1.6.0","privacy_level":"standard"},"created_at":"2025-11-29T03:12:25.077Z","updated_at":"2025-11-29T03:12:25.077Z","access_code":"TRACE-4fc2c6b913","user_identifier":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '515' + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 29 Nov 2025 03:12:25 GMT + cache-control: + - no-store + content-security-policy: + - CSP-FILTERED + etag: + - ETAG-XXX + expires: + - '0' + permissions-policy: + - PERMISSIONS-POLICY-XXX + pragma: + - no-cache + referrer-policy: + - REFERRER-POLICY-XXX + strict-transport-security: + - STS-XXX + vary: + - Accept + x-content-type-options: + - X-CONTENT-TYPE-XXX + x-frame-options: + - X-FRAME-OPTIONS-XXX + x-permitted-cross-domain-policies: + - X-PERMITTED-XXX + x-request-id: + - X-REQUEST-ID-XXX + x-runtime: + - X-RUNTIME-XXX + x-xss-protection: + - X-XSS-PROTECTION-XXX + status: + code: 201 + message: Created +- request: + body: '{"messages":[{"role":"system","content":"You are Test Agent. Test backstory\nYour + personal goal is: Test goal\nTo give my best complete final answer to the task + respond using the exact following format:\n\nThought: I now can give a great + answer\nFinal Answer: Your final answer must be the great and the most complete + as possible, it must be outcome described.\n\nI MUST use these formats, my job + depends on it!"},{"role":"user","content":"\nCurrent Task: Say hello to the + world\n\nThis is the expected criteria for your final answer: hello world\nyou + MUST return the actual complete content as the final answer, not a summary.\n\nBegin! + This is VERY important to you, use the tools available and give your best Final + Answer, your job depends on it!\n\nThought:"}],"model":"gpt-4o-mini"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - AUTHORIZATION-XXX + connection: + - keep-alive + content-length: + - '787' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - X-STAINLESS-ARCH-XXX + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - X-STAINLESS-OS-XXX + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - X-STAINLESS-READ-TIMEOUT-XXX + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.10 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: !!binary | + H4sIAAAAAAAAAwAAAP//jFLLbtswELzrKxY8W4UdW3aiW5CiQIAeivbURyBsyJXEluIyJGU3CPzv + BSXHkvsAehGgnZ3hzO6+ZABCK1GCkC1G2TmT37VFbfD926f7p+eP6rPxfv9l/aHfUvcJjVgkBj9+ + JxlfWW8kd85Q1GxHWHrCSEl1tdtu1tfFzaYYgI4VmURrXMw3nHfa6vxqebXJl7t8dX1it6wlBVHC + 1wwA4GX4Jp9W0U9RwnLxWukoBGxIlOcmAOHZpIrAEHSIaKNYTKBkG8kO1u/B8gEkWmj0ngChSbYB + bTiQB/hm32mLBm6H/xJaMobhwN6ouaCnug+YQtnemBmA1nLENJQhysMJOZ7NG26c58fwG1XU2urQ + Vp4wsE1GQ2QnBvSYATwMQ+ovcgvnuXOxivyDhudWxW7UE9NuZuj6BEaOaGb13Wm0l3qVoojahNmY + hUTZkpqo006wV5pnQDZL/aebv2mPybVt/kd+AqQkF0lVzpPS8jLx1OYpne6/2s5THgyLQH6vJVVR + k0+bUFRjb8aDEuE5ROqqWtuGvPN6vKraVcV2ifWWiuJGZMfsFwAAAP//AwAmYCjLYwMAAA== + headers: + CF-RAY: + - CF-RAY-XXX + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Sat, 29 Nov 2025 03:12:26 GMT + Server: + - cloudflare + Set-Cookie: + - SET-COOKIE-XXX + Strict-Transport-Security: + - STS-XXX + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - X-CONTENT-TYPE-XXX + access-control-expose-headers: + - ACCESS-CONTROL-XXX + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - OPENAI-ORG-XXX + openai-processing-ms: + - '443' + openai-project: + - OPENAI-PROJECT-XXX + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '655' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-project-tokens: + - '150000000' + x-ratelimit-limit-requests: + - X-RATELIMIT-LIMIT-REQUESTS-XXX + x-ratelimit-limit-tokens: + - X-RATELIMIT-LIMIT-TOKENS-XXX + x-ratelimit-remaining-project-tokens: + - '149999827' + x-ratelimit-remaining-requests: + - X-RATELIMIT-REMAINING-REQUESTS-XXX + x-ratelimit-remaining-tokens: + - X-RATELIMIT-REMAINING-TOKENS-XXX + x-ratelimit-reset-project-tokens: + - 0s + x-ratelimit-reset-requests: + - X-RATELIMIT-RESET-REQUESTS-XXX + x-ratelimit-reset-tokens: + - X-RATELIMIT-RESET-TOKENS-XXX + x-request-id: + - X-REQUEST-ID-XXX + status: + code: 200 + message: OK +- request: + body: '{"events": [{"event_id": "e73c85bb-b00f-46be-b07f-cfbd398e24d9", "timestamp": + "2025-11-29T03:12:24.774266+00:00", "type": "crew_kickoff_started", "event_data": + {"timestamp": "2025-11-29T03:12:24.774266+00:00", "type": "crew_kickoff_started", + "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, + "task_id": null, "task_name": null, "agent_id": null, "agent_role": null, "crew_name": + "crew", "crew": null, "inputs": null}}, {"event_id": "8d1cdbd9-17a6-499b-a0f7-66daad11da4f", + "timestamp": "2025-11-29T03:12:24.776776+00:00", "type": "agent_execution_started", + "event_data": {"agent_role": "Test Agent", "agent_goal": "Test goal", "agent_backstory": + "Test backstory"}}, {"event_id": "8d7964bd-5e3a-4089-a792-8c63b5b3df8a", "timestamp": + "2025-11-29T03:12:24.776973+00:00", "type": "llm_call_started", "event_data": + {"timestamp": "2025-11-29T03:12:24.776973+00:00", "type": "llm_call_started", + "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, + "task_id": "2b5b469c-61f1-4439-bb50-1bb59a539305", "task_name": "Say hello to + the world", "agent_id": "589d6b85-044a-4df7-b0b9-e4aafeb642a9", "agent_role": + "Test Agent", "from_task": null, "from_agent": null, "model": "gpt-4o-mini", + "messages": [{"role": "system", "content": "You are Test Agent. Test backstory\nYour + personal goal is: Test goal\nTo give my best complete final answer to the task + respond using the exact following format:\n\nThought: I now can give a great + answer\nFinal Answer: Your final answer must be the great and the most complete + as possible, it must be outcome described.\n\nI MUST use these formats, my job + depends on it!"}, {"role": "user", "content": "\nCurrent Task: Say hello to + the world\n\nThis is the expected criteria for your final answer: hello world\nyou + MUST return the actual complete content as the final answer, not a summary.\n\nBegin! + This is VERY important to you, use the tools available and give your best Final + Answer, your job depends on it!\n\nThought:"}], "tools": null, "callbacks": + [""], + "available_functions": null}}, {"event_id": "3d4ec456-f3e3-4dc5-93a1-ac42a5baa307", + "timestamp": "2025-11-29T03:12:26.197379+00:00", "type": "llm_call_completed", + "event_data": {"timestamp": "2025-11-29T03:12:26.197379+00:00", "type": "llm_call_completed", + "source_fingerprint": null, "source_type": null, "fingerprint_metadata": null, + "task_id": "2b5b469c-61f1-4439-bb50-1bb59a539305", "task_name": "Say hello to + the world", "agent_id": "589d6b85-044a-4df7-b0b9-e4aafeb642a9", "agent_role": + "Test Agent", "from_task": null, "from_agent": null, "messages": [{"role": "system", + "content": "You are Test Agent. Test backstory\nYour personal goal is: Test + goal\nTo give my best complete final answer to the task respond using the exact + following format:\n\nThought: I now can give a great answer\nFinal Answer: Your + final answer must be the great and the most complete as possible, it must be + outcome described.\n\nI MUST use these formats, my job depends on it!"}, {"role": + "user", "content": "\nCurrent Task: Say hello to the world\n\nThis is the expected + criteria for your final answer: hello world\nyou MUST return the actual complete + content as the final answer, not a summary.\n\nBegin! This is VERY important + to you, use the tools available and give your best Final Answer, your job depends + on it!\n\nThought:"}], "response": "I now can give a great answer \nFinal Answer: + hello world", "call_type": "", "model": + "gpt-4o-mini"}}, {"event_id": "b3621b9b-6034-4ed7-8ea8-435a6359e733", "timestamp": + "2025-11-29T03:12:26.197544+00:00", "type": "agent_execution_completed", "event_data": + {"agent_role": "Test Agent", "agent_goal": "Test goal", "agent_backstory": "Test + backstory"}}, {"event_id": "d0802db5-cb55-4913-ad79-deed426b286b", "timestamp": + "2025-11-29T03:12:26.197674+00:00", "type": "task_completed", "event_data": + {"task_description": "Say hello to the world", "task_name": "Say hello to the + world", "task_id": "2b5b469c-61f1-4439-bb50-1bb59a539305", "output_raw": "hello + world", "output_format": "OutputFormat.RAW", "agent_role": "Test Agent"}}, {"event_id": + "5dd064ff-f773-4fca-b3d9-624dde505177", "timestamp": "2025-11-29T03:12:26.199670+00:00", + "type": "crew_kickoff_completed", "event_data": {"timestamp": "2025-11-29T03:12:26.199670+00:00", + "type": "crew_kickoff_completed", "source_fingerprint": null, "source_type": + null, "fingerprint_metadata": null, "task_id": null, "task_name": null, "agent_id": + null, "agent_role": null, "crew_name": "crew", "crew": null, "output": {"description": + "Say hello to the world", "name": "Say hello to the world", "expected_output": + "hello world", "summary": "Say hello to the world...", "raw": "hello world", + "pydantic": null, "json_dict": null, "agent": "Test Agent", "output_format": + "raw", "messages": [{"role": "''system''", "content": "''You are Test Agent. + Test backstory\\nYour personal goal is: Test goal\\nTo give my best complete + final answer to the task respond using the exact following format:\\n\\nThought: + I now can give a great answer\\nFinal Answer: Your final answer must be the + great and the most complete as possible, it must be outcome described.\\n\\nI + MUST use these formats, my job depends on it!''"}, {"role": "''user''", "content": + "''\\nCurrent Task: Say hello to the world\\n\\nThis is the expected criteria + for your final answer: hello world\\nyou MUST return the actual complete content + as the final answer, not a summary.\\n\\nBegin! This is VERY important to you, + use the tools available and give your best Final Answer, your job depends on + it!\\n\\nThought:''"}, {"role": "''assistant''", "content": "''I now can give + a great answer \\nFinal Answer: hello world''"}]}, "total_tokens": 170}}], + "batch_metadata": {"events_count": 7, "batch_sequence": 1, "is_final_batch": + false}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '5901' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.6.0 + X-Crewai-Version: + - 1.6.0 + authorization: + - AUTHORIZATION-XXX + method: POST + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches/8356baf7-abbe-446a-9668-a63d501765d0/events + response: + body: + string: '{"events_created":7,"ephemeral_trace_batch_id":"f8dfcffd-d0ef-4ca2-afad-9eff37f8c9d9"}' + headers: + Connection: + - keep-alive + Content-Length: + - '86' + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 29 Nov 2025 03:12:26 GMT + cache-control: + - no-store + content-security-policy: + - CSP-FILTERED + etag: + - ETAG-XXX + expires: + - '0' + permissions-policy: + - PERMISSIONS-POLICY-XXX + pragma: + - no-cache + referrer-policy: + - REFERRER-POLICY-XXX + strict-transport-security: + - STS-XXX + vary: + - Accept + x-content-type-options: + - X-CONTENT-TYPE-XXX + x-frame-options: + - X-FRAME-OPTIONS-XXX + x-permitted-cross-domain-policies: + - X-PERMITTED-XXX + x-request-id: + - X-REQUEST-ID-XXX + x-runtime: + - X-RUNTIME-XXX + x-xss-protection: + - X-XSS-PROTECTION-XXX + status: + code: 200 + message: OK +- request: + body: '{"status": "completed", "duration_ms": 1752, "final_event_count": 7}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '68' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.6.0 + X-Crewai-Version: + - 1.6.0 + authorization: + - AUTHORIZATION-XXX + method: PATCH + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches/8356baf7-abbe-446a-9668-a63d501765d0/finalize + response: + body: + string: '{"id":"f8dfcffd-d0ef-4ca2-afad-9eff37f8c9d9","ephemeral_trace_id":"8356baf7-abbe-446a-9668-a63d501765d0","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"completed","duration_ms":1752,"crewai_version":"1.6.0","total_events":7,"execution_context":{"crew_name":"crew","flow_name":null,"privacy_level":"standard","crewai_version":"1.6.0","crew_fingerprint":null},"created_at":"2025-11-29T03:12:25.077Z","updated_at":"2025-11-29T03:12:26.848Z","access_code":"TRACE-4fc2c6b913","user_identifier":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '517' + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 29 Nov 2025 03:12:26 GMT + cache-control: + - no-store + content-security-policy: + - CSP-FILTERED + etag: + - ETAG-XXX + expires: + - '0' + permissions-policy: + - PERMISSIONS-POLICY-XXX + pragma: + - no-cache + referrer-policy: + - REFERRER-POLICY-XXX + strict-transport-security: + - STS-XXX + vary: + - Accept + x-content-type-options: + - X-CONTENT-TYPE-XXX + x-frame-options: + - X-FRAME-OPTIONS-XXX + x-permitted-cross-domain-policies: + - X-PERMITTED-XXX + x-request-id: + - X-REQUEST-ID-XXX + x-runtime: + - X-RUNTIME-XXX + x-xss-protection: + - X-XSS-PROTECTION-XXX + status: + code: 200 + message: OK +version: 1 diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_setup_correctly_with_tracing_flag.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_setup_correctly_with_tracing_flag.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_setup_correctly_with_tracing_flag.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_setup_correctly_with_tracing_flag.yaml diff --git a/lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_with_authenticated_user.yaml b/lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_with_authenticated_user.yaml similarity index 100% rename from lib/crewai/tests/cassettes/TestTraceListenerSetup.test_trace_listener_with_authenticated_user.yaml rename to lib/crewai/tests/cassettes/tracing/TestTraceListenerSetup.test_trace_listener_with_authenticated_user.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_agent_emits_execution_started_and_completed_events.yaml b/lib/crewai/tests/cassettes/utilities/test_agent_emits_execution_started_and_completed_events.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_agent_emits_execution_started_and_completed_events.yaml rename to lib/crewai/tests/cassettes/utilities/test_agent_emits_execution_started_and_completed_events.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_convert_with_instructions.yaml b/lib/crewai/tests/cassettes/utilities/test_convert_with_instructions.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_convert_with_instructions.yaml rename to lib/crewai/tests/cassettes/utilities/test_convert_with_instructions.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_converter_with_llama3_1_model.yaml b/lib/crewai/tests/cassettes/utilities/test_converter_with_llama3_1_model.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_converter_with_llama3_1_model.yaml rename to lib/crewai/tests/cassettes/utilities/test_converter_with_llama3_1_model.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_converter_with_llama3_2_model.yaml b/lib/crewai/tests/cassettes/utilities/test_converter_with_llama3_2_model.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_converter_with_llama3_2_model.yaml rename to lib/crewai/tests/cassettes/utilities/test_converter_with_llama3_2_model.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_converter_with_nested_model.yaml b/lib/crewai/tests/cassettes/utilities/test_converter_with_nested_model.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_converter_with_nested_model.yaml rename to lib/crewai/tests/cassettes/utilities/test_converter_with_nested_model.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_end_kickoff_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_end_kickoff_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_end_kickoff_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_end_kickoff_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_end_task_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_end_task_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_end_task_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_end_task_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_kickoff_events.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_kickoff_events.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_kickoff_events.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_kickoff_events.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_start_kickoff_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_start_kickoff_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_start_kickoff_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_start_kickoff_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_start_task_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_start_task_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_start_task_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_start_task_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_task_failed_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_task_failed_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_task_failed_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_task_failed_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_crew_emits_test_kickoff_type_event.yaml b/lib/crewai/tests/cassettes/utilities/test_crew_emits_test_kickoff_type_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_crew_emits_test_kickoff_type_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_crew_emits_test_kickoff_type_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_emits_call_failed_event.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_emits_call_failed_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_emits_call_failed_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_emits_call_failed_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_emits_call_started_event.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_emits_call_started_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_emits_call_started_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_emits_call_started_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_emits_event_with_lite_agent.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_emits_event_with_lite_agent.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_emits_event_with_lite_agent.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_emits_event_with_lite_agent.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_emits_event_with_task_and_agent_info.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_emits_event_with_task_and_agent_info.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_emits_event_with_task_and_agent_info.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_emits_event_with_task_and_agent_info.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_emits_stream_chunk_events.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_emits_stream_chunk_events.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_emits_stream_chunk_events.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_emits_stream_chunk_events.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_llm_no_stream_chunks_when_streaming_disabled.yaml b/lib/crewai/tests/cassettes/utilities/test_llm_no_stream_chunks_when_streaming_disabled.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_llm_no_stream_chunks_when_streaming_disabled.yaml rename to lib/crewai/tests/cassettes/utilities/test_llm_no_stream_chunks_when_streaming_disabled.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_multiple_handlers_for_same_event.yaml b/lib/crewai/tests/cassettes/utilities/test_multiple_handlers_for_same_event.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_multiple_handlers_for_same_event.yaml rename to lib/crewai/tests/cassettes/utilities/test_multiple_handlers_for_same_event.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_register_handler_adds_new_handler.yaml b/lib/crewai/tests/cassettes/utilities/test_register_handler_adds_new_handler.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_register_handler_adds_new_handler.yaml rename to lib/crewai/tests/cassettes/utilities/test_register_handler_adds_new_handler.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_stream_llm_emits_event_with_task_and_agent_info.yaml b/lib/crewai/tests/cassettes/utilities/test_stream_llm_emits_event_with_task_and_agent_info.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_stream_llm_emits_event_with_task_and_agent_info.yaml rename to lib/crewai/tests/cassettes/utilities/test_stream_llm_emits_event_with_task_and_agent_info.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_task_emits_failed_event_on_execution_error.yaml b/lib/crewai/tests/cassettes/utilities/test_task_emits_failed_event_on_execution_error.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_task_emits_failed_event_on_execution_error.yaml rename to lib/crewai/tests/cassettes/utilities/test_task_emits_failed_event_on_execution_error.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_tools_emits_error_events.yaml b/lib/crewai/tests/cassettes/utilities/test_tools_emits_error_events.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_tools_emits_error_events.yaml rename to lib/crewai/tests/cassettes/utilities/test_tools_emits_error_events.yaml diff --git a/lib/crewai/tests/utilities/cassettes/test_tools_emits_finished_events.yaml b/lib/crewai/tests/cassettes/utilities/test_tools_emits_finished_events.yaml similarity index 100% rename from lib/crewai/tests/utilities/cassettes/test_tools_emits_finished_events.yaml rename to lib/crewai/tests/cassettes/utilities/test_tools_emits_finished_events.yaml diff --git a/lib/crewai/tests/conftest.py b/lib/crewai/tests/conftest.py deleted file mode 100644 index 18498358f..000000000 --- a/lib/crewai/tests/conftest.py +++ /dev/null @@ -1,222 +0,0 @@ -# conftest.py -import os -import tempfile -from pathlib import Path -from unittest.mock import Mock, patch - -import pytest -from dotenv import load_dotenv - -load_result = load_dotenv(override=True) - - -@pytest.fixture(autouse=True) -def setup_test_environment(): - """Set up test environment with a temporary directory for SQLite storage.""" - with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as temp_dir: - # Create the directory with proper permissions - storage_dir = Path(temp_dir) / "crewai_test_storage" - storage_dir.mkdir(parents=True, exist_ok=True) - - # Validate that the directory was created successfully - if not storage_dir.exists() or not storage_dir.is_dir(): - raise RuntimeError( - f"Failed to create test storage directory: {storage_dir}" - ) - - # Verify directory permissions - try: - # Try to create a test file to verify write permissions - test_file = storage_dir / ".permissions_test" - test_file.touch() - test_file.unlink() - except (OSError, IOError) as e: - raise RuntimeError( - f"Test storage directory {storage_dir} is not writable: {e}" - ) from e - - os.environ["CREWAI_STORAGE_DIR"] = str(storage_dir) - os.environ["CREWAI_TESTING"] = "true" - yield - - os.environ.pop("CREWAI_TESTING", None) - # Cleanup is handled automatically when tempfile context exits - - -def pytest_configure(config): - config.addinivalue_line( - "markers", "telemetry: mark test as a telemetry test (don't mock telemetry)" - ) - - -@pytest.fixture(autouse=True) -def auto_mock_telemetry(request): - if request.node.get_closest_marker("telemetry"): - telemetry_env = { - key: value - for key, value in os.environ.items() - if key not in ["CREWAI_DISABLE_TELEMETRY", "OTEL_SDK_DISABLED"] - } - with patch.dict(os.environ, telemetry_env, clear=True): - yield - return - - if "telemetry" in str(request.fspath): - telemetry_env = { - key: value - for key, value in os.environ.items() - if key not in ["CREWAI_DISABLE_TELEMETRY", "OTEL_SDK_DISABLED"] - } - with patch.dict(os.environ, telemetry_env, clear=True): - yield - return - - with patch.dict( - os.environ, {"CREWAI_DISABLE_TELEMETRY": "true", "OTEL_SDK_DISABLED": "true"} - ): - with patch("crewai.telemetry.Telemetry") as mock_telemetry_class: - mock_instance = create_mock_telemetry_instance() - mock_telemetry_class.return_value = mock_instance - - # Create mock for TraceBatchManager - mock_trace_manager = Mock() - mock_trace_manager.add_trace = Mock() - mock_trace_manager.send_batch = Mock() - mock_trace_manager.stop = Mock() - - # Create mock for BatchSpanProcessor to prevent OpenTelemetry background threads - mock_batch_processor = Mock() - mock_batch_processor.shutdown = Mock() - mock_batch_processor.force_flush = Mock() - - with ( - patch( - "crewai.events.event_listener.Telemetry", - mock_telemetry_class, - ), - patch("crewai.tools.tool_usage.Telemetry", mock_telemetry_class), - patch("crewai.cli.command.Telemetry", mock_telemetry_class), - patch("crewai.cli.create_flow.Telemetry", mock_telemetry_class), - patch( - "crewai.events.listeners.tracing.trace_batch_manager.TraceBatchManager", - return_value=mock_trace_manager, - ), - patch( - "crewai.events.listeners.tracing.trace_listener.TraceBatchManager", - return_value=mock_trace_manager, - ), - patch( - "crewai.events.listeners.tracing.first_time_trace_handler.TraceBatchManager", - return_value=mock_trace_manager, - ), - patch( - "opentelemetry.sdk.trace.export.BatchSpanProcessor", - return_value=mock_batch_processor, - ), - ): - yield mock_instance - - -def create_mock_telemetry_instance(): - mock_instance = Mock() - - mock_instance.ready = False - mock_instance.trace_set = False - mock_instance._initialized = True - - mock_instance._is_telemetry_disabled.return_value = True - mock_instance._should_execute_telemetry.return_value = False - - telemetry_methods = [ - "set_tracer", - "crew_creation", - "task_started", - "task_ended", - "tool_usage", - "tool_repeated_usage", - "tool_usage_error", - "crew_execution_span", - "end_crew", - "flow_creation_span", - "flow_execution_span", - "individual_test_result_span", - "test_execution_span", - "deploy_signup_error_span", - "start_deployment_span", - "create_crew_deployment_span", - "get_crew_logs_span", - "remove_crew_span", - "flow_plotting_span", - "_add_attribute", - "_safe_telemetry_operation", - ] - - for method in telemetry_methods: - setattr(mock_instance, method, Mock(return_value=None)) - - mock_instance.task_started.return_value = None - - return mock_instance - - -@pytest.fixture -def mock_opentelemetry_components(): - with ( - patch("opentelemetry.trace.get_tracer") as mock_get_tracer, - patch("opentelemetry.trace.set_tracer_provider") as mock_set_provider, - patch("opentelemetry.baggage.set_baggage") as mock_set_baggage, - patch("opentelemetry.baggage.get_baggage") as mock_get_baggage, - patch("opentelemetry.context.attach") as mock_attach, - patch("opentelemetry.context.detach") as mock_detach, - ): - mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_span.return_value = mock_span - mock_get_tracer.return_value = mock_tracer - - yield { - "get_tracer": mock_get_tracer, - "set_tracer_provider": mock_set_provider, - "tracer": mock_tracer, - "span": mock_span, - "set_baggage": mock_set_baggage, - "get_baggage": mock_get_baggage, - "attach": mock_attach, - "detach": mock_detach, - } - - -@pytest.fixture(autouse=True) -def clear_event_bus_handlers(setup_test_environment): - """Clear event bus handlers after each test for isolation. - - Handlers registered during the test are allowed to run, then cleaned up - after the test completes. - - Depends on setup_test_environment to ensure cleanup happens in correct order. - """ - from crewai.events.event_bus import crewai_event_bus - from crewai.experimental.evaluation.evaluation_listener import ( - EvaluationTraceCallback, - ) - - yield - - # Shutdown event bus without waiting to avoid hanging on blocked threads - crewai_event_bus.shutdown(wait=False) - crewai_event_bus._initialize() - - callback = EvaluationTraceCallback() - callback.traces.clear() - callback.current_agent_id = None - callback.current_task_id = None - - -@pytest.fixture(scope="module") -def vcr_config(request) -> dict: - import os - return { - "cassette_library_dir": os.path.join(os.path.dirname(__file__), "cassettes"), - "record_mode": "new_episodes", - "filter_headers": [("authorization", "AUTHORIZATION-XXX")], - } diff --git a/lib/crewai/tests/experimental/evaluation/test_agent_evaluator.py b/lib/crewai/tests/experimental/evaluation/test_agent_evaluator.py index f95372aca..fd9e2c1df 100644 --- a/lib/crewai/tests/experimental/evaluation/test_agent_evaluator.py +++ b/lib/crewai/tests/experimental/evaluation/test_agent_evaluator.py @@ -54,7 +54,7 @@ class TestAgentEvaluator: agent_evaluator.set_iteration(3) assert agent_evaluator._execution_state.iteration == 3 - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_evaluate_current_iteration(self, mock_crew): from crewai.events.types.task_events import TaskCompletedEvent @@ -62,23 +62,22 @@ class TestAgentEvaluator: agents=mock_crew.agents, evaluators=[GoalAlignmentEvaluator()] ) - task_completed_condition = threading.Condition() - task_completed = False + evaluation_condition = threading.Condition() + evaluation_completed = False - @crewai_event_bus.on(TaskCompletedEvent) - async def on_task_completed(source, event): - # TaskCompletedEvent fires AFTER evaluation results are stored - nonlocal task_completed - with task_completed_condition: - task_completed = True - task_completed_condition.notify() + @crewai_event_bus.on(AgentEvaluationCompletedEvent) + async def on_evaluation_completed(source, event): + nonlocal evaluation_completed + with evaluation_condition: + evaluation_completed = True + evaluation_condition.notify() mock_crew.kickoff() - with task_completed_condition: - assert task_completed_condition.wait_for( - lambda: task_completed, timeout=5 - ), "Timeout waiting for task completion" + with evaluation_condition: + assert evaluation_condition.wait_for( + lambda: evaluation_completed, timeout=5 + ), "Timeout waiting for evaluation completion" results = agent_evaluator.get_evaluation_results() @@ -126,7 +125,7 @@ class TestAgentEvaluator: ): assert isinstance(evaluator, expected_type) - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_eval_specific_agents_from_crew(self, mock_crew): agent = Agent( role="Test Agent Eval", @@ -207,28 +206,31 @@ class TestAgentEvaluator: assert goal_alignment.raw_response is not None assert '"score": 5' in goal_alignment.raw_response - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_failed_evaluation(self, mock_crew): (agent,) = mock_crew.agents (task,) = mock_crew.tasks - events = {} - started_event = threading.Event() - failed_event = threading.Event() + events: dict[str, AgentEvaluationStartedEvent | AgentEvaluationCompletedEvent | AgentEvaluationFailedEvent] = {} + condition = threading.Condition() @crewai_event_bus.on(AgentEvaluationStartedEvent) def capture_started(source, event): - events["started"] = event - started_event.set() + with condition: + events["started"] = event + condition.notify() @crewai_event_bus.on(AgentEvaluationCompletedEvent) def capture_completed(source, event): - events["completed"] = event + with condition: + events["completed"] = event + condition.notify() @crewai_event_bus.on(AgentEvaluationFailedEvent) def capture_failed(source, event): - events["failed"] = event - failed_event.set() + with condition: + events["failed"] = event + condition.notify() class FailingEvaluator(BaseEvaluator): metric_category = MetricCategory.GOAL_ALIGNMENT @@ -241,8 +243,12 @@ class TestAgentEvaluator: ) mock_crew.kickoff() - assert started_event.wait(timeout=5), "Timeout waiting for started event" - assert failed_event.wait(timeout=5), "Timeout waiting for failed event" + with condition: + success = condition.wait_for( + lambda: "started" in events and "failed" in events, + timeout=10, + ) + assert success, "Timeout waiting for evaluation events" assert events.keys() == {"started", "failed"} assert events["started"].agent_id == str(agent.id) @@ -256,6 +262,14 @@ class TestAgentEvaluator: assert events["failed"].iteration == 1 assert events["failed"].error == "Forced evaluation failure" + # Wait for results to be stored - the event is emitted before storage + with condition: + success = condition.wait_for( + lambda: agent.role in agent_evaluator.get_evaluation_results(), + timeout=5, + ) + assert success, "Timeout waiting for evaluation results to be stored" + results = agent_evaluator.get_evaluation_results() (result,) = results[agent.role] assert isinstance(result, AgentEvaluationResult) diff --git a/lib/crewai/tests/knowledge/test_knowledge.py b/lib/crewai/tests/knowledge/test_knowledge.py index b56d46a74..b0f35c4d9 100644 --- a/lib/crewai/tests/knowledge/test_knowledge.py +++ b/lib/crewai/tests/knowledge/test_knowledge.py @@ -53,7 +53,7 @@ def test_single_short_string(mock_vector_db): mock_vector_db.query.assert_called_once() -# @pytest.mark.vcr(filter_headers=["authorization"]) +# @pytest.mark.vcr() def test_single_2k_character_string(mock_vector_db): # Create a 2k character string with various facts about Brandon content = ( @@ -374,7 +374,7 @@ def test_multiple_2k_character_files(mock_vector_db, tmpdir): mock_vector_db.query.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hybrid_string_and_files(mock_vector_db, tmpdir): # Create string sources string_contents = [ @@ -443,7 +443,7 @@ def test_pdf_knowledge_source(mock_vector_db): mock_vector_db.query.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_csv_knowledge_source(mock_vector_db, tmpdir): """Test CSVKnowledgeSource with a simple CSV file.""" diff --git a/lib/crewai/tests/llms/anthropic/test_anthropic.py b/lib/crewai/tests/llms/anthropic/test_anthropic.py index 72e3149b6..340aeb642 100644 --- a/lib/crewai/tests/llms/anthropic/test_anthropic.py +++ b/lib/crewai/tests/llms/anthropic/test_anthropic.py @@ -686,7 +686,7 @@ def test_anthropic_stop_sequences_sync(): assert llm.stop == [] -@pytest.mark.vcr(filter_headers=["authorization", "x-api-key"]) +@pytest.mark.vcr() def test_anthropic_stop_sequences_sent_to_api(): """Test that stop_sequences are properly sent to the Anthropic API.""" llm = LLM(model="anthropic/claude-3-5-haiku-20241022") diff --git a/lib/crewai/tests/llms/hooks/test_anthropic_interceptor.py b/lib/crewai/tests/llms/hooks/test_anthropic_interceptor.py index 4002d7b97..b70aa6ff8 100644 --- a/lib/crewai/tests/llms/hooks/test_anthropic_interceptor.py +++ b/lib/crewai/tests/llms/hooks/test_anthropic_interceptor.py @@ -64,7 +64,7 @@ class TestAnthropicInterceptorIntegration: assert llm.interceptor is interceptor - @pytest.mark.vcr(filter_headers=["authorization", "x-api-key"]) + @pytest.mark.vcr() def test_anthropic_call_with_interceptor_tracks_requests(self) -> None: """Test that interceptor tracks Anthropic API requests.""" interceptor = AnthropicTestInterceptor() @@ -164,7 +164,7 @@ class TestAnthropicLoggingInterceptor: assert llm.interceptor is interceptor assert isinstance(llm.interceptor, AnthropicLoggingInterceptor) - @pytest.mark.vcr(filter_headers=["authorization", "x-api-key"]) + @pytest.mark.vcr() def test_logging_interceptor_tracks_details(self) -> None: """Test that logging interceptor tracks request/response details.""" interceptor = AnthropicLoggingInterceptor() @@ -257,7 +257,7 @@ class TestAnthropicHeaderInterceptor: assert "X-Custom-Client" in modified_request.headers assert modified_request.headers["X-Custom-Client"] == "crewai-interceptor" - @pytest.mark.vcr(filter_headers=["authorization", "x-api-key"]) + @pytest.mark.vcr() def test_header_interceptor_with_real_call(self) -> None: """Test that header interceptor works with real Anthropic API call.""" interceptor = AnthropicHeaderInterceptor(workspace_id="ws-999", user_id="u-888") diff --git a/lib/crewai/tests/llms/hooks/test_openai_interceptor.py b/lib/crewai/tests/llms/hooks/test_openai_interceptor.py index 9c3b8537e..32d5a070e 100644 --- a/lib/crewai/tests/llms/hooks/test_openai_interceptor.py +++ b/lib/crewai/tests/llms/hooks/test_openai_interceptor.py @@ -55,7 +55,7 @@ class TestOpenAIInterceptorIntegration: assert llm.interceptor is interceptor - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_openai_call_with_interceptor_tracks_requests(self) -> None: """Test that interceptor tracks OpenAI API requests.""" interceptor = OpenAITestInterceptor() @@ -152,7 +152,7 @@ class TestOpenAILoggingInterceptor: assert llm.interceptor is interceptor assert isinstance(llm.interceptor, LoggingInterceptor) - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_logging_interceptor_tracks_details(self) -> None: """Test that logging interceptor tracks request/response details.""" interceptor = LoggingInterceptor() @@ -241,7 +241,7 @@ class TestOpenAIAuthInterceptor: assert "X-Organization-ID" in modified_request.headers assert modified_request.headers["X-Organization-ID"] == "test-org" - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_auth_interceptor_with_real_call(self) -> None: """Test that auth interceptor works with real OpenAI API call.""" interceptor = AuthInterceptor(api_key="custom-123", org_id="org-789") diff --git a/lib/crewai/tests/llms/openai/test_openai.py b/lib/crewai/tests/llms/openai/test_openai.py index 82ba12f2e..47e70a97b 100644 --- a/lib/crewai/tests/llms/openai/test_openai.py +++ b/lib/crewai/tests/llms/openai/test_openai.py @@ -34,7 +34,7 @@ def test_openai_completion_is_used_when_no_provider_prefix(): assert llm.provider == "openai" assert llm.model == "gpt-4o" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_openai_is_default_provider_without_explicit_llm_set_on_agent(): """ Test that OpenAI is the default provider when no explicit LLM is set on the agent @@ -302,7 +302,7 @@ def test_openai_completion_with_tools(): assert call_kwargs['tools'] is not None assert len(call_kwargs['tools']) > 0 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_openai_completion_call_returns_usage_metrics(): """ Test that OpenAICompletion.call returns usage metrics @@ -530,7 +530,7 @@ def test_openai_streaming_with_response_model(): assert "text_format" not in call_kwargs -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_openai_response_format_with_pydantic_model(): """ Test that response_format with a Pydantic BaseModel returns structured output. @@ -551,7 +551,7 @@ def test_openai_response_format_with_pydantic_model(): assert 0 <= result.confidence <= 1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_openai_response_format_with_dict(): """ Test that response_format with a dict returns JSON output. @@ -565,7 +565,7 @@ def test_openai_response_format_with_dict(): assert "status" in parsed -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_openai_response_format_none(): """ Test that when response_format is None, the API returns plain text. diff --git a/lib/crewai/tests/memory/test_external_memory.py b/lib/crewai/tests/memory/test_external_memory.py index ac8516bca..ddd7f0049 100644 --- a/lib/crewai/tests/memory/test_external_memory.py +++ b/lib/crewai/tests/memory/test_external_memory.py @@ -194,7 +194,7 @@ def test_crew_external_memory_reset(mem_type, crew_with_external_memory): @pytest.mark.parametrize("mem_method", ["search", "save"]) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_external_memory_save_with_memory_flag( mem_method, crew_with_external_memory ): @@ -206,7 +206,7 @@ def test_crew_external_memory_save_with_memory_flag( @pytest.mark.parametrize("mem_method", ["search", "save"]) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_external_memory_save_using_crew_without_memory_flag( mem_method, crew_with_external_memory_without_memory_flag ): @@ -259,19 +259,22 @@ def test_external_memory_custom_storage(custom_storage, crew_with_external_memor def test_external_memory_search_events( custom_storage, external_memory_with_mocked_config ): - events = defaultdict(list) - event_received = threading.Event() + events: dict[str, list] = defaultdict(list) + condition = threading.Condition() external_memory_with_mocked_config.storage = custom_storage @crewai_event_bus.on(MemoryQueryStartedEvent) def on_search_started(source, event): - events["MemoryQueryStartedEvent"].append(event) + with condition: + events["MemoryQueryStartedEvent"].append(event) + condition.notify() @crewai_event_bus.on(MemoryQueryCompletedEvent) def on_search_completed(source, event): - events["MemoryQueryCompletedEvent"].append(event) - event_received.set() + with condition: + events["MemoryQueryCompletedEvent"].append(event) + condition.notify() external_memory_with_mocked_config.search( query="test value", @@ -279,7 +282,13 @@ def test_external_memory_search_events( score_threshold=0.35, ) - assert event_received.wait(timeout=5), "Timeout waiting for search events" + with condition: + success = condition.wait_for( + lambda: len(events["MemoryQueryStartedEvent"]) >= 1 + and len(events["MemoryQueryCompletedEvent"]) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for search events" assert len(events["MemoryQueryStartedEvent"]) == 1 assert len(events["MemoryQueryCompletedEvent"]) == 1 @@ -323,26 +332,35 @@ def test_external_memory_search_events( def test_external_memory_save_events( custom_storage, external_memory_with_mocked_config ): - events = defaultdict(list) - event_received = threading.Event() + events: dict[str, list] = defaultdict(list) + condition = threading.Condition() external_memory_with_mocked_config.storage = custom_storage @crewai_event_bus.on(MemorySaveStartedEvent) def on_save_started(source, event): - events["MemorySaveStartedEvent"].append(event) + with condition: + events["MemorySaveStartedEvent"].append(event) + condition.notify() @crewai_event_bus.on(MemorySaveCompletedEvent) def on_save_completed(source, event): - events["MemorySaveCompletedEvent"].append(event) - event_received.set() + with condition: + events["MemorySaveCompletedEvent"].append(event) + condition.notify() external_memory_with_mocked_config.save( value="saving value", metadata={"task": "test_task"}, ) - assert event_received.wait(timeout=5), "Timeout waiting for save events" + with condition: + success = condition.wait_for( + lambda: len(events["MemorySaveStartedEvent"]) >= 1 + and len(events["MemorySaveCompletedEvent"]) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for save events" assert len(events["MemorySaveStartedEvent"]) == 1 assert len(events["MemorySaveCompletedEvent"]) == 1 diff --git a/lib/crewai/tests/memory/test_long_term_memory.py b/lib/crewai/tests/memory/test_long_term_memory.py index 3461a3b7b..639724329 100644 --- a/lib/crewai/tests/memory/test_long_term_memory.py +++ b/lib/crewai/tests/memory/test_long_term_memory.py @@ -23,25 +23,19 @@ def long_term_memory(): def test_long_term_memory_save_events(long_term_memory): events = defaultdict(list) - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(MemorySaveStartedEvent) def on_save_started(source, event): - events["MemorySaveStartedEvent"].append(event) - if ( - len(events["MemorySaveStartedEvent"]) == 1 - and len(events["MemorySaveCompletedEvent"]) == 1 - ): - all_events_received.set() + with condition: + events["MemorySaveStartedEvent"].append(event) + condition.notify() @crewai_event_bus.on(MemorySaveCompletedEvent) def on_save_completed(source, event): - events["MemorySaveCompletedEvent"].append(event) - if ( - len(events["MemorySaveStartedEvent"]) == 1 - and len(events["MemorySaveCompletedEvent"]) == 1 - ): - all_events_received.set() + with condition: + events["MemorySaveCompletedEvent"].append(event) + condition.notify() memory = LongTermMemoryItem( agent="test_agent", @@ -53,7 +47,13 @@ def test_long_term_memory_save_events(long_term_memory): ) long_term_memory.save(memory) - assert all_events_received.wait(timeout=5), "Timeout waiting for save events" + with condition: + success = condition.wait_for( + lambda: len(events["MemorySaveStartedEvent"]) >= 1 + and len(events["MemorySaveCompletedEvent"]) >= 1, + timeout=5, + ) + assert success, "Timeout waiting for save events" assert len(events["MemorySaveStartedEvent"]) == 1 assert len(events["MemorySaveCompletedEvent"]) == 1 assert len(events["MemorySaveFailedEvent"]) == 0 @@ -98,31 +98,31 @@ def test_long_term_memory_save_events(long_term_memory): def test_long_term_memory_search_events(long_term_memory): events = defaultdict(list) - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(MemoryQueryStartedEvent) def on_search_started(source, event): - events["MemoryQueryStartedEvent"].append(event) - if ( - len(events["MemoryQueryStartedEvent"]) == 1 - and len(events["MemoryQueryCompletedEvent"]) == 1 - ): - all_events_received.set() + with condition: + events["MemoryQueryStartedEvent"].append(event) + condition.notify() @crewai_event_bus.on(MemoryQueryCompletedEvent) def on_search_completed(source, event): - events["MemoryQueryCompletedEvent"].append(event) - if ( - len(events["MemoryQueryStartedEvent"]) == 1 - and len(events["MemoryQueryCompletedEvent"]) == 1 - ): - all_events_received.set() + with condition: + events["MemoryQueryCompletedEvent"].append(event) + condition.notify() test_query = "test query" long_term_memory.search(test_query, latest_n=5) - assert all_events_received.wait(timeout=5), "Timeout waiting for search events" + with condition: + success = condition.wait_for( + lambda: len(events["MemoryQueryStartedEvent"]) >= 1 + and len(events["MemoryQueryCompletedEvent"]) >= 1, + timeout=5, + ) + assert success, "Timeout waiting for search events" assert len(events["MemoryQueryStartedEvent"]) == 1 assert len(events["MemoryQueryCompletedEvent"]) == 1 assert len(events["MemoryQueryFailedEvent"]) == 0 diff --git a/lib/crewai/tests/memory/test_short_term_memory.py b/lib/crewai/tests/memory/test_short_term_memory.py index 049e38972..a619c51ba 100644 --- a/lib/crewai/tests/memory/test_short_term_memory.py +++ b/lib/crewai/tests/memory/test_short_term_memory.py @@ -107,27 +107,33 @@ def test_short_term_memory_search_events(short_term_memory): def test_short_term_memory_save_events(short_term_memory): - events = defaultdict(list) - save_started = threading.Event() - save_completed = threading.Event() + events: dict[str, list] = defaultdict(list) + condition = threading.Condition() @crewai_event_bus.on(MemorySaveStartedEvent) def on_save_started(source, event): - events["MemorySaveStartedEvent"].append(event) - save_started.set() + with condition: + events["MemorySaveStartedEvent"].append(event) + condition.notify() @crewai_event_bus.on(MemorySaveCompletedEvent) def on_save_completed(source, event): - events["MemorySaveCompletedEvent"].append(event) - save_completed.set() + with condition: + events["MemorySaveCompletedEvent"].append(event) + condition.notify() short_term_memory.save( value="test value", metadata={"task": "test_task"}, ) - assert save_started.wait(timeout=2), "Timeout waiting for save started event" - assert save_completed.wait(timeout=2), "Timeout waiting for save completed event" + with condition: + success = condition.wait_for( + lambda: len(events["MemorySaveStartedEvent"]) >= 1 + and len(events["MemorySaveCompletedEvent"]) >= 1, + timeout=5, + ) + assert success, "Timeout waiting for save events" assert len(events["MemorySaveStartedEvent"]) == 1 assert len(events["MemorySaveCompletedEvent"]) == 1 diff --git a/lib/crewai/tests/telemetry/test_telemetry.py b/lib/crewai/tests/telemetry/test_telemetry.py index 2429a4ade..b76a2e130 100644 --- a/lib/crewai/tests/telemetry/test_telemetry.py +++ b/lib/crewai/tests/telemetry/test_telemetry.py @@ -54,28 +54,34 @@ def test_telemetry_enabled_by_default(): "opentelemetry.exporter.otlp.proto.http.trace_exporter.OTLPSpanExporter.export", side_effect=Exception("Test exception"), ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_telemetry_fails_due_connect_timeout(export_mock, logger_mock): error = Exception("Test exception") export_mock.side_effect = error - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span("test-span"): - agent = Agent( - role="agent", - llm="gpt-4o-mini", - goal="Just say hi", - backstory="You are a helpful assistant that just says hi", - ) - task = Task( - description="Just say hi", - expected_output="hi", - agent=agent, - ) - crew = Crew(agents=[agent], tasks=[task], name="TestCrew") - crew.kickoff() + with patch.dict( + os.environ, {"CREWAI_DISABLE_TELEMETRY": "false", "OTEL_SDK_DISABLED": "false"} + ): + telemetry = Telemetry() + telemetry.set_tracer() - trace.get_tracer_provider().force_flush() + tracer = trace.get_tracer(__name__) + with tracer.start_as_current_span("test-span"): + agent = Agent( + role="agent", + llm="gpt-4o-mini", + goal="Just say hi", + backstory="You are a helpful assistant that just says hi", + ) + task = Task( + description="Just say hi", + expected_output="hi", + agent=agent, + ) + crew = Crew(agents=[agent], tasks=[task], name="TestCrew") + crew.kickoff() + + trace.get_tracer_provider().force_flush() assert export_mock.called assert logger_mock.call_count == export_mock.call_count diff --git a/lib/crewai/tests/test_crew.py b/lib/crewai/tests/test_crew.py index d4cf1acbf..4f485f207 100644 --- a/lib/crewai/tests/test_crew.py +++ b/lib/crewai/tests/test_crew.py @@ -286,7 +286,7 @@ def test_crew_config_with_wrong_keys(): Crew(process=Process.sequential, config=no_agents_config) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_creation(researcher, writer): tasks = [ Task( @@ -318,7 +318,7 @@ def test_crew_creation(researcher, writer): assert result.raw == expected_string_output -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_sync_task_execution(researcher, writer): tasks = [ Task( @@ -357,7 +357,7 @@ def test_sync_task_execution(researcher, writer): assert mock_execute_sync.call_count == len(tasks) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_process(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -393,7 +393,7 @@ def test_manager_llm_requirement_for_hierarchical_process(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_manager_agent_delegating_to_assigned_task_agent(researcher, writer): """ Test that the manager agent delegates to the assigned task agent. @@ -445,7 +445,7 @@ def test_manager_agent_delegating_to_assigned_task_agent(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_manager_agent_delegating_to_all_agents(researcher, writer): """ Test that the manager agent delegates to all agents when none are specified. @@ -478,7 +478,7 @@ def test_manager_agent_delegating_to_all_agents(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_manager_agent_delegates_with_varied_role_cases(): """ Test that the manager agent can delegate to agents regardless of case or whitespace variations in role names. @@ -555,7 +555,7 @@ def test_manager_agent_delegates_with_varied_role_cases(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_with_delegating_agents(ceo, writer): tasks = [ Task( @@ -579,7 +579,7 @@ def test_crew_with_delegating_agents(ceo, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_with_delegating_agents_should_not_override_task_tools(ceo, writer): class TestToolInput(BaseModel): """Input schema for TestTool.""" @@ -635,7 +635,7 @@ def test_crew_with_delegating_agents_should_not_override_task_tools(ceo, writer) ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_with_delegating_agents_should_not_override_agent_tools(ceo, writer): class TestToolInput(BaseModel): """Input schema for TestTool.""" @@ -693,7 +693,7 @@ def test_crew_with_delegating_agents_should_not_override_agent_tools(ceo, writer ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_tools_override_agent_tools(researcher): class TestToolInput(BaseModel): """Input schema for TestTool.""" @@ -742,7 +742,7 @@ def test_task_tools_override_agent_tools(researcher): assert isinstance(new_researcher.tools[0], TestTool) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_tools_override_agent_tools_with_allow_delegation(researcher, writer): """ Test that task tools override agent tools while preserving delegation tools when allow_delegation=True @@ -819,7 +819,7 @@ def test_task_tools_override_agent_tools_with_allow_delegation(researcher, write assert isinstance(researcher_with_delegation.tools[0], TestTool) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_verbose_output(researcher, writer, capsys): tasks = [ Task( @@ -863,7 +863,7 @@ def test_crew_verbose_output(researcher, writer, capsys): assert crew_quiet.verbose is False -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_cache_hitting_between_agents(researcher, writer, ceo): @tool def multiplier(first_number: int, second_number: int) -> float: @@ -917,7 +917,7 @@ def test_cache_hitting_between_agents(researcher, writer, ceo): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_api_calls_throttling(capsys): @tool def get_final_answer() -> float: @@ -952,7 +952,7 @@ def test_api_calls_throttling(capsys): moveon.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_kickoff_usage_metrics(): inputs = [ {"topic": "dog"}, @@ -987,7 +987,7 @@ def test_crew_kickoff_usage_metrics(): assert result.token_usage.cached_prompt_tokens == 0 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_kickoff_streaming_usage_metrics(): inputs = [ {"topic": "dog"}, @@ -1043,7 +1043,7 @@ def test_agents_rpm_is_never_set_if_crew_max_rpm_is_not_set(): assert agent._rpm_controller is None -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_sequential_async_task_execution_completion(researcher, writer): list_ideas = Task( description="Give me a list of 5 interesting ideas to explore for an article, what makes them unique and interesting.", @@ -1075,7 +1075,7 @@ def test_sequential_async_task_execution_completion(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_single_task_with_async_execution(): researcher_agent = Agent( role="Researcher", @@ -1103,7 +1103,7 @@ def test_single_task_with_async_execution(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_three_task_with_async_execution(): researcher_agent = Agent( role="Researcher", @@ -1149,7 +1149,7 @@ def test_three_task_with_async_execution(): @pytest.mark.asyncio -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() async def test_crew_async_kickoff(): inputs = [ {"topic": "dog"}, @@ -1197,7 +1197,7 @@ async def test_crew_async_kickoff(): @pytest.mark.asyncio -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() async def test_async_task_execution_call_count(researcher, writer): list_ideas = Task( description="Give me a list of 5 interesting ideas to explore for na article, what makes them unique and interesting.", @@ -1251,7 +1251,7 @@ async def test_async_task_execution_call_count(researcher, writer): assert mock_execute_sync.call_count == 1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_kickoff_for_each_single_input(): """Tests if kickoff_for_each works with a single input.""" @@ -1275,7 +1275,7 @@ def test_kickoff_for_each_single_input(): assert len(results) == 1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_kickoff_for_each_multiple_inputs(): """Tests if kickoff_for_each works with multiple inputs.""" @@ -1303,7 +1303,7 @@ def test_kickoff_for_each_multiple_inputs(): assert len(results) == len(inputs) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_kickoff_for_each_empty_input(): """Tests if kickoff_for_each handles an empty input list.""" agent = Agent( @@ -1323,7 +1323,7 @@ def test_kickoff_for_each_empty_input(): assert results == [] -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_kickoff_for_each_invalid_input(): """Tests if kickoff_for_each raises TypeError for invalid input types.""" @@ -1554,7 +1554,7 @@ def test_dont_set_agents_step_callback_if_already_set(): assert researcher_agent.step_callback is agent_callback -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_function_calling_llm(): llm = LLM(model="gpt-4o-mini") @@ -1583,7 +1583,7 @@ def test_crew_function_calling_llm(): assert result.raw == "Howdy!" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_with_no_arguments(): @tool def return_data() -> str: @@ -1649,7 +1649,7 @@ def test_code_execution_flag_adds_code_tool_upon_kickoff(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_delegation_is_not_enabled_if_there_are_only_one_agent(): researcher = Agent( role="Researcher", @@ -1670,7 +1670,7 @@ def test_delegation_is_not_enabled_if_there_are_only_one_agent(): assert task.tools == [] -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agents_do_not_get_delegation_tools_with_there_is_only_one_agent(): agent = Agent( role="Researcher", @@ -1688,7 +1688,7 @@ def test_agents_do_not_get_delegation_tools_with_there_is_only_one_agent(): assert len(agent.tools) == 0 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_sequential_crew_creation_tasks_without_agents(researcher): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -1711,7 +1711,7 @@ def test_sequential_crew_creation_tasks_without_agents(researcher): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_usage_metrics_are_captured_for_hierarchical_process(): agent = Agent( role="Researcher", @@ -1808,7 +1808,7 @@ def test_hierarchical_kickoff_usage_metrics_include_manager(researcher): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_crew_creation_tasks_with_agents(researcher, writer): """ Agents are not required for tasks in a hierarchical process but sometimes they are still added @@ -1861,7 +1861,7 @@ def test_hierarchical_crew_creation_tasks_with_agents(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, writer, ceo): """ Tests that async tasks in hierarchical crews are handled correctly with proper delegation tools @@ -1918,7 +1918,7 @@ def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, write ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_crew_creation_tasks_with_sync_last(researcher, writer, ceo): """ Agents are not required for tasks in a hierarchical process but sometimes they are still added @@ -2006,7 +2006,7 @@ def test_crew_inputs_interpolate_both_agents_and_tasks_diff(): interpolate_task_inputs.assert_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_does_not_interpolate_without_inputs(): agent = Agent( role="{topic} Researcher", @@ -2133,7 +2133,7 @@ def test_task_same_callback_both_on_task_and_crew(): mock_callback.assert_called_once_with(list_ideas.output) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_tools_with_custom_caching(): @tool def multiplcation_tool(first_number: int, second_number: int) -> int: @@ -2205,7 +2205,7 @@ def test_tools_with_custom_caching(): assert result.raw == "3" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_task_uses_last_output(researcher, writer): """Test that conditional tasks use the last task output for condition evaluation.""" task1 = Task( @@ -2281,7 +2281,7 @@ def test_conditional_task_uses_last_output(researcher, writer): ) # Third task used first task's output -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_tasks_result_collection(researcher, writer): """Test that task outputs are properly collected based on execution status.""" task1 = Task( @@ -2364,7 +2364,7 @@ def test_conditional_tasks_result_collection(researcher, writer): ) # Third task executed -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multiple_conditional_tasks(researcher, writer): """Test that having multiple conditional tasks in sequence works correctly.""" task1 = Task( @@ -2414,7 +2414,7 @@ def test_multiple_conditional_tasks(researcher, writer): assert len(result.tasks_output) == 3 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_using_contextual_memory(): math_researcher = Agent( role="Researcher", @@ -2442,7 +2442,7 @@ def test_using_contextual_memory(): contextual_mem.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_memory_events_are_emitted(): events = defaultdict(list) condition = threading.Condition() @@ -2539,7 +2539,7 @@ def test_memory_events_are_emitted(): assert len(events["MemoryRetrievalCompletedEvent"]) == 1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_using_contextual_memory_with_long_term_memory(): math_researcher = Agent( role="Researcher", @@ -2568,7 +2568,7 @@ def test_using_contextual_memory_with_long_term_memory(): assert crew.memory is False -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_warning_long_term_memory_without_entity_memory(): math_researcher = Agent( role="Researcher", @@ -2603,7 +2603,7 @@ def test_warning_long_term_memory_without_entity_memory(): save_memory.assert_not_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_long_term_memory_with_memory_flag(): math_researcher = Agent( role="Researcher", @@ -2633,7 +2633,7 @@ def test_long_term_memory_with_memory_flag(): save_memory.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_using_contextual_memory_with_short_term_memory(): math_researcher = Agent( role="Researcher", @@ -2662,7 +2662,7 @@ def test_using_contextual_memory_with_short_term_memory(): assert crew.memory is False -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_disabled_memory_using_contextual_memory(): math_researcher = Agent( role="Researcher", @@ -2690,7 +2690,7 @@ def test_disabled_memory_using_contextual_memory(): contextual_mem.assert_not_called() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_log_file_output(tmp_path, researcher): test_file = tmp_path / "logs.txt" tasks = [ @@ -2706,7 +2706,7 @@ def test_crew_log_file_output(tmp_path, researcher): assert test_file.exists() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_output_file_end_to_end(tmp_path): """Test output file functionality in a full crew context.""" # Create an agent @@ -2789,7 +2789,7 @@ def test_crew_output_file_validation_failures(): Crew(agents=[agent], tasks=[task]).kickoff() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_manager_agent(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -2848,7 +2848,7 @@ def test_manager_agent_in_agents_raises_exception(researcher, writer): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_manager_agent_with_tools_raises_exception(researcher, writer): @tool def testing_tool(first_number: int, second_number: int) -> int: @@ -2879,7 +2879,7 @@ def test_manager_agent_with_tools_raises_exception(researcher, writer): crew.kickoff() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_train_success(researcher, writer, monkeypatch): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -2899,15 +2899,13 @@ def test_crew_train_success(researcher, writer, monkeypatch): def on_crew_train_started(source, event: CrewTrainStartedEvent): with condition: received_events.append(event) - if len(received_events) == 2: - condition.notify() + condition.notify() @crewai_event_bus.on(CrewTrainCompletedEvent) def on_crew_train_completed(source, event: CrewTrainCompletedEvent): with condition: received_events.append(event) - if len(received_events) == 2: - condition.notify() + condition.notify() # Mock human input to avoid blocking during training # Use StringIO to simulate user input for multiple calls to input() @@ -2927,7 +2925,7 @@ def test_crew_train_success(researcher, writer, monkeypatch): assert isinstance(received_events[1], CrewTrainCompletedEvent) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_train_error(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article", @@ -2977,7 +2975,7 @@ def test__setup_for_training(researcher, writer): assert agent.allow_delegation is False -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_feature(researcher, writer): list_ideas = Task( description="Generate a list of 5 interesting ideas to explore for an article, where each bulletpoint is under 15 words.", @@ -3015,7 +3013,7 @@ def test_replay_feature(researcher, writer): assert mock_execute_task.call_count == 3 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_replay_error(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article", @@ -3033,7 +3031,7 @@ def test_crew_replay_error(researcher, writer): assert "task_id is required" in str(e) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_task_db_init(): agent = Agent( role="Content Writer", @@ -3072,7 +3070,7 @@ def test_crew_task_db_init(): pytest.fail(f"An exception was raised: {e!s}") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_task_with_context(): agent1 = Agent( role="Researcher", @@ -3175,7 +3173,7 @@ def test_replay_task_with_context(): db_handler.reset() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_preserves_messages(): """Test that replay preserves messages from stored task outputs.""" from crewai.utilities.types import LLMMessage @@ -3239,7 +3237,7 @@ def test_replay_preserves_messages(): db_handler.reset() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_with_context(): agent = Agent(role="test_agent", backstory="Test Description", goal="Test Goal") task1 = Task( @@ -3298,7 +3296,7 @@ def test_replay_with_context(): assert crew.tasks[1].context[0].output.raw == "context raw output" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_with_context_set_to_nullable(): agent = Agent(role="test_agent", backstory="Test Description", goal="Test Goal") task1 = Task( @@ -3324,7 +3322,7 @@ def test_replay_with_context_set_to_nullable(): mock_execute_task.assert_called_with(agent=ANY, context="", tools=ANY) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_with_invalid_task_id(): agent = Agent(role="test_agent", backstory="Test Description", goal="Test Goal") task1 = Task( @@ -3387,7 +3385,7 @@ def test_replay_with_invalid_task_id(): crew.replay("bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @patch.object(Crew, "_interpolate_inputs") def test_replay_interpolates_inputs_properly(mock_interpolate_inputs): agent = Agent(role="test_agent", backstory="Test Description", goal="Test Goal") @@ -3449,7 +3447,7 @@ def test_replay_interpolates_inputs_properly(mock_interpolate_inputs): assert mock_interpolate_inputs.call_count == 2 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_replay_setup_context(): agent = Agent(role="test_agent", backstory="Test Description", goal="Test Goal") task1 = Task(description="Context Task", expected_output="Say {name}", agent=agent) @@ -3603,7 +3601,7 @@ def test_conditional_task_requirement_breaks_when_singular_conditional_task( ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_task_last_task_when_conditional_is_true(researcher, writer): def condition_fn(output) -> bool: return True @@ -3630,7 +3628,7 @@ def test_conditional_task_last_task_when_conditional_is_true(researcher, writer) ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_task_last_task_when_conditional_is_false(researcher, writer): def condition_fn(output) -> bool: return False @@ -3679,7 +3677,7 @@ def test_conditional_task_requirement_breaks_when_task_async(researcher, writer) ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_should_skip(researcher, writer): task1 = Task(description="Return hello", expected_output="say hi", agent=researcher) @@ -3712,7 +3710,7 @@ def test_conditional_should_skip(researcher, writer): assert result.raw.startswith("Task 1 output") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_conditional_should_execute(researcher, writer): task1 = Task(description="Return hello", expected_output="say hi", agent=researcher) @@ -3744,7 +3742,7 @@ def test_conditional_should_execute(researcher, writer): assert mock_execute_sync.call_count == 2 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_testing_function(researcher): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -3767,15 +3765,13 @@ def test_crew_testing_function(researcher): def on_crew_test_started(source, event: CrewTestStartedEvent): with condition: received_events.append(event) - if len(received_events) == 2: - condition.notify() + condition.notify() @crewai_event_bus.on(CrewTestCompletedEvent) def on_crew_test_completed(source, event: CrewTestCompletedEvent): with condition: received_events.append(event) - if len(received_events) == 2: - condition.notify() + condition.notify() crew.test(n_iterations, llm_instance, inputs={"topic": "AI"}) @@ -3788,7 +3784,7 @@ def test_crew_testing_function(researcher): assert isinstance(received_events[1], CrewTestCompletedEvent) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_verbose_manager_agent(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -3809,7 +3805,7 @@ def test_hierarchical_verbose_manager_agent(researcher, writer): assert crew.manager_agent.verbose -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_hierarchical_verbose_false_manager_agent(researcher, writer): task = Task( description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", @@ -3853,7 +3849,7 @@ def test_fetch_inputs(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_tools_preserve_code_execution_tools(): """ Test that task tools don't override code execution tools when allow_code_execution=True @@ -3935,7 +3931,7 @@ def test_task_tools_preserve_code_execution_tools(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multimodal_flag_adds_multimodal_tools(): """ Test that an agent with multimodal=True automatically has multimodal tools added to the task execution. @@ -3982,7 +3978,7 @@ def test_multimodal_flag_adds_multimodal_tools(): assert len(used_tools) == 1, "Should only have the AddImageTool" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multimodal_agent_image_tool_handling(): """ Test that multimodal agents properly handle image tools in the CrewAgentExecutor @@ -4057,7 +4053,7 @@ def test_multimodal_agent_image_tool_handling(): assert result["content"][1]["type"] == "image_url" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multimodal_agent_describing_image_successfully(): """ Test that a multimodal agent can process images without validation errors. @@ -4095,7 +4091,7 @@ def test_multimodal_agent_describing_image_successfully(): assert task_output.raw == result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multimodal_agent_live_image_analysis(): """ Test that multimodal agents can analyze images through a real API call @@ -4138,7 +4134,7 @@ def test_multimodal_agent_live_image_analysis(): assert "error" not in result.raw.lower() # No error messages in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_with_failing_task_guardrails(): """Test that crew properly handles failing guardrails and retries with validation feedback.""" @@ -4195,7 +4191,7 @@ def test_crew_with_failing_task_guardrails(): assert task_output.raw == result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_guardrail_feedback_in_context(): """Test that guardrail feedback is properly appended to task context for retries.""" @@ -4252,7 +4248,7 @@ def test_crew_guardrail_feedback_in_context(): assert task.retry_count == 1, "Task should have been retried once" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_before_kickoff_callback(): @CrewBase class TestCrewClass: @@ -4309,7 +4305,7 @@ def test_before_kickoff_callback(): assert inputs.get("modified") -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_before_kickoff_without_inputs(): @CrewBase class TestCrewClass: @@ -4365,7 +4361,7 @@ def test_before_kickoff_without_inputs(): assert test_crew_instance.received_inputs.get("modified") is True -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_with_knowledge_sources_works_with_copy(researcher, writer): content = "Brandon's favorite color is red and he likes Mexican food." string_source = StringKnowledgeSource(content=content) @@ -4516,7 +4512,7 @@ def test_sets_parent_flow_when_outside_flow(researcher, writer): assert crew.parent_flow is None -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_sets_parent_flow_when_inside_flow(researcher, writer): class MyFlow(Flow): @start() @@ -4722,7 +4718,7 @@ def test_default_crew_name(researcher, writer): assert crew.name == "crew" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_ensure_exchanged_messages_are_propagated_to_external_memory(): external_memory = ExternalMemory(storage=MagicMock()) diff --git a/lib/crewai/tests/test_custom_llm.py b/lib/crewai/tests/test_custom_llm.py index fef1bb5b5..25ec1151e 100644 --- a/lib/crewai/tests/test_custom_llm.py +++ b/lib/crewai/tests/test_custom_llm.py @@ -78,7 +78,7 @@ class CustomLLM(BaseLLM): return 4096 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_custom_llm_implementation(): """Test that a custom LLM implementation works with create_llm.""" custom_llm = CustomLLM(response="The answer is 42") @@ -97,7 +97,7 @@ def test_custom_llm_implementation(): assert "42" in response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_custom_llm_within_crew(): """Test that a custom LLM implementation works with create_llm.""" custom_llm = CustomLLM(response="Hello! Nice to meet you!", model="test-model") diff --git a/lib/crewai/tests/test_llm.py b/lib/crewai/tests/test_llm.py index 977d40f2c..6f3bcd70a 100644 --- a/lib/crewai/tests/test_llm.py +++ b/lib/crewai/tests/test_llm.py @@ -12,13 +12,14 @@ from crewai.events.event_types import ( ToolUsageStartedEvent, ) from crewai.llm import CONTEXT_WINDOW_USAGE_RATIO, LLM +from crewai.llms.providers.anthropic.completion import AnthropicCompletion from crewai.utilities.token_counter_callback import TokenCalcHandler from pydantic import BaseModel import pytest # TODO: This test fails without print statement, which makes me think that something is happening asynchronously that we need to eventually fix and dive deeper into at a later date -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_callback_replacement(): llm1 = LLM(model="gpt-4o-mini", is_litellm=True) llm2 = LLM(model="gpt-4o-mini", is_litellm=True) @@ -45,7 +46,7 @@ def test_llm_callback_replacement(): assert usage_metrics_1 == calc_handler_1.token_cost_process.get_summary() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_string_input(): llm = LLM(model="gpt-4o-mini") @@ -55,7 +56,7 @@ def test_llm_call_with_string_input(): assert len(result.strip()) > 0 # Ensure the response is not empty -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_string_input_and_callbacks(): llm = LLM(model="gpt-4o-mini", is_litellm=True) calc_handler = TokenCalcHandler(token_cost_process=TokenProcess()) @@ -72,7 +73,7 @@ def test_llm_call_with_string_input_and_callbacks(): assert usage_metrics.successful_requests == 1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_message_list(): llm = LLM(model="gpt-4o-mini") messages = [{"role": "user", "content": "What is the capital of France?"}] @@ -83,7 +84,7 @@ def test_llm_call_with_message_list(): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_tool_and_string_input(): llm = LLM(model="gpt-4o-mini") @@ -121,7 +122,7 @@ def test_llm_call_with_tool_and_string_input(): assert result == get_current_year() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_call_with_tool_and_message_list(): llm = LLM(model="gpt-4o-mini", is_litellm=True) @@ -161,7 +162,7 @@ def test_llm_call_with_tool_and_message_list(): assert result == 25 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_passes_additional_params(): llm = LLM( model="gpt-4o-mini", @@ -259,7 +260,7 @@ def test_validate_call_params_no_response_format(): llm._validate_call_params() -@pytest.mark.vcr(filter_headers=["authorization"], filter_query_parameters=["key"]) +@pytest.mark.vcr() @pytest.mark.parametrize( "model", [ @@ -267,19 +268,17 @@ def test_validate_call_params_no_response_format(): "gemini/gemini-2.0-flash-thinking-exp-01-21", "gemini/gemini-2.0-flash-001", "gemini/gemini-2.0-flash-lite-001", - "gemini/gemini-2.5-flash-preview-04-17", - "gemini/gemini-2.5-pro-exp-03-25", ], ) def test_gemini_models(model): # Use LiteLLM for VCR compatibility (VCR can intercept HTTP calls but not native SDK calls) - llm = LLM(model=model, is_litellm=True) + llm = LLM(model=model, is_litellm=False) result = llm.call("What is the capital of France?") assert isinstance(result, str) assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"], filter_query_parameters=["key"]) +@pytest.mark.vcr() @pytest.mark.parametrize( "model", [ @@ -294,7 +293,7 @@ def test_gemma3(model): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.parametrize( "model", ["gpt-4.1", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano-2025-04-14"] ) @@ -305,7 +304,7 @@ def test_gpt_4_1(model): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_o3_mini_reasoning_effort_high(): llm = LLM( model="o3-mini", @@ -316,7 +315,7 @@ def test_o3_mini_reasoning_effort_high(): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_o3_mini_reasoning_effort_low(): llm = LLM( model="o3-mini", @@ -327,7 +326,7 @@ def test_o3_mini_reasoning_effort_low(): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_o3_mini_reasoning_effort_medium(): llm = LLM( model="o3-mini", @@ -416,11 +415,10 @@ def test_context_window_exceeded_error_handling(): assert "8192 tokens" in str(excinfo.value) -@pytest.mark.vcr(filter_headers=["authorization"]) @pytest.fixture def anthropic_llm(): """Fixture providing an Anthropic LLM instance.""" - return LLM(model="anthropic/claude-3-sonnet", is_litellm=True) + return LLM(model="anthropic/claude-3-sonnet", is_litellm=False) @pytest.fixture @@ -438,18 +436,19 @@ def user_message(): def test_anthropic_message_formatting_edge_cases(anthropic_llm): """Test edge cases for Anthropic message formatting.""" # Test None messages - with pytest.raises(TypeError, match="Messages cannot be None"): - anthropic_llm._format_messages_for_provider(None) + anthropic_llm = AnthropicCompletion(model="claude-3-sonnet", is_litellm=False) + with pytest.raises(TypeError): + anthropic_llm._format_messages_for_anthropic(None) - # Test empty message list - formatted = anthropic_llm._format_messages_for_provider([]) + # Test empty message list - Anthropic requires first message to be from user + formatted, system_message = anthropic_llm._format_messages_for_anthropic([]) assert len(formatted) == 1 assert formatted[0]["role"] == "user" - assert formatted[0]["content"] == "." + assert formatted[0]["content"] == "Hello" # Test invalid message format - with pytest.raises(TypeError, match="Invalid message format"): - anthropic_llm._format_messages_for_provider([{"invalid": "message"}]) + with pytest.raises(ValueError, match="must have 'role' and 'content' keys"): + anthropic_llm._format_messages_for_anthropic([{"invalid": "message"}]) def test_anthropic_model_detection(): @@ -471,13 +470,15 @@ def test_anthropic_message_formatting(anthropic_llm, system_message, user_messag """Test Anthropic message formatting with fixtures.""" # Test when first message is system - formatted = anthropic_llm._format_messages_for_provider([]) + # Test empty message list - Anthropic requires first message to be from user + formatted, extracted_system = anthropic_llm._format_messages_for_anthropic([]) assert len(formatted) == 1 assert formatted[0]["role"] == "user" - assert formatted[0]["content"] == "." + assert formatted[0]["content"] == "Hello" - with pytest.raises(TypeError, match="Invalid message format"): - anthropic_llm._format_messages_for_provider([{"invalid": "message"}]) + # Test invalid message format + with pytest.raises(ValueError, match="must have 'role' and 'content' keys"): + anthropic_llm._format_messages_for_anthropic([{"invalid": "message"}]) def test_deepseek_r1_with_open_router(): @@ -556,7 +557,7 @@ def mock_emit() -> MagicMock: yield mock_emit -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_streaming_tool_calls(get_weather_tool_schema, mock_emit): llm = LLM(model="openai/gpt-4o", stream=True, is_litellm=True) response = llm.call( @@ -584,7 +585,7 @@ def test_handle_streaming_tool_calls(get_weather_tool_schema, mock_emit): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_streaming_tool_calls_with_error(get_weather_tool_schema, mock_emit): def get_weather_error(location): raise Exception("Error") @@ -609,7 +610,7 @@ def test_handle_streaming_tool_calls_with_error(get_weather_tool_schema, mock_em ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_streaming_tool_calls_no_available_functions( get_weather_tool_schema, mock_emit ): @@ -630,7 +631,7 @@ def test_handle_streaming_tool_calls_no_available_functions( ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_handle_streaming_tool_calls_no_tools(mock_emit): llm = LLM(model="openai/gpt-4o", stream=True, is_litellm=True) response = llm.call( @@ -651,7 +652,7 @@ def test_handle_streaming_tool_calls_no_tools(mock_emit): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.skip(reason="Highly flaky on ci") def test_llm_call_when_stop_is_unsupported(caplog): llm = LLM(model="o1-mini", stop=["stop"], is_litellm=True) @@ -662,7 +663,7 @@ def test_llm_call_when_stop_is_unsupported(caplog): assert "Paris" in result -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() @pytest.mark.skip(reason="Highly flaky on ci") def test_llm_call_when_stop_is_unsupported_when_additional_drop_params_is_provided( caplog, diff --git a/lib/crewai/tests/test_project.py b/lib/crewai/tests/test_project.py index 33cf228f7..4962ff08c 100644 --- a/lib/crewai/tests/test_project.py +++ b/lib/crewai/tests/test_project.py @@ -171,7 +171,7 @@ def test_task_guardrail(): assert reporting_task.guardrail is None -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_before_kickoff_modification(): crew = InternalCrew() inputs = {"topic": "LLMs"} @@ -179,7 +179,7 @@ def test_before_kickoff_modification(): assert "bicycles" in result.raw, "Before kickoff function did not modify inputs" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_after_kickoff_modification(): crew = InternalCrew() # Assuming the crew execution returns a dict @@ -190,14 +190,14 @@ def test_after_kickoff_modification(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_before_kickoff_with_none_input(): crew = InternalCrew() crew.crew().kickoff(None) # Test should pass without raising exceptions -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multiple_before_after_kickoff(): @CrewBase class MultipleHooksCrew: diff --git a/lib/crewai/tests/test_streaming_integration.py b/lib/crewai/tests/test_streaming_integration.py index 00066e6ea..f89fe7ff7 100644 --- a/lib/crewai/tests/test_streaming_integration.py +++ b/lib/crewai/tests/test_streaming_integration.py @@ -31,7 +31,7 @@ def simple_task(researcher: Agent) -> Task: class TestStreamingCrewIntegration: """Integration tests for crew streaming that match documentation examples.""" - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_basic_crew_streaming_from_docs( self, researcher: Agent, simple_task: Task ) -> None: @@ -57,7 +57,7 @@ class TestStreamingCrewIntegration: assert result.raw is not None assert len(result.raw) > 0 - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_streaming_with_chunk_context_from_docs( self, researcher: Agent, simple_task: Task ) -> None: @@ -89,7 +89,7 @@ class TestStreamingCrewIntegration: result = streaming.result assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_streaming_properties_from_docs( self, researcher: Agent, simple_task: Task ) -> None: @@ -114,7 +114,7 @@ class TestStreamingCrewIntegration: result = streaming.result assert result.raw is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() @pytest.mark.asyncio async def test_async_streaming_from_docs( self, researcher: Agent, simple_task: Task @@ -140,7 +140,7 @@ class TestStreamingCrewIntegration: result = streaming.result assert result.raw is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_kickoff_for_each_streaming_from_docs( self, researcher: Agent, simple_task: Task ) -> None: @@ -174,7 +174,7 @@ class TestStreamingCrewIntegration: class TestStreamingFlowIntegration: """Integration tests for flow streaming that match documentation examples.""" - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_basic_flow_streaming_from_docs(self) -> None: """Test basic flow streaming example from documentation.""" @@ -223,7 +223,7 @@ class TestStreamingFlowIntegration: result = streaming.result assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_flow_streaming_properties_from_docs(self) -> None: """Test flow streaming properties example from documentation.""" @@ -247,7 +247,7 @@ class TestStreamingFlowIntegration: result = streaming.result assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() @pytest.mark.asyncio async def test_async_flow_streaming_from_docs(self) -> None: """Test async flow streaming example from documentation.""" diff --git a/lib/crewai/tests/test_task.py b/lib/crewai/tests/test_task.py index 370b5d270..41c14cb87 100644 --- a/lib/crewai/tests/test_task.py +++ b/lib/crewai/tests/test_task.py @@ -289,7 +289,7 @@ def test_guardrail_type_error(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_pydantic_sequential(): class ScoreOutput(BaseModel): score: int @@ -314,7 +314,7 @@ def test_output_pydantic_sequential(): assert result.to_dict() == {"score": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_pydantic_hierarchical(): class ScoreOutput(BaseModel): score: int @@ -344,7 +344,7 @@ def test_output_pydantic_hierarchical(): assert result.to_dict() == {"score": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_json_sequential(): import uuid @@ -376,7 +376,7 @@ def test_output_json_sequential(): os.remove(output_file) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_json_hierarchical(): class ScoreOutput(BaseModel): score: int @@ -406,7 +406,7 @@ def test_output_json_hierarchical(): assert result.to_dict() == {"score": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_inject_date(): reporter = Agent( role="Reporter", @@ -431,7 +431,7 @@ def test_inject_date(): assert "2025-05-21" in result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_inject_date_custom_format(): reporter = Agent( role="Reporter", @@ -457,7 +457,7 @@ def test_inject_date_custom_format(): assert "May 21, 2025" in result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_no_inject_date(): reporter = Agent( role="Reporter", @@ -482,7 +482,7 @@ def test_no_inject_date(): assert "2025-05-21" not in result.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_json_property_without_output_json(): class ScoreOutput(BaseModel): score: int @@ -510,7 +510,7 @@ def test_json_property_without_output_json(): assert "No JSON output found in the final task." in str(excinfo.value) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_json_dict_sequential(): class ScoreOutput(BaseModel): score: int @@ -535,7 +535,7 @@ def test_output_json_dict_sequential(): assert result.to_dict() == {"score": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_json_dict_hierarchical(): class ScoreOutput(BaseModel): score: int @@ -565,7 +565,7 @@ def test_output_json_dict_hierarchical(): assert result.to_dict() == {"score": 4} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_pydantic_to_another_task(): class ScoreOutput(BaseModel): score: int @@ -603,7 +603,7 @@ def test_output_pydantic_to_another_task(): assert pydantic_result.score == 5 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_output_json_to_another_task(): class ScoreOutput(BaseModel): score: int @@ -634,7 +634,7 @@ def test_output_json_to_another_task(): assert '{"score": 3}' == result.json -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_save_task_output(): scorer = Agent( role="Scorer", @@ -658,7 +658,7 @@ def test_save_task_output(): save_file.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_save_task_json_output(): from unittest.mock import patch @@ -696,7 +696,7 @@ def test_save_task_json_output(): assert "score" in data -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_save_task_pydantic_output(tmp_path, monkeypatch): """Test saving pydantic output to a file. @@ -734,7 +734,7 @@ def test_save_task_pydantic_output(tmp_path, monkeypatch): assert {"score": 4} == json.loads(output_path.read_text()) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_custom_converter_cls(): class ScoreOutput(BaseModel): score: int @@ -766,7 +766,7 @@ def test_custom_converter_cls(): mock_to_pydantic.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_increment_delegations_for_hierarchical_process(): scorer = Agent( role="Scorer", @@ -793,7 +793,7 @@ def test_increment_delegations_for_hierarchical_process(): increment_delegations.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_increment_delegations_for_sequential_process(): manager = Agent( role="Manager", @@ -827,7 +827,7 @@ def test_increment_delegations_for_sequential_process(): increment_delegations.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_increment_tool_errors(): from crewai.tools import tool @@ -1281,7 +1281,7 @@ def test_github_issue_3149_reproduction(): assert task.output_file == "test_output.txt" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_execution_times(): researcher = Agent( role="Researcher", @@ -1551,7 +1551,7 @@ def test_task_with_no_max_execution_time(): execute.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_with_max_execution_time(): from crewai.tools import tool @@ -1585,7 +1585,7 @@ def test_task_with_max_execution_time(): assert result.raw == "okay" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_with_max_execution_time_exceeded(): from crewai.tools import tool @@ -1619,7 +1619,7 @@ def test_task_with_max_execution_time_exceeded(): task.execute_sync(agent=researcher) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_interpolation_with_hyphens(): agent = Agent( role="Researcher", @@ -1688,7 +1688,7 @@ def test_task_copy_with_list_context(): assert copied_task2.context[0] is task1 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_output_includes_messages(): """Test that TaskOutput includes messages from agent execution.""" researcher = Agent( diff --git a/lib/crewai/tests/test_task_guardrails.py b/lib/crewai/tests/test_task_guardrails.py index dd24458d3..7ceaf847b 100644 --- a/lib/crewai/tests/test_task_guardrails.py +++ b/lib/crewai/tests/test_task_guardrails.py @@ -177,7 +177,7 @@ def task_output(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_guardrail_process_output(task_output): guardrail = LLMGuardrail( description="Ensure the result has less than 10 words", llm=LLM(model="gpt-4o") @@ -197,30 +197,25 @@ def test_task_guardrail_process_output(task_output): assert result[1] == task_output.raw -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_guardrail_emits_events(sample_agent): + import threading + started_guardrail = [] completed_guardrail = [] + condition = threading.Condition() - task = create_smart_task( - description="Gather information about available books on the First World War", - agent=sample_agent, - expected_output="A list of available books on the First World War", - guardrail="Ensure the authors are from Italy", - ) - - with crewai_event_bus.scoped_handlers(): - - @crewai_event_bus.on(LLMGuardrailStartedEvent) - def handle_guardrail_started(source, event): - assert source == task + @crewai_event_bus.on(LLMGuardrailStartedEvent) + def handle_guardrail_started(source, event): + with condition: started_guardrail.append( {"guardrail": event.guardrail, "retry_count": event.retry_count} ) + condition.notify() - @crewai_event_bus.on(LLMGuardrailCompletedEvent) - def handle_guardrail_completed(source, event): - assert source == task + @crewai_event_bus.on(LLMGuardrailCompletedEvent) + def handle_guardrail_completed(source, event): + with condition: completed_guardrail.append( { "success": event.success, @@ -229,50 +224,72 @@ def test_guardrail_emits_events(sample_agent): "retry_count": event.retry_count, } ) + condition.notify() - result = task.execute_sync(agent=sample_agent) + task = create_smart_task( + description="Gather information about available books on the First World War", + agent=sample_agent, + expected_output="A list of available books on the First World War", + guardrail="Ensure the authors are from Italy", + ) - def custom_guardrail(result: TaskOutput): - return (True, "good result from callable function") + result = task.execute_sync(agent=sample_agent) - task = create_smart_task( - description="Test task", - expected_output="Output", - guardrail=custom_guardrail, + with condition: + success = condition.wait_for( + lambda: len(started_guardrail) >= 2 and len(completed_guardrail) >= 2, + timeout=5 ) + assert success, f"Timeout waiting for first task events. Started: {len(started_guardrail)}, Completed: {len(completed_guardrail)}" - task.execute_sync(agent=sample_agent) + def custom_guardrail(result: TaskOutput): + return (True, "good result from callable function") - expected_started_events = [ - {"guardrail": "Ensure the authors are from Italy", "retry_count": 0}, - {"guardrail": "Ensure the authors are from Italy", "retry_count": 1}, - { - "guardrail": """def custom_guardrail(result: TaskOutput): - return (True, "good result from callable function")""", - "retry_count": 0, - }, - ] + task = create_smart_task( + description="Test task", + expected_output="Output", + guardrail=custom_guardrail, + ) - expected_completed_events = [ - { - "success": False, - "result": None, - "error": "The output indicates that none of the authors mentioned are from Italy, while the guardrail requires authors to be from Italy. Therefore, the output does not comply with the guardrail.", - "retry_count": 0, - }, - {"success": True, "result": result.raw, "error": None, "retry_count": 1}, - { - "success": True, - "result": "good result from callable function", - "error": None, - "retry_count": 0, - }, - ] - assert started_guardrail == expected_started_events - assert completed_guardrail == expected_completed_events + task.execute_sync(agent=sample_agent) + + with condition: + success = condition.wait_for( + lambda: len(started_guardrail) >= 3 and len(completed_guardrail) >= 3, + timeout=5 + ) + assert success, f"Timeout waiting for second task events. Started: {len(started_guardrail)}, Completed: {len(completed_guardrail)}" + + expected_started_events = [ + {"guardrail": "Ensure the authors are from Italy", "retry_count": 0}, + {"guardrail": "Ensure the authors are from Italy", "retry_count": 1}, + { + "guardrail": """def custom_guardrail(result: TaskOutput): + return (True, "good result from callable function")""", + "retry_count": 0, + }, + ] + + expected_completed_events = [ + { + "success": False, + "result": None, + "error": "The output indicates that none of the authors mentioned are from Italy, while the guardrail requires authors to be from Italy. Therefore, the output does not comply with the guardrail.", + "retry_count": 0, + }, + {"success": True, "result": result.raw, "error": None, "retry_count": 1}, + { + "success": True, + "result": "good result from callable function", + "error": None, + "retry_count": 0, + }, + ] + assert started_guardrail == expected_started_events + assert completed_guardrail == expected_completed_events -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_guardrail_when_an_error_occurs(sample_agent, task_output): with ( patch( diff --git a/lib/crewai/tests/tools/agent_tools/test_agent_tools.py b/lib/crewai/tests/tools/agent_tools/test_agent_tools.py index 89d2798d6..8119ec0fd 100644 --- a/lib/crewai/tests/tools/agent_tools/test_agent_tools.py +++ b/lib/crewai/tests/tools/agent_tools/test_agent_tools.py @@ -16,14 +16,7 @@ delegate_tool = tools[0] ask_tool = tools[1] -@pytest.fixture(scope="module") -def vcr_config(request) -> dict: - return { - "cassette_library_dir": os.path.join(os.path.dirname(__file__), "cassettes"), - } - - -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_delegate_work(): result = delegate_tool.run( coworker="researcher", @@ -37,7 +30,7 @@ def test_delegate_work(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_delegate_work_with_wrong_co_worker_variable(): result = delegate_tool.run( coworker="researcher", @@ -51,7 +44,7 @@ def test_delegate_work_with_wrong_co_worker_variable(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_ask_question(): result = ask_tool.run( coworker="researcher", @@ -65,7 +58,7 @@ def test_ask_question(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_ask_question_with_wrong_co_worker_variable(): result = ask_tool.run( coworker="researcher", @@ -79,7 +72,7 @@ def test_ask_question_with_wrong_co_worker_variable(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_delegate_work_withwith_coworker_as_array(): result = delegate_tool.run( coworker="[researcher]", @@ -93,7 +86,7 @@ def test_delegate_work_withwith_coworker_as_array(): ) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_ask_question_with_coworker_as_array(): result = ask_tool.run( coworker="[researcher]", diff --git a/lib/crewai/tests/tools/test_base_tool.py b/lib/crewai/tests/tools/test_base_tool.py index 2aa9ac8bf..c23f3b876 100644 --- a/lib/crewai/tests/tools/test_base_tool.py +++ b/lib/crewai/tests/tools/test_base_tool.py @@ -197,7 +197,7 @@ def test_run_does_not_call_asyncio_run_for_sync_tools(): assert sync_result == "Processed test synchronously" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_max_usage_count_is_respected(): class IteratingTool(BaseTool): name: str = "iterating_tool" diff --git a/lib/crewai/tests/tools/test_structured_tool.py b/lib/crewai/tests/tools/test_structured_tool.py index 15e034f3e..999c13072 100644 --- a/lib/crewai/tests/tools/test_structured_tool.py +++ b/lib/crewai/tests/tools/test_structured_tool.py @@ -197,7 +197,7 @@ def build_simple_crew(tool): return Crew(agents=[agent1], tasks=[say_hi_task]) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_async_tool_using_within_isolated_crew(custom_tool): crew = build_simple_crew(custom_tool) result = crew.kickoff() @@ -205,7 +205,7 @@ def test_async_tool_using_within_isolated_crew(custom_tool): assert result.raw == "Hello World from Custom Tool" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_async_tool_using_decorator_within_isolated_crew(custom_tool_decorator): crew = build_simple_crew(custom_tool_decorator) result = crew.kickoff() @@ -213,7 +213,7 @@ def test_async_tool_using_decorator_within_isolated_crew(custom_tool_decorator): assert result.raw == "Hello World from Custom Tool" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_async_tool_within_flow(custom_tool): from crewai.flow.flow import Flow @@ -230,7 +230,7 @@ def test_async_tool_within_flow(custom_tool): assert result.raw == "Hello World from Custom Tool" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_async_tool_using_decorator_within_flow(custom_tool_decorator): from crewai.flow.flow import Flow diff --git a/lib/crewai/tests/tools/test_tool_usage.py b/lib/crewai/tests/tools/test_tool_usage.py index 927031302..bf65f9ec6 100644 --- a/lib/crewai/tests/tools/test_tool_usage.py +++ b/lib/crewai/tests/tools/test_tool_usage.py @@ -564,16 +564,23 @@ def test_tool_validate_input_error_event(): patch("json_repair.repair_json", side_effect=Exception("Failed to repair")), ): received_events = [] + condition = threading.Condition() @crewai_event_bus.on(ToolValidateInputErrorEvent) def event_handler(source, event): - received_events.append(event) + with condition: + received_events.append(event) + condition.notify() # Test invalid input invalid_input = "invalid json {[}" with pytest.raises(Exception): # noqa: B017 tool_usage._validate_tool_input(invalid_input) + with condition: + if not received_events: + condition.wait(timeout=5) + # Verify event was emitted assert len(received_events) == 1, "Expected one event to be emitted" event = received_events[0] diff --git a/lib/crewai/tests/tracing/test_trace_enable_disable.py b/lib/crewai/tests/tracing/test_trace_enable_disable.py index 6304fbb78..ae710863b 100644 --- a/lib/crewai/tests/tracing/test_trace_enable_disable.py +++ b/lib/crewai/tests/tracing/test_trace_enable_disable.py @@ -11,7 +11,7 @@ from tests.utils import wait_for_event_handlers class TestTraceEnableDisable: """Test suite to verify trace sending behavior with VCR cassette recording.""" - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_no_http_calls_when_disabled_via_env(self): """Test execution when tracing disabled via CREWAI_TRACING_ENABLED=false.""" with pytest.MonkeyPatch.context() as mp: @@ -36,7 +36,7 @@ class TestTraceEnableDisable: assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_no_http_calls_when_disabled_via_tracing_false(self): """Test execution when tracing=False explicitly set.""" with pytest.MonkeyPatch.context() as mp: @@ -60,7 +60,7 @@ class TestTraceEnableDisable: assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_calls_when_enabled_via_env(self): """Test execution when tracing enabled via CREWAI_TRACING_ENABLED=true.""" with pytest.MonkeyPatch.context() as mp: @@ -86,7 +86,7 @@ class TestTraceEnableDisable: assert result is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_calls_when_enabled_via_tracing_true(self): """Test execution when tracing=True explicitly set.""" with pytest.MonkeyPatch.context() as mp: diff --git a/lib/crewai/tests/tracing/test_tracing.py b/lib/crewai/tests/tracing/test_tracing.py index cb340c6d4..555446b26 100644 --- a/lib/crewai/tests/tracing/test_tracing.py +++ b/lib/crewai/tests/tracing/test_tracing.py @@ -154,7 +154,7 @@ class TestTraceListenerSetup: "mark_trace_batch_as_failed": mock_mark_failed, } - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_listener_collects_crew_events(self): """Test that trace listener properly collects events from crew execution""" @@ -191,7 +191,7 @@ class TestTraceListenerSetup: assert trace_listener.batch_manager.is_batch_initialized() assert trace_listener.batch_manager.current_batch is not None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_batch_manager_finalizes_batch_clears_buffer(self): """Test that batch manager properly finalizes batch and clears buffer""" @@ -257,7 +257,7 @@ class TestTraceListenerSetup: assert finalize_mock.call_count >= 1 - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_events_collection_batch_manager(self, mock_plus_api_calls): """Test that trace listener properly collects events from crew execution""" @@ -318,7 +318,7 @@ class TestTraceListenerSetup: assert hasattr(event, "event_data") assert hasattr(event, "type") - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_listener_disabled_when_env_false(self): """Test that trace listener doesn't make HTTP calls when tracing is disabled""" @@ -389,7 +389,7 @@ class TestTraceListenerSetup: Crew(agents=[agent], tasks=[task], verbose=True) assert mock_listener_setup.call_count >= 1 - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_listener_setup_correctly_for_flow(self): """Test that trace listener is set up correctly when enabled""" @@ -413,7 +413,7 @@ class TestTraceListenerSetup: FlowExample() assert mock_listener_setup.call_count >= 1 - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_trace_listener_ephemeral_batch(self): """Test that trace listener properly handles ephemeral batches""" with ( @@ -449,13 +449,14 @@ class TestTraceListenerSetup: crew.kickoff() - wait_for_event_handlers() - - assert trace_listener.batch_manager.is_batch_initialized(), ( + initialized = trace_listener.batch_manager.wait_for_batch_initialization(timeout=5.0) + assert initialized, ( "Batch should have been initialized for unauthenticated user" ) - @pytest.mark.vcr(filter_headers=["authorization"]) + wait_for_event_handlers() + + @pytest.mark.vcr() def test_trace_listener_with_authenticated_user(self): """Test that trace listener properly handles authenticated batches""" with patch.dict( @@ -485,12 +486,13 @@ class TestTraceListenerSetup: crew = Crew(agents=[agent], tasks=[task], tracing=True) crew.kickoff() - wait_for_event_handlers() - - assert trace_listener.batch_manager.is_batch_initialized(), ( + initialized = trace_listener.batch_manager.wait_for_batch_initialization(timeout=5.0) + assert initialized, ( "Batch should have been initialized for authenticated user" ) + wait_for_event_handlers() + # Helper method to ensure cleanup def teardown_method(self): """Cleanup after each test method""" @@ -523,7 +525,7 @@ class TestTraceListenerSetup: if hasattr(EventListener, "_instance"): EventListener._instance = None - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_first_time_user_trace_collection_with_timeout(self, mock_plus_api_calls): """Test first-time user trace collection logic with timeout behavior""" @@ -596,7 +598,7 @@ class TestTraceListenerSetup: mock_mark_completed.assert_called_once() - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_first_time_user_trace_collection_user_accepts(self, mock_plus_api_calls): """Test first-time user trace collection when user accepts viewing traces""" @@ -657,6 +659,10 @@ class TestTraceListenerSetup: "https://crewai.com/trace/mock-id" ) + assert trace_listener.first_time_handler.is_first_time is True + + trace_listener.first_time_handler.collected_events = True + with ( patch.object( trace_listener.first_time_handler, @@ -667,22 +673,16 @@ class TestTraceListenerSetup: trace_listener.first_time_handler, "_display_ephemeral_trace_link" ) as mock_display_link, ): - assert trace_listener.first_time_handler.is_first_time is True - - trace_listener.first_time_handler.collected_events = True - crew.kickoff() wait_for_event_handlers() - trace_listener.first_time_handler.handle_execution_completion() - mock_init_backend.assert_called_once() mock_display_link.assert_called_once() - mock_mark_completed.assert_called_once() + mock_mark_completed.assert_called_once() - @pytest.mark.vcr(filter_headers=["authorization"]) + @pytest.mark.vcr() def test_first_time_user_trace_consolidation_logic(self, mock_plus_api_calls): """Test the consolidation logic for first-time users vs regular tracing""" with ( diff --git a/lib/crewai/tests/utilities/events/test_async_event_bus.py b/lib/crewai/tests/utilities/events/test_async_event_bus.py index 9925a0e6b..00f8c88e5 100644 --- a/lib/crewai/tests/utilities/events/test_async_event_bus.py +++ b/lib/crewai/tests/utilities/events/test_async_event_bus.py @@ -81,22 +81,27 @@ async def test_multiple_async_handlers(): async def test_mixed_sync_and_async_handlers(): sync_events = [] async_events = [] + sync_done = asyncio.Event() + async_done = asyncio.Event() with crewai_event_bus.scoped_handlers(): @crewai_event_bus.on(AsyncTestEvent) def sync_handler(source: object, event: BaseEvent) -> None: sync_events.append(event) + sync_done.set() @crewai_event_bus.on(AsyncTestEvent) async def async_handler(source: object, event: BaseEvent) -> None: await asyncio.sleep(0.01) async_events.append(event) + async_done.set() event = AsyncTestEvent(type="mixed_test") crewai_event_bus.emit("test_source", event) - await asyncio.sleep(0.1) + await asyncio.wait_for(sync_done.wait(), timeout=5) + await asyncio.wait_for(async_done.wait(), timeout=5) assert len(sync_events) == 1 assert len(async_events) == 1 diff --git a/lib/crewai/tests/utilities/events/test_crewai_event_bus.py b/lib/crewai/tests/utilities/events/test_crewai_event_bus.py index 9e9d9adaf..abeb4d619 100644 --- a/lib/crewai/tests/utilities/events/test_crewai_event_bus.py +++ b/lib/crewai/tests/utilities/events/test_crewai_event_bus.py @@ -11,14 +11,24 @@ class TestEvent(BaseEvent): def test_specific_event_handler(): mock_handler = Mock() + condition = threading.Condition() + handler_called = [False] @crewai_event_bus.on(TestEvent) def handler(source, event): - mock_handler(source, event) + with condition: + mock_handler(source, event) + handler_called[0] = True + condition.notify() event = TestEvent(type="test_event") crewai_event_bus.emit("source_object", event) + with condition: + if not handler_called[0]: + condition.wait(timeout=5) + + assert handler_called[0], "Handler was not called within timeout" mock_handler.assert_called_once_with("source_object", event) @@ -26,18 +36,34 @@ def test_multiple_handlers_same_event(): """Test that multiple handlers can be registered for the same event type.""" mock_handler1 = Mock() mock_handler2 = Mock() + condition = threading.Condition() + handlers_called = {"handler1": False, "handler2": False} @crewai_event_bus.on(TestEvent) def handler1(source, event): - mock_handler1(source, event) + with condition: + mock_handler1(source, event) + handlers_called["handler1"] = True + condition.notify() @crewai_event_bus.on(TestEvent) def handler2(source, event): - mock_handler2(source, event) + with condition: + mock_handler2(source, event) + handlers_called["handler2"] = True + condition.notify() event = TestEvent(type="test_event") crewai_event_bus.emit("source_object", event) + with condition: + while not all(handlers_called.values()): + condition.wait(timeout=5) + if not all(handlers_called.values()): + break + + assert handlers_called["handler1"], "Handler1 was not called within timeout" + assert handlers_called["handler2"], "Handler2 was not called within timeout" mock_handler1.assert_called_once_with("source_object", event) mock_handler2.assert_called_once_with("source_object", event) diff --git a/lib/crewai/tests/utilities/events/test_shutdown.py b/lib/crewai/tests/utilities/events/test_shutdown.py index eeac0c667..28cd2b587 100644 --- a/lib/crewai/tests/utilities/events/test_shutdown.py +++ b/lib/crewai/tests/utilities/events/test_shutdown.py @@ -63,17 +63,22 @@ async def test_aemit_during_shutdown(): def test_shutdown_flag_prevents_emit(): bus = CrewAIEventsBus() emitted_count = [0] + condition = threading.Condition() with bus.scoped_handlers(): @bus.on(ShutdownTestEvent) def handler(source: object, event: BaseEvent) -> None: - emitted_count[0] += 1 + with condition: + emitted_count[0] += 1 + condition.notify() event1 = ShutdownTestEvent(type="before_shutdown") - bus.emit("test_source", event1) + future = bus.emit("test_source", event1) + + if future: + future.result(timeout=2.0) - time.sleep(0.1) assert emitted_count[0] == 1 bus._shutting_down = True @@ -90,14 +95,15 @@ def test_shutdown_flag_prevents_emit(): def test_concurrent_access_during_shutdown_flag(): bus = CrewAIEventsBus() received_events = [] - lock = threading.Lock() + condition = threading.Condition() with bus.scoped_handlers(): @bus.on(ShutdownTestEvent) def handler(source: object, event: BaseEvent) -> None: - with lock: + with condition: received_events.append(event) + condition.notify() def emit_events() -> None: for i in range(10): @@ -118,7 +124,8 @@ def test_concurrent_access_during_shutdown_flag(): emit_thread.join() shutdown_thread.join() - time.sleep(0.2) + with condition: + condition.wait_for(lambda: len(received_events) > 0, timeout=2) assert len(received_events) < 10 assert len(received_events) > 0 @@ -153,36 +160,47 @@ def test_scoped_handlers_cleanup(): received_before = [] received_during = [] received_after = [] + condition = threading.Condition() with bus.scoped_handlers(): @bus.on(ShutdownTestEvent) def before_handler(source: object, event: BaseEvent) -> None: - received_before.append(event) + with condition: + received_before.append(event) + condition.notify() with bus.scoped_handlers(): @bus.on(ShutdownTestEvent) def during_handler(source: object, event: BaseEvent) -> None: - received_during.append(event) + with condition: + received_during.append(event) + condition.notify() event1 = ShutdownTestEvent(type="during") bus.emit("source", event1) - time.sleep(0.1) + + with condition: + condition.wait_for(lambda: len(received_during) >= 1, timeout=2) assert len(received_before) == 0 assert len(received_during) == 1 event2 = ShutdownTestEvent(type="after_inner_scope") bus.emit("source", event2) - time.sleep(0.1) + + with condition: + condition.wait_for(lambda: len(received_before) >= 1, timeout=2) assert len(received_before) == 1 assert len(received_during) == 1 event3 = ShutdownTestEvent(type="after_outer_scope") bus.emit("source", event3) - time.sleep(0.1) + + with condition: + condition.wait(timeout=0.2) assert len(received_before) == 1 assert len(received_during) == 1 @@ -224,24 +242,36 @@ async def test_mixed_sync_async_handler_execution(): bus = CrewAIEventsBus() sync_executed = [] async_executed = [] + condition = threading.Condition() with bus.scoped_handlers(): @bus.on(ShutdownTestEvent) def sync_handler(source: object, event: BaseEvent) -> None: time.sleep(0.01) - sync_executed.append(event) + with condition: + sync_executed.append(event) + condition.notify() @bus.on(ShutdownTestEvent) async def async_handler(source: object, event: BaseEvent) -> None: await asyncio.sleep(0.01) - async_executed.append(event) + with condition: + async_executed.append(event) + condition.notify() for i in range(5): event = ShutdownTestEvent(type=f"event_{i}") bus.emit("source", event) - await asyncio.sleep(0.2) + def wait_for_completion(): + with condition: + return condition.wait_for( + lambda: len(sync_executed) == 5 and len(async_executed) == 5, + timeout=5 + ) + + await asyncio.get_event_loop().run_in_executor(None, wait_for_completion) assert len(sync_executed) == 5 assert len(async_executed) == 5 diff --git a/lib/crewai/tests/utilities/test_converter.py b/lib/crewai/tests/utilities/test_converter.py index 4fa6d2c2b..3a9bacdd1 100644 --- a/lib/crewai/tests/utilities/test_converter.py +++ b/lib/crewai/tests/utilities/test_converter.py @@ -21,13 +21,6 @@ from pydantic import BaseModel import pytest -@pytest.fixture(scope="module") -def vcr_config(request: pytest.FixtureRequest) -> dict[str, str]: - return { - "cassette_library_dir": os.path.join(os.path.dirname(__file__), "cassettes"), - } - - # Sample Pydantic models for testing class EmailResponse(BaseModel): previous_message_content: str @@ -352,7 +345,7 @@ def test_generate_model_description_dict_field() -> None: assert description["json_schema"]["strict"] is True -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_convert_with_instructions() -> None: llm = LLM(model="gpt-4o-mini") sample_text = "Name: Alice, Age: 30" @@ -374,7 +367,7 @@ def test_convert_with_instructions() -> None: assert output.age == 30 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_converter_with_llama3_2_model() -> None: llm = LLM(model="openrouter/meta-llama/llama-3.2-3b-instruct") sample_text = "Name: Alice Llama, Age: 30" @@ -410,7 +403,7 @@ def test_converter_with_llama3_1_model() -> None: assert output.age == 30 -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_converter_with_nested_model() -> None: llm = LLM(model="gpt-4o-mini") sample_text = "Name: John Doe\nAge: 30\nAddress: 123 Main St, Anytown, 12345" diff --git a/lib/crewai/tests/utilities/test_events.py b/lib/crewai/tests/utilities/test_events.py index cc410a300..e655c0c5a 100644 --- a/lib/crewai/tests/utilities/test_events.py +++ b/lib/crewai/tests/utilities/test_events.py @@ -54,13 +54,6 @@ import pytest from ..utils import wait_for_event_handlers -@pytest.fixture(scope="module") -def vcr_config(request) -> dict: - return { - "cassette_library_dir": os.path.join(os.path.dirname(__file__), "cassettes"), - } - - @pytest.fixture(scope="module") def base_agent(): return Agent( @@ -99,7 +92,7 @@ def reset_event_listener_singleton(): EventListener._instance._initialized = original_initialized -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_emits_start_kickoff_event( base_agent, base_task, reset_event_listener_singleton ): @@ -133,7 +126,7 @@ def test_crew_emits_start_kickoff_event( assert received_events[0].type == "crew_kickoff_started" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_emits_end_kickoff_event(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -156,7 +149,7 @@ def test_crew_emits_end_kickoff_event(base_agent, base_task): assert received_events[0].type == "crew_kickoff_completed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_emits_test_kickoff_type_event(base_agent, base_task): received_events = [] @@ -189,7 +182,7 @@ def test_crew_emits_test_kickoff_type_event(base_agent, base_task): assert received_events[2].type == "crew_test_completed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_emits_kickoff_failed_event(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -215,7 +208,7 @@ def test_crew_emits_kickoff_failed_event(base_agent, base_task): assert received_events[0].type == "crew_kickoff_failed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_crew_emits_start_task_event(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -235,10 +228,8 @@ def test_crew_emits_start_task_event(base_agent, base_task): assert received_events[0].type == "task_started" -@pytest.mark.vcr(filter_headers=["authorization"]) -def test_crew_emits_end_task_event( - base_agent, base_task, reset_event_listener_singleton -): +@pytest.mark.vcr() +def test_crew_emits_end_task_event(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -247,21 +238,8 @@ def test_crew_emits_end_task_event( received_events.append(event) event_received.set() - mock_span = Mock() - - mock_telemetry = Mock() - mock_telemetry.task_started = Mock(return_value=mock_span) - mock_telemetry.task_ended = Mock(return_value=mock_span) - mock_telemetry.set_tracer = Mock() - mock_telemetry.crew_execution_span = Mock() - mock_telemetry.end_crew = Mock() - - with patch("crewai.events.event_listener.Telemetry", return_value=mock_telemetry): - crew = Crew(agents=[base_agent], tasks=[base_task], name="TestCrew") - crew.kickoff() - - mock_telemetry.task_started.assert_called_once_with(crew=crew, task=base_task) - mock_telemetry.task_ended.assert_called_once_with(mock_span, base_task, crew) + crew = Crew(agents=[base_agent], tasks=[base_task], name="TestCrew") + crew.kickoff() assert event_received.wait(timeout=5), "Timeout waiting for task completed event" assert len(received_events) == 1 @@ -269,7 +247,7 @@ def test_crew_emits_end_task_event( assert received_events[0].type == "task_completed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_task_emits_failed_event_on_execution_error(base_agent, base_task): received_events = [] received_sources = [] @@ -311,46 +289,51 @@ def test_task_emits_failed_event_on_execution_error(base_agent, base_task): assert received_events[0].type == "task_failed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_emits_execution_started_and_completed_events(base_agent, base_task): - received_events = [] - lock = threading.Lock() - all_events_received = threading.Event() + started_events: list[AgentExecutionStartedEvent] = [] + completed_events: list[AgentExecutionCompletedEvent] = [] + condition = threading.Condition() @crewai_event_bus.on(AgentExecutionStartedEvent) def handle_agent_start(source, event): - with lock: - received_events.append(event) + with condition: + started_events.append(event) + condition.notify() @crewai_event_bus.on(AgentExecutionCompletedEvent) def handle_agent_completed(source, event): - with lock: - received_events.append(event) - if len(received_events) >= 2: - all_events_received.set() + with condition: + completed_events.append(event) + condition.notify() crew = Crew(agents=[base_agent], tasks=[base_task], name="TestCrew") crew.kickoff() - assert all_events_received.wait(timeout=5), ( - "Timeout waiting for agent execution events" - ) - assert len(received_events) == 2 - assert received_events[0].agent == base_agent - assert received_events[0].task == base_task - assert received_events[0].tools == [] - assert isinstance(received_events[0].task_prompt, str) + with condition: + success = condition.wait_for( + lambda: len(started_events) >= 1 and len(completed_events) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for agent execution events" + + assert len(started_events) == 1 + assert len(completed_events) == 1 + assert started_events[0].agent == base_agent + assert started_events[0].task == base_task + assert started_events[0].tools == [] + assert isinstance(started_events[0].task_prompt, str) assert ( - received_events[0].task_prompt + started_events[0].task_prompt == "Just say hi\n\nThis is the expected criteria for your final answer: hi\nyou MUST return the actual complete content as the final answer, not a summary." ) - assert isinstance(received_events[0].timestamp, datetime) - assert received_events[0].type == "agent_execution_started" - assert isinstance(received_events[1].timestamp, datetime) - assert received_events[1].type == "agent_execution_completed" + assert isinstance(started_events[0].timestamp, datetime) + assert started_events[0].type == "agent_execution_started" + assert isinstance(completed_events[0].timestamp, datetime) + assert completed_events[0].type == "agent_execution_completed" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_agent_emits_execution_error_event(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -393,7 +376,7 @@ class SayHiTool(BaseTool): return "hi" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_tools_emits_finished_events(): received_events = [] event_received = threading.Event() @@ -430,7 +413,7 @@ def test_tools_emits_finished_events(): assert isinstance(received_events[0].timestamp, datetime) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_tools_emits_error_events(): received_events = [] lock = threading.Lock() @@ -606,7 +589,7 @@ def test_flow_emits_method_execution_started_event(): assert event.type == "method_execution_started" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_register_handler_adds_new_handler(base_agent, base_task): received_events = [] event_received = threading.Event() @@ -626,7 +609,7 @@ def test_register_handler_adds_new_handler(base_agent, base_task): assert received_events[0].type == "crew_kickoff_started" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_multiple_handlers_for_same_event(base_agent, base_task): received_events_1 = [] received_events_2 = [] @@ -854,34 +837,44 @@ def test_flow_method_execution_finished_includes_serialized_state(): assert final_output == "final_result" -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_emits_call_started_event(): - received_events = [] + started_events: list[LLMCallStartedEvent] = [] + completed_events: list[LLMCallCompletedEvent] = [] + condition = threading.Condition() @crewai_event_bus.on(LLMCallStartedEvent) def handle_llm_call_started(source, event): - received_events.append(event) + with condition: + started_events.append(event) + condition.notify() @crewai_event_bus.on(LLMCallCompletedEvent) def handle_llm_call_completed(source, event): - received_events.append(event) + with condition: + completed_events.append(event) + condition.notify() llm = LLM(model="gpt-4o-mini") llm.call("Hello, how are you?") - wait_for_event_handlers() - assert len(received_events) == 2 - assert received_events[0].type == "llm_call_started" - assert received_events[1].type == "llm_call_completed" + with condition: + success = condition.wait_for( + lambda: len(started_events) >= 1 and len(completed_events) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for LLM events" - assert received_events[0].task_name is None - assert received_events[0].agent_role is None - assert received_events[0].agent_id is None - assert received_events[0].task_id is None + assert started_events[0].type == "llm_call_started" + assert completed_events[0].type == "llm_call_completed" + + assert started_events[0].task_name is None + assert started_events[0].agent_role is None + assert started_events[0].agent_id is None + assert started_events[0].task_id is None -@pytest.mark.vcr(filter_headers=["authorization"]) -@pytest.mark.isolated +@pytest.mark.vcr() def test_llm_emits_call_failed_event(): received_events = [] event_received = threading.Event() @@ -913,7 +906,7 @@ def test_llm_emits_call_failed_event(): assert received_events[0].task_id is None -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_emits_stream_chunk_events(): """Test that LLM emits stream chunk events when streaming is enabled.""" received_chunks = [] @@ -941,7 +934,7 @@ def test_llm_emits_stream_chunk_events(): assert "".join(received_chunks) == response -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_no_stream_chunks_when_streaming_disabled(): """Test that LLM doesn't emit stream chunk events when streaming is disabled.""" received_chunks = [] @@ -963,7 +956,7 @@ def test_llm_no_stream_chunks_when_streaming_disabled(): assert response and isinstance(response, str) -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_streaming_fallback_to_non_streaming(): """Test that streaming falls back to non-streaming when there's an error.""" received_chunks = [] @@ -1021,7 +1014,7 @@ def test_streaming_fallback_to_non_streaming(): llm.call = original_call -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_streaming_empty_response_handling(): """Test that streaming handles empty responses correctly.""" received_chunks = [] @@ -1069,37 +1062,37 @@ def test_streaming_empty_response_handling(): llm.call = original_call -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_stream_llm_emits_event_with_task_and_agent_info(): completed_event = [] failed_event = [] started_event = [] stream_event = [] - event_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(LLMCallFailedEvent) def handle_llm_failed(source, event): - failed_event.append(event) + with condition: + failed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallStartedEvent) def handle_llm_started(source, event): - started_event.append(event) + with condition: + started_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallCompletedEvent) def handle_llm_completed(source, event): - completed_event.append(event) - if len(started_event) >= 1 and len(stream_event) >= 12: - event_received.set() + with condition: + completed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMStreamChunkEvent) def handle_llm_stream_chunk(source, event): - stream_event.append(event) - if ( - len(completed_event) >= 1 - and len(started_event) >= 1 - and len(stream_event) >= 12 - ): - event_received.set() + with condition: + stream_event.append(event) + condition.notify() agent = Agent( role="TestAgent", @@ -1117,7 +1110,14 @@ def test_stream_llm_emits_event_with_task_and_agent_info(): crew = Crew(agents=[agent], tasks=[task]) crew.kickoff() - assert event_received.wait(timeout=10), "Timeout waiting for LLM events" + with condition: + success = condition.wait_for( + lambda: len(completed_event) >= 1 + and len(started_event) >= 1 + and len(stream_event) >= 12, + timeout=10, + ) + assert success, "Timeout waiting for LLM events" assert len(completed_event) == 1 assert len(failed_event) == 0 assert len(started_event) == 1 @@ -1141,36 +1141,47 @@ def test_stream_llm_emits_event_with_task_and_agent_info(): assert set(all_task_name) == {task.name or task.description} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_emits_event_with_task_and_agent_info(base_agent, base_task): - completed_event = [] - failed_event = [] - started_event = [] - stream_event = [] - event_received = threading.Event() + completed_event: list[LLMCallCompletedEvent] = [] + failed_event: list[LLMCallFailedEvent] = [] + started_event: list[LLMCallStartedEvent] = [] + stream_event: list[LLMStreamChunkEvent] = [] + condition = threading.Condition() @crewai_event_bus.on(LLMCallFailedEvent) def handle_llm_failed(source, event): - failed_event.append(event) + with condition: + failed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallStartedEvent) def handle_llm_started(source, event): - started_event.append(event) + with condition: + started_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallCompletedEvent) def handle_llm_completed(source, event): - completed_event.append(event) - if len(started_event) >= 1: - event_received.set() + with condition: + completed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMStreamChunkEvent) def handle_llm_stream_chunk(source, event): - stream_event.append(event) + with condition: + stream_event.append(event) + condition.notify() crew = Crew(agents=[base_agent], tasks=[base_task]) crew.kickoff() - assert event_received.wait(timeout=10), "Timeout waiting for LLM events" + with condition: + success = condition.wait_for( + lambda: len(completed_event) >= 1 and len(started_event) >= 1, + timeout=10, + ) + assert success, "Timeout waiting for LLM events" assert len(completed_event) == 1 assert len(failed_event) == 0 assert len(started_event) == 1 @@ -1194,37 +1205,37 @@ def test_llm_emits_event_with_task_and_agent_info(base_agent, base_task): assert set(all_task_name) == {base_task.name or base_task.description} -@pytest.mark.vcr(filter_headers=["authorization"]) +@pytest.mark.vcr() def test_llm_emits_event_with_lite_agent(): completed_event = [] failed_event = [] started_event = [] stream_event = [] - all_events_received = threading.Event() + condition = threading.Condition() @crewai_event_bus.on(LLMCallFailedEvent) def handle_llm_failed(source, event): - failed_event.append(event) + with condition: + failed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallStartedEvent) def handle_llm_started(source, event): - started_event.append(event) + with condition: + started_event.append(event) + condition.notify() @crewai_event_bus.on(LLMCallCompletedEvent) def handle_llm_completed(source, event): - completed_event.append(event) - if len(started_event) >= 1 and len(stream_event) >= 15: - all_events_received.set() + with condition: + completed_event.append(event) + condition.notify() @crewai_event_bus.on(LLMStreamChunkEvent) def handle_llm_stream_chunk(source, event): - stream_event.append(event) - if ( - len(completed_event) >= 1 - and len(started_event) >= 1 - and len(stream_event) >= 15 - ): - all_events_received.set() + with condition: + stream_event.append(event) + condition.notify() agent = Agent( role="Speaker", @@ -1234,7 +1245,14 @@ def test_llm_emits_event_with_lite_agent(): ) agent.kickoff(messages=[{"role": "user", "content": "say hi!"}]) - assert all_events_received.wait(timeout=10), "Timeout waiting for all events" + with condition: + success = condition.wait_for( + lambda: len(completed_event) >= 1 + and len(started_event) >= 1 + and len(stream_event) >= 15, + timeout=10, + ) + assert success, "Timeout waiting for all events" assert len(completed_event) == 1 assert len(failed_event) == 0 diff --git a/pyproject.toml b/pyproject.toml index f99ed190b..2432f317c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,6 @@ dev = [ [tool.ruff] src = ["lib/*"] extend-exclude = [ - "lib/crewai/src/crewai/experimental/a2a", "lib/crewai/src/crewai/cli/templates", "lib/crewai/tests/", "lib/crewai-tools/tests/", @@ -135,7 +134,7 @@ testpaths = [ ] asyncio_mode = "strict" asyncio_default_fixture_loop_scope = "function" -addopts = "--tb=short" +addopts = "--tb=short -n auto --timeout=60 --dist=loadfile --max-worker-restart=2 --block-network --import-mode=importlib" python_files = "test_*.py" python_classes = "Test*" python_functions = "test_*"