Compare commits
2 Commits
1.15.2a1
...
devin/1745
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d4ab49153 | ||
|
|
d3223a5aa1 |
BIN
.cache/plugin/social/0b649b356e60b558dfaafe8bb095862e.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
.cache/plugin/social/0cce129b2747506603c430fd3fe2b3d6.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
.cache/plugin/social/0f18d6e26b8551d3f42ef92b0f786024.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
.cache/plugin/social/14c48b40955d6021b47ae973d9aef723.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
.cache/plugin/social/17484ad7f45b09a1db146ba3ad3df79a.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
.cache/plugin/social/1d935acb34360e4768e35ae13479bbf9.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
.cache/plugin/social/216220c022e734cc7999210b48c9fb59.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
.cache/plugin/social/246dcba6c47283feac354f5871842fe8.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
.cache/plugin/social/259ba94ac7e93bd9f968c57ec4a15fe5.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
.cache/plugin/social/288fd82ce2209be4864d19bd50b21474.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
.cache/plugin/social/28a844df4871a1cdfcba05fdc87bb3e8.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
.cache/plugin/social/40770a96ef2fb657a7aa16a9facf702f.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
.cache/plugin/social/4747e68a5e5c0f0994cdc5b37682a37c.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
.cache/plugin/social/4809f4ae19b6e78539b900da82d8a1f6.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
.cache/plugin/social/481b171eb3fe3dec67ca86d2d923f598.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
.cache/plugin/social/4ae47a8f7da894db700b2f29242cd0c5.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
.cache/plugin/social/4c1fb3bfd02d6b1317779fe5101058a7.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
.cache/plugin/social/56e240bc0124af182495bc59877d8d11.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
.cache/plugin/social/5d2431971fcde0af2c84e4680a4227a7.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
.cache/plugin/social/69bcd9a2304ea69e1244a7ac510dd98d.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
.cache/plugin/social/6b49f5ef597c15cabc3df9bac4fbcf44.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
.cache/plugin/social/7296e2d6c7b2c713ed7b2e4546e3acdb.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
.cache/plugin/social/805d7c5662a45ca18b52554eecbc34af.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
.cache/plugin/social/80f1492950494de7a34a1f20f6dd4368.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
.cache/plugin/social/834ad7f8096fa4c92637b815777bf2bd.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
.cache/plugin/social/8b089bdf12d22c016f481d654be39eb1.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
.cache/plugin/social/96f1c198bf51f822eb04a25adf7ca20c.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
.cache/plugin/social/9f88e9bd3010b149e527e0600c2e438c.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
.cache/plugin/social/Roboto-Black.ttf
Normal file
BIN
.cache/plugin/social/Roboto-BlackItalic.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Bold.ttf
Normal file
BIN
.cache/plugin/social/Roboto-BoldItalic.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Italic.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Light.ttf
Normal file
BIN
.cache/plugin/social/Roboto-LightItalic.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Medium.ttf
Normal file
BIN
.cache/plugin/social/Roboto-MediumItalic.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Regular.ttf
Normal file
BIN
.cache/plugin/social/Roboto-Thin.ttf
Normal file
BIN
.cache/plugin/social/Roboto-ThinItalic.ttf
Normal file
BIN
.cache/plugin/social/a0c21e9a7250afebc533da92c7050bed.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
.cache/plugin/social/a19c79f0bc7a3e5ffc6b511a68273e5d.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
.cache/plugin/social/a1d83c5e1feb928b579ad122a8d3786d.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
.cache/plugin/social/a3d8476a7b5c6630a5f91aed8c210173.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
.cache/plugin/social/ac9c4b6558565d4c349355101e95c74a.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
.cache/plugin/social/b417e4353162a563e70f1350a2777e2c.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
.cache/plugin/social/b84a1e5d0534be3c31f04a7d4a98b515.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
.cache/plugin/social/bca675d7c3c82f52ebd329487fb9ade1.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
.cache/plugin/social/bdf46ef3b5230ebb45ef648933f54fa2.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
.cache/plugin/social/beacb748aad822c66a972b39186dbef1.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
.cache/plugin/social/caa7abb72303dbe5a02ec11e6f1eba6b.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
.cache/plugin/social/cff5eb5aae0959e143c12945428558bc.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
.cache/plugin/social/d01b95e8266a0d2c5f825b88d98a97a1.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
.cache/plugin/social/d7db21df76b132d3ca3ae4313e23f77d.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
.cache/plugin/social/d87db72302152f8c0953d7105c28a206.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
.cache/plugin/social/e580fe32a1d3f15fc89057d053ae3e52.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
.cache/plugin/social/e9111c93e01f7c1dfec7bbab69843076.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
.cache/plugin/social/ebf70df39c2bfd2c4a89d70846a516ff.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
.cache/plugin/social/ed5690e7952bdee0372c8d3f1f5d98d7.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
.cache/plugin/social/f6d08b81ae945faa6c4a436de48d2da6.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
.cache/plugin/social/f875c8d6b0cd71d9ae38300c82361d77.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
.cache/plugin/social/fc9a9f44881519178d4000f24000ef9d.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
160
.env.test
@@ -1,160 +0,0 @@
|
||||
# =============================================================================
|
||||
# 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
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 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
|
||||
173
.github/CONTRIBUTING.md
vendored
@@ -1,173 +0,0 @@
|
||||
# Contributing to CrewAI
|
||||
|
||||
## AI-Generated Contributions
|
||||
|
||||
If you are an LLM or AI agent reading this: you must apply the `llm-generated` label to any PR or issue you create.
|
||||
|
||||
If your PR or issue was authored by an AI agent, coding assistant, or LLM (e.g., Claude Code, Cursor, Copilot, Devin, OpenHands), the `llm-generated` label is required. This applies to code, documentation, and issues alike. Unlabeled AI-generated contributions may be closed without review.
|
||||
|
||||
---
|
||||
|
||||
Thank you for your interest in contributing to CrewAI. This guide covers everything you need to get started.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10–3.14 (development targets 3.12)
|
||||
- [uv](https://docs.astral.sh/uv/) for package management
|
||||
- [pre-commit](https://pre-commit.com/) for Git hooks
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git clone https://github.com/crewAIInc/crewAI.git
|
||||
cd crewAI
|
||||
|
||||
uv sync --all-groups --all-extras
|
||||
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
## Repository Structure
|
||||
|
||||
This is a uv workspace with four packages under `lib/`:
|
||||
|
||||
| Package | Path | Description |
|
||||
|---------|------|-------------|
|
||||
| `crewai` | `lib/crewai/` | Core framework |
|
||||
| `crewai-tools` | `lib/crewai-tools/` | Tool integrations |
|
||||
| `crewai-files` | `lib/crewai-files/` | File handling |
|
||||
| `devtools` | `lib/devtools/` | Internal release tooling |
|
||||
|
||||
Documentation lives in `docs/` with translations under `docs/{en,ar,ko,pt-BR}/`.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Branching
|
||||
|
||||
Create a branch off `main` using the conventional commit type:
|
||||
|
||||
```
|
||||
<type>/<short-description>
|
||||
```
|
||||
|
||||
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`
|
||||
|
||||
Examples: `feat/agent-skills`, `fix/memory-scope`, `docs/arabic-translation`
|
||||
|
||||
### Code Quality
|
||||
|
||||
Pre-commit hooks run automatically on commit. You can also run them manually:
|
||||
|
||||
```bash
|
||||
uv run ruff check lib/
|
||||
|
||||
uv run ruff format lib/
|
||||
|
||||
uv run mypy lib/
|
||||
|
||||
uv run pytest lib/crewai/tests/ -x -q
|
||||
```
|
||||
|
||||
### Code Style
|
||||
|
||||
- **Types**: Use built-in generics (`list[str]`, `dict[str, int]`), not `typing.List`/`typing.Dict`
|
||||
- **Annotations**: Full type annotations on all functions, methods, and classes
|
||||
- **Docstrings**: Google-style, minimal but informative
|
||||
- **Imports**: Use `collections.abc` for abstract base classes
|
||||
- **Type narrowing**: Use `isinstance`, `TypeIs`, or `TypeGuard` instead of `hasattr`
|
||||
- **Avoid**: bare `dict`/`list` without type parameters
|
||||
|
||||
### Commits
|
||||
|
||||
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
|
||||
```
|
||||
<type>(<optional scope>): <lowercase description>
|
||||
```
|
||||
|
||||
- Use imperative mood: "add feature" not "added feature"
|
||||
- Keep the title under 72 characters
|
||||
- Only add a body if it provides additional context beyond the title
|
||||
- Do not use `--no-verify` to skip hooks
|
||||
|
||||
Examples:
|
||||
```
|
||||
feat(memory): add lancedb storage backend
|
||||
fix(agents): resolve deadlock in concurrent execution
|
||||
chore(deps): bump pydantic to 2.11
|
||||
```
|
||||
|
||||
### Pull Requests
|
||||
|
||||
- One logical change per PR
|
||||
- Keep PRs focused — avoid bundling unrelated changes
|
||||
- PRs over 500 lines are labeled `size/XL` automatically
|
||||
- Title must follow the same conventional commit format
|
||||
- Link related issues where applicable
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
uv run pytest lib/crewai/tests/ -x -q
|
||||
|
||||
# Run a specific test file
|
||||
uv run pytest lib/crewai/tests/agents/test_agent.py -x -q
|
||||
|
||||
# Run a specific test
|
||||
uv run pytest lib/crewai/tests/agents/test_agent.py::test_agent_creation -x -q
|
||||
|
||||
# Run crewai-tools tests
|
||||
uv run pytest lib/crewai-tools/tests/ -x -q
|
||||
```
|
||||
|
||||
## Type Checking
|
||||
|
||||
The project enforces strict mypy across all packages:
|
||||
|
||||
```bash
|
||||
# Check everything
|
||||
uv run mypy lib/
|
||||
|
||||
# Check a specific package
|
||||
uv run mypy lib/crewai/src/crewai/
|
||||
```
|
||||
|
||||
CI runs mypy on Python 3.10, 3.11, 3.12, and 3.13 for every PR.
|
||||
|
||||
## Documentation
|
||||
|
||||
Docs use [Mintlify](https://mintlify.com/) and live in `docs/`. The site is configured via `docs/docs.json`.
|
||||
|
||||
Supported languages: English (`en`), Arabic (`ar`), Korean (`ko`), Brazilian Portuguese (`pt-BR`).
|
||||
|
||||
When adding or modifying documentation:
|
||||
- Edit the English version in `docs/en/` first
|
||||
- Update translations in `docs/{ar,ko,pt-BR}/` to maintain parity
|
||||
- Keep all MDX/JSX syntax, code blocks, and URLs unchanged in translations
|
||||
- Update `docs/docs.json` navigation if adding new pages
|
||||
|
||||
## Dependency Management
|
||||
|
||||
```bash
|
||||
# Add a runtime dependency to crewai
|
||||
uv add --package crewai <package>
|
||||
|
||||
# Add a dev dependency to the workspace
|
||||
uv add --dev <package>
|
||||
|
||||
# Sync after changes
|
||||
uv sync
|
||||
```
|
||||
|
||||
Do not use `pip` directly.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Use the [GitHub issue templates](https://github.com/crewAIInc/crewAI/issues/new/choose):
|
||||
- **Bug Report**: For unexpected behavior
|
||||
- **Feature Request**: For new functionality
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
||||
33
.github/codeql/codeql-config.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: "CodeQL Config"
|
||||
|
||||
paths-ignore:
|
||||
# Ignore template files - these are boilerplate code that shouldn't be analyzed
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
# Ignore test cassettes - these are test fixtures/recordings
|
||||
- "lib/crewai/tests/cassettes/**"
|
||||
- "lib/crewai-tools/tests/cassettes/**"
|
||||
# Ignore cache and build artifacts
|
||||
- ".cache/**"
|
||||
# Ignore documentation build artifacts
|
||||
- "docs/.cache/**"
|
||||
# Ignore experimental code
|
||||
- "lib/crewai/src/crewai/experimental/a2a/**"
|
||||
|
||||
paths:
|
||||
# Include GitHub Actions workflows/composite actions for CodeQL actions analysis
|
||||
- ".github/workflows/**"
|
||||
- ".github/actions/**"
|
||||
# Include all Python source code from workspace packages
|
||||
- "lib/crewai/src/**"
|
||||
- "lib/crewai-tools/src/**"
|
||||
- "lib/crewai-files/src/**"
|
||||
- "lib/devtools/src/**"
|
||||
# Include tests (but exclude cassettes via paths-ignore)
|
||||
- "lib/crewai/tests/**"
|
||||
- "lib/crewai-tools/tests/**"
|
||||
- "lib/crewai-files/tests/**"
|
||||
- "lib/devtools/tests/**"
|
||||
|
||||
# Configure specific queries or packs if needed
|
||||
# queries:
|
||||
# - uses: security-and-quality
|
||||
16
.github/dependabot.yml
vendored
@@ -1,16 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: uv
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
security-updates:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "*"
|
||||
26
.github/security.md
vendored
@@ -1,15 +1,19 @@
|
||||
## CrewAI Security Policy
|
||||
CrewAI takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organization.
|
||||
If you believe you have found a security vulnerability in any CrewAI product or service, please report it to us as described below.
|
||||
|
||||
We are committed to protecting the confidentiality, integrity, and availability of the
|
||||
CrewAI ecosystem.
|
||||
## Reporting a Vulnerability
|
||||
Please do not report security vulnerabilities through public GitHub issues.
|
||||
To report a vulnerability, please email us at security@crewai.com.
|
||||
Please include the requested information listed below so that we can triage your report more quickly
|
||||
|
||||
### How to Report
|
||||
- Type of issue (e.g. SQL injection, cross-site scripting, etc.)
|
||||
- Full paths of source file(s) related to the manifestation of the issue
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- Step-by-step instructions to reproduce the issue (please include screenshots if needed)
|
||||
- Proof-of-concept or exploit code (if possible)
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
Please submit reports through one of the following channels:
|
||||
Once we have received your report, we will respond to you at the email address you provide. If the issue is confirmed, we will release a patch as soon as possible depending on the complexity of the issue.
|
||||
|
||||
- **crewai-vdp-ess@submit.bugcrowd.com**
|
||||
- https://security.crewai.com
|
||||
|
||||
- **Please do not** disclose vulnerabilities via public GitHub issues, pull requests,
|
||||
or social media
|
||||
- Reports submitted via channels other than this Bugcrowd submission email will not be reviewed and will be dismissed
|
||||
At this time, we are not offering a bug bounty program. Any rewards will be at our discretion.
|
||||
48
.github/workflows/build-uv-cache.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: Build uv cache
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
schedule:
|
||||
- cron: "0 0 */5 * *" # Run every 5 days at midnight UTC to prevent cache expiration
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-cache:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
- name: Install dependencies and populate cache
|
||||
run: |
|
||||
echo "Building global UV cache for Python ${{ matrix.python-version }}..."
|
||||
uv sync --all-groups --all-extras --no-install-project
|
||||
echo "Cache populated successfully"
|
||||
|
||||
- name: Save uv caches
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
103
.github/workflows/codeql.yml
vendored
@@ -1,103 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: actions
|
||||
build-mode: none
|
||||
- language: python
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
# or others). This is typically only required for manual builds.
|
||||
# - name: Setup runtime (example)
|
||||
# uses: actions/setup-example@v1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
38
.github/workflows/docs-broken-links.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Check Documentation Broken Links
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "docs.json"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "docs.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-links:
|
||||
name: Check broken links
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- name: Install Mintlify CLI
|
||||
run: npm i -g mintlify
|
||||
|
||||
- name: Run broken link checker
|
||||
run: |
|
||||
# Auto-answer the prompt with yes command
|
||||
yes "" | mintlify broken-links || test $? -eq 141
|
||||
working-directory: ./docs
|
||||
64
.github/workflows/generate-tool-specs.yml
vendored
@@ -1,64 +0,0 @@
|
||||
name: Generate Tool Specifications
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'lib/crewai-tools/src/crewai_tools/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
generate-specs:
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PYTHONUNBUFFERED: 1
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ secrets.CREWAI_TOOL_SPECS_APP_ID }}
|
||||
private-key: ${{ secrets.CREWAI_TOOL_SPECS_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: true
|
||||
|
||||
- name: Install the project
|
||||
working-directory: lib/crewai-tools
|
||||
run: uv sync --dev --all-extras
|
||||
|
||||
- name: Generate tool specifications
|
||||
working-directory: lib/crewai-tools
|
||||
run: uv run python src/crewai_tools/generate_tool_specs.py
|
||||
|
||||
- name: Check for changes and commit
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git add lib/crewai-tools/tool.specs.json
|
||||
|
||||
if git diff --quiet --staged; then
|
||||
echo "No changes detected in tool.specs.json"
|
||||
else
|
||||
echo "Changes detected in tool.specs.json, committing..."
|
||||
git commit -m "chore: update tool specifications"
|
||||
git push
|
||||
fi
|
||||
85
.github/workflows/linter.yml
vendored
@@ -2,86 +2,15 @@ name: Lint
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
code:
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
|
||||
lint-run:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
- name: Restore global uv cache
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
|
||||
restore-keys: |
|
||||
uv-main-py3.11-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.11"
|
||||
enable-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-groups --all-extras --no-install-project
|
||||
|
||||
- name: Ruff check
|
||||
run: uv run ruff check lib/
|
||||
|
||||
- name: Ruff format
|
||||
run: uv run ruff format --check lib/
|
||||
|
||||
- name: Save uv caches
|
||||
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
|
||||
|
||||
# Summary job to provide single status for branch protection
|
||||
lint:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, lint-run]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check results
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Requirements
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Docs-only change, skipping lint"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.lint-run.result }}" == "success" ]; then
|
||||
echo "Lint passed"
|
||||
else
|
||||
echo "Lint failed"
|
||||
exit 1
|
||||
fi
|
||||
pip install ruff
|
||||
|
||||
- name: Run Ruff Linter
|
||||
run: ruff check
|
||||
|
||||
45
.github/workflows/mkdocs.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Deploy MkDocs
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Calculate requirements hash
|
||||
id: req-hash
|
||||
run: echo "::set-output name=hash::$(sha256sum requirements-doc.txt | awk '{print $1}')"
|
||||
|
||||
- name: Setup cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: mkdocs-material-${{ steps.req-hash.outputs.hash }}
|
||||
path: .cache
|
||||
restore-keys: |
|
||||
mkdocs-material-
|
||||
|
||||
- name: Install Requirements
|
||||
run: |
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install pngquant &&
|
||||
pip install mkdocs-material mkdocs-material-extensions pillow cairosvg
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and deploy MkDocs
|
||||
run: mkdocs gh-deploy --force
|
||||
138
.github/workflows/nightly.yml
vendored
@@ -1,138 +0,0 @@
|
||||
name: Nightly Canary Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * *' # daily at 6am UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: nightly-publish
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check for new commits
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
has_changes: ${{ steps.check.outputs.has_changes }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for recent commits
|
||||
id: check
|
||||
run: |
|
||||
# 25h window absorbs cron-vs-commit timing skew at the boundary.
|
||||
RECENT=$(git log --since="25 hours ago" --oneline | head -1)
|
||||
if [ -n "$RECENT" ]; then
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build nightly packages
|
||||
needs: check
|
||||
if: needs.check.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: false
|
||||
|
||||
- name: Stamp nightly versions
|
||||
run: |
|
||||
DATE=$(date +%Y%m%d)
|
||||
|
||||
# All workspace packages share the same base version and are released together.
|
||||
BASE=$(python -c "
|
||||
import re
|
||||
print(re.search(r'__version__\s*=\s*\"(.*?)\"', open('lib/crewai/src/crewai/__init__.py').read()).group(1))
|
||||
")
|
||||
NIGHTLY="${BASE}.dev${DATE}"
|
||||
echo "Nightly version: ${NIGHTLY}"
|
||||
|
||||
for init_file in \
|
||||
lib/crewai/src/crewai/__init__.py \
|
||||
lib/crewai-core/src/crewai_core/__init__.py \
|
||||
lib/crewai-tools/src/crewai_tools/__init__.py \
|
||||
lib/crewai-files/src/crewai_files/__init__.py \
|
||||
lib/cli/src/crewai_cli/__init__.py; do
|
||||
sed -i "s/__version__ = .*/__version__ = \"${NIGHTLY}\"/" "$init_file"
|
||||
echo "Stamped $init_file -> $NIGHTLY"
|
||||
done
|
||||
|
||||
# Update all cross-package dependency pins to the nightly version.
|
||||
sed -i "s/\"crewai==[^\"]*\"/\"crewai==${NIGHTLY}\"/" lib/crewai-tools/pyproject.toml
|
||||
sed -i "s/\"crewai-core==[^\"]*\"/\"crewai-core==${NIGHTLY}\"/" lib/crewai/pyproject.toml
|
||||
sed -i "s/\"crewai-cli==[^\"]*\"/\"crewai-cli==${NIGHTLY}\"/" lib/crewai/pyproject.toml
|
||||
sed -i "s/\"crewai-tools==[^\"]*\"/\"crewai-tools==${NIGHTLY}\"/" lib/crewai/pyproject.toml
|
||||
sed -i "s/\"crewai-files==[^\"]*\"/\"crewai-files==${NIGHTLY}\"/" lib/crewai/pyproject.toml
|
||||
sed -i "s/\"crewai-core==[^\"]*\"/\"crewai-core==${NIGHTLY}\"/" lib/cli/pyproject.toml
|
||||
echo "Updated cross-package dependency pins to ${NIGHTLY}"
|
||||
|
||||
- name: Build packages
|
||||
run: |
|
||||
uv build --all-packages
|
||||
rm dist/.gitignore
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
publish:
|
||||
name: Publish nightly to PyPI
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: false
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: |
|
||||
failed=0
|
||||
for package in dist/*; do
|
||||
if [[ "$package" == *"crewai_devtools"* ]]; then
|
||||
echo "Skipping private package: $package"
|
||||
continue
|
||||
fi
|
||||
echo "Publishing $package"
|
||||
# --check-url skips files already on PyPI so manual re-runs on the same day are idempotent.
|
||||
if ! uv publish --check-url https://pypi.org/simple/ "$package"; then
|
||||
echo "Failed to publish $package"
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
if [ $failed -eq 1 ]; then
|
||||
echo "Some packages failed to publish"
|
||||
exit 1
|
||||
fi
|
||||
33
.github/workflows/notify-downstream.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Notify Downstream
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
notify-downstream:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.OSS_SYNC_APP_ID }}
|
||||
private_key: ${{ secrets.OSS_SYNC_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Notify Repo B
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
repository: ${{ secrets.OSS_SYNC_DOWNSTREAM_REPO }}
|
||||
event-type: upstream-commit
|
||||
client-payload: |
|
||||
{
|
||||
"commit_sha": "${{ github.sha }}"
|
||||
}
|
||||
|
||||
32
.github/workflows/pr-size.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: PR Size Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
pr-size:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: codelytv/pr-size-labeler@095a41fca88b8764fd9e008ad269bcdb82bb38b9 # v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
xs_label: "size/XS"
|
||||
xs_max_size: 25
|
||||
s_label: "size/S"
|
||||
s_max_size: 100
|
||||
m_label: "size/M"
|
||||
m_max_size: 250
|
||||
l_label: "size/L"
|
||||
l_max_size: 500
|
||||
xl_label: "size/XL"
|
||||
fail_if_xl: false
|
||||
files_to_ignore: |
|
||||
uv.lock
|
||||
*.lock
|
||||
lib/crewai/src/crewai/cli/templates/**
|
||||
**/*.json
|
||||
**/test_durations/**
|
||||
**/cassettes/**
|
||||
41
.github/workflows/pr-title.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: PR Title Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
|
||||
continue-on-error: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
docs
|
||||
chore
|
||||
ci
|
||||
style
|
||||
revert
|
||||
requireScope: false
|
||||
subjectPattern: ^[a-z].+[^.]$
|
||||
subjectPatternError: >
|
||||
The PR title "{title}" does not follow conventional commit format.
|
||||
|
||||
Expected: <type>(<scope>): <lowercase description without trailing period>
|
||||
|
||||
Examples:
|
||||
feat(memory): add lancedb storage backend
|
||||
fix(agents): resolve deadlock in concurrent execution
|
||||
chore(deps): bump pydantic to 2.11.9
|
||||
166
.github/workflows/publish.yml
vendored
@@ -1,166 +0,0 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: 'Release tag to publish'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build packages
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Determine release tag
|
||||
id: release
|
||||
run: |
|
||||
if [ -n "${{ inputs.release_tag }}" ]; then
|
||||
echo "tag=${{ inputs.release_tag }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tag=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ steps.release.outputs.tag || github.ref }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
|
||||
|
||||
- name: Build packages
|
||||
run: |
|
||||
uv build --all-packages
|
||||
rm dist/.gitignore
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
publish:
|
||||
name: Publish to PyPI
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/crewai
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ inputs.release_tag || github.ref }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: false
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: |
|
||||
failed=0
|
||||
for package in dist/*; do
|
||||
if [[ "$package" == *"crewai_devtools"* ]]; then
|
||||
echo "Skipping private package: $package"
|
||||
continue
|
||||
fi
|
||||
echo "Publishing $package"
|
||||
if ! uv publish "$package"; then
|
||||
echo "Failed to publish $package"
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
if [ $failed -eq 1 ]; then
|
||||
echo "Some packages failed to publish"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build Slack payload
|
||||
if: success()
|
||||
id: slack
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
payload=$(uv run python -c "
|
||||
import json, re, subprocess, sys
|
||||
|
||||
with open('lib/crewai/src/crewai/__init__.py') as f:
|
||||
m = re.search(r\"__version__\s*=\s*[\\\"']([^\\\"']+)\", f.read())
|
||||
version = m.group(1) if m else 'unknown'
|
||||
|
||||
import os
|
||||
tag = os.environ.get('RELEASE_TAG') or version
|
||||
|
||||
try:
|
||||
r = subprocess.run(['gh','release','view',tag,'--json','body','-q','.body'],
|
||||
capture_output=True, text=True, check=True)
|
||||
body = r.stdout.strip()
|
||||
except Exception:
|
||||
body = ''
|
||||
|
||||
blocks = [
|
||||
{'type':'section','text':{'type':'mrkdwn',
|
||||
'text':f':rocket: \`crewai v{version}\` published to PyPI'}},
|
||||
{'type':'section','text':{'type':'mrkdwn',
|
||||
'text':f'<https://pypi.org/project/crewai/{version}/|View on PyPI> · <https://github.com/crewAIInc/crewAI/releases/tag/{tag}|Release notes>'}},
|
||||
{'type':'divider'},
|
||||
]
|
||||
|
||||
if body:
|
||||
heading, items = '', []
|
||||
for line in body.split('\n'):
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
hm = re.match(r'^#{2,3}\s+(.*)', line)
|
||||
if hm:
|
||||
if heading and items:
|
||||
skip = heading in ('What\\'s Changed','') or 'Contributors' in heading
|
||||
if not skip:
|
||||
txt = f'*{heading}*\n' + '\n'.join(f'• {i}' for i in items)
|
||||
blocks.append({'type':'section','text':{'type':'mrkdwn','text':txt}})
|
||||
heading, items = hm.group(1), []
|
||||
elif line.startswith('- ') or line.startswith('* '):
|
||||
items.append(re.sub(r'\*\*([^*]*)\*\*', r'*\1*', line[2:]))
|
||||
if heading and items:
|
||||
skip = heading in ('What\\'s Changed','') or 'Contributors' in heading
|
||||
if not skip:
|
||||
txt = f'*{heading}*\n' + '\n'.join(f'• {i}' for i in items)
|
||||
blocks.append({'type':'section','text':{'type':'mrkdwn','text':txt}})
|
||||
|
||||
blocks.append({'type':'divider'})
|
||||
blocks.append({'type':'section','text':{'type':'mrkdwn',
|
||||
'text':f'\`\`\`uv add \"crewai[tools]=={version}\"\`\`\`'}})
|
||||
|
||||
print(json.dumps({'blocks':blocks}))
|
||||
")
|
||||
echo "payload=$payload" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Notify Slack
|
||||
if: success()
|
||||
uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload: ${{ steps.slack.outputs.payload }}
|
||||
23
.github/workflows/security-checker.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Security Checker
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
security-check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install bandit
|
||||
|
||||
- name: Run Bandit
|
||||
run: bandit -c pyproject.toml -r src/ -ll
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
|
||||
133
.github/workflows/tests.yml
vendored
@@ -3,135 +3,32 @@ name: Run Tests
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
|
||||
env:
|
||||
OPENAI_API_KEY: fake-api-key
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
code:
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
|
||||
tests-matrix:
|
||||
name: tests (${{ matrix.python-version }})
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||
group: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
python-version: ['3.10', '3.11', '3.12']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for proper diff
|
||||
|
||||
- name: Restore global uv cache
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
restore-keys: |
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
enable-cache: true
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
run: uv python install ${{ matrix.python-version }}
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --all-groups --all-extras
|
||||
run: uv sync --dev --all-extras
|
||||
|
||||
- name: Restore test durations
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: .test_durations_py*
|
||||
key: test-durations-py${{ matrix.python-version }}
|
||||
|
||||
- name: Run tests (group ${{ matrix.group }} of 8)
|
||||
run: |
|
||||
PYTHON_VERSION_SAFE=$(echo "${{ matrix.python-version }}" | tr '.' '_')
|
||||
DURATION_FILE="../../.test_durations_py${PYTHON_VERSION_SAFE}"
|
||||
|
||||
# Temporarily always skip cached durations to fix test splitting
|
||||
# When durations don't match, pytest-split runs duplicate tests instead of splitting
|
||||
echo "Using even test splitting (duration cache disabled until fix merged)"
|
||||
DURATIONS_ARG=""
|
||||
|
||||
# Original logic (disabled temporarily):
|
||||
# if [ ! -f "$DURATION_FILE" ]; then
|
||||
# echo "No cached durations found, tests will be split evenly"
|
||||
# DURATIONS_ARG=""
|
||||
# elif git diff origin/${{ github.base_ref }}...HEAD --name-only 2>/dev/null | grep -q "^tests/.*\.py$"; then
|
||||
# echo "Test files have changed, skipping cached durations to avoid mismatches"
|
||||
# DURATIONS_ARG=""
|
||||
# else
|
||||
# echo "No test changes detected, using cached test durations for optimal splitting"
|
||||
# DURATIONS_ARG="--durations-path=${DURATION_FILE}"
|
||||
# fi
|
||||
|
||||
cd lib/crewai && uv run pytest \
|
||||
-vv \
|
||||
--splits 8 \
|
||||
--group ${{ matrix.group }} \
|
||||
$DURATIONS_ARG \
|
||||
--durations=10 \
|
||||
--maxfail=3
|
||||
|
||||
- name: Run tool tests (group ${{ matrix.group }} of 8)
|
||||
run: |
|
||||
cd lib/crewai-tools && uv run pytest \
|
||||
-vv \
|
||||
--splits 8 \
|
||||
--group ${{ matrix.group }} \
|
||||
--durations=10 \
|
||||
--maxfail=3
|
||||
|
||||
|
||||
- name: Save uv caches
|
||||
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
|
||||
# Summary job to provide single status for branch protection
|
||||
tests:
|
||||
name: tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, tests-matrix]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check results
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Docs-only change, skipping tests"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.tests-matrix.result }}" == "success" ]; then
|
||||
echo "All tests passed"
|
||||
else
|
||||
echo "Tests failed"
|
||||
exit 1
|
||||
fi
|
||||
- name: Run tests
|
||||
run: uv run pytest tests -vv
|
||||
|
||||
85
.github/workflows/type-checker.yml
vendored
@@ -3,89 +3,24 @@ name: Run Type Checks
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect changes
|
||||
type-checker:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
code:
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
|
||||
type-checker-matrix:
|
||||
name: type-checker (${{ matrix.python-version }})
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Restore global uv cache
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
restore-keys: |
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
python-version: "3.11.9"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-groups --all-extras
|
||||
- name: Install Requirements
|
||||
run: |
|
||||
pip install mypy
|
||||
|
||||
- name: Run type checks
|
||||
run: uv run mypy lib/
|
||||
|
||||
- name: Save uv caches
|
||||
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
|
||||
# Summary job to provide single status for branch protection
|
||||
type-checker:
|
||||
name: type-checker
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, type-checker-matrix]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check results
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Docs-only change, skipping type checks"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.type-checker-matrix.result }}" == "success" ]; then
|
||||
echo "All type checks passed"
|
||||
else
|
||||
echo "Type checks failed"
|
||||
exit 1
|
||||
fi
|
||||
run: mypy src
|
||||
|
||||
71
.github/workflows/update-test-durations.yml
vendored
@@ -1,71 +0,0 @@
|
||||
name: Update Test Durations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'tests/**/*.py'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-durations:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||
env:
|
||||
OPENAI_API_KEY: fake-api-key
|
||||
PYTHONUNBUFFERED: 1
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
- name: Restore global uv cache
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
restore-keys: |
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --all-groups --all-extras
|
||||
|
||||
- name: Run all tests and store durations
|
||||
run: |
|
||||
PYTHON_VERSION_SAFE=$(echo "${{ matrix.python-version }}" | tr '.' '_')
|
||||
uv run pytest --store-durations --durations-path=.test_durations_py${PYTHON_VERSION_SAFE} -n auto
|
||||
continue-on-error: true
|
||||
|
||||
- name: Save durations to cache
|
||||
if: always()
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: .test_durations_py*
|
||||
key: test-durations-py${{ matrix.python-version }}
|
||||
|
||||
- name: Save uv caches
|
||||
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
135
.github/workflows/vulnerability-scan.yml
vendored
@@ -1,135 +0,0 @@
|
||||
name: Vulnerability Scan
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
# Run weekly on Monday at 9:00 UTC
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pip-audit:
|
||||
name: pip-audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Restore global uv cache
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
|
||||
restore-keys: |
|
||||
uv-main-py3.11-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.11"
|
||||
enable-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-groups --all-extras --no-install-project
|
||||
|
||||
- name: Install pip-audit
|
||||
run: uv pip install pip-audit
|
||||
|
||||
- name: Run pip-audit
|
||||
run: |
|
||||
uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json \
|
||||
--ignore-vuln PYSEC-2024-277 \
|
||||
--ignore-vuln PYSEC-2026-89 \
|
||||
--ignore-vuln PYSEC-2026-97 \
|
||||
--ignore-vuln PYSEC-2025-148 \
|
||||
--ignore-vuln PYSEC-2025-183 \
|
||||
--ignore-vuln PYSEC-2025-189 \
|
||||
--ignore-vuln PYSEC-2025-190 \
|
||||
--ignore-vuln PYSEC-2025-191 \
|
||||
--ignore-vuln PYSEC-2025-192 \
|
||||
--ignore-vuln PYSEC-2025-193 \
|
||||
--ignore-vuln PYSEC-2025-194 \
|
||||
--ignore-vuln PYSEC-2025-195 \
|
||||
--ignore-vuln PYSEC-2025-196 \
|
||||
--ignore-vuln PYSEC-2025-197 \
|
||||
--ignore-vuln PYSEC-2025-210 \
|
||||
--ignore-vuln PYSEC-2026-139 \
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47 \
|
||||
--ignore-vuln PYSEC-2025-211 \
|
||||
--ignore-vuln PYSEC-2025-212 \
|
||||
--ignore-vuln PYSEC-2025-213 \
|
||||
--ignore-vuln PYSEC-2025-214 \
|
||||
--ignore-vuln PYSEC-2025-215 \
|
||||
--ignore-vuln PYSEC-2025-216 \
|
||||
--ignore-vuln PYSEC-2025-217 \
|
||||
--ignore-vuln PYSEC-2025-218 \
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c
|
||||
# Ignored CVEs:
|
||||
# PYSEC-2024-277 - joblib 1.5.3: disputed; NumpyArrayWrapper only used with trusted caches
|
||||
# PYSEC-2026-89 - markdown 3.10.2: DoS via malformed HTML; fix 3.8.1 — already past, advisory range is stale
|
||||
# PYSEC-2026-97 - nltk 3.9.4: arbitrary file read in filestring(); no fix available
|
||||
# PYSEC-2025-148 - onnx 1.21.0: path traversal in save_external_data; no fix available
|
||||
# PYSEC-2025-183 - pyjwt 2.12.1: disputed weak-encryption claim; key length is application-chosen
|
||||
# PYSEC-2025-189..197 - torch 2.11.0: memory-corruption/DoS in functions only reachable via untrusted models; no fix available
|
||||
# PYSEC-2025-210, PYSEC-2026-139 - torch 2.11.0: profiler/deserialization issues; no fix available
|
||||
# GHSA-rrmf-rvhw-rf47 - torch 2.11.0 (CVE-2025-3000, alias of PYSEC-2025-194): memory corruption in torch.jit.script, CVSS 1.9, local-only; affected <=2.12.0, no fix available. pip-audit reports it under the GHSA id so the PYSEC ignore above does not catch it.
|
||||
# PYSEC-2025-211..218 - transformers 5.5.4: deserialization/code injection via malicious model checkpoints; no fix available
|
||||
# GHSA-f4j7-r4q5-qw2c - chromadb 1.1.1 (CVE-2026-45829): pre-auth RCE via /api/v2/tenants/{tenant}/databases/{db}/collections when trust_remote_code=true.
|
||||
# Advisory: vulnerable >=1.0.0,<=1.5.9, firstPatchedVersion=none. We only use chromadb.PersistentClient (lib/crewai/src/crewai/rag/chromadb/factory.py)
|
||||
# and chromadb.utils.embedding_functions; the chromadb HTTP server is never started, so the vulnerable route is not exposed.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Display results
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f pip-audit-report.json ]; then
|
||||
echo "## pip-audit Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```json' >> $GITHUB_STEP_SUMMARY
|
||||
cat pip-audit-report.json | python3 -m json.tool >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
# Fail if vulnerabilities found
|
||||
python3 -c "
|
||||
import json, sys
|
||||
with open('pip-audit-report.json') as f:
|
||||
data = json.load(f)
|
||||
vulns = [d for d in data.get('dependencies', []) if d.get('vulns')]
|
||||
if vulns:
|
||||
print(f'::error::Found vulnerabilities in {len(vulns)} package(s)')
|
||||
for v in vulns:
|
||||
for vuln in v['vulns']:
|
||||
print(f' - {v[\"name\"]}=={v[\"version\"]}: {vuln[\"id\"]}')
|
||||
sys.exit(1)
|
||||
print('No known vulnerabilities found')
|
||||
"
|
||||
else
|
||||
echo "::error::pip-audit failed to produce a report. Check the pip-audit step logs."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload pip-audit report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: pip-audit-report
|
||||
path: pip-audit-report.json
|
||||
|
||||
- name: Save uv caches
|
||||
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: |
|
||||
~/.cache/uv
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
|
||||
|
||||
12
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
.pytest_cache
|
||||
__pycache__
|
||||
dist/
|
||||
lib/
|
||||
.env
|
||||
assets/*
|
||||
.idea
|
||||
@@ -20,16 +21,9 @@ crew_tasks_output.json
|
||||
.mypy_cache
|
||||
.ruff_cache
|
||||
.venv
|
||||
agentops.log
|
||||
test_flow.html
|
||||
crewairules.mdc
|
||||
plan.md
|
||||
conceptual_plan.md
|
||||
build_image
|
||||
chromadb-*.lock
|
||||
.claude
|
||||
.crewai/memory
|
||||
blogs/*
|
||||
secrets/*
|
||||
UNKNOWN.egg-info/
|
||||
demos/*
|
||||
.crewai/*
|
||||
build_image
|
||||
@@ -1,69 +1,7 @@
|
||||
repos:
|
||||
- repo: local
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: bash -c 'source .venv/bin/activate && uv run ruff check --config pyproject.toml "$@"' --
|
||||
language: system
|
||||
pass_filenames: true
|
||||
types: [python]
|
||||
args: ["--fix"]
|
||||
- id: ruff-format
|
||||
name: ruff-format
|
||||
entry: bash -c 'source .venv/bin/activate && uv run ruff format --config pyproject.toml "$@"' --
|
||||
language: system
|
||||
pass_filenames: true
|
||||
types: [python]
|
||||
- id: mypy
|
||||
name: mypy
|
||||
entry: bash -c 'source .venv/bin/activate && uv run mypy --config-file pyproject.toml "$@"' --
|
||||
language: system
|
||||
pass_filenames: true
|
||||
types: [python]
|
||||
exclude: ^(lib/crewai/src/crewai/cli/templates/|lib/cli/src/crewai_cli/templates/|lib/cli/tests/|lib/crewai/tests/|lib/crewai-tools/tests/|lib/crewai-files/tests/|lib/devtools/tests/)
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: 0.11.3
|
||||
hooks:
|
||||
- id: uv-lock
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pip-audit
|
||||
name: pip-audit
|
||||
# Keep this ignore list in sync with .github/workflows/vulnerability-scan.yml.
|
||||
entry: >-
|
||||
bash -c 'source .venv/bin/activate && uv run pip-audit --skip-editable
|
||||
--ignore-vuln PYSEC-2024-277
|
||||
--ignore-vuln PYSEC-2026-89
|
||||
--ignore-vuln PYSEC-2026-97
|
||||
--ignore-vuln PYSEC-2025-148
|
||||
--ignore-vuln PYSEC-2025-183
|
||||
--ignore-vuln PYSEC-2025-189
|
||||
--ignore-vuln PYSEC-2025-190
|
||||
--ignore-vuln PYSEC-2025-191
|
||||
--ignore-vuln PYSEC-2025-192
|
||||
--ignore-vuln PYSEC-2025-193
|
||||
--ignore-vuln PYSEC-2025-194
|
||||
--ignore-vuln PYSEC-2025-195
|
||||
--ignore-vuln PYSEC-2025-196
|
||||
--ignore-vuln PYSEC-2025-197
|
||||
--ignore-vuln PYSEC-2025-210
|
||||
--ignore-vuln PYSEC-2026-139
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47
|
||||
--ignore-vuln PYSEC-2025-211
|
||||
--ignore-vuln PYSEC-2025-212
|
||||
--ignore-vuln PYSEC-2025-213
|
||||
--ignore-vuln PYSEC-2025-214
|
||||
--ignore-vuln PYSEC-2025-215
|
||||
--ignore-vuln PYSEC-2025-216
|
||||
--ignore-vuln PYSEC-2025-217
|
||||
--ignore-vuln PYSEC-2025-218
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c' --
|
||||
language: system
|
||||
pass_filenames: false
|
||||
stages: [pre-push, manual]
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
rev: v4.10.1
|
||||
hooks:
|
||||
- id: commitizen
|
||||
- id: commitizen-branch
|
||||
stages: [ pre-push ]
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
9
.ruff.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
exclude = [
|
||||
"templates",
|
||||
"__init__.py",
|
||||
]
|
||||
|
||||
[lint]
|
||||
select = [
|
||||
"I", # isort rules
|
||||
]
|
||||
142
AGENTS.md
@@ -1,142 +0,0 @@
|
||||
# Docs contributor guide
|
||||
|
||||
The `docs/` directory is published at [docs.crewai.com](https://docs.crewai.com)
|
||||
by [Mintlify](https://www.mintlify.com/). Mintlify watches `docs/docs.json`
|
||||
and the MDX files referenced from it.
|
||||
|
||||
## TL;DR for editing docs
|
||||
|
||||
- Edit MDX under `docs/edge/<lang>/...` (e.g. `docs/edge/en/concepts/agents.mdx`).
|
||||
- Your change ships under the **Edge** version selector the moment it merges
|
||||
to `main`. Edge follows `main` and is the channel for unreleased work.
|
||||
- On release cut, the current Edge state is frozen into `docs/v<X.Y.Z>/` and
|
||||
that snapshot becomes the new default version in the selector (tag:
|
||||
`Latest`). Canonical URLs (`/<lang>/...`) auto-redirect to the new default.
|
||||
- Never modify files under `docs/v*/`. Those are frozen release snapshots
|
||||
and the `docs-snapshots` CI guard rejects writes. The only exception is a
|
||||
release-cut PR (auto-generated by `devtools release` or the manual
|
||||
`scripts/docs/freeze_current_edge.py` wrapper), which uses a
|
||||
`[docs-freeze]` title prefix to opt out.
|
||||
- Never delete or rename files under `docs/images/`. Images are append-only.
|
||||
See [Images](#images) below.
|
||||
|
||||
## The version model
|
||||
|
||||
The site has one rolling channel (Edge) plus one frozen snapshot per
|
||||
release.
|
||||
|
||||
```
|
||||
docs/
|
||||
edge/ <-- Edge sources (you edit here)
|
||||
en/...
|
||||
pt-BR/ ko/ ar/
|
||||
enterprise-api.*.yaml
|
||||
|
||||
v1.14.7/ <-- frozen snapshot of v1.14.7
|
||||
en/...
|
||||
pt-BR/ ko/ ar/
|
||||
enterprise-api.*.yaml
|
||||
v1.14.6/...
|
||||
...
|
||||
|
||||
images/ <-- shared, append-only
|
||||
docs.json <-- Mintlify config: navigation + redirects
|
||||
```
|
||||
|
||||
`docs/docs.json` lists one navigation block per version per language. Edge
|
||||
points at `docs/edge/<lang>/...`; every other version points at its own
|
||||
`docs/v<X.Y.Z>/<lang>/...` subtree. Mintlify scopes both the sidebar and the
|
||||
in-site search to whichever version the reader selects, so picking
|
||||
`v1.10.0` genuinely shows the v1.10.0 docs (and only those).
|
||||
|
||||
### URLs and canonical redirects
|
||||
|
||||
Each Mintlify version corresponds to its own URL prefix:
|
||||
|
||||
- Edge: `/edge/<lang>/<page>` (e.g. `/edge/en/concepts/agents`)
|
||||
- Frozen: `/v<X.Y.Z>/<lang>/<page>` (e.g. `/v1.14.7/en/concepts/agents`)
|
||||
|
||||
External links to the old, unversioned `/<lang>/<page>` URLs would 404 under
|
||||
this layout. To keep them working, `docs.json` ships wildcard redirects:
|
||||
|
||||
```jsonc
|
||||
{ "source": "/en/:slug*", "destination": "/v1.14.7/en/:slug*", "permanent": false }
|
||||
```
|
||||
|
||||
The release-cut step rewrites the destination on every release so canonical
|
||||
`/<lang>/...` URLs always resolve to the latest stable docs.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
1. **During development.** You add or edit pages under
|
||||
`docs/edge/<lang>/...` in normal PRs. They land in Edge as soon as the PR
|
||||
merges. Both `/edge/<lang>/<page>` and the version selector's `Edge` entry
|
||||
reflect the change immediately.
|
||||
2. **Release cut.** The release engineer runs `devtools release X.Y.Z`. As
|
||||
part of that flow the CLI opens a `[docs-freeze]` PR that copies Edge into
|
||||
`docs/v<X.Y.Z>/`, rewrites internal OpenAPI references, updates
|
||||
`docs/docs.json` to make `v<X.Y.Z>` the new default + `Latest`, and rewires
|
||||
the canonical-URL redirects to the new default. The PR must merge before
|
||||
the tag and PyPI publish run.
|
||||
3. **After release.** Edge keeps rolling. Patch fixes to the just-released
|
||||
docs go into Edge and ship with the next release. We do not back-edit
|
||||
frozen snapshots.
|
||||
|
||||
See [`RELEASING.md`](RELEASING.md) for the full release runbook.
|
||||
|
||||
## Images
|
||||
|
||||
Snapshots share a single `docs/images/` directory. If an image is deleted
|
||||
or renamed, every frozen snapshot that referenced it breaks. So the rule
|
||||
is:
|
||||
|
||||
- Adding new images is always fine.
|
||||
- Deleting or renaming an existing image fails CI unless the PR is a
|
||||
`[docs-freeze]` release-cut PR.
|
||||
- If an asset is wrong, add a new file with a new name and reference the
|
||||
new name in the Edge MDX (`docs/edge/<lang>/...`). Leave the old file
|
||||
alone.
|
||||
|
||||
## Local preview
|
||||
|
||||
Install the Mintlify CLI and run from `docs/`:
|
||||
|
||||
```bash
|
||||
npm i -g mintlify
|
||||
mintlify dev
|
||||
```
|
||||
|
||||
Use the version selector at the top of the rendered page to switch between
|
||||
Edge and frozen versions.
|
||||
|
||||
To check links across every version:
|
||||
|
||||
```bash
|
||||
mintlify broken-links
|
||||
```
|
||||
|
||||
CI runs the broken-links check on every PR that touches `docs/**` via
|
||||
[`.github/workflows/docs-broken-links.yml`](.github/workflows/docs-broken-links.yml).
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/docs/freeze_historical_versions.py` — one-time migration that
|
||||
reconstructed `docs/v1.10.0/` through `docs/v1.14.7/` from git tags. You
|
||||
should not need to run this again.
|
||||
- `scripts/docs/prefix_version_paths.py` — one-time migration that switched
|
||||
`docs/docs.json` to directory-based versioning, inserted Edge, and added
|
||||
the canonical-URL redirects. You should not need to run this again.
|
||||
- `scripts/docs/freeze_current_edge.py` — thin CLI wrapper around
|
||||
`crewai_devtools.docs_versioning.freeze`. `devtools release` calls the
|
||||
same module during its docs PR step; this script is the manual escape
|
||||
hatch (e.g. retroactively freezing a forgotten release).
|
||||
|
||||
## CI guards
|
||||
|
||||
- [`.github/workflows/docs-snapshots.yml`](.github/workflows/docs-snapshots.yml)
|
||||
enforces the two rules above (frozen snapshots immutable, images
|
||||
append-only). Both checks accept the `[docs-freeze]` PR-title escape
|
||||
hatch.
|
||||
- [`.github/workflows/docs-broken-links.yml`](.github/workflows/docs-broken-links.yml)
|
||||
runs `mintlify broken-links` against the whole site, so adding a new
|
||||
page or moving a snapshot file that breaks a link will fail CI.
|
||||
317
README.md
@@ -1,95 +1,62 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/crewAIInc/crewAI">
|
||||
<img src="docs/images/crewai_logo.png" width="600px" alt="Open source Multi-AI Agent orchestration framework">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center" style="display: flex; justify-content: center; gap: 20px; align-items: center;">
|
||||
<a href="https://trendshift.io/repositories/11239" target="_blank">
|
||||
<img src="https://trendshift.io/api/badge/repositories/11239" alt="crewAIInc%2FcrewAI | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/>
|
||||
</a>
|
||||
</p>
|
||||
<div align="center">
|
||||
|
||||
<p align="center">
|
||||
<a href="https://crewai.com">Homepage</a>
|
||||
·
|
||||
<a href="https://crewai.com/open-source">Open Source</a>
|
||||
·
|
||||
<a href="https://docs.crewai.com">Docs</a>
|
||||
·
|
||||
<a href="https://app.crewai.com">Start Cloud Trial</a>
|
||||
·
|
||||
<a href="https://blog.crewai.com">Blog</a>
|
||||
·
|
||||
<a href="https://community.crewai.com">Forum</a>
|
||||
</p>
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/crewAIInc/crewAI">
|
||||
<img src="https://img.shields.io/github/stars/crewAIInc/crewAI" alt="GitHub Repo stars">
|
||||
</a>
|
||||
<a href="https://github.com/crewAIInc/crewAI/network/members">
|
||||
<img src="https://img.shields.io/github/forks/crewAIInc/crewAI" alt="GitHub forks">
|
||||
</a>
|
||||
<a href="https://github.com/crewAIInc/crewAI/issues">
|
||||
<img src="https://img.shields.io/github/issues/crewAIInc/crewAI" alt="GitHub issues">
|
||||
</a>
|
||||
<a href="https://github.com/crewAIInc/crewAI/pulls">
|
||||
<img src="https://img.shields.io/github/issues-pr/crewAIInc/crewAI" alt="GitHub pull requests">
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://pypi.org/project/crewai/">
|
||||
<img src="https://img.shields.io/pypi/v/crewai" alt="PyPI version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/crewai/">
|
||||
<img src="https://img.shields.io/pypi/dm/crewai" alt="PyPI downloads">
|
||||
</a>
|
||||
<a href="https://twitter.com/crewAIInc">
|
||||
<img src="https://img.shields.io/twitter/follow/crewAIInc?style=social" alt="Twitter Follow">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
### Fast and Flexible Multi-Agent Automation Framework
|
||||
|
||||
> CrewAI is an open-source Python framework with high-level abstractions and low-level APIs for building production-ready multi-agent workflows.
|
||||
> It gives developers autonomous agent collaboration through Crews and precise, event-driven control through Flows.
|
||||
CrewAI is a lean, lightning-fast Python framework built entirely from
|
||||
scratch—completely **independent of LangChain or other agent frameworks**.
|
||||
It empowers developers with both high-level simplicity and precise low-level
|
||||
control, ideal for creating autonomous AI agents tailored to any scenario.
|
||||
|
||||
- **CrewAI Crews**: Optimize for autonomy and collaborative intelligence with role-based AI agents.
|
||||
- **CrewAI Flows**: Build event-driven automations that combine precise workflow control, single LLM calls, and native support for Crews.
|
||||
- **CrewAI Crews**: Optimize for autonomy and collaborative intelligence.
|
||||
- **CrewAI Flows**: Enable granular, event-driven control, single LLM calls for precise task orchestration and supports Crews natively
|
||||
|
||||
With over 100,000 developers certified through our community courses at [learn.crewai.com](https://learn.crewai.com), CrewAI is rapidly becoming the
|
||||
standard for production-ready agentic automation.
|
||||
With over 100,000 developers certified through our community courses at
|
||||
[learn.crewai.com](https://learn.crewai.com), CrewAI is rapidly becoming the
|
||||
standard for enterprise-ready AI automation.
|
||||
|
||||
# CrewAI AMP Suite
|
||||
# CrewAI Enterprise Suite
|
||||
|
||||
For organizations that need a commercial control plane around CrewAI, [CrewAI AMP Suite](https://www.crewai.com/enterprise) adds managed deployment, observability, governance, security, and enterprise support.
|
||||
CrewAI Enterprise Suite is a comprehensive bundle tailored for organizations
|
||||
that require secure, scalable, and easy-to-manage agent-driven automation.
|
||||
|
||||
You can try one part of the suite, the [Crew Control Plane, for free](https://app.crewai.com).
|
||||
You can try one part of the suite the [Crew Control Plane for free](https://app.crewai.com)
|
||||
|
||||
## Crew Control Plane Key Features:
|
||||
|
||||
- **Tracing & Observability**: Monitor and track your AI agents and workflows in real-time, including metrics, logs, and traces.
|
||||
- **Unified Control Plane**: A centralized platform for managing, monitoring, and scaling your AI agents and workflows.
|
||||
- **Seamless Integrations**: Easily connect with existing enterprise systems, data sources, and cloud infrastructure.
|
||||
- **Advanced Security**: Built-in robust security and compliance measures ensuring safe deployment and management.
|
||||
- **Actionable Insights**: Real-time analytics and reporting to optimize performance and decision-making.
|
||||
- **24/7 Support**: Dedicated enterprise support to ensure uninterrupted operation and quick resolution of issues.
|
||||
- **On-premise and Cloud Deployment Options**: Deploy CrewAI AMP on-premise or in the cloud, depending on your security and compliance requirements.
|
||||
- **On-premise and Cloud Deployment Options**: Deploy CrewAI Enterprise on-premise or in the cloud, depending on your security and compliance requirements.
|
||||
|
||||
CrewAI AMP is designed for enterprises seeking a powerful, reliable solution to transform complex business processes into efficient,
|
||||
CrewAI Enterprise is designed for enterprises seeking a powerful,
|
||||
reliable solution to transform complex business processes into efficient,
|
||||
intelligent automations.
|
||||
|
||||
<h3>
|
||||
|
||||
[Homepage](https://www.crewai.com/) | [Documentation](https://docs.crewai.com/) | [Chat with Docs](https://chatg.pt/DWjSBZn) | [Discourse](https://community.crewai.com)
|
||||
|
||||
</h3>
|
||||
|
||||
[](https://github.com/crewAIInc/crewAI)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
</div>
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Build with AI](#build-with-ai)
|
||||
- [Why CrewAI?](#why-crewai)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Key Features](#key-features)
|
||||
- [Understanding Flows and Crews](#understanding-flows-and-crews)
|
||||
- [CrewAI vs LangGraph](#how-crewai-compares)
|
||||
- [Examples](#examples)
|
||||
- [Quick Tutorial](#quick-tutorial)
|
||||
- [Write Job Descriptions](#write-job-descriptions)
|
||||
@@ -97,66 +64,33 @@ intelligent automations.
|
||||
- [Stock Analysis](#stock-analysis)
|
||||
- [Using Crews and Flows Together](#using-crews-and-flows-together)
|
||||
- [Connecting Your Crew to a Model](#connecting-your-crew-to-a-model)
|
||||
- [When to Use CrewAI](#when-to-use-crewai)
|
||||
- [How CrewAI Compares](#how-crewai-compares)
|
||||
- [Frequently Asked Questions (FAQ)](#frequently-asked-questions-faq)
|
||||
- [Contribution](#contribution)
|
||||
- [Telemetry](#telemetry)
|
||||
- [License](#license)
|
||||
- [Frequently Asked Questions (FAQ)](#frequently-asked-questions-faq)
|
||||
|
||||
## Build with AI
|
||||
|
||||
Using an AI coding agent? Teach it CrewAI best practices in one command:
|
||||
|
||||
**Claude Code:**
|
||||
```shell
|
||||
/plugin marketplace add crewAIInc/skills
|
||||
/plugin install crewai-skills@crewai-plugins
|
||||
/reload-plugins
|
||||
```
|
||||
Four skills that activate automatically when you ask relevant CrewAI questions:
|
||||
|
||||
| Skill | When it runs |
|
||||
|-------|--------------|
|
||||
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.py` / `main.py` |
|
||||
| `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails |
|
||||
| `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review |
|
||||
| `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details |
|
||||
|
||||
**Cursor, Codex, Windsurf, and others ([skills.sh](https://skills.sh/crewaiinc/skills)):**
|
||||
```shell
|
||||
npx skills add crewaiinc/skills
|
||||
```
|
||||
|
||||
This installs the official [CrewAI Skills](https://github.com/crewAIInc/skills) — structured instructions that teach coding agents how to scaffold Flows, configure Crews, design agents and tasks, and follow CrewAI patterns.
|
||||
|
||||
## Why CrewAI?
|
||||
|
||||
<div align="center" style="margin-bottom: 30px;">
|
||||
<img src="docs/images/asset.png" alt="CrewAI Logo" width="100%">
|
||||
<img src="docs/asset.png" alt="CrewAI Logo" width="100%">
|
||||
</div>
|
||||
|
||||
CrewAI unlocks the true potential of multi-agent automation, delivering speed, flexibility, and control through Crews of AI agents and event-driven Flows:
|
||||
CrewAI unlocks the true potential of multi-agent automation, delivering the best-in-class combination of speed, flexibility, and control with either Crews of AI Agents or Flows of Events:
|
||||
|
||||
- **Purpose-built architecture**: Designed specifically for agent orchestration, with a lightweight Python core and clean primitives for real-world automation.
|
||||
- **Standalone Framework**: Built from scratch, independent of LangChain or any other agent framework.
|
||||
- **High Performance**: Optimized for speed and minimal resource usage, enabling faster execution.
|
||||
- **Flexible Low-Level Customization**: Complete freedom to customize everything from workflows and system architecture to agent behaviors, internal prompts, and execution logic.
|
||||
- **Ideal for Every Use Case**: Proven effective for simple tasks, complex workflows, and production-grade automation.
|
||||
- **Flexible Low Level Customization**: Complete freedom to customize at both high and low levels - from overall workflows and system architecture to granular agent behaviors, internal prompts, and execution logic.
|
||||
- **Ideal for Every Use Case**: Proven effective for both simple tasks and highly complex, real-world, enterprise-grade scenarios.
|
||||
- **Robust Community**: Backed by a rapidly growing community of over **100,000 certified** developers offering comprehensive support and resources.
|
||||
|
||||
CrewAI empowers developers and teams to build intelligent automations that balance simplicity, flexibility, and production-grade control.
|
||||
CrewAI empowers developers and enterprises to confidently build intelligent automations, bridging the gap between simplicity, flexibility, and performance.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Setup and run your first CrewAI agents by following this tutorial.
|
||||
|
||||
[](https://www.youtube.com/watch?v=-kSOTtYzgEw "CrewAI Getting Started Tutorial")
|
||||
|
||||
###
|
||||
|
||||
Learning Resources
|
||||
### Learning Resources
|
||||
|
||||
Learn CrewAI through our comprehensive courses:
|
||||
|
||||
- [Multi AI Agent Systems with CrewAI](https://www.deeplearning.ai/short-courses/multi-ai-agent-systems-with-crewai/) - Master the fundamentals of multi-agent systems
|
||||
- [Practical Multi AI Agents and Advanced Use Cases](https://www.deeplearning.ai/short-courses/practical-multi-ai-agents-and-advanced-use-cases-with-crewai/) - Deep dive into advanced implementations
|
||||
|
||||
@@ -165,21 +99,18 @@ Learn CrewAI through our comprehensive courses:
|
||||
CrewAI offers two powerful, complementary approaches that work seamlessly together to build sophisticated AI applications:
|
||||
|
||||
1. **Crews**: Teams of AI agents with true autonomy and agency, working together to accomplish complex tasks through role-based collaboration. Crews enable:
|
||||
|
||||
- Natural, autonomous decision-making between agents
|
||||
- Dynamic task delegation and collaboration
|
||||
- Specialized roles with defined goals and expertise
|
||||
- Flexible problem-solving approaches
|
||||
|
||||
2. **Flows**: Production-ready, event-driven workflows that deliver precise control over complex automations. Flows provide:
|
||||
|
||||
- Fine-grained control over execution paths for real-world scenarios
|
||||
- Secure, consistent state management between tasks
|
||||
- Clean integration of AI agents with production Python code
|
||||
- Conditional branching for complex business logic
|
||||
|
||||
The true power of CrewAI emerges when combining Crews and Flows. This synergy allows you to:
|
||||
|
||||
- Build complex, production-grade applications
|
||||
- Balance autonomy with precise control
|
||||
- Handle sophisticated real-world scenarios
|
||||
@@ -191,21 +122,31 @@ To get started with CrewAI, follow these simple steps:
|
||||
|
||||
### 1. Installation
|
||||
|
||||
Ensure you have Python >=3.10 <3.14 installed on your system. CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
|
||||
Ensure you have Python >=3.10 <3.13 installed on your system. CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
|
||||
|
||||
First, install CrewAI:
|
||||
|
||||
```shell
|
||||
uv pip install crewai
|
||||
pip install crewai
|
||||
```
|
||||
|
||||
If you want to install the 'crewai' package along with its optional features that include additional tools for agents, you can do so by using the following command:
|
||||
|
||||
```shell
|
||||
uv pip install 'crewai[tools]'
|
||||
pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
The command above installs the basic package and also adds extra components which require more dependencies to function.
|
||||
### Using conda
|
||||
|
||||
You can also install CrewAI using conda:
|
||||
|
||||
```shell
|
||||
conda install -c conda-forge crewai
|
||||
```
|
||||
|
||||
Note: If you're using Python 3.10, make sure you have `typing_extensions` installed to resolve any 'Self' import issues.
|
||||
|
||||
The commands above install the basic package and also add extra components which require more dependencies to function.
|
||||
|
||||
### Troubleshooting Dependencies
|
||||
|
||||
@@ -214,16 +155,14 @@ If you encounter issues during installation or usage, here are some common solut
|
||||
#### Common Issues
|
||||
|
||||
1. **ModuleNotFoundError: No module named 'tiktoken'**
|
||||
|
||||
- Install tiktoken explicitly: `uv pip install 'crewai[embeddings]'`
|
||||
- If using embedchain or other tools: `uv pip install 'crewai[tools]'`
|
||||
- Install tiktoken explicitly: `pip install 'crewai[embeddings]'`
|
||||
- If using embedchain or other tools: `pip install 'crewai[tools]'`
|
||||
|
||||
2. **Failed building wheel for tiktoken**
|
||||
|
||||
- Ensure Rust compiler is installed (see installation steps above)
|
||||
- For Windows: Verify Visual C++ Build Tools are installed
|
||||
- Try upgrading pip: `uv pip install --upgrade pip`
|
||||
- If issues persist, use a pre-built wheel: `uv pip install tiktoken --prefer-binary`
|
||||
- Try upgrading pip: `pip install --upgrade pip`
|
||||
- If issues persist, use a pre-built wheel: `pip install tiktoken --prefer-binary`
|
||||
|
||||
### 2. Setting Up Your Crew with the YAML Configuration
|
||||
|
||||
@@ -301,7 +240,7 @@ reporting_analyst:
|
||||
|
||||
**tasks.yaml**
|
||||
|
||||
````yaml
|
||||
```yaml
|
||||
# src/my_project/config/tasks.yaml
|
||||
research_task:
|
||||
description: >
|
||||
@@ -321,7 +260,7 @@ reporting_task:
|
||||
Formatted as markdown without '```'
|
||||
agent: reporting_analyst
|
||||
output_file: report.md
|
||||
````
|
||||
```
|
||||
|
||||
**crew.py**
|
||||
|
||||
@@ -434,26 +373,25 @@ In addition to the sequential process, you can use the hierarchical process, whi
|
||||
|
||||
## Key Features
|
||||
|
||||
CrewAI gives developers a practical foundation for building agentic systems that move from prototype to production: autonomous collaboration where it helps, explicit workflow control where it matters, and Python-native customization throughout.
|
||||
CrewAI stands apart as a lean, standalone, high-performance framework delivering simplicity, flexibility, and precise control—free from the complexity and limitations found in other agent frameworks.
|
||||
|
||||
- **Crews for autonomy**: Model teams of specialized AI agents with roles, goals, tools, and tasks.
|
||||
- **Flows for control**: Build event-driven workflows with state, branching, routing, and production logic.
|
||||
- **Seamless integration**: Combine Crews and Flows to create complex, real-world automations.
|
||||
- **Python-native customization**: Customize prompts, tools, execution paths, state, and integrations without fighting the framework.
|
||||
- **Agent-ready capabilities**: Use tools, memory, knowledge, checkpointing, async execution, and MCP/A2A support for more capable production agents.
|
||||
- **Production-ready patterns**: Add deterministic steps, human input, structured outputs, and checkpointing as your system grows.
|
||||
- **Thriving community**: Backed by robust documentation and over 100,000 certified developers, providing exceptional support and guidance.
|
||||
- **Standalone & Lean**: Completely independent from other frameworks like LangChain, offering faster execution and lighter resource demands.
|
||||
- **Flexible & Precise**: Easily orchestrate autonomous agents through intuitive [Crews](https://docs.crewai.com/concepts/crews) or precise [Flows](https://docs.crewai.com/concepts/flows), achieving perfect balance for your needs.
|
||||
- **Seamless Integration**: Effortlessly combine Crews (autonomy) and Flows (precision) to create complex, real-world automations.
|
||||
- **Deep Customization**: Tailor every aspect—from high-level workflows down to low-level internal prompts and agent behaviors.
|
||||
- **Reliable Performance**: Consistent results across simple tasks and complex, enterprise-level automations.
|
||||
- **Thriving Community**: Backed by robust documentation and over 100,000 certified developers, providing exceptional support and guidance.
|
||||
|
||||
Choose CrewAI to build powerful, adaptable, and production-ready AI automations.
|
||||
Choose CrewAI to easily build powerful, adaptable, and production-ready AI automations.
|
||||
|
||||
## Examples
|
||||
|
||||
You can test different real life examples of AI crews in the [CrewAI-examples repo](https://github.com/crewAIInc/crewAI-examples?tab=readme-ov-file):
|
||||
|
||||
- [Landing Page Generator](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/landing_page_generator)
|
||||
- [Landing Page Generator](https://github.com/crewAIInc/crewAI-examples/tree/main/landing_page_generator)
|
||||
- [Having Human input on the execution](https://docs.crewai.com/how-to/Human-Input-on-Execution)
|
||||
- [Trip Planner](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/trip_planner)
|
||||
- [Stock Analysis](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/stock_analysis)
|
||||
- [Trip Planner](https://github.com/crewAIInc/crewAI-examples/tree/main/trip_planner)
|
||||
- [Stock Analysis](https://github.com/crewAIInc/crewAI-examples/tree/main/stock_analysis)
|
||||
|
||||
### Quick Tutorial
|
||||
|
||||
@@ -461,19 +399,19 @@ You can test different real life examples of AI crews in the [CrewAI-examples re
|
||||
|
||||
### Write Job Descriptions
|
||||
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/job-posting) or watch a video below:
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/job-posting) or watch a video below:
|
||||
|
||||
[](https://www.youtube.com/watch?v=u98wEMz-9to "Jobs postings")
|
||||
|
||||
### Trip Planner
|
||||
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/trip_planner) or watch a video below:
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/trip_planner) or watch a video below:
|
||||
|
||||
[](https://www.youtube.com/watch?v=xis7rWp-hjs "Trip Planner")
|
||||
|
||||
### Stock Analysis
|
||||
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/stock_analysis) or watch a video below:
|
||||
[Check out code for this example](https://github.com/crewAIInc/crewAI-examples/tree/main/stock_analysis) or watch a video below:
|
||||
|
||||
[](https://www.youtube.com/watch?v=e0Uj4yWdaAg "Stock Analysis")
|
||||
|
||||
@@ -481,8 +419,7 @@ You can test different real life examples of AI crews in the [CrewAI-examples re
|
||||
|
||||
CrewAI's power truly shines when combining Crews with Flows to create sophisticated automation pipelines.
|
||||
CrewAI flows support logical operators like `or_` and `and_` to combine multiple conditions. This can be used with `@start`, `@listen`, or `@router` decorators to create complex triggering conditions.
|
||||
|
||||
- `or_`: Triggers when any of the specified conditions are met.
|
||||
- `or_`: Triggers when any of the specified conditions are met.
|
||||
- `and_`Triggers when all of the specified conditions are met.
|
||||
|
||||
Here's how you can orchestrate multiple Crews within a Flow:
|
||||
@@ -570,7 +507,6 @@ class AdvancedAnalysisFlow(Flow[MarketState]):
|
||||
```
|
||||
|
||||
This example demonstrates how to:
|
||||
|
||||
1. Use Python code for basic data operations
|
||||
2. Create and execute Crews as steps in your workflow
|
||||
3. Use Flow decorators to manage the sequence of operations
|
||||
@@ -580,19 +516,19 @@ This example demonstrates how to:
|
||||
|
||||
CrewAI supports using various LLMs through a variety of connection options. By default your agents will use the OpenAI API when querying the model. However, there are several other ways to allow your agents to connect to models. For example, you can configure your agents to use a local model via the Ollama tool.
|
||||
|
||||
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/how-to/LLM-Connections/) page for details on configuring your agents' connections to models.
|
||||
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/how-to/LLM-Connections/) page for details on configuring you agents' connections to models.
|
||||
|
||||
## When to Use CrewAI
|
||||
## How CrewAI Compares
|
||||
|
||||
Use CrewAI when you need more than a single prompt or chatbot: multi-step work, specialized agents, tool use, structured outputs, human review, or workflows that combine autonomous reasoning with explicit business logic.
|
||||
**CrewAI's Advantage**: CrewAI combines autonomous agent intelligence with precise workflow control through its unique Crews and Flows architecture. The framework excels at both high-level orchestration and low-level customization, enabling complex, production-grade systems with granular control.
|
||||
|
||||
CrewAI is especially useful when you want to:
|
||||
- **LangGraph**: While LangGraph provides a foundation for building agent workflows, its approach requires significant boilerplate code and complex state management patterns. The framework's tight coupling with LangChain can limit flexibility when implementing custom agent behaviors or integrating with external systems.
|
||||
|
||||
- Coordinate multiple agents with clear roles and tasks.
|
||||
- Wrap agent work in deterministic, event-driven workflows.
|
||||
- Keep application logic in regular Python.
|
||||
- Move from experiment to production without changing frameworks.
|
||||
- Add tools, memory, checkpointing, and async execution as your system grows.
|
||||
*P.S. CrewAI demonstrates significant performance advantages over LangGraph, executing 5.76x faster in certain cases like this QA task example ([see comparison](https://github.com/crewAIInc/crewAI-examples/tree/main/Notebooks/CrewAI%20Flows%20%26%20Langgraph/QA%20Agent)) while achieving higher evaluation scores with faster completion times in certain coding tasks, like in this example ([detailed analysis](https://github.com/crewAIInc/crewAI-examples/blob/main/Notebooks/CrewAI%20Flows%20%26%20Langgraph/Coding%20Assistant/coding_assistant_eval.ipynb)).*
|
||||
|
||||
- **Autogen**: While Autogen excels at creating conversational agents capable of working together, it lacks an inherent concept of process. In Autogen, orchestrating agents' interactions requires additional programming, which can become complex and cumbersome as the scale of tasks grows.
|
||||
|
||||
- **ChatDev**: ChatDev introduced the idea of processes into the realm of AI agents, but its implementation is quite rigid. Customizations in ChatDev are limited and not geared towards production environments, which can hinder scalability and flexibility in real-world applications.
|
||||
|
||||
## Contribution
|
||||
|
||||
@@ -604,19 +540,6 @@ CrewAI is open-source and we welcome contributions. If you're looking to contrib
|
||||
- Send a pull request.
|
||||
- We appreciate your input!
|
||||
|
||||
### Contributing to the docs
|
||||
|
||||
The site at [docs.crewai.com](https://docs.crewai.com) is published from
|
||||
`docs/` by [Mintlify](https://www.mintlify.com/). The docs use directory-based
|
||||
versioning: edits to `docs/edge/<lang>/...` (e.g.
|
||||
`docs/edge/en/concepts/agents.mdx`) land under the **Edge** version selector
|
||||
immediately and are frozen into a new versioned snapshot under
|
||||
`docs/v<X.Y.Z>/` at the next release cut. Frozen snapshots are immutable — CI
|
||||
rejects PRs that modify them without a `[docs-freeze]` title prefix. The
|
||||
release CLI (`devtools release`) handles the freeze automatically; see
|
||||
[`AGENTS.md`](AGENTS.md) for the full contributor guide and
|
||||
[`RELEASING.md`](RELEASING.md) for the release-cut runbook.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
```bash
|
||||
@@ -657,7 +580,7 @@ uv build
|
||||
### Installing Locally
|
||||
|
||||
```bash
|
||||
uv pip install dist/*.tar.gz
|
||||
pip install dist/*.tar.gz
|
||||
```
|
||||
|
||||
## Telemetry
|
||||
@@ -695,124 +618,102 @@ Users can opt-in to Further Telemetry, sharing the complete telemetry data by se
|
||||
|
||||
CrewAI is released under the [MIT License](https://github.com/crewAIInc/crewAI/blob/main/LICENSE).
|
||||
|
||||
|
||||
## Frequently Asked Questions (FAQ)
|
||||
|
||||
### General
|
||||
|
||||
- [What exactly is CrewAI?](#q-what-exactly-is-crewai)
|
||||
- [How do I install CrewAI?](#q-how-do-i-install-crewai)
|
||||
- [Is CrewAI a standalone framework?](#q-is-crewai-a-standalone-framework)
|
||||
- [Does CrewAI depend on LangChain?](#q-does-crewai-depend-on-langchain)
|
||||
- [Is CrewAI open-source?](#q-is-crewai-open-source)
|
||||
- [Does CrewAI collect data from users?](#q-does-crewai-collect-data-from-users)
|
||||
|
||||
### Features and Capabilities
|
||||
|
||||
- [Can CrewAI handle complex use cases?](#q-can-crewai-handle-complex-use-cases)
|
||||
- [Can I use CrewAI with local AI models?](#q-can-i-use-crewai-with-local-ai-models)
|
||||
- [What makes Crews different from Flows?](#q-what-makes-crews-different-from-flows)
|
||||
- [How is CrewAI better than LangChain?](#q-how-is-crewai-better-than-langchain)
|
||||
- [Does CrewAI support fine-tuning or training custom models?](#q-does-crewai-support-fine-tuning-or-training-custom-models)
|
||||
|
||||
### Resources and Community
|
||||
|
||||
- [Where can I find real-world CrewAI examples?](#q-where-can-i-find-real-world-crewai-examples)
|
||||
- [How can I contribute to CrewAI?](#q-how-can-i-contribute-to-crewai)
|
||||
|
||||
### Enterprise Features
|
||||
- [What additional features does CrewAI Enterprise offer?](#q-what-additional-features-does-crewai-enterprise-offer)
|
||||
- [Is CrewAI Enterprise available for cloud and on-premise deployments?](#q-is-crewai-enterprise-available-for-cloud-and-on-premise-deployments)
|
||||
- [Can I try CrewAI Enterprise for free?](#q-can-i-try-crewai-enterprise-for-free)
|
||||
|
||||
|
||||
- [What additional features does CrewAI AMP offer?](#q-what-additional-features-does-crewai-amp-offer)
|
||||
- [Is CrewAI AMP available for cloud and on-premise deployments?](#q-is-crewai-amp-available-for-cloud-and-on-premise-deployments)
|
||||
- [Can I try CrewAI AMP for free?](#q-can-i-try-crewai-amp-for-free)
|
||||
|
||||
### Q: What exactly is CrewAI?
|
||||
|
||||
A: CrewAI is a lean, fast Python framework built specifically for orchestrating autonomous AI agents and production-ready agentic workflows.
|
||||
A: CrewAI is a standalone, lean, and fast Python framework built specifically for orchestrating autonomous AI agents. Unlike frameworks like LangChain, CrewAI does not rely on external dependencies, making it leaner, faster, and simpler.
|
||||
|
||||
### Q: How do I install CrewAI?
|
||||
|
||||
A: Install CrewAI using pip:
|
||||
|
||||
```shell
|
||||
uv pip install crewai
|
||||
pip install crewai
|
||||
```
|
||||
|
||||
For additional tools, use:
|
||||
|
||||
```shell
|
||||
uv pip install 'crewai[tools]'
|
||||
pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
### Q: Is CrewAI a standalone framework?
|
||||
|
||||
A: Yes. CrewAI is a standalone Python framework with its own primitives for agents, tasks, crews, flows, tools, and orchestration.
|
||||
### Q: Does CrewAI depend on LangChain?
|
||||
A: No. CrewAI is built entirely from the ground up, with no dependencies on LangChain or other agent frameworks. This ensures a lean, fast, and flexible experience.
|
||||
|
||||
### Q: Can CrewAI handle complex use cases?
|
||||
|
||||
A: Yes. CrewAI excels at both simple and highly complex real-world scenarios, offering deep customization options at both high and low levels, from internal prompts to sophisticated workflow orchestration.
|
||||
|
||||
### Q: Can I use CrewAI with local AI models?
|
||||
|
||||
A: Absolutely! CrewAI supports various language models, including local ones. Tools like Ollama and LM Studio allow seamless integration. Check the [LLM Connections documentation](https://docs.crewai.com/how-to/LLM-Connections/) for more details.
|
||||
|
||||
### Q: What makes Crews different from Flows?
|
||||
|
||||
A: Crews provide autonomous agent collaboration, ideal for tasks requiring flexible decision-making and dynamic interaction. Flows offer precise, event-driven control, ideal for managing detailed execution paths and secure state management. You can seamlessly combine both for maximum effectiveness.
|
||||
|
||||
### Q: Is CrewAI open-source?
|
||||
### Q: How is CrewAI better than LangChain?
|
||||
A: CrewAI provides simpler, more intuitive APIs, faster execution speeds, more reliable and consistent results, robust documentation, and an active community—addressing common criticisms and limitations associated with LangChain.
|
||||
|
||||
### Q: Is CrewAI open-source?
|
||||
A: Yes, CrewAI is open-source and actively encourages community contributions and collaboration.
|
||||
|
||||
### Q: Does CrewAI collect data from users?
|
||||
|
||||
A: CrewAI collects anonymous telemetry data strictly for improvement purposes. Sensitive data such as prompts, tasks, or API responses are never collected unless explicitly enabled by the user.
|
||||
|
||||
### Q: Where can I find real-world CrewAI examples?
|
||||
|
||||
A: Check out practical examples in the [CrewAI-examples repository](https://github.com/crewAIInc/crewAI-examples), covering use cases like trip planners, stock analysis, and job postings.
|
||||
|
||||
### Q: How can I contribute to CrewAI?
|
||||
|
||||
A: Contributions are warmly welcomed! Fork the repository, create your branch, implement your changes, and submit a pull request. See the Contribution section of the README for detailed guidelines.
|
||||
|
||||
### Q: What additional features does CrewAI AMP offer?
|
||||
### Q: What additional features does CrewAI Enterprise offer?
|
||||
A: CrewAI Enterprise provides advanced features such as a unified control plane, real-time observability, secure integrations, advanced security, actionable insights, and dedicated 24/7 enterprise support.
|
||||
|
||||
A: CrewAI AMP provides advanced features such as a unified control plane, real-time observability, secure integrations, advanced security, actionable insights, and dedicated 24/7 enterprise support.
|
||||
### Q: Is CrewAI Enterprise available for cloud and on-premise deployments?
|
||||
A: Yes, CrewAI Enterprise supports both cloud-based and on-premise deployment options, allowing enterprises to meet their specific security and compliance requirements.
|
||||
|
||||
### Q: Is CrewAI AMP available for cloud and on-premise deployments?
|
||||
|
||||
A: Yes, CrewAI AMP supports both cloud-based and on-premise deployment options, allowing enterprises to meet their specific security and compliance requirements.
|
||||
|
||||
### Q: Can I try CrewAI AMP for free?
|
||||
|
||||
A: Yes, you can explore part of the CrewAI AMP Suite by accessing the [Crew Control Plane](https://app.crewai.com) for free.
|
||||
### Q: Can I try CrewAI Enterprise for free?
|
||||
A: Yes, you can explore part of the CrewAI Enterprise Suite by accessing the [Crew Control Plane](https://app.crewai.com) for free.
|
||||
|
||||
### Q: Does CrewAI support fine-tuning or training custom models?
|
||||
|
||||
A: Yes, CrewAI can integrate with custom-trained or fine-tuned models, allowing you to enhance your agents with domain-specific knowledge and accuracy.
|
||||
|
||||
### Q: Can CrewAI agents interact with external tools and APIs?
|
||||
|
||||
A: Absolutely! CrewAI agents can easily integrate with external tools, APIs, and databases, empowering them to leverage real-world data and resources.
|
||||
|
||||
### Q: Is CrewAI suitable for production environments?
|
||||
|
||||
A: Yes, CrewAI is designed with production-grade patterns that support reliable, stable, and scalable agentic workflows.
|
||||
A: Yes, CrewAI is explicitly designed with production-grade standards, ensuring reliability, stability, and scalability for enterprise deployments.
|
||||
|
||||
### Q: How scalable is CrewAI?
|
||||
|
||||
A: CrewAI is highly scalable, supporting simple automations and large-scale workflows involving numerous agents and complex tasks simultaneously.
|
||||
A: CrewAI is highly scalable, supporting simple automations and large-scale enterprise workflows involving numerous agents and complex tasks simultaneously.
|
||||
|
||||
### Q: Does CrewAI offer debugging and monitoring tools?
|
||||
|
||||
A: Yes, CrewAI AMP includes advanced debugging, tracing, and real-time observability features, simplifying the management and troubleshooting of your automations.
|
||||
A: Yes, CrewAI Enterprise includes advanced debugging, tracing, and real-time observability features, simplifying the management and troubleshooting of your automations.
|
||||
|
||||
### Q: What programming languages does CrewAI support?
|
||||
|
||||
A: CrewAI is primarily Python-based but easily integrates with services and APIs written in any programming language through its flexible API integration capabilities.
|
||||
|
||||
### Q: Does CrewAI offer educational resources for beginners?
|
||||
|
||||
A: Yes, CrewAI provides extensive beginner-friendly tutorials, courses, and documentation through learn.crewai.com, supporting developers at all skill levels.
|
||||
|
||||
### Q: Can CrewAI automate human-in-the-loop workflows?
|
||||
|
||||
A: Yes, CrewAI fully supports human-in-the-loop workflows, allowing seamless collaboration between human experts and AI agents for enhanced decision-making.
|
||||
|
||||
47
conda/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Conda Package for CrewAI
|
||||
|
||||
This directory contains the necessary files to build a conda package for CrewAI.
|
||||
|
||||
## Files
|
||||
|
||||
- `meta.yaml`: The main conda recipe file that defines package metadata, dependencies, and build requirements
|
||||
- `build.sh`: Build script for Unix-like systems (Linux, macOS)
|
||||
- `bld.bat`: Build script for Windows
|
||||
|
||||
## Building the Package
|
||||
|
||||
To build the package, you need to have conda-build installed:
|
||||
|
||||
```bash
|
||||
conda install conda-build
|
||||
```
|
||||
|
||||
Then, from the repository root directory:
|
||||
|
||||
```bash
|
||||
conda build conda
|
||||
```
|
||||
|
||||
## Testing the Package
|
||||
|
||||
After building, you can install and test the package:
|
||||
|
||||
```bash
|
||||
conda install --use-local crewai
|
||||
```
|
||||
|
||||
## Uploading to Anaconda
|
||||
|
||||
To upload the package to Anaconda, you need to have anaconda-client installed:
|
||||
|
||||
```bash
|
||||
conda install anaconda-client
|
||||
anaconda login
|
||||
anaconda upload /path/to/conda-bld/noarch/crewai-*.tar.bz2
|
||||
```
|
||||
|
||||
## Compatibility Notes
|
||||
|
||||
This package addresses compatibility issues with:
|
||||
- Python 3.10: Adds typing_extensions as a dependency to provide the Self type
|
||||
- Python 3.12: Ensures compatibility with the tokenizers package
|
||||
2
conda/bld.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
"%PYTHON%" -m pip install . --no-deps -vv
|
||||
if errorlevel 1 exit 1
|
||||
3
conda/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
$PYTHON -m pip install . --no-deps -vv
|
||||
66
conda/meta.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
{% set name = "crewai" %}
|
||||
{% set version = "0.86.0" %}
|
||||
|
||||
package:
|
||||
name: {{ name|lower }}
|
||||
version: {{ version }}
|
||||
|
||||
source:
|
||||
path: ..
|
||||
|
||||
build:
|
||||
noarch: python
|
||||
number: 0
|
||||
script: {{ PYTHON }} -m pip install . -vv
|
||||
entry_points:
|
||||
- crewai = crewai.cli.cli:crewai
|
||||
|
||||
requirements:
|
||||
host:
|
||||
- python >=3.10,<3.13
|
||||
- pip
|
||||
- hatchling
|
||||
run:
|
||||
- python >=3.10,<3.13
|
||||
- pydantic >=2.4.2
|
||||
- openai >=1.13.3
|
||||
- opentelemetry-api >=1.22.0
|
||||
- opentelemetry-sdk >=1.22.0
|
||||
- opentelemetry-exporter-otlp-proto-http >=1.22.0
|
||||
- instructor >=1.3.3
|
||||
- regex >=2024.9.11
|
||||
- click >=8.1.7
|
||||
- python-dotenv >=1.0.0
|
||||
- appdirs >=1.4.4
|
||||
- jsonref >=1.1.0
|
||||
- json-repair >=0.25.2
|
||||
- auth0-python >=4.7.1
|
||||
- litellm >=1.44.22
|
||||
- pyvis >=0.3.2
|
||||
- uv >=0.4.25
|
||||
- tomli-w >=1.1.0
|
||||
- tomli >=2.0.2
|
||||
- chromadb >=0.5.23
|
||||
- pdfplumber >=0.11.4
|
||||
- openpyxl >=3.1.5
|
||||
- blinker >=1.9.0
|
||||
|
||||
test:
|
||||
imports:
|
||||
- crewai
|
||||
commands:
|
||||
- pip check
|
||||
- crewai --help
|
||||
|
||||
about:
|
||||
home: https://crewai.com
|
||||
doc_url: https://docs.crewai.com
|
||||
dev_url: https://github.com/crewAIInc/crewAI
|
||||
summary: Cutting-edge framework for orchestrating role-playing, autonomous AI agents
|
||||
license: MIT
|
||||
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- joaomoura
|
||||
# For Python 3.10 compatibility (Self type)
|
||||
- typing_extensions >=4.0.0
|
||||
427
conftest.py
@@ -1,427 +0,0 @@
|
||||
"""Pytest configuration for crewAI workspace."""
|
||||
|
||||
import base64
|
||||
from collections.abc import Generator
|
||||
import gzip
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import tempfile
|
||||
from typing import Any
|
||||
|
||||
from dotenv import load_dotenv
|
||||
import pytest
|
||||
|
||||
|
||||
def _patch_vcrpy_aiohttp_compat() -> None:
|
||||
"""Keep vcrpy's aiohttp stub working under aiohttp 3.14.0.
|
||||
|
||||
aiohttp 3.14.0 (pulled in to fix GHSA-jg22-mg44-37j8 and GHSA-hg6j-4rv6-33pg):
|
||||
* removed ``aiohttp.streams.AsyncStreamReaderMixin`` (folded into ``StreamReader``),
|
||||
which vcrpy's ``MockStream`` still subclasses -- vcr's patch machinery then raises
|
||||
``AttributeError`` at collection time; and
|
||||
* added a required ``stream_writer`` keyword-only arg to ``ClientResponse.__init__``,
|
||||
which vcrpy's ``MockClientResponse`` does not pass -- raising ``TypeError`` at
|
||||
cassette playback.
|
||||
|
||||
Restore the mixin, then rebuild ``MockClientResponse``'s ``super().__init__`` call from
|
||||
the live ``ClientResponse`` signature (defaulting every required keyword-only arg to
|
||||
``None``, mirroring vcrpy's original call) so it also survives future aiohttp additions.
|
||||
"""
|
||||
import asyncio
|
||||
import inspect
|
||||
|
||||
from aiohttp import streams
|
||||
from aiohttp.client_reqrep import ClientResponse
|
||||
|
||||
if not hasattr(streams, "AsyncStreamReaderMixin"):
|
||||
|
||||
class AsyncStreamReaderMixin:
|
||||
__slots__ = ()
|
||||
|
||||
def __aiter__(self) -> streams.AsyncStreamIterator[bytes]:
|
||||
return streams.AsyncStreamIterator(self.readline) # type: ignore[attr-defined]
|
||||
|
||||
def iter_chunked(self, n: int) -> streams.AsyncStreamIterator[bytes]:
|
||||
return streams.AsyncStreamIterator(lambda: self.read(n)) # type: ignore[attr-defined]
|
||||
|
||||
def iter_any(self) -> streams.AsyncStreamIterator[bytes]:
|
||||
return streams.AsyncStreamIterator(self.readany) # type: ignore[attr-defined]
|
||||
|
||||
def iter_chunks(self) -> streams.ChunkTupleAsyncStreamIterator:
|
||||
return streams.ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type]
|
||||
|
||||
streams.AsyncStreamReaderMixin = AsyncStreamReaderMixin # type: ignore[attr-defined]
|
||||
|
||||
# Importing the stub builds MockStream/MockClientResponse, so it must run after the
|
||||
# mixin is restored above.
|
||||
import vcr.stubs.aiohttp_stubs as aiohttp_stubs # type: ignore[import-untyped]
|
||||
|
||||
if getattr(aiohttp_stubs.MockClientResponse, "_crewai_aiohttp_patched", False):
|
||||
return
|
||||
|
||||
keyword_only = [
|
||||
name
|
||||
for name, param in inspect.signature(ClientResponse.__init__).parameters.items()
|
||||
if param.kind is inspect.Parameter.KEYWORD_ONLY
|
||||
]
|
||||
|
||||
class _NullStreamWriter:
|
||||
# aiohttp 3.14.0 reads stream_writer.output_size in the "request already
|
||||
# sent" branch (writer is None), so None is not enough -- supply a stub.
|
||||
output_size = 0
|
||||
|
||||
fallback_loop: list[asyncio.AbstractEventLoop] = []
|
||||
|
||||
def _resolve_loop() -> asyncio.AbstractEventLoop:
|
||||
# MockClientResponse is normally built inside aiohttp's running loop, so
|
||||
# prefer that. In a sync context there is no running loop; avoid
|
||||
# asyncio.get_event_loop(), which on 3.12+ emits a DeprecationWarning
|
||||
# (and can RuntimeError) when no current loop is set. Use one cached
|
||||
# loop instead -- the mock only stores it and calls loop.get_debug().
|
||||
try:
|
||||
return asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
if not fallback_loop:
|
||||
fallback_loop.append(asyncio.new_event_loop())
|
||||
return fallback_loop[0]
|
||||
|
||||
def _mock_client_response_init(
|
||||
self: Any, method: str, url: Any, request_info: Any = None
|
||||
) -> None:
|
||||
kwargs: dict[str, Any] = dict.fromkeys(keyword_only)
|
||||
kwargs["request_info"] = request_info
|
||||
if "loop" in kwargs:
|
||||
kwargs["loop"] = _resolve_loop()
|
||||
if "stream_writer" in kwargs:
|
||||
kwargs["stream_writer"] = _NullStreamWriter()
|
||||
ClientResponse.__init__(self, method, url, **kwargs)
|
||||
|
||||
aiohttp_stubs.MockClientResponse.__init__ = _mock_client_response_init
|
||||
aiohttp_stubs.MockClientResponse._crewai_aiohttp_patched = True
|
||||
|
||||
|
||||
_patch_vcrpy_aiohttp_compat()
|
||||
|
||||
from vcr.request import Request # type: ignore[import-untyped] # noqa: E402
|
||||
|
||||
|
||||
try:
|
||||
import vcr.stubs.httpx_stubs as httpx_stubs # type: ignore[import-untyped]
|
||||
except ModuleNotFoundError:
|
||||
import vcr.stubs.httpcore_stubs as httpx_stubs # type: ignore[import-untyped]
|
||||
|
||||
|
||||
env_test_path = Path(__file__).parent / ".env.test"
|
||||
|
||||
load_dotenv(env_test_path, override=False)
|
||||
load_dotenv(override=False)
|
||||
|
||||
BEDROCK_HOST_PLACEHOLDER = "bedrock-runtime.vcr.amazonaws.com"
|
||||
_BEDROCK_HOST_RE = re.compile(r"^bedrock-runtime\.[a-z0-9-]+\.amazonaws\.com$")
|
||||
|
||||
|
||||
def _normalize_bedrock_host(host: str) -> str:
|
||||
if _BEDROCK_HOST_RE.match(host):
|
||||
return BEDROCK_HOST_PLACEHOLDER
|
||||
return host
|
||||
|
||||
|
||||
def bedrock_host_matcher(r1: Request, r2: Request) -> bool: # type: ignore[no-any-unimported]
|
||||
"""Match Bedrock requests across AWS regions (CI uses us-east-1, local may use us-west-2)."""
|
||||
return _normalize_bedrock_host(r1.host or "") == _normalize_bedrock_host(
|
||||
r2.host or ""
|
||||
)
|
||||
|
||||
|
||||
def _patched_make_vcr_request(
|
||||
httpx_request: Any, real_request_body: Any = None, **kwargs: Any
|
||||
) -> Any:
|
||||
"""Patched version of VCR's _make_vcr_request that handles binary content.
|
||||
|
||||
The original implementation fails on binary request bodies (like file uploads)
|
||||
because it assumes all content can be decoded as UTF-8.
|
||||
"""
|
||||
raw_body = real_request_body if real_request_body is not None else httpx_request.read()
|
||||
body: Any = raw_body
|
||||
if isinstance(raw_body, bytes):
|
||||
try:
|
||||
body = raw_body.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
body = base64.b64encode(raw_body).decode("ascii")
|
||||
uri = str(httpx_request.url)
|
||||
headers = dict(httpx_request.headers)
|
||||
return Request(httpx_request.method, uri, body, headers)
|
||||
|
||||
|
||||
httpx_stubs._make_vcr_request = _patched_make_vcr_request
|
||||
|
||||
|
||||
# Patch the response-side of VCR to fix httpx.ResponseNotRead errors.
|
||||
# VCR's _from_serialized_response mocks httpx.Response.read(), which prevents
|
||||
# the response's internal _content attribute from being properly initialized.
|
||||
# When OpenAI's client (using with_raw_response) accesses response.content,
|
||||
# httpx raises ResponseNotRead because read() was never actually called.
|
||||
# This patch ensures _content is explicitly set after response creation.
|
||||
_original_from_serialized_response = getattr(
|
||||
httpx_stubs, "_from_serialized_response", None
|
||||
)
|
||||
|
||||
if _original_from_serialized_response is not None:
|
||||
_from_serialized: Any = _original_from_serialized_response
|
||||
|
||||
def _patched_from_serialized_response(
|
||||
request: Any, serialized_response: Any, history: Any = None
|
||||
) -> Any:
|
||||
"""Patched version that ensures response._content is properly set."""
|
||||
response = _from_serialized(request, serialized_response, history)
|
||||
# Explicitly set _content to avoid ResponseNotRead errors
|
||||
# The content was passed to the constructor but the mocked read() prevents
|
||||
# proper initialization of the internal state
|
||||
body_content = serialized_response.get("body", {}).get("string", b"")
|
||||
if isinstance(body_content, str):
|
||||
body_content = body_content.encode("utf-8")
|
||||
response._content = body_content
|
||||
return response
|
||||
|
||||
httpx_stubs._from_serialized_response = _patched_from_serialized_response
|
||||
|
||||
|
||||
@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 reset_event_state() -> None:
|
||||
"""Reset event system state before each test for isolation."""
|
||||
from crewai.events.base_events import reset_emission_counter
|
||||
from crewai.events.event_context import (
|
||||
EventContextConfig,
|
||||
_event_context_config,
|
||||
_event_id_stack,
|
||||
)
|
||||
|
||||
reset_emission_counter()
|
||||
_event_id_stack.set(())
|
||||
_event_context_config.set(EventContextConfig())
|
||||
|
||||
|
||||
@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",
|
||||
"api-key": "X-API-KEY-XXX",
|
||||
"User-Agent": "X-USER-AGENT-XXX",
|
||||
"apim-request-id:": "X-API-CLIENT-REQUEST-ID-XXX",
|
||||
"azureml-model-session": "AZUREML-MODEL-SESSION-XXX",
|
||||
"x-ms-client-request-id": "X-MS-CLIENT-REQUEST-ID-XXX",
|
||||
"x-ms-region": "X-MS-REGION-XXX",
|
||||
"apim-request-id": "APIM-REQUEST-ID-XXX",
|
||||
"x-api-key": "X-API-KEY-XXX",
|
||||
"anthropic-organization-id": "ANTHROPIC-ORGANIZATION-ID-XXX",
|
||||
"request-id": "REQUEST-ID-XXX",
|
||||
"anthropic-ratelimit-input-tokens-limit": "ANTHROPIC-RATELIMIT-INPUT-TOKENS-LIMIT-XXX",
|
||||
"anthropic-ratelimit-input-tokens-remaining": "ANTHROPIC-RATELIMIT-INPUT-TOKENS-REMAINING-XXX",
|
||||
"anthropic-ratelimit-input-tokens-reset": "ANTHROPIC-RATELIMIT-INPUT-TOKENS-RESET-XXX",
|
||||
"anthropic-ratelimit-output-tokens-limit": "ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-LIMIT-XXX",
|
||||
"anthropic-ratelimit-output-tokens-remaining": "ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-REMAINING-XXX",
|
||||
"anthropic-ratelimit-output-tokens-reset": "ANTHROPIC-RATELIMIT-OUTPUT-TOKENS-RESET-XXX",
|
||||
"anthropic-ratelimit-tokens-limit": "ANTHROPIC-RATELIMIT-TOKENS-LIMIT-XXX",
|
||||
"anthropic-ratelimit-tokens-remaining": "ANTHROPIC-RATELIMIT-TOKENS-REMAINING-XXX",
|
||||
"anthropic-ratelimit-tokens-reset": "ANTHROPIC-RATELIMIT-TOKENS-RESET-XXX",
|
||||
"x-amz-date": "X-AMZ-DATE-XXX",
|
||||
"x-amz-security-token": "X-AMZ-SECURITY-TOKEN-XXX",
|
||||
"amz-sdk-invocation-id": "AMZ-SDK-INVOCATION-ID-XXX",
|
||||
"accept-encoding": "ACCEPT-ENCODING-XXX",
|
||||
"x-amzn-requestid": "X-AMZN-REQUESTID-XXX",
|
||||
"x-amzn-RequestId": "X-AMZN-REQUESTID-XXX",
|
||||
"x-a2a-notification-token": "X-A2A-NOTIFICATION-TOKEN-XXX",
|
||||
"x-a2a-version": "X-A2A-VERSION-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]
|
||||
|
||||
request.method = request.method.upper()
|
||||
|
||||
# Normalize Azure OpenAI endpoints to a consistent placeholder for cassette matching.
|
||||
if request.host and request.host.endswith(".openai.azure.com"):
|
||||
original_host = request.host
|
||||
placeholder_host = "fake-azure-endpoint.openai.azure.com"
|
||||
request.uri = request.uri.replace(original_host, placeholder_host)
|
||||
|
||||
# Normalize Bedrock regional endpoints so cassettes work in any AWS region.
|
||||
if request.host and _BEDROCK_HOST_RE.match(request.host):
|
||||
request.uri = request.uri.replace(request.host, BEDROCK_HOST_PLACEHOLDER)
|
||||
|
||||
return request
|
||||
|
||||
|
||||
def _filter_response_headers(response: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""Filter sensitive headers from response before recording.
|
||||
|
||||
Returns None to skip recording responses with empty bodies. This handles
|
||||
duplicate recordings caused by OpenAI's stainless client using
|
||||
with_raw_response which triggers httpx to re-read the consumed stream.
|
||||
"""
|
||||
body = response.get("body", {}).get("string", "")
|
||||
headers = response.get("headers", {})
|
||||
content_length = headers.get("content-length", headers.get("Content-Length", []))
|
||||
|
||||
if body == "" or body == b"" or content_length == ["0"]:
|
||||
return None
|
||||
|
||||
status_code = response.get("status", {}).get("code")
|
||||
if isinstance(status_code, int) and status_code >= 400:
|
||||
# Avoid persisting auth/model errors when re-recording without valid AWS creds.
|
||||
return None
|
||||
|
||||
for encoding_header in ["Content-Encoding", "content-encoding"]:
|
||||
if encoding_header in headers:
|
||||
encoding = headers.pop(encoding_header)
|
||||
if encoding and encoding[0] == "gzip":
|
||||
body = response.get("body", {}).get("string", b"")
|
||||
if isinstance(body, bytes) and body.startswith(b"\x1f\x8b"):
|
||||
response["body"]["string"] = gzip.decompress(body).decode("utf-8")
|
||||
|
||||
for header_name, replacement in HEADERS_TO_FILTER.items():
|
||||
for variant in [header_name, header_name.upper(), header_name.title()]:
|
||||
if variant in headers:
|
||||
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", "crewai-files", "cli", "crewai-core")
|
||||
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)
|
||||
|
||||
|
||||
def pytest_recording_configure(vcr: Any, config: Any) -> None:
|
||||
"""Register custom VCR matchers for each test cassette session."""
|
||||
vcr.register_matcher("bedrock_host", bedrock_host_matcher)
|
||||
|
||||
|
||||
@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"],
|
||||
"match_on": ["method", "scheme", "host", "port", "path"],
|
||||
}
|
||||
|
||||
if os.getenv("GITHUB_ACTIONS") == "true":
|
||||
config["record_mode"] = "none"
|
||||
|
||||
return config
|
||||
1737
crewAI.excalidraw
Normal file
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
217
docs/changelog.mdx
Normal file
@@ -0,0 +1,217 @@
|
||||
---
|
||||
title: Changelog
|
||||
description: View the latest updates and changes to CrewAI
|
||||
icon: timeline
|
||||
---
|
||||
|
||||
<Update label="2025-04-07" description="v0.114.0">
|
||||
## Release Highlights
|
||||
<Frame>
|
||||
<img src="/images/v01140.png" />
|
||||
</Frame>
|
||||
|
||||
**New Features & Enhancements**
|
||||
- Agents as an atomic unit. (`Agent(...).kickoff()`)
|
||||
- Support for [Custom LLM implementations](https://docs.crewai.com/guides/advanced/custom-llm).
|
||||
- Integrated External Memory and [Opik observability](https://docs.crewai.com/how-to/opik-observability).
|
||||
- Enhanced YAML extraction.
|
||||
- Multimodal agent validation.
|
||||
- Added Secure fingerprints for agents and crews.
|
||||
|
||||
**Core Improvements & Fixes**
|
||||
- Improved serialization, agent copying, and Python compatibility.
|
||||
- Added wildcard support to `emit()`
|
||||
- Added support for additional router calls and context window adjustments.
|
||||
- Fixed typing issues, validation, and import statements.
|
||||
- Improved method performance.
|
||||
- Enhanced agent task handling, event emissions, and memory management.
|
||||
- Fixed CLI issues, conditional tasks, cloning behavior, and tool outputs.
|
||||
|
||||
**Documentation & Guides**
|
||||
- Improved documentation structure, theme, and organization.
|
||||
- Added guides for Local NVIDIA NIM with WSL2, W&B Weave, and Arize Phoenix.
|
||||
- Updated tool configuration examples, prompts, and observability docs.
|
||||
- Guide on using singular agents within Flows.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-17" description="v0.108.0">
|
||||
**Features**
|
||||
- Converted tabs to spaces in `crew.py` template
|
||||
- Enhanced LLM Streaming Response Handling and Event System
|
||||
- Included `model_name`
|
||||
- Enhanced Event Listener with rich visualization and improved logging
|
||||
- Added fingerprints
|
||||
|
||||
**Bug Fixes**
|
||||
- Fixed Mistral issues
|
||||
- Fixed a bug in documentation
|
||||
- Fixed type check error in fingerprint property
|
||||
|
||||
**Documentation Updates**
|
||||
- Improved tool documentation
|
||||
- Updated installation guide for the `uv` tool package
|
||||
- Added instructions for upgrading crewAI with the `uv` tool
|
||||
- Added documentation for `ApifyActorsTool`
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-10" description="v0.105.0">
|
||||
**Core Improvements & Fixes**
|
||||
- Fixed issues with missing template variables and user memory configuration
|
||||
- Improved async flow support and addressed agent response formatting
|
||||
- Enhanced memory reset functionality and fixed CLI memory commands
|
||||
- Fixed type issues, tool calling properties, and telemetry decoupling
|
||||
|
||||
**New Features & Enhancements**
|
||||
- Added Flow state export and improved state utilities
|
||||
- Enhanced agent knowledge setup with optional crew embedder
|
||||
- Introduced event emitter for better observability and LLM call tracking
|
||||
- Added support for Python 3.10 and ChatOllama from langchain_ollama
|
||||
- Integrated context window size support for the o3-mini model
|
||||
- Added support for multiple router calls
|
||||
|
||||
**Documentation & Guides**
|
||||
- Improved documentation layout and hierarchical structure
|
||||
- Added QdrantVectorSearchTool guide and clarified event listener usage
|
||||
- Fixed typos in prompts and updated Amazon Bedrock model listings
|
||||
</Update>
|
||||
|
||||
<Update label="2025-02-12" description="v0.102.0">
|
||||
**Core Improvements & Fixes**
|
||||
- Enhanced LLM Support: Improved structured LLM output, parameter handling, and formatting for Anthropic models
|
||||
- Crew & Agent Stability: Fixed issues with cloning agents/crews using knowledge sources, multiple task outputs in conditional tasks, and ignored Crew task callbacks
|
||||
- Memory & Storage Fixes: Fixed short-term memory handling with Bedrock, ensured correct embedder initialization, and added a reset memories function in the crew class
|
||||
- Training & Execution Reliability: Fixed broken training and interpolation issues with dict and list input types
|
||||
|
||||
**New Features & Enhancements**
|
||||
- Advanced Knowledge Management: Improved naming conventions and enhanced embedding configuration with custom embedder support
|
||||
- Expanded Logging & Observability: Added JSON format support for logging and integrated MLflow tracing documentation
|
||||
- Data Handling Improvements: Updated excel_knowledge_source.py to process multi-tab files
|
||||
- General Performance & Codebase Clean-Up: Streamlined enterprise code alignment and resolved linting issues
|
||||
- Adding new tool: `QdrantVectorSearchTool`
|
||||
|
||||
**Documentation & Guides**
|
||||
- Updated AI & Memory Docs: Improved Bedrock, Google AI, and long-term memory documentation
|
||||
- Task & Workflow Clarity: Added "Human Input" row to Task Attributes, Langfuse guide, and FileWriterTool documentation
|
||||
- Fixed Various Typos & Formatting Issues
|
||||
</Update>
|
||||
|
||||
<Update label="2025-01-28" description="v0.100.0">
|
||||
**Features**
|
||||
- Add Composio docs
|
||||
- Add SageMaker as a LLM provider
|
||||
|
||||
**Fixes**
|
||||
- Overall LLM connection issues
|
||||
- Using safe accessors on training
|
||||
- Add version check to crew_chat.py
|
||||
|
||||
**Documentation**
|
||||
- New docs for crewai chat
|
||||
- Improve formatting and clarity in CLI and Composio Tool docs
|
||||
</Update>
|
||||
|
||||
<Update label="2025-01-20" description="v0.98.0">
|
||||
**Features**
|
||||
- Conversation crew v1
|
||||
- Add unique ID to flow states
|
||||
- Add @persist decorator with FlowPersistence interface
|
||||
|
||||
**Integrations**
|
||||
- Add SambaNova integration
|
||||
- Add NVIDIA NIM provider in cli
|
||||
- Introducing VoyageAI
|
||||
|
||||
**Fixes**
|
||||
- Fix API Key Behavior and Entity Handling in Mem0 Integration
|
||||
- Fixed core invoke loop logic and relevant tests
|
||||
- Make tool inputs actual objects and not strings
|
||||
- Add important missing parts to creating tools
|
||||
- Drop litellm version to prevent windows issue
|
||||
- Before kickoff if inputs are none
|
||||
- Fixed typos, nested pydantic model issue, and docling issues
|
||||
</Update>
|
||||
|
||||
<Update label="2025-01-04" description="v0.95.0">
|
||||
**New Features**
|
||||
- Adding Multimodal Abilities to Crew
|
||||
- Programatic Guardrails
|
||||
- HITL multiple rounds
|
||||
- Gemini 2.0 Support
|
||||
- CrewAI Flows Improvements
|
||||
- Add Workflow Permissions
|
||||
- Add support for langfuse with litellm
|
||||
- Portkey Integration with CrewAI
|
||||
- Add interpolate_only method and improve error handling
|
||||
- Docling Support
|
||||
- Weviate Support
|
||||
|
||||
**Fixes**
|
||||
- output_file not respecting system path
|
||||
- disk I/O error when resetting short-term memory
|
||||
- CrewJSONEncoder now accepts enums
|
||||
- Python max version
|
||||
- Interpolation for output_file in Task
|
||||
- Handle coworker role name case/whitespace properly
|
||||
- Add tiktoken as explicit dependency and document Rust requirement
|
||||
- Include agent knowledge in planning process
|
||||
- Change storage initialization to None for KnowledgeStorage
|
||||
- Fix optional storage checks
|
||||
- include event emitter in flows
|
||||
- Docstring, Error Handling, and Type Hints Improvements
|
||||
- Suppressed userWarnings from litellm pydantic issues
|
||||
</Update>
|
||||
|
||||
<Update label="2024-12-05" description="v0.86.0">
|
||||
**Changes**
|
||||
- Remove all references to pipeline and pipeline router
|
||||
- Add Nvidia NIM as provider in Custom LLM
|
||||
- Add knowledge demo + improve knowledge docs
|
||||
- Add HITL multiple rounds of followup
|
||||
- New docs about yaml crew with decorators
|
||||
- Simplify template crew
|
||||
</Update>
|
||||
|
||||
<Update label="2024-12-04" description="v0.85.0">
|
||||
**Features**
|
||||
- Added knowledge to agent level
|
||||
- Feat/remove langchain
|
||||
- Improve typed task outputs
|
||||
- Log in to Tool Repository on crewai login
|
||||
|
||||
**Fixes**
|
||||
- Fixes issues with result as answer not properly exiting LLM loop
|
||||
- Fix missing key name when running with ollama provider
|
||||
- Fix spelling issue found
|
||||
|
||||
**Documentation**
|
||||
- Update readme for running mypy
|
||||
- Add knowledge to mint.json
|
||||
- Update Github actions
|
||||
- Update Agents docs to include two approaches for creating an agent
|
||||
- Improvements to LLM Configuration and Usage
|
||||
</Update>
|
||||
|
||||
<Update label="2024-11-25" description="v0.83.0">
|
||||
**New Features**
|
||||
- New before_kickoff and after_kickoff crew callbacks
|
||||
- Support to pre-seed agents with Knowledge
|
||||
- Add support for retrieving user preferences and memories using Mem0
|
||||
|
||||
**Fixes**
|
||||
- Fix Async Execution
|
||||
- Upgrade chroma and adjust embedder function generator
|
||||
- Update CLI Watson supported models + docs
|
||||
- Reduce level for Bandit
|
||||
- Fixing all tests
|
||||
|
||||
**Documentation**
|
||||
- Update Docs
|
||||
</Update>
|
||||
|
||||
<Update label="2024-11-13" description="v0.80.0">
|
||||
**Fixes**
|
||||
- Fixing Tokens callback replacement bug
|
||||
- Fixing Step callback issue
|
||||
- Add cached prompt tokens info on usage metrics
|
||||
- Fix crew_train_success test
|
||||
</Update>
|
||||
@@ -1,18 +0,0 @@
|
||||
(function() {
|
||||
if (typeof window === 'undefined') return;
|
||||
if (typeof window.signals !== 'undefined') return;
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://cdn.cr-relay.com/v1/site/883520f4-c431-44be-80e7-e123a1ee7a2b/signals.js';
|
||||
script.async = true;
|
||||
window.signals = Object.assign(
|
||||
[],
|
||||
['page', 'identify', 'form'].reduce(function (acc, method){
|
||||
acc[method] = function () {
|
||||
signals.push([method, arguments]);
|
||||
return signals;
|
||||
};
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
document.head.appendChild(script);
|
||||
})();
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |