Compare commits

...

35 Commits

Author SHA1 Message Date
Lucas Gomide
ea0a4c6bea fix: ensure Agent is able to load a list of Tools dynamically 2025-06-20 11:44:28 -03:00
Lucas Gomide
65af0173fe Merge branch 'main' into lg-agent-initialize 2025-06-19 17:21:16 -03:00
Vidit Ostwal
463ea2b97f Fixed type annotation in task (#3021)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* Added Union of List of Task, None, NotSpecified

* Seems like a flaky test

* Fixed run time issue

* Fixed Linting issues

* fix pydantic error

* aesthetic changes

---------

Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
2025-06-19 14:37:46 -04:00
Lucas Gomide
d0e9964f2d Merge branch 'main' into lg-agent-initialize 2025-06-19 13:56:39 -03:00
Jannik Maierhöfer
ec2903e5ee fix: upgrade langfuse code examples to langfuse python sdk v3 (#3030)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com>
2025-06-19 12:18:33 -04:00
Lucas Gomide
a00dd73614 Merge branch 'main' into lg-agent-initialize 2025-06-19 12:57:36 -03:00
Daniel Barreto
4364585ebc Remove mkdocs from project dependencies (#3036)
CrewAI has been using https://mintlify.com/
to serve its docs
2025-06-19 11:21:08 -04:00
Lorenze Jay
0a6b7c655b docs: add comprehensive integration documentation for various services (#2999)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
- Introduced detailed documentation for integrations including Asana, Box, ClickUp, GitHub, Gmail, Google Calendar, Google Sheets, HubSpot, Jira, Linear, Notion, Salesforce, Shopify, Slack, Stripe, and Zendesk.
- Updated main docs.json to include a new "Integration Docs" section, organizing the documentation for easy access.
- Each integration includes setup instructions, available actions, and example tasks to streamline user onboarding and usage.
2025-06-18 10:21:18 -04:00
Lucas Gomide
b63a8f738c feat: support to initialize a tool from defined Tool attributes 2025-06-17 18:31:57 -03:00
Lucas Gomide
db1e9e9b9a fix: fix pydantic support to 2.7.x (#3016)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Pydantic 2.7.x does not support a second parameter in model validators with mode="after"
2025-06-16 16:20:10 -04:00
Lucas Gomide
d92382b6cf fix: SSL error while getting LLM data from GH (#3014)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
When running behind cloud-based security users are struggling to donwload LLM data from Github. Usually the following error is raised

```
SSL certificate verification failed: HTTPSConnectionPool(host='raw.githubusercontent.com', port=443): Max retries exceeded with url: /BerriAI/litellm/main/model_prices_and_context_window.json (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1010)')))
Current CA bundle path: /usr/local/etc///.pem
```

This commit ensures the SSL config is beign provided while requesting data
2025-06-16 11:34:04 -04:00
Lucas Gomide
7c8f2a1325 docs: add missing docs about LLMGuardrail events (#3013) 2025-06-16 11:05:36 -04:00
Vidit Ostwal
a40447df29 updated docs (#2989)
Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
2025-06-16 10:49:27 -04:00
leopardracer
5d6b467042 Update quickstart.mdx (#2998)
Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
2025-06-16 10:35:52 -04:00
Greyson LaLonde
e0ff30c212 Fix tools parameter syntax 2025-06-16 10:25:34 -04:00
Lorenze Jay
a5b5c8ab37 Lorenze/console printer nice (#3004)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* fix: possible fix for Thinking stuck

* feat: add agent logging events for execution tracking

- Introduced AgentLogsStartedEvent and AgentLogsExecutionEvent to enhance logging capabilities during agent execution.
- Updated CrewAgentExecutor to emit these events at the start and during execution, respectively.
- Modified EventListener to handle the new logging events and format output accordingly in the console.
- Enhanced ConsoleFormatter to display agent logs in a structured format, improving visibility of agent actions and outputs.

* drop emoji

* refactor: improve code structure and logging in LiteAgent and ConsoleFormatter

- Refactored imports in lite_agent.py for better readability.
- Enhanced guardrail property initialization in LiteAgent.
- Updated logging functionality to emit AgentLogsExecutionEvent for better tracking.
- Modified ConsoleFormatter to include tool arguments and final output in status updates.
- Improved output formatting for long text in ConsoleFormatter.

* fix tests

---------

Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com>
2025-06-14 12:21:46 -07:00
Vidit Ostwal
7f12e98de5 Added sanitize role feature in mem0 storage (#2988)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* Added sanitize role feature in mme0 storage

* Used chroma db functionality
2025-06-12 13:14:34 -04:00
Lorenze Jay
99133104dd Update version to 0.130.0 and dependencies in pyproject.toml and uv.lock (#3002)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
- Bump CrewAI version from 0.126.0 to 0.130.0 in pyproject.toml and uv.lock.
- Update optional dependency 'crewai-tools' version from 0.46.0 to 0.47.1.
- Adjust dependency specifications in CLI templates to reflect the new version.
2025-06-11 17:01:11 -07:00
devin-ai-integration[bot]
970a63c13c Fix issue 2993: Prevent Flow status logs from hiding human input (#2994)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
* Fix issue 2993: Prevent Flow status logs from hiding human input

- Add pause_live_updates() and resume_live_updates() methods to ConsoleFormatter
- Modify _ask_human_input() to pause Flow status updates during human input
- Add comprehensive tests for pause/resume functionality and integration
- Ensure Live session is properly managed during human input prompts
- Fix prevents Flow status logs from overwriting user input prompts

Fixes #2993

Co-Authored-By: João <joao@crewai.com>

* Fix lint: Remove unused pytest import

- Remove unused pytest import from test_console_formatter_pause_resume.py
- Fixes F401 lint error identified in CI

Co-Authored-By: João <joao@crewai.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: João <joao@crewai.com>
2025-06-11 12:08:00 -04:00
devin-ai-integration[bot]
06c991d8c3 Fix telemetry singleton pattern to respect dynamic environment variables (#2946)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* Fix telemetry singleton pattern to respect dynamic environment variables

- Modified Telemetry.__init__ to prevent re-initialization with _initialized flag
- Updated _safe_telemetry_operation to check _is_telemetry_disabled() dynamically
- Added comprehensive tests for environment variables set after singleton creation
- Fixed singleton contamination in existing tests by adding proper reset
- Resolves issue #2945 where CREWAI_DISABLE_TELEMETRY=true was ignored when set after import

Co-Authored-By: João <joao@crewai.com>

* Implement code review improvements

- Move _initialized flag to __new__ method for better encapsulation
- Add type hints to _safe_telemetry_operation method
- Consolidate telemetry execution checks into _should_execute_telemetry helper
- Add pytest fixtures to reduce test setup redundancy
- Enhanced documentation for singleton behavior

Co-Authored-By: João <joao@crewai.com>

* Fix mypy type-checker errors

- Add explicit bool type annotation to _initialized field
- Fix return value in task_started method to not return _safe_telemetry_operation result
- Simplify initialization logic to set _initialized once in __init__

Co-Authored-By: João <joao@crewai.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: João <joao@crewai.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2025-06-10 17:38:40 -07:00
Lucas Gomide
739eb72fd0 LiteAgent w/ Guardrail (#2982)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
* feat: add guardrail support for Agents when using direct kickoff calls

* refactor: expose guardrail func in a proper utils file

* fix: resolve Self import on python 3.10
2025-06-10 13:32:32 -04:00
Lucas Gomide
b0d2e9fe31 docs: update Python version requirement from <=3.13 to <3.14 (#2987)
This correctly reflects support for all 3.13.x patch version
2025-06-10 12:44:28 -04:00
Lucas Gomide
5c51349a85 Support async tool executions (#2983)
* test: fix structured tool tests

No tests were being executed from this file

* feat: support to run async tool

Some Tool requires async execution. This commit allow us to collect tool result from coroutines

* docs: add docs about asynchronous tool support
2025-06-10 12:17:06 -04:00
Richard Luo
5b740467cb docs: fix the guide on persistence (#2849)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
2025-06-09 14:09:56 -04:00
hegasz
e9d9dd2a79 Fix missing manager_agent tokens in usage_metrics from kickoff (#2848)
* fix(metrics): prevent usage_metrics from dropping manager_agent tokens

* Add test to verify hierarchical kickoff aggregates manager and agent usage metrics

---------

Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
2025-06-09 13:16:05 -04:00
Lorenze Jay
3e74cb4832 docs: add integrations documentation and images for enterprise features (#2981)
- Introduced a new documentation file for Integrations, detailing supported services and setup instructions.
- Updated the main docs.json to include the new "integrations" feature in the contextual options.
- Added several images related to integrations to enhance the documentation.

Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com>
2025-06-09 12:46:09 -04:00
Lucas Gomide
db3c8a49bd feat: improve docs and logging for Multi-Org actions in CLI (#2980)
* docs: add organization management in our CLI docs

* feat: improve user feedback when user is not authenticated

* feat: improve logging about current organization while publishing/install a Tool

* feat: improve logging when Agent repository is not found during fetch

* fix linter offences

* test: fix auth token error
2025-06-09 12:21:12 -04:00
Lucas Gomide
8a37b535ed docs: improve docs about planning LLM usage (#2977) 2025-06-09 10:17:04 -04:00
Lucas Gomide
e6ac1311e7 build: upgrade LiteLLM to support latest Openai version (#2963)
Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com>
2025-06-09 08:55:12 -04:00
Akshit Madan
b0d89698fd docs: added Maxim support for Agent Observability (#2861)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* docs: added Maxim support for Agent Observability

* enhanced the maxim integration doc page as per the github PR reviewer bot suggestions

* Update maxim-observability.mdx

* Update maxim-observability.mdx

- Fixed Python version, >=3.10
- added expected_output field in Task
- Removed marketing links and added github link

* added maxim in observability

---------

Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com>
2025-06-08 13:39:01 -04:00
Lucas Gomide
21d063a46c Support multi org in CLI (#2969)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* feat: support to list, switch and see your current organization

* feat: store the current org after logged in

* feat: filtering agents, tools and their actions by organization_uuid if present

* fix linter offenses

* refactor: propagate the current org thought Header instead of params

* refactor: rename org column name to ID instead of Handle

---------

Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com>
2025-06-06 15:28:09 -04:00
Mike Plachta
02912a653e Increasing the default X-axis spacing for flow plotting (#2967)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
* Increasing the default X-axis spacing for flow plotting

* removing unused imports
2025-06-06 09:43:38 -07:00
Greyson LaLonde
f1cfba7527 docs: update hallucination guardrail examples
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
- Add basic usage example showing guardrail uses task's expected_output as default context
- Add explicit context example for custom reference content
2025-06-05 12:34:52 -04:00
Lucas Gomide
3e075cd48d docs: add minimum UV version required to use the Tool repository (#2965)
* docs: add minimum UV version required to use the Tool repository

* docs: remove memory from Agent docs

The Agent does not support `memory` attribute
2025-06-05 11:37:19 -04:00
Lucas Gomide
e03ec4d60f fix: remove duplicated message about Tool result (#2964)
We are currently inserting tool results into LLM messages twice, which may unnecessarily increase processing costs, especially for longer outputs.
2025-06-05 09:42:10 -04:00
91 changed files with 12568 additions and 912 deletions

View File

@@ -1,45 +0,0 @@
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

View File

@@ -43,7 +43,6 @@ The Visual Agent Builder enables:
| **Max Iterations** _(optional)_ | `max_iter` | `int` | Maximum iterations before the agent must provide its best answer. Default is 20. |
| **Max RPM** _(optional)_ | `max_rpm` | `Optional[int]` | Maximum requests per minute to avoid rate limits. |
| **Max Execution Time** _(optional)_ | `max_execution_time` | `Optional[int]` | Maximum time (in seconds) for task execution. |
| **Memory** _(optional)_ | `memory` | `bool` | Whether the agent should maintain memory of interactions. Default is True. |
| **Verbose** _(optional)_ | `verbose` | `bool` | Enable detailed execution logs for debugging. Default is False. |
| **Allow Delegation** _(optional)_ | `allow_delegation` | `bool` | Allow the agent to delegate tasks to other agents. Default is False. |
| **Step Callback** _(optional)_ | `step_callback` | `Optional[Any]` | Function called after each agent step, overrides crew callback. |
@@ -156,7 +155,6 @@ agent = Agent(
"you excel at finding patterns in complex datasets.",
llm="gpt-4", # Default: OPENAI_MODEL_NAME or "gpt-4"
function_calling_llm=None, # Optional: Separate LLM for tool calling
memory=True, # Default: True
verbose=False, # Default: False
allow_delegation=False, # Default: False
max_iter=20, # Default: 20 iterations
@@ -297,6 +295,11 @@ multimodal_agent = Agent(
- `"safe"`: Uses Docker (recommended for production)
- `"unsafe"`: Direct execution (use only in trusted environments)
<Note>
This runs a default Docker image. If you want to configure the docker image, the checkout the Code Interpreter Tool in the tools section.
Add the code interpreter tool as a tool in the agent as a tool parameter.
</Note>
#### Advanced Features
- `multimodal`: Enable multimodal capabilities for processing text and visual content
- `reasoning`: Enable agent to reflect and create plans before executing tasks
@@ -537,7 +540,6 @@ The context window management feature works automatically in the background. You
- Adjust `max_iter` and `max_retry_limit` based on task complexity
### Memory and Context Management
- Use `memory: true` for tasks requiring historical context
- Leverage `knowledge_sources` for domain-specific information
- Configure `embedder` when using custom embedding models
- Use custom templates (`system_template`, `prompt_template`, `response_template`) for fine-grained control over agent behavior
@@ -585,7 +587,6 @@ The context window management feature works automatically in the background. You
- Review code sandbox settings
4. **Memory Issues**: If agent responses seem inconsistent:
- Verify memory is enabled
- Check knowledge source configuration
- Review conversation history management

View File

@@ -200,6 +200,37 @@ Deploy the crew or flow to [CrewAI Enterprise](https://app.crewai.com).
```
- Reads your local project configuration.
- Prompts you to confirm the environment variables (like `OPENAI_API_KEY`, `SERPER_API_KEY`) found locally. These will be securely stored with the deployment on the Enterprise platform. Ensure your sensitive keys are correctly configured locally (e.g., in a `.env` file) before running this.
### 11. Organization Management
Manage your CrewAI Enterprise organizations.
```shell Terminal
crewai org [COMMAND] [OPTIONS]
```
#### Commands:
- `list`: List all organizations you belong to
```shell Terminal
crewai org list
```
- `current`: Display your currently active organization
```shell Terminal
crewai org current
```
- `switch`: Switch to a specific organization
```shell Terminal
crewai org switch <organization_id>
```
<Note>
You must be authenticated to CrewAI Enterprise to use these organization management commands.
</Note>
- **Create a deployment** (continued):
- Links the deployment to the corresponding remote GitHub repository (it usually detects this automatically).
- **Deploy the Crew**: Once you are authenticated, you can deploy your crew or flow to CrewAI Enterprise.

View File

@@ -233,6 +233,11 @@ CrewAI provides a wide range of events that you can listen for:
- **KnowledgeQueryFailedEvent**: Emitted when a knowledge query fails
- **KnowledgeSearchQueryFailedEvent**: Emitted when a knowledge search query fails
### LLM Guardrail Events
- **LLMGuardrailStartedEvent**: Emitted when a guardrail validation starts. Contains details about the guardrail being applied and retry count.
- **LLMGuardrailCompletedEvent**: Emitted when a guardrail validation completes. Contains details about validation success/failure, results, and error messages if any.
### Flow Events
- **FlowCreatedEvent**: Emitted when a Flow is created

View File

@@ -29,6 +29,10 @@ my_crew = Crew(
From this point on, your crew will have planning enabled, and the tasks will be planned before each iteration.
<Warning>
When planning is enabled, crewAI will use `gpt-4o-mini` as the default LLM for planning, which requires a valid OpenAI API key. Since your agents might be using different LLMs, this could cause confusion if you don't have an OpenAI API key configured or if you're experiencing unexpected behavior related to LLM API calls.
</Warning>
#### Planning LLM
Now you can define the LLM that will be used to plan the tasks.

View File

@@ -32,6 +32,7 @@ The Enterprise Tools Repository includes:
- **Customizability**: Provides the flexibility to develop custom tools or utilize existing ones, catering to the specific needs of agents.
- **Error Handling**: Incorporates robust error handling mechanisms to ensure smooth operation.
- **Caching Mechanism**: Features intelligent caching to optimize performance and reduce redundant operations.
- **Asynchronous Support**: Handles both synchronous and asynchronous tools, enabling non-blocking operations.
## Using CrewAI Tools
@@ -177,6 +178,62 @@ class MyCustomTool(BaseTool):
return "Tool's result"
```
## Asynchronous Tool Support
CrewAI supports asynchronous tools, allowing you to implement tools that perform non-blocking operations like network requests, file I/O, or other async operations without blocking the main execution thread.
### Creating Async Tools
You can create async tools in two ways:
#### 1. Using the `tool` Decorator with Async Functions
```python Code
from crewai.tools import tool
@tool("fetch_data_async")
async def fetch_data_async(query: str) -> str:
"""Asynchronously fetch data based on the query."""
# Simulate async operation
await asyncio.sleep(1)
return f"Data retrieved for {query}"
```
#### 2. Implementing Async Methods in Custom Tool Classes
```python Code
from crewai.tools import BaseTool
class AsyncCustomTool(BaseTool):
name: str = "async_custom_tool"
description: str = "An asynchronous custom tool"
async def _run(self, query: str = "") -> str:
"""Asynchronously run the tool"""
# Your async implementation here
await asyncio.sleep(1)
return f"Processed {query} asynchronously"
```
### Using Async Tools
Async tools work seamlessly in both standard Crew workflows and Flow-based workflows:
```python Code
# In standard Crew
agent = Agent(role="researcher", tools=[async_custom_tool])
# In Flow
class MyFlow(Flow):
@start()
async def begin(self):
crew = Crew(agents=[agent])
result = await crew.kickoff_async()
return result
```
The CrewAI framework automatically handles the execution of both synchronous and asynchronous tools, so you don't need to worry about how to call them differently.
### Utilizing the `tool` Decorator
```python Code

View File

@@ -9,7 +9,12 @@
},
"favicon": "images/favicon.svg",
"contextual": {
"options": ["copy", "view", "chatgpt", "claude"]
"options": [
"copy",
"view",
"chatgpt",
"claude"
]
},
"navigation": {
"tabs": [
@@ -201,6 +206,7 @@
"observability/arize-phoenix",
"observability/langfuse",
"observability/langtrace",
"observability/maxim",
"observability/mlflow",
"observability/openlit",
"observability/opik",
@@ -256,7 +262,29 @@
"enterprise/features/tool-repository",
"enterprise/features/webhook-streaming",
"enterprise/features/traces",
"enterprise/features/hallucination-guardrail"
"enterprise/features/hallucination-guardrail",
"enterprise/features/integrations"
]
},
{
"group": "Integration Docs",
"pages": [
"enterprise/integrations/asana",
"enterprise/integrations/box",
"enterprise/integrations/clickup",
"enterprise/integrations/github",
"enterprise/integrations/gmail",
"enterprise/integrations/google_calendar",
"enterprise/integrations/google_sheets",
"enterprise/integrations/hubspot",
"enterprise/integrations/jira",
"enterprise/integrations/linear",
"enterprise/integrations/notion",
"enterprise/integrations/salesforce",
"enterprise/integrations/shopify",
"enterprise/integrations/slack",
"enterprise/integrations/stripe",
"enterprise/integrations/zendesk"
]
},
{

View File

@@ -25,8 +25,13 @@ AI hallucinations occur when language models generate content that appears plaus
from crewai.tasks.hallucination_guardrail import HallucinationGuardrail
from crewai import LLM
# Initialize the guardrail with reference context
# Basic usage - will use task's expected_output as context
guardrail = HallucinationGuardrail(
llm=LLM(model="gpt-4o-mini")
)
# With explicit reference context
context_guardrail = HallucinationGuardrail(
context="AI helps with various tasks including analysis and generation.",
llm=LLM(model="gpt-4o-mini")
)

View File

@@ -0,0 +1,185 @@
---
title: Integrations
description: "Connected applications for your agents to take actions."
icon: "plug"
---
## Overview
Enable your agents to authenticate with any OAuth enabled provider and take actions. From Salesforce and HubSpot to Google and GitHub, we've got you covered with 16+ integrated services.
<Frame>
![Integrations](/images/enterprise/crew_connectors.png)
</Frame>
## Supported Integrations
### **Communication & Collaboration**
- **Gmail** - Manage emails and drafts
- **Slack** - Workspace notifications and alerts
- **Microsoft** - Office 365 and Teams integration
### **Project Management**
- **Jira** - Issue tracking and project management
- **ClickUp** - Task and productivity management
- **Asana** - Team task and project coordination
- **Notion** - Page and database management
- **Linear** - Software project and bug tracking
- **GitHub** - Repository and issue management
### **Customer Relationship Management**
- **Salesforce** - CRM account and opportunity management
- **HubSpot** - Sales pipeline and contact management
- **Zendesk** - Customer support ticket management
### **Business & Finance**
- **Stripe** - Payment processing and customer management
- **Shopify** - E-commerce store and product management
### **Productivity & Storage**
- **Google Sheets** - Spreadsheet data synchronization
- **Google Calendar** - Event and schedule management
- **Box** - File storage and document management
and more to come!
## Prerequisites
Before using Authentication Integrations, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account. You can get started with a free trial.
## Setting Up Integrations
### 1. Connect Your Account
1. Navigate to [CrewAI Enterprise](https://app.crewai.com)
2. Go to **Integrations** tab - https://app.crewai.com/crewai_plus/connectors
3. Click **Connect** on your desired service from the Authentication Integrations section
4. Complete the OAuth authentication flow
5. Grant necessary permissions for your use case
6. Get your Enterprise Token from your [CrewAI Enterprise](https://app.crewai.com) account page - https://app.crewai.com/crewai_plus/settings/account
<Frame>
![Integrations](/images/enterprise/enterprise_action_auth_token.png)
</Frame>
### 2. Install Integration Tools
All you need is the latest version of `crewai-tools` package.
```bash
uv add crewai-tools
```
## Usage Examples
### Basic Usage
<Tip>
All the services you are authenticated into will be available as tools. So all you need to do is add the `CrewaiEnterpriseTools` to your agent and you are good to go.
</Tip>
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Gmail tool will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# print the tools
print(enterprise_tools)
# Create an agent with Gmail capabilities
email_agent = Agent(
role="Email Manager",
goal="Manage and organize email communications",
backstory="An AI assistant specialized in email management and communication.",
tools=enterprise_tools
)
# Task to send an email
email_task = Task(
description="Draft and send a follow-up email to john@example.com about the project update",
agent=email_agent,
expected_output="Confirmation that email was sent successfully"
)
# Run the task
crew = Crew(
agents=[email_agent],
tasks=[email_task]
)
# Run the crew
crew.kickoff()
```
### Filtering Tools
```python
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
actions_list=["gmail_find_email"] # only gmail_find_email tool will be available
)
gmail_tool = enterprise_tools[0]
gmail_agent = Agent(
role="Gmail Manager",
goal="Manage gmail communications and notifications",
backstory="An AI assistant that helps coordinate gmail communications.",
tools=[gmail_tool]
)
notification_task = Task(
description="Find the email from john@example.com",
agent=gmail_agent,
expected_output="Email found from john@example.com"
)
# Run the task
crew = Crew(
agents=[slack_agent],
tasks=[notification_task]
)
```
## Best Practices
### Security
- **Principle of Least Privilege**: Only grant the minimum permissions required for your agents' tasks
- **Regular Audits**: Periodically review connected integrations and their permissions
- **Secure Credentials**: Never hardcode credentials; use CrewAI's secure authentication flow
### Filtering Tools
On a deployed crew, you can specify which actions are avialbel for each integration from the settings page of the service you connected to.
<Frame>
![Integrations](/images/enterprise/filtering_enterprise_action_tools.png)
</Frame>
### Scoped Deployments for multi user organizations
You can deploy your crew and scope each integration to a specific user. For example, a crew that connects to google can use a specific user's gmail account.
<Tip>
This is useful for multi user organizations where you want to scope the integration to a specific user.
</Tip>
Use the `user_bearer_token` to scope the integration to a specific user so that when the crew is kicked off, it will use the user's bearer token to authenticate with the integration. If user is not logged in, then the crew will not use any connected integrations. Use the default bearer token to authenticate with the integrations thats deployed with the crew.
<Frame>
![Integrations](/images/enterprise/user_bearer_token.png)
</Frame>
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with integration setup or troubleshooting.
</Card>

View File

@@ -21,6 +21,7 @@ Before using the Tool Repository, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account
- [CrewAI CLI](https://docs.crewai.com/concepts/cli#cli) installed
- uv>=0.5.0 installed. Check out [how to upgrade](https://docs.astral.sh/uv/getting-started/installation/#upgrading-uv)
- [Git](https://git-scm.com) installed and configured
- Access permissions to publish or install tools in your CrewAI Enterprise organization

View File

@@ -0,0 +1,253 @@
---
title: Asana Integration
description: "Team task and project coordination with Asana integration for CrewAI."
icon: "circle"
---
## Overview
Enable your agents to manage tasks, projects, and team coordination through Asana. Create tasks, update project status, manage assignments, and streamline your team's workflow with AI-powered automation.
## Prerequisites
Before using the Asana integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- An Asana account with appropriate permissions
- Connected your Asana account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Asana Integration
### 1. Connect Your Asana Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Asana** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for task and project management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="ASANA_CREATE_COMMENT">
**Description:** Create a comment in Asana.
**Parameters:**
- `task` (string, required): Task ID - The ID of the Task the comment will be added to. The comment will be authored by the currently authenticated user.
- `text` (string, required): Text (example: "This is a comment.").
</Accordion>
<Accordion title="ASANA_CREATE_PROJECT">
**Description:** Create a project in Asana.
**Parameters:**
- `name` (string, required): Name (example: "Stuff to buy").
- `workspace` (string, required): Workspace - Use Connect Portal Workflow Settings to allow users to select which Workspace to create Projects in. Defaults to the user's first Workspace if left blank.
- `team` (string, optional): Team - Use Connect Portal Workflow Settings to allow users to select which Team to share this Project with. Defaults to the user's first Team if left blank.
- `notes` (string, optional): Notes (example: "These are things we need to purchase.").
</Accordion>
<Accordion title="ASANA_GET_PROJECTS">
**Description:** Get a list of projects in Asana.
**Parameters:**
- `archived` (string, optional): Archived - Choose "true" to show archived projects, "false" to display only active projects, or "default" to show both archived and active projects.
- Options: `default`, `true`, `false`
</Accordion>
<Accordion title="ASANA_GET_PROJECT_BY_ID">
**Description:** Get a project by ID in Asana.
**Parameters:**
- `projectFilterId` (string, required): Project ID.
</Accordion>
<Accordion title="ASANA_CREATE_TASK">
**Description:** Create a task in Asana.
**Parameters:**
- `name` (string, required): Name (example: "Task Name").
- `workspace` (string, optional): Workspace - Use Connect Portal Workflow Settings to allow users to select which Workspace to create Tasks in. Defaults to the user's first Workspace if left blank..
- `project` (string, optional): Project - Use Connect Portal Workflow Settings to allow users to select which Project to create this Task in.
- `notes` (string, optional): Notes.
- `dueOnDate` (string, optional): Due On - The date on which this task is due. Cannot be used together with Due At. (example: "YYYY-MM-DD").
- `dueAtDate` (string, optional): Due At - The date and time (ISO timestamp) at which this task is due. Cannot be used together with Due On. (example: "2019-09-15T02:06:58.147Z").
- `assignee` (string, optional): Assignee - The ID of the Asana user this task will be assigned to. Use Connect Portal Workflow Settings to allow users to select an Assignee.
- `gid` (string, optional): External ID - An ID from your application to associate this task with. You can use this ID to sync updates to this task later.
</Accordion>
<Accordion title="ASANA_UPDATE_TASK">
**Description:** Update a task in Asana.
**Parameters:**
- `taskId` (string, required): Task ID - The ID of the Task that will be updated.
- `completeStatus` (string, optional): Completed Status.
- Options: `true`, `false`
- `name` (string, optional): Name (example: "Task Name").
- `notes` (string, optional): Notes.
- `dueOnDate` (string, optional): Due On - The date on which this task is due. Cannot be used together with Due At. (example: "YYYY-MM-DD").
- `dueAtDate` (string, optional): Due At - The date and time (ISO timestamp) at which this task is due. Cannot be used together with Due On. (example: "2019-09-15T02:06:58.147Z").
- `assignee` (string, optional): Assignee - The ID of the Asana user this task will be assigned to. Use Connect Portal Workflow Settings to allow users to select an Assignee.
- `gid` (string, optional): External ID - An ID from your application to associate this task with. You can use this ID to sync updates to this task later.
</Accordion>
<Accordion title="ASANA_GET_TASKS">
**Description:** Get a list of tasks in Asana.
**Parameters:**
- `workspace` (string, optional): Workspace - The ID of the Workspace to filter tasks on. Use Connect Portal Workflow Settings to allow users to select a Workspace.
- `project` (string, optional): Project - The ID of the Project to filter tasks on. Use Connect Portal Workflow Settings to allow users to select a Project.
- `assignee` (string, optional): Assignee - The ID of the assignee to filter tasks on. Use Connect Portal Workflow Settings to allow users to select an Assignee.
- `completedSince` (string, optional): Completed since - Only return tasks that are either incomplete or that have been completed since this time (ISO or Unix timestamp). (example: "2014-04-25T16:15:47-04:00").
</Accordion>
<Accordion title="ASANA_GET_TASKS_BY_ID">
**Description:** Get a list of tasks by ID in Asana.
**Parameters:**
- `taskId` (string, required): Task ID.
</Accordion>
<Accordion title="ASANA_GET_TASK_BY_EXTERNAL_ID">
**Description:** Get a task by external ID in Asana.
**Parameters:**
- `gid` (string, required): External ID - The ID that this task is associated or synced with, from your application.
</Accordion>
<Accordion title="ASANA_ADD_TASK_TO_SECTION">
**Description:** Add a task to a section in Asana.
**Parameters:**
- `sectionId` (string, required): Section ID - The ID of the section to add this task to.
- `taskId` (string, required): Task ID - The ID of the task. (example: "1204619611402340").
- `beforeTaskId` (string, optional): Before Task ID - The ID of a task in this section that this task will be inserted before. Cannot be used with After Task ID. (example: "1204619611402340").
- `afterTaskId` (string, optional): After Task ID - The ID of a task in this section that this task will be inserted after. Cannot be used with Before Task ID. (example: "1204619611402340").
</Accordion>
<Accordion title="ASANA_GET_TEAMS">
**Description:** Get a list of teams in Asana.
**Parameters:**
- `workspace` (string, required): Workspace - Returns the teams in this workspace visible to the authorized user.
</Accordion>
<Accordion title="ASANA_GET_WORKSPACES">
**Description:** Get a list of workspaces in Asana.
**Parameters:** None required.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Asana Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Asana tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Asana capabilities
asana_agent = Agent(
role="Project Manager",
goal="Manage tasks and projects in Asana efficiently",
backstory="An AI assistant specialized in project management and task coordination.",
tools=[enterprise_tools]
)
# Task to create a new project
create_project_task = Task(
description="Create a new project called 'Q1 Marketing Campaign' in the Marketing workspace",
agent=asana_agent,
expected_output="Confirmation that the project was created successfully with project ID"
)
# Run the task
crew = Crew(
agents=[asana_agent],
tasks=[create_project_task]
)
crew.kickoff()
```
### Filtering Specific Asana Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Asana tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["asana_create_task", "asana_update_task", "asana_get_tasks"]
)
task_manager_agent = Agent(
role="Task Manager",
goal="Create and manage tasks efficiently",
backstory="An AI assistant that focuses on task creation and management.",
tools=enterprise_tools
)
# Task to create and assign a task
task_management = Task(
description="Create a task called 'Review quarterly reports' and assign it to the appropriate team member",
agent=task_manager_agent,
expected_output="Task created and assigned successfully"
)
crew = Crew(
agents=[task_manager_agent],
tasks=[task_management]
)
crew.kickoff()
```
### Advanced Project Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
project_coordinator = Agent(
role="Project Coordinator",
goal="Coordinate project activities and track progress",
backstory="An experienced project coordinator who ensures projects run smoothly.",
tools=[enterprise_tools]
)
# Complex task involving multiple Asana operations
coordination_task = Task(
description="""
1. Get all active projects in the workspace
2. For each project, get the list of incomplete tasks
3. Create a summary report task in the 'Management Reports' project
4. Add comments to overdue tasks to request status updates
""",
agent=project_coordinator,
expected_output="Summary report created and status update requests sent for overdue tasks"
)
crew = Crew(
agents=[project_coordinator],
tasks=[coordination_task]
)
crew.kickoff()
```

View File

@@ -0,0 +1,268 @@
---
title: Box Integration
description: "File storage and document management with Box integration for CrewAI."
icon: "box"
---
## Overview
Enable your agents to manage files, folders, and documents through Box. Upload files, organize folder structures, search content, and streamline your team's document management with AI-powered automation.
## Prerequisites
Before using the Box integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Box account with appropriate permissions
- Connected your Box account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Box Integration
### 1. Connect Your Box Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Box** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for file and folder management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="BOX_SAVE_FILE">
**Description:** Save a file from URL in Box.
**Parameters:**
- `fileAttributes` (object, required): Attributes - File metadata including name, parent folder, and timestamps.
```json
{
"content_created_at": "2012-12-12T10:53:43-08:00",
"content_modified_at": "2012-12-12T10:53:43-08:00",
"name": "qwerty.png",
"parent": { "id": "1234567" }
}
```
- `file` (string, required): File URL - Files must be smaller than 50MB in size. (example: "https://picsum.photos/200/300").
</Accordion>
<Accordion title="BOX_SAVE_FILE_FROM_OBJECT">
**Description:** Save a file in Box.
**Parameters:**
- `file` (string, required): File - Accepts a File Object containing file data. Files must be smaller than 50MB in size.
- `fileName` (string, required): File Name (example: "qwerty.png").
- `folder` (string, optional): Folder - Use Connect Portal Workflow Settings to allow users to select the File's Folder destination. Defaults to the user's root folder if left blank.
</Accordion>
<Accordion title="BOX_GET_FILE_BY_ID">
**Description:** Get a file by ID in Box.
**Parameters:**
- `fileId` (string, required): File ID - The unique identifier that represents a file. (example: "12345").
</Accordion>
<Accordion title="BOX_LIST_FILES">
**Description:** List files in Box.
**Parameters:**
- `folderId` (string, required): Folder ID - The unique identifier that represents a folder. (example: "0").
- `filterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "direction",
"operator": "$stringExactlyMatches",
"value": "ASC"
}
]
}
]
}
```
</Accordion>
<Accordion title="BOX_CREATE_FOLDER">
**Description:** Create a folder in Box.
**Parameters:**
- `folderName` (string, required): Name - The name for the new folder. (example: "New Folder").
- `folderParent` (object, required): Parent Folder - The parent folder where the new folder will be created.
```json
{
"id": "123456"
}
```
</Accordion>
<Accordion title="BOX_MOVE_FOLDER">
**Description:** Move a folder in Box.
**Parameters:**
- `folderId` (string, required): Folder ID - The unique identifier that represents a folder. (example: "0").
- `folderName` (string, required): Name - The name for the folder. (example: "New Folder").
- `folderParent` (object, required): Parent Folder - The new parent folder destination.
```json
{
"id": "123456"
}
```
</Accordion>
<Accordion title="BOX_GET_FOLDER_BY_ID">
**Description:** Get a folder by ID in Box.
**Parameters:**
- `folderId` (string, required): Folder ID - The unique identifier that represents a folder. (example: "0").
</Accordion>
<Accordion title="BOX_SEARCH_FOLDERS">
**Description:** Search folders in Box.
**Parameters:**
- `folderId` (string, required): Folder ID - The folder to search within.
- `filterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "sort",
"operator": "$stringExactlyMatches",
"value": "name"
}
]
}
]
}
```
</Accordion>
<Accordion title="BOX_DELETE_FOLDER">
**Description:** Delete a folder in Box.
**Parameters:**
- `folderId` (string, required): Folder ID - The unique identifier that represents a folder. (example: "0").
- `recursive` (boolean, optional): Recursive - Delete a folder that is not empty by recursively deleting the folder and all of its content.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Box Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Box tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Box capabilities
box_agent = Agent(
role="Document Manager",
goal="Manage files and folders in Box efficiently",
backstory="An AI assistant specialized in document management and file organization.",
tools=[enterprise_tools]
)
# Task to create a folder structure
create_structure_task = Task(
description="Create a folder called 'Project Files' in the root directory and upload a document from URL",
agent=box_agent,
expected_output="Folder created and file uploaded successfully"
)
# Run the task
crew = Crew(
agents=[box_agent],
tasks=[create_structure_task]
)
crew.kickoff()
```
### Filtering Specific Box Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Box tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["box_create_folder", "box_save_file", "box_list_files"]
)
file_organizer_agent = Agent(
role="File Organizer",
goal="Organize and manage file storage efficiently",
backstory="An AI assistant that focuses on file organization and storage management.",
tools=enterprise_tools
)
# Task to organize files
organization_task = Task(
description="Create a folder structure for the marketing team and organize existing files",
agent=file_organizer_agent,
expected_output="Folder structure created and files organized"
)
crew = Crew(
agents=[file_organizer_agent],
tasks=[organization_task]
)
crew.kickoff()
```
### Advanced File Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
file_manager = Agent(
role="File Manager",
goal="Maintain organized file structure and manage document lifecycle",
backstory="An experienced file manager who ensures documents are properly organized and accessible.",
tools=[enterprise_tools]
)
# Complex task involving multiple Box operations
management_task = Task(
description="""
1. List all files in the root folder
2. Create monthly archive folders for the current year
3. Move old files to appropriate archive folders
4. Generate a summary report of the file organization
""",
agent=file_manager,
expected_output="Files organized into archive structure with summary report"
)
crew = Crew(
agents=[file_manager],
tasks=[management_task]
)
crew.kickoff()
```

View File

@@ -0,0 +1,293 @@
---
title: ClickUp Integration
description: "Task and productivity management with ClickUp integration for CrewAI."
icon: "list-check"
---
## Overview
Enable your agents to manage tasks, projects, and productivity workflows through ClickUp. Create and update tasks, organize projects, manage team assignments, and streamline your productivity management with AI-powered automation.
## Prerequisites
Before using the ClickUp integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A ClickUp account with appropriate permissions
- Connected your ClickUp account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up ClickUp Integration
### 1. Connect Your ClickUp Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **ClickUp** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for task and project management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="CLICKUP_SEARCH_TASKS">
**Description:** Search for tasks in ClickUp using advanced filters.
**Parameters:**
- `taskFilterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "statuses%5B%5D",
"operator": "$stringExactlyMatches",
"value": "open"
}
]
}
]
}
```
Available fields: `space_ids%5B%5D`, `project_ids%5B%5D`, `list_ids%5B%5D`, `statuses%5B%5D`, `include_closed`, `assignees%5B%5D`, `tags%5B%5D`, `due_date_gt`, `due_date_lt`, `date_created_gt`, `date_created_lt`, `date_updated_gt`, `date_updated_lt`
</Accordion>
<Accordion title="CLICKUP_GET_TASK_IN_LIST">
**Description:** Get tasks in a specific list in ClickUp.
**Parameters:**
- `listId` (string, required): List - Select a List to get tasks from. Use Connect Portal User Settings to allow users to select a ClickUp List.
- `taskFilterFormula` (string, optional): Search for tasks that match specified filters. For example: name=task1.
</Accordion>
<Accordion title="CLICKUP_CREATE_TASK">
**Description:** Create a task in ClickUp.
**Parameters:**
- `listId` (string, required): List - Select a List to create this task in. Use Connect Portal User Settings to allow users to select a ClickUp List.
- `name` (string, required): Name - The task name.
- `description` (string, optional): Description - Task description.
- `status` (string, optional): Status - Select a Status for this task. Use Connect Portal User Settings to allow users to select a ClickUp Status.
- `assignees` (string, optional): Assignees - Select a Member (or an array of member IDs) to be assigned to this task. Use Connect Portal User Settings to allow users to select a ClickUp Member.
- `dueDate` (string, optional): Due Date - Specify a date for this task to be due on.
- `additionalFields` (string, optional): Additional Fields - Specify additional fields to include on this task as JSON.
</Accordion>
<Accordion title="CLICKUP_UPDATE_TASK">
**Description:** Update a task in ClickUp.
**Parameters:**
- `taskId` (string, required): Task ID - The ID of the task to update.
- `listId` (string, required): List - Select a List to create this task in. Use Connect Portal User Settings to allow users to select a ClickUp List.
- `name` (string, optional): Name - The task name.
- `description` (string, optional): Description - Task description.
- `status` (string, optional): Status - Select a Status for this task. Use Connect Portal User Settings to allow users to select a ClickUp Status.
- `assignees` (string, optional): Assignees - Select a Member (or an array of member IDs) to be assigned to this task. Use Connect Portal User Settings to allow users to select a ClickUp Member.
- `dueDate` (string, optional): Due Date - Specify a date for this task to be due on.
- `additionalFields` (string, optional): Additional Fields - Specify additional fields to include on this task as JSON.
</Accordion>
<Accordion title="CLICKUP_DELETE_TASK">
**Description:** Delete a task in ClickUp.
**Parameters:**
- `taskId` (string, required): Task ID - The ID of the task to delete.
</Accordion>
<Accordion title="CLICKUP_GET_LIST">
**Description:** Get List information in ClickUp.
**Parameters:**
- `spaceId` (string, required): Space ID - The ID of the space containing the lists.
</Accordion>
<Accordion title="CLICKUP_GET_CUSTOM_FIELDS_IN_LIST">
**Description:** Get Custom Fields in a List in ClickUp.
**Parameters:**
- `listId` (string, required): List ID - The ID of the list to get custom fields from.
</Accordion>
<Accordion title="CLICKUP_GET_ALL_FIELDS_IN_LIST">
**Description:** Get All Fields in a List in ClickUp.
**Parameters:**
- `listId` (string, required): List ID - The ID of the list to get all fields from.
</Accordion>
<Accordion title="CLICKUP_GET_SPACE">
**Description:** Get Space information in ClickUp.
**Parameters:**
- `spaceId` (string, optional): Space ID - The ID of the space to retrieve.
</Accordion>
<Accordion title="CLICKUP_GET_FOLDERS">
**Description:** Get Folders in ClickUp.
**Parameters:**
- `spaceId` (string, required): Space ID - The ID of the space containing the folders.
</Accordion>
<Accordion title="CLICKUP_GET_MEMBER">
**Description:** Get Member information in ClickUp.
**Parameters:** None required.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic ClickUp Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (ClickUp tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with ClickUp capabilities
clickup_agent = Agent(
role="Task Manager",
goal="Manage tasks and projects in ClickUp efficiently",
backstory="An AI assistant specialized in task management and productivity coordination.",
tools=[enterprise_tools]
)
# Task to create a new task
create_task = Task(
description="Create a task called 'Review Q1 Reports' in the Marketing list with high priority",
agent=clickup_agent,
expected_output="Task created successfully with task ID"
)
# Run the task
crew = Crew(
agents=[clickup_agent],
tasks=[create_task]
)
crew.kickoff()
```
### Filtering Specific ClickUp Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific ClickUp tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["clickup_create_task", "clickup_update_task", "clickup_search_tasks"]
)
task_coordinator = Agent(
role="Task Coordinator",
goal="Create and manage tasks efficiently",
backstory="An AI assistant that focuses on task creation and status management.",
tools=enterprise_tools
)
# Task to manage task workflow
task_workflow = Task(
description="Create a task for project planning and assign it to the development team",
agent=task_coordinator,
expected_output="Task created and assigned successfully"
)
crew = Crew(
agents=[task_coordinator],
tasks=[task_workflow]
)
crew.kickoff()
```
### Advanced Project Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
project_manager = Agent(
role="Project Manager",
goal="Coordinate project activities and track team productivity",
backstory="An experienced project manager who ensures projects are delivered on time.",
tools=[enterprise_tools]
)
# Complex task involving multiple ClickUp operations
project_coordination = Task(
description="""
1. Get all open tasks in the current space
2. Identify overdue tasks and update their status
3. Create a weekly report task summarizing project progress
4. Assign the report task to the team lead
""",
agent=project_manager,
expected_output="Project status updated and weekly report task created and assigned"
)
crew = Crew(
agents=[project_manager],
tasks=[project_coordination]
)
crew.kickoff()
```
### Task Search and Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
task_analyst = Agent(
role="Task Analyst",
goal="Analyze task patterns and optimize team productivity",
backstory="An AI assistant that analyzes task data to improve team efficiency.",
tools=[enterprise_tools]
)
# Task to analyze and optimize task distribution
task_analysis = Task(
description="""
Search for all tasks assigned to team members in the last 30 days,
analyze completion patterns, and create optimization recommendations
""",
agent=task_analyst,
expected_output="Task analysis report with optimization recommendations"
)
crew = Crew(
agents=[task_analyst],
tasks=[task_analysis]
)
crew.kickoff()
```
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with ClickUp integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,323 @@
---
title: GitHub Integration
description: "Repository and issue management with GitHub integration for CrewAI."
icon: "github"
---
## Overview
Enable your agents to manage repositories, issues, and releases through GitHub. Create and update issues, manage releases, track project development, and streamline your software development workflow with AI-powered automation.
## Prerequisites
Before using the GitHub integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A GitHub account with appropriate repository permissions
- Connected your GitHub account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up GitHub Integration
### 1. Connect Your GitHub Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **GitHub** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for repository and issue management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="GITHUB_CREATE_ISSUE">
**Description:** Create an issue in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Issue. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Issue.
- `title` (string, required): Issue Title - Specify the title of the issue to create.
- `body` (string, optional): Issue Body - Specify the body contents of the issue to create.
- `assignees` (string, optional): Assignees - Specify the assignee(s)' GitHub login as an array of strings for this issue. (example: `["octocat"]`).
</Accordion>
<Accordion title="GITHUB_UPDATE_ISSUE">
**Description:** Update an issue in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Issue. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Issue.
- `issue_number` (string, required): Issue Number - Specify the number of the issue to update.
- `title` (string, required): Issue Title - Specify the title of the issue to update.
- `body` (string, optional): Issue Body - Specify the body contents of the issue to update.
- `assignees` (string, optional): Assignees - Specify the assignee(s)' GitHub login as an array of strings for this issue. (example: `["octocat"]`).
- `state` (string, optional): State - Specify the updated state of the issue.
- Options: `open`, `closed`
</Accordion>
<Accordion title="GITHUB_GET_ISSUE_BY_NUMBER">
**Description:** Get an issue by number in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Issue. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Issue.
- `issue_number` (string, required): Issue Number - Specify the number of the issue to fetch.
</Accordion>
<Accordion title="GITHUB_LOCK_ISSUE">
**Description:** Lock an issue in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Issue. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Issue.
- `issue_number` (string, required): Issue Number - Specify the number of the issue to lock.
- `lock_reason` (string, required): Lock Reason - Specify a reason for locking the issue or pull request conversation.
- Options: `off-topic`, `too heated`, `resolved`, `spam`
</Accordion>
<Accordion title="GITHUB_SEARCH_ISSUE">
**Description:** Search for issues in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Issue. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Issue.
- `filter` (object, required): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "assignee",
"operator": "$stringExactlyMatches",
"value": "octocat"
}
]
}
]
}
```
Available fields: `assignee`, `creator`, `mentioned`, `labels`
</Accordion>
<Accordion title="GITHUB_CREATE_RELEASE">
**Description:** Create a release in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Release. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Release.
- `tag_name` (string, required): Name - Specify the name of the release tag to be created. (example: "v1.0.0").
- `target_commitish` (string, optional): Target - Specify the target of the release. This can either be a branch name or a commit SHA. Defaults to the main branch. (example: "master").
- `body` (string, optional): Body - Specify a description for this release.
- `draft` (string, optional): Draft - Specify whether the created release should be a draft (unpublished) release.
- Options: `true`, `false`
- `prerelease` (string, optional): Prerelease - Specify whether the created release should be a prerelease.
- Options: `true`, `false`
- `discussion_category_name` (string, optional): Discussion Category Name - If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository.
- `generate_release_notes` (string, optional): Release Notes - Specify whether the created release should automatically create release notes using the provided name and body specified.
- Options: `true`, `false`
</Accordion>
<Accordion title="GITHUB_UPDATE_RELEASE">
**Description:** Update a release in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Release. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Release.
- `id` (string, required): Release ID - Specify the ID of the release to update.
- `tag_name` (string, optional): Name - Specify the name of the release tag to be updated. (example: "v1.0.0").
- `target_commitish` (string, optional): Target - Specify the target of the release. This can either be a branch name or a commit SHA. Defaults to the main branch. (example: "master").
- `body` (string, optional): Body - Specify a description for this release.
- `draft` (string, optional): Draft - Specify whether the created release should be a draft (unpublished) release.
- Options: `true`, `false`
- `prerelease` (string, optional): Prerelease - Specify whether the created release should be a prerelease.
- Options: `true`, `false`
- `discussion_category_name` (string, optional): Discussion Category Name - If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository.
- `generate_release_notes` (string, optional): Release Notes - Specify whether the created release should automatically create release notes using the provided name and body specified.
- Options: `true`, `false`
</Accordion>
<Accordion title="GITHUB_GET_RELEASE_BY_ID">
**Description:** Get a release by ID in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Release. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Release.
- `id` (string, required): Release ID - Specify the release ID of the release to fetch.
</Accordion>
<Accordion title="GITHUB_GET_RELEASE_BY_TAG_NAME">
**Description:** Get a release by tag name in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Release. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Release.
- `tag_name` (string, required): Name - Specify the tag of the release to fetch. (example: "v1.0.0").
</Accordion>
<Accordion title="GITHUB_DELETE_RELEASE">
**Description:** Delete a release in GitHub.
**Parameters:**
- `owner` (string, required): Owner - Specify the name of the account owner of the associated repository for this Release. (example: "abc").
- `repo` (string, required): Repository - Specify the name of the associated repository for this Release.
- `id` (string, required): Release ID - Specify the ID of the release to delete.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic GitHub Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (GitHub tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with GitHub capabilities
github_agent = Agent(
role="Repository Manager",
goal="Manage GitHub repositories, issues, and releases efficiently",
backstory="An AI assistant specialized in repository management and issue tracking.",
tools=[enterprise_tools]
)
# Task to create a new issue
create_issue_task = Task(
description="Create a bug report issue for the login functionality in the main repository",
agent=github_agent,
expected_output="Issue created successfully with issue number"
)
# Run the task
crew = Crew(
agents=[github_agent],
tasks=[create_issue_task]
)
crew.kickoff()
```
### Filtering Specific GitHub Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific GitHub tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["github_create_issue", "github_update_issue", "github_search_issue"]
)
issue_manager = Agent(
role="Issue Manager",
goal="Create and manage GitHub issues efficiently",
backstory="An AI assistant that focuses on issue tracking and management.",
tools=enterprise_tools
)
# Task to manage issue workflow
issue_workflow = Task(
description="Create a feature request issue and assign it to the development team",
agent=issue_manager,
expected_output="Feature request issue created and assigned successfully"
)
crew = Crew(
agents=[issue_manager],
tasks=[issue_workflow]
)
crew.kickoff()
```
### Release Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
release_manager = Agent(
role="Release Manager",
goal="Manage software releases and versioning",
backstory="An experienced release manager who handles version control and release processes.",
tools=[enterprise_tools]
)
# Task to create a new release
release_task = Task(
description="""
Create a new release v2.1.0 for the project with:
- Auto-generated release notes
- Target the main branch
- Include a description of new features and bug fixes
""",
agent=release_manager,
expected_output="Release v2.1.0 created successfully with release notes"
)
crew = Crew(
agents=[release_manager],
tasks=[release_task]
)
crew.kickoff()
```
### Issue Tracking and Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
project_coordinator = Agent(
role="Project Coordinator",
goal="Track and coordinate project issues and development progress",
backstory="An AI assistant that helps coordinate development work and track project progress.",
tools=[enterprise_tools]
)
# Complex task involving multiple GitHub operations
coordination_task = Task(
description="""
1. Search for all open issues assigned to the current milestone
2. Identify overdue issues and update their priority labels
3. Create a weekly progress report issue
4. Lock resolved issues that have been inactive for 30 days
""",
agent=project_coordinator,
expected_output="Project coordination completed with progress report and issue management"
)
crew = Crew(
agents=[project_coordinator],
tasks=[coordination_task]
)
crew.kickoff()
```
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with GitHub integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,356 @@
---
title: Gmail Integration
description: "Email and contact management with Gmail integration for CrewAI."
icon: "envelope"
---
## Overview
Enable your agents to manage emails, contacts, and drafts through Gmail. Send emails, search messages, manage contacts, create drafts, and streamline your email communications with AI-powered automation.
## Prerequisites
Before using the Gmail integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Gmail account with appropriate permissions
- Connected your Gmail account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Gmail Integration
### 1. Connect Your Gmail Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Gmail** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for email and contact management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="GMAIL_SEND_EMAIL">
**Description:** Send an email in Gmail.
**Parameters:**
- `toRecipients` (array, required): To - Specify the recipients as either a single string or a JSON array.
```json
[
"recipient1@domain.com",
"recipient2@domain.com"
]
```
- `from` (string, required): From - Specify the email of the sender.
- `subject` (string, required): Subject - Specify the subject of the message.
- `messageContent` (string, required): Message Content - Specify the content of the email message as plain text or HTML.
- `attachments` (string, optional): Attachments - Accepts either a single file object or a JSON array of file objects.
- `additionalHeaders` (object, optional): Additional Headers - Specify any additional header fields here.
```json
{
"reply-to": "Sender Name <sender@domain.com>"
}
```
</Accordion>
<Accordion title="GMAIL_GET_EMAIL_BY_ID">
**Description:** Get an email by ID in Gmail.
**Parameters:**
- `userId` (string, required): User ID - Specify the user's email address. (example: "user@domain.com").
- `messageId` (string, required): Message ID - Specify the ID of the message to retrieve.
</Accordion>
<Accordion title="GMAIL_SEARCH_FOR_EMAIL">
**Description:** Search for emails in Gmail using advanced filters.
**Parameters:**
- `emailFilterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "from",
"operator": "$stringContains",
"value": "example@domain.com"
}
]
}
]
}
```
Available fields: `from`, `to`, `date`, `label`, `subject`, `cc`, `bcc`, `category`, `deliveredto:`, `size`, `filename`, `older_than`, `newer_than`, `list`, `is:important`, `is:unread`, `is:snoozed`, `is:starred`, `is:read`, `has:drive`, `has:document`, `has:spreadsheet`, `has:presentation`, `has:attachment`, `has:youtube`, `has:userlabels`
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "page_cursor_string"
}
```
</Accordion>
<Accordion title="GMAIL_DELETE_EMAIL">
**Description:** Delete an email in Gmail.
**Parameters:**
- `userId` (string, required): User ID - Specify the user's email address. (example: "user@domain.com").
- `messageId` (string, required): Message ID - Specify the ID of the message to trash.
</Accordion>
<Accordion title="GMAIL_CREATE_A_CONTACT">
**Description:** Create a contact in Gmail.
**Parameters:**
- `givenName` (string, required): Given Name - Specify the Given Name of the Contact to create. (example: "John").
- `familyName` (string, required): Family Name - Specify the Family Name of the Contact to create. (example: "Doe").
- `email` (string, required): Email - Specify the Email Address of the Contact to create.
- `additionalFields` (object, optional): Additional Fields - Additional contact information.
```json
{
"addresses": [
{
"streetAddress": "1000 North St.",
"city": "Los Angeles"
}
]
}
```
</Accordion>
<Accordion title="GMAIL_GET_CONTACT_BY_RESOURCE_NAME">
**Description:** Get a contact by resource name in Gmail.
**Parameters:**
- `resourceName` (string, required): Resource Name - Specify the resource name of the contact to fetch.
</Accordion>
<Accordion title="GMAIL_SEARCH_FOR_CONTACT">
**Description:** Search for a contact in Gmail.
**Parameters:**
- `searchTerm` (string, required): Term - Specify a search term to search for near or exact matches on the names, nickNames, emailAddresses, phoneNumbers, or organizations Contact properties.
</Accordion>
<Accordion title="GMAIL_DELETE_CONTACT">
**Description:** Delete a contact in Gmail.
**Parameters:**
- `resourceName` (string, required): Resource Name - Specify the resource name of the contact to delete.
</Accordion>
<Accordion title="GMAIL_CREATE_DRAFT">
**Description:** Create a draft in Gmail.
**Parameters:**
- `toRecipients` (array, optional): To - Specify the recipients as either a single string or a JSON array.
```json
[
"recipient1@domain.com",
"recipient2@domain.com"
]
```
- `from` (string, optional): From - Specify the email of the sender.
- `subject` (string, optional): Subject - Specify the subject of the message.
- `messageContent` (string, optional): Message Content - Specify the content of the email message as plain text or HTML.
- `attachments` (string, optional): Attachments - Accepts either a single file object or a JSON array of file objects.
- `additionalHeaders` (object, optional): Additional Headers - Specify any additional header fields here.
```json
{
"reply-to": "Sender Name <sender@domain.com>"
}
```
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Gmail Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Gmail tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Gmail capabilities
gmail_agent = Agent(
role="Email Manager",
goal="Manage email communications and contacts efficiently",
backstory="An AI assistant specialized in email management and communication.",
tools=[enterprise_tools]
)
# Task to send a follow-up email
send_email_task = Task(
description="Send a follow-up email to john@example.com about the project update meeting",
agent=gmail_agent,
expected_output="Email sent successfully with confirmation"
)
# Run the task
crew = Crew(
agents=[gmail_agent],
tasks=[send_email_task]
)
crew.kickoff()
```
### Filtering Specific Gmail Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Gmail tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["gmail_send_email", "gmail_search_for_email", "gmail_create_draft"]
)
email_coordinator = Agent(
role="Email Coordinator",
goal="Coordinate email communications and manage drafts",
backstory="An AI assistant that focuses on email coordination and draft management.",
tools=enterprise_tools
)
# Task to prepare and send emails
email_coordination = Task(
description="Search for emails from the marketing team, create a summary draft, and send it to stakeholders",
agent=email_coordinator,
expected_output="Summary email sent to stakeholders"
)
crew = Crew(
agents=[email_coordinator],
tasks=[email_coordination]
)
crew.kickoff()
```
### Contact Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
contact_manager = Agent(
role="Contact Manager",
goal="Manage and organize email contacts efficiently",
backstory="An experienced contact manager who maintains organized contact databases.",
tools=[enterprise_tools]
)
# Task to manage contacts
contact_task = Task(
description="""
1. Search for contacts from the 'example.com' domain
2. Create new contacts for recent email senders not in the contact list
3. Update contact information with recent interaction data
""",
agent=contact_manager,
expected_output="Contact database updated with new contacts and recent interactions"
)
crew = Crew(
agents=[contact_manager],
tasks=[contact_task]
)
crew.kickoff()
```
### Email Search and Analysis
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
email_analyst = Agent(
role="Email Analyst",
goal="Analyze email patterns and provide insights",
backstory="An AI assistant that analyzes email data to provide actionable insights.",
tools=[enterprise_tools]
)
# Task to analyze email patterns
analysis_task = Task(
description="""
Search for all unread emails from the last 7 days,
categorize them by sender domain,
and create a summary report of communication patterns
""",
agent=email_analyst,
expected_output="Email analysis report with communication patterns and recommendations"
)
crew = Crew(
agents=[email_analyst],
tasks=[analysis_task]
)
crew.kickoff()
```
### Automated Email Workflows
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
workflow_manager = Agent(
role="Email Workflow Manager",
goal="Automate email workflows and responses",
backstory="An AI assistant that manages automated email workflows and responses.",
tools=[enterprise_tools]
)
# Complex task involving multiple Gmail operations
workflow_task = Task(
description="""
1. Search for emails with 'urgent' in the subject from the last 24 hours
2. Create draft responses for each urgent email
3. Send automated acknowledgment emails to senders
4. Create a summary report of urgent items requiring attention
""",
agent=workflow_manager,
expected_output="Urgent emails processed with automated responses and summary report"
)
crew = Crew(
agents=[workflow_manager],
tasks=[workflow_task]
)
crew.kickoff()
```
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Gmail integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,391 @@
---
title: Google Calendar Integration
description: "Event and schedule management with Google Calendar integration for CrewAI."
icon: "calendar"
---
## Overview
Enable your agents to manage calendar events, schedules, and availability through Google Calendar. Create and update events, manage attendees, check availability, and streamline your scheduling workflows with AI-powered automation.
## Prerequisites
Before using the Google Calendar integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Google account with Google Calendar access
- Connected your Google account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Google Calendar Integration
### 1. Connect Your Google Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Google Calendar** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for calendar and contact access
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="GOOGLE_CALENDAR_CREATE_EVENT">
**Description:** Create an event in Google Calendar.
**Parameters:**
- `eventName` (string, required): Event name.
- `startTime` (string, required): Start time - Accepts Unix timestamp or ISO8601 date formats.
- `endTime` (string, optional): End time - Defaults to one hour after the start time if left blank.
- `calendar` (string, optional): Calendar - Use Connect Portal Workflow Settings to allow users to select which calendar the event will be added to. Defaults to the user's primary calendar if left blank.
- `attendees` (string, optional): Attendees - Accepts an array of email addresses or email addresses separated by commas.
- `eventLocation` (string, optional): Event location.
- `eventDescription` (string, optional): Event description.
- `eventId` (string, optional): Event ID - An ID from your application to associate this event with. You can use this ID to sync updates to this event later.
- `includeMeetLink` (boolean, optional): Include Google Meet link? - Automatically creates Google Meet conference link for this event.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_UPDATE_EVENT">
**Description:** Update an existing event in Google Calendar.
**Parameters:**
- `eventId` (string, required): Event ID - The ID of the event to update.
- `eventName` (string, optional): Event name.
- `startTime` (string, optional): Start time - Accepts Unix timestamp or ISO8601 date formats.
- `endTime` (string, optional): End time - Defaults to one hour after the start time if left blank.
- `calendar` (string, optional): Calendar - Use Connect Portal Workflow Settings to allow users to select which calendar the event will be added to. Defaults to the user's primary calendar if left blank.
- `attendees` (string, optional): Attendees - Accepts an array of email addresses or email addresses separated by commas.
- `eventLocation` (string, optional): Event location.
- `eventDescription` (string, optional): Event description.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_LIST_EVENTS">
**Description:** List events from Google Calendar.
**Parameters:**
- `calendar` (string, optional): Calendar - Use Connect Portal Workflow Settings to allow users to select which calendar the event will be added to. Defaults to the user's primary calendar if left blank.
- `after` (string, optional): After - Filters events that start after the provided date (Unix in milliseconds or ISO timestamp). (example: "2025-04-12T10:00:00Z or 1712908800000").
- `before` (string, optional): Before - Filters events that end before the provided date (Unix in milliseconds or ISO timestamp). (example: "2025-04-12T10:00:00Z or 1712908800000").
</Accordion>
<Accordion title="GOOGLE_CALENDAR_GET_EVENT_BY_ID">
**Description:** Get a specific event by ID from Google Calendar.
**Parameters:**
- `eventId` (string, required): Event ID.
- `calendar` (string, optional): Calendar - Use Connect Portal Workflow Settings to allow users to select which calendar the event will be added to. Defaults to the user's primary calendar if left blank.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_DELETE_EVENT">
**Description:** Delete an event from Google Calendar.
**Parameters:**
- `eventId` (string, required): Event ID - The ID of the calendar event to be deleted.
- `calendar` (string, optional): Calendar - Use Connect Portal Workflow Settings to allow users to select which calendar the event will be added to. Defaults to the user's primary calendar if left blank.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_GET_CONTACTS">
**Description:** Get contacts from Google Calendar.
**Parameters:**
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "page_cursor_string"
}
```
</Accordion>
<Accordion title="GOOGLE_CALENDAR_SEARCH_CONTACTS">
**Description:** Search for contacts in Google Calendar.
**Parameters:**
- `query` (string, optional): Search query to search contacts.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_LIST_DIRECTORY_PEOPLE">
**Description:** List directory people.
**Parameters:**
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "page_cursor_string"
}
```
</Accordion>
<Accordion title="GOOGLE_CALENDAR_SEARCH_DIRECTORY_PEOPLE">
**Description:** Search directory people.
**Parameters:**
- `query` (string, required): Search query to search contacts.
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "page_cursor_string"
}
```
</Accordion>
<Accordion title="GOOGLE_CALENDAR_LIST_OTHER_CONTACTS">
**Description:** List other contacts.
**Parameters:**
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "page_cursor_string"
}
```
</Accordion>
<Accordion title="GOOGLE_CALENDAR_SEARCH_OTHER_CONTACTS">
**Description:** Search other contacts.
**Parameters:**
- `query` (string, optional): Search query to search contacts.
</Accordion>
<Accordion title="GOOGLE_CALENDAR_GET_AVAILABILITY">
**Description:** Get availability information for calendars.
**Parameters:**
- `timeMin` (string, required): The start of the interval. In ISO format.
- `timeMax` (string, required): The end of the interval. In ISO format.
- `timeZone` (string, optional): Time zone used in the response. Optional. The default is UTC.
- `items` (array, optional): List of calendars and/or groups to query. Defaults to the user default calendar.
```json
[
{
"id": "calendar_id_1"
},
{
"id": "calendar_id_2"
}
]
```
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Calendar Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Google Calendar tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Google Calendar capabilities
calendar_agent = Agent(
role="Schedule Manager",
goal="Manage calendar events and scheduling efficiently",
backstory="An AI assistant specialized in calendar management and scheduling coordination.",
tools=[enterprise_tools]
)
# Task to create a meeting
create_meeting_task = Task(
description="Create a team standup meeting for tomorrow at 9 AM with the development team",
agent=calendar_agent,
expected_output="Meeting created successfully with Google Meet link"
)
# Run the task
crew = Crew(
agents=[calendar_agent],
tasks=[create_meeting_task]
)
crew.kickoff()
```
### Filtering Specific Calendar Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Google Calendar tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["google_calendar_create_event", "google_calendar_list_events", "google_calendar_get_availability"]
)
meeting_coordinator = Agent(
role="Meeting Coordinator",
goal="Coordinate meetings and check availability",
backstory="An AI assistant that focuses on meeting scheduling and availability management.",
tools=enterprise_tools
)
# Task to schedule a meeting with availability check
schedule_meeting = Task(
description="Check availability for next week and schedule a project review meeting with stakeholders",
agent=meeting_coordinator,
expected_output="Meeting scheduled after checking availability of all participants"
)
crew = Crew(
agents=[meeting_coordinator],
tasks=[schedule_meeting]
)
crew.kickoff()
```
### Event Management and Updates
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
event_manager = Agent(
role="Event Manager",
goal="Manage and update calendar events efficiently",
backstory="An experienced event manager who handles event logistics and updates.",
tools=[enterprise_tools]
)
# Task to manage event updates
event_management = Task(
description="""
1. List all events for this week
2. Update any events that need location changes to include video conference links
3. Send calendar invitations to new team members for recurring meetings
""",
agent=event_manager,
expected_output="Weekly events updated with proper locations and new attendees added"
)
crew = Crew(
agents=[event_manager],
tasks=[event_management]
)
crew.kickoff()
```
### Contact and Availability Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
availability_coordinator = Agent(
role="Availability Coordinator",
goal="Coordinate availability and manage contacts for scheduling",
backstory="An AI assistant that specializes in availability management and contact coordination.",
tools=[enterprise_tools]
)
# Task to coordinate availability
availability_task = Task(
description="""
1. Search for contacts in the engineering department
2. Check availability for all engineers next Friday afternoon
3. Create a team meeting for the first available 2-hour slot
4. Include Google Meet link and send invitations
""",
agent=availability_coordinator,
expected_output="Team meeting scheduled based on availability with all engineers invited"
)
crew = Crew(
agents=[availability_coordinator],
tasks=[availability_task]
)
crew.kickoff()
```
### Automated Scheduling Workflows
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
scheduling_automator = Agent(
role="Scheduling Automator",
goal="Automate scheduling workflows and calendar management",
backstory="An AI assistant that automates complex scheduling scenarios and calendar workflows.",
tools=[enterprise_tools]
)
# Complex scheduling automation task
automation_task = Task(
description="""
1. List all upcoming events for the next two weeks
2. Identify any scheduling conflicts or back-to-back meetings
3. Suggest optimal meeting times by checking availability
4. Create buffer time between meetings where needed
5. Update event descriptions with agenda items and meeting links
""",
agent=scheduling_automator,
expected_output="Calendar optimized with resolved conflicts, buffer times, and updated meeting details"
)
crew = Crew(
agents=[scheduling_automator],
tasks=[automation_task]
)
crew.kickoff()
```
## Troubleshooting
### Common Issues
**Authentication Errors**
- Ensure your Google account has the necessary permissions for calendar access
- Verify that the OAuth connection includes all required scopes for Google Calendar API
- Check if calendar sharing settings allow the required access level
**Event Creation Issues**
- Verify that time formats are correct (ISO8601 or Unix timestamps)
- Ensure attendee email addresses are properly formatted
- Check that the target calendar exists and is accessible
- Verify time zones are correctly specified
**Availability and Time Conflicts**
- Use proper ISO format for time ranges when checking availability
- Ensure time zones are consistent across all operations
- Verify that calendar IDs are correct when checking multiple calendars
**Contact and People Search**
- Ensure search queries are properly formatted
- Check that directory access permissions are granted
- Verify that contact information is up to date and accessible
**Event Updates and Deletions**
- Verify that event IDs are correct and events exist
- Ensure you have edit permissions for the events
- Check that calendar ownership allows modifications
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Google Calendar integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,321 @@
---
title: Google Sheets Integration
description: "Spreadsheet data synchronization with Google Sheets integration for CrewAI."
icon: "google"
---
## Overview
Enable your agents to manage spreadsheet data through Google Sheets. Read rows, create new entries, update existing data, and streamline your data management workflows with AI-powered automation. Perfect for data tracking, reporting, and collaborative data management.
## Prerequisites
Before using the Google Sheets integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Google account with Google Sheets access
- Connected your Google account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
- Spreadsheets with proper column headers for data operations
## Setting Up Google Sheets Integration
### 1. Connect Your Google Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Google Sheets** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for spreadsheet access
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="GOOGLE_SHEETS_GET_ROW">
**Description:** Get rows from a Google Sheets spreadsheet.
**Parameters:**
- `spreadsheetId` (string, required): Spreadsheet - Use Connect Portal Workflow Settings to allow users to select a spreadsheet. Defaults to using the first worksheet in the selected spreadsheet.
- `limit` (string, optional): Limit rows - Limit the maximum number of rows to return.
</Accordion>
<Accordion title="GOOGLE_SHEETS_CREATE_ROW">
**Description:** Create a new row in a Google Sheets spreadsheet.
**Parameters:**
- `spreadsheetId` (string, required): Spreadsheet - Use Connect Portal Workflow Settings to allow users to select a spreadsheet. Defaults to using the first worksheet in the selected spreadsheet..
- `worksheet` (string, required): Worksheet - Your worksheet must have column headers.
- `additionalFields` (object, required): Fields - Include fields to create this row with, as an object with keys of Column Names. Use Connect Portal Workflow Settings to allow users to select a Column Mapping.
```json
{
"columnName1": "columnValue1",
"columnName2": "columnValue2",
"columnName3": "columnValue3",
"columnName4": "columnValue4"
}
```
</Accordion>
<Accordion title="GOOGLE_SHEETS_UPDATE_ROW">
**Description:** Update existing rows in a Google Sheets spreadsheet.
**Parameters:**
- `spreadsheetId` (string, required): Spreadsheet - Use Connect Portal Workflow Settings to allow users to select a spreadsheet. Defaults to using the first worksheet in the selected spreadsheet.
- `worksheet` (string, required): Worksheet - Your worksheet must have column headers.
- `filterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions to identify which rows to update.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "status",
"operator": "$stringExactlyMatches",
"value": "pending"
}
]
}
]
}
```
Available operators: `$stringContains`, `$stringDoesNotContain`, `$stringExactlyMatches`, `$stringDoesNotExactlyMatch`, `$stringStartsWith`, `$stringDoesNotStartWith`, `$stringEndsWith`, `$stringDoesNotEndWith`, `$numberGreaterThan`, `$numberLessThan`, `$numberEquals`, `$numberDoesNotEqual`, `$dateTimeAfter`, `$dateTimeBefore`, `$dateTimeEquals`, `$booleanTrue`, `$booleanFalse`, `$exists`, `$doesNotExist`
- `additionalFields` (object, required): Fields - Include fields to update, as an object with keys of Column Names. Use Connect Portal Workflow Settings to allow users to select a Column Mapping.
```json
{
"columnName1": "newValue1",
"columnName2": "newValue2",
"columnName3": "newValue3",
"columnName4": "newValue4"
}
```
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Google Sheets Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Google Sheets tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Google Sheets capabilities
sheets_agent = Agent(
role="Data Manager",
goal="Manage spreadsheet data and track information efficiently",
backstory="An AI assistant specialized in data management and spreadsheet operations.",
tools=[enterprise_tools]
)
# Task to add new data to a spreadsheet
data_entry_task = Task(
description="Add a new customer record to the customer database spreadsheet with name, email, and signup date",
agent=sheets_agent,
expected_output="New customer record added successfully to the spreadsheet"
)
# Run the task
crew = Crew(
agents=[sheets_agent],
tasks=[data_entry_task]
)
crew.kickoff()
```
### Filtering Specific Google Sheets Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Google Sheets tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["google_sheets_get_row", "google_sheets_create_row"]
)
data_collector = Agent(
role="Data Collector",
goal="Collect and organize data in spreadsheets",
backstory="An AI assistant that focuses on data collection and organization.",
tools=enterprise_tools
)
# Task to collect and organize data
data_collection = Task(
description="Retrieve current inventory data and add new product entries to the inventory spreadsheet",
agent=data_collector,
expected_output="Inventory data retrieved and new products added successfully"
)
crew = Crew(
agents=[data_collector],
tasks=[data_collection]
)
crew.kickoff()
```
### Data Analysis and Reporting
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
data_analyst = Agent(
role="Data Analyst",
goal="Analyze spreadsheet data and generate insights",
backstory="An experienced data analyst who extracts insights from spreadsheet data.",
tools=[enterprise_tools]
)
# Task to analyze data and create reports
analysis_task = Task(
description="""
1. Retrieve all sales data from the current month's spreadsheet
2. Analyze the data for trends and patterns
3. Create a summary report in a new row with key metrics
""",
agent=data_analyst,
expected_output="Sales data analyzed and summary report created with key insights"
)
crew = Crew(
agents=[data_analyst],
tasks=[analysis_task]
)
crew.kickoff()
```
### Automated Data Updates
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
data_updater = Agent(
role="Data Updater",
goal="Automatically update and maintain spreadsheet data",
backstory="An AI assistant that maintains data accuracy and updates records automatically.",
tools=[enterprise_tools]
)
# Task to update data based on conditions
update_task = Task(
description="""
1. Find all pending orders in the orders spreadsheet
2. Update their status to 'processing'
3. Add a timestamp for when the status was updated
4. Log the changes in a separate tracking sheet
""",
agent=data_updater,
expected_output="All pending orders updated to processing status with timestamps logged"
)
crew = Crew(
agents=[data_updater],
tasks=[update_task]
)
crew.kickoff()
```
### Complex Data Management Workflow
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
workflow_manager = Agent(
role="Data Workflow Manager",
goal="Manage complex data workflows across multiple spreadsheets",
backstory="An AI assistant that orchestrates complex data operations across multiple spreadsheets.",
tools=[enterprise_tools]
)
# Complex workflow task
workflow_task = Task(
description="""
1. Get all customer data from the main customer spreadsheet
2. Create monthly summary entries for active customers
3. Update customer status based on activity in the last 30 days
4. Generate a monthly report with customer metrics
5. Archive inactive customer records to a separate sheet
""",
agent=workflow_manager,
expected_output="Monthly customer workflow completed with updated statuses and generated reports"
)
crew = Crew(
agents=[workflow_manager],
tasks=[workflow_task]
)
crew.kickoff()
```
## Troubleshooting
### Common Issues
**Permission Errors**
- Ensure your Google account has edit access to the target spreadsheets
- Verify that the OAuth connection includes required scopes for Google Sheets API
- Check that spreadsheets are shared with the authenticated account
**Spreadsheet Structure Issues**
- Ensure worksheets have proper column headers before creating or updating rows
- Verify that column names in `additionalFields` match the actual column headers
- Check that the specified worksheet exists in the spreadsheet
**Data Type and Format Issues**
- Ensure data values match the expected format for each column
- Use proper date formats for date columns (ISO format recommended)
- Verify that numeric values are properly formatted for number columns
**Filter Formula Issues**
- Ensure filter formulas follow the correct JSON structure for disjunctive normal form
- Use valid field names that match actual column headers
- Test simple filters before building complex multi-condition queries
- Verify that operator types match the data types in the columns
**Row Limits and Performance**
- Be mindful of row limits when using `GOOGLE_SHEETS_GET_ROW`
- Consider pagination for large datasets
- Use specific filters to reduce the amount of data processed
**Update Operations**
- Ensure filter conditions properly identify the intended rows for updates
- Test filter conditions with small datasets before large updates
- Verify that all required fields are included in update operations
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Google Sheets integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,579 @@
---
title: "HubSpot Integration"
description: "Manage companies and contacts in HubSpot with CrewAI."
icon: "briefcase"
---
## Overview
Enable your agents to manage companies and contacts within HubSpot. Create new records and streamline your CRM processes with AI-powered automation.
## Prerequisites
Before using the HubSpot integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription.
- A HubSpot account with appropriate permissions.
- Connected your HubSpot account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors).
## Setting Up HubSpot Integration
### 1. Connect Your HubSpot Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors).
2. Find **HubSpot** in the Authentication Integrations section.
3. Click **Connect** and complete the OAuth flow.
4. Grant the necessary permissions for company and contact management.
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account).
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="HUBSPOT_CREATE_RECORD_COMPANIES">
**Description:** Create a new company record in HubSpot.
**Parameters:**
- `name` (string, required): Name of the company.
- `domain` (string, optional): Company Domain Name.
- `industry` (string, optional): Industry. Must be one of the predefined values from HubSpot.
- `phone` (string, optional): Phone Number.
- `hubspot_owner_id` (string, optional): Company owner ID.
- `type` (string, optional): Type of the company. Available values: `PROSPECT`, `PARTNER`, `RESELLER`, `VENDOR`, `OTHER`.
- `city` (string, optional): City.
- `state` (string, optional): State/Region.
- `zip` (string, optional): Postal Code.
- `numberofemployees` (number, optional): Number of Employees.
- `annualrevenue` (number, optional): Annual Revenue.
- `timezone` (string, optional): Time Zone.
- `description` (string, optional): Description.
- `linkedin_company_page` (string, optional): LinkedIn Company Page URL.
- `company_email` (string, optional): Company Email.
- `first_name` (string, optional): First Name of a contact at the company.
- `last_name` (string, optional): Last Name of a contact at the company.
- `about_us` (string, optional): About Us.
- `hs_csm_sentiment` (string, optional): CSM Sentiment. Available values: `at_risk`, `neutral`, `healthy`.
- `closedate` (string, optional): Close Date.
- `hs_keywords` (string, optional): Company Keywords. Must be one of the predefined values.
- `country` (string, optional): Country/Region.
- `hs_country_code` (string, optional): Country/Region Code.
- `hs_employee_range` (string, optional): Employee range.
- `facebook_company_page` (string, optional): Facebook Company Page URL.
- `facebookfans` (number, optional): Number of Facebook Fans.
- `hs_gps_coordinates` (string, optional): GPS Coordinates.
- `hs_gps_error` (string, optional): GPS Error.
- `googleplus_page` (string, optional): Google Plus Page URL.
- `owneremail` (string, optional): HubSpot Owner Email.
- `ownername` (string, optional): HubSpot Owner Name.
- `hs_ideal_customer_profile` (string, optional): Ideal Customer Profile Tier. Available values: `tier_1`, `tier_2`, `tier_3`.
- `hs_industry_group` (string, optional): Industry group.
- `is_public` (boolean, optional): Is Public.
- `hs_last_metered_enrichment_timestamp` (string, optional): Last Metered Enrichment Timestamp.
- `hs_lead_status` (string, optional): Lead Status. Available values: `NEW`, `OPEN`, `IN_PROGRESS`, `OPEN_DEAL`, `UNQUALIFIED`, `ATTEMPTED_TO_CONTACT`, `CONNECTED`, `BAD_TIMING`.
- `lifecyclestage` (string, optional): Lifecycle Stage. Available values: `subscriber`, `lead`, `marketingqualifiedlead`, `salesqualifiedlead`, `opportunity`, `customer`, `evangelist`, `other`.
- `linkedinbio` (string, optional): LinkedIn Bio.
- `hs_linkedin_handle` (string, optional): LinkedIn handle.
- `hs_live_enrichment_deadline` (string, optional): Live enrichment deadline.
- `hs_logo_url` (string, optional): Logo URL.
- `hs_analytics_source` (string, optional): Original Traffic Source.
- `hs_pinned_engagement_id` (number, optional): Pinned Engagement ID.
- `hs_quick_context` (string, optional): Quick context.
- `hs_revenue_range` (string, optional): Revenue range.
- `hs_state_code` (string, optional): State/Region Code.
- `address` (string, optional): Street Address.
- `address2` (string, optional): Street Address 2.
- `hs_is_target_account` (boolean, optional): Target Account.
- `hs_target_account` (string, optional): Target Account Tier. Available values: `tier_1`, `tier_2`, `tier_3`.
- `hs_target_account_recommendation_snooze_time` (string, optional): Target Account Recommendation Snooze Time.
- `hs_target_account_recommendation_state` (string, optional): Target Account Recommendation State. Available values: `DISMISSED`, `NONE`, `SNOOZED`.
- `total_money_raised` (string, optional): Total Money Raised.
- `twitterbio` (string, optional): Twitter Bio.
- `twitterfollowers` (number, optional): Twitter Followers.
- `twitterhandle` (string, optional): Twitter Handle.
- `web_technologies` (string, optional): Web Technologies used. Must be one of the predefined values.
- `website` (string, optional): Website URL.
- `founded_year` (string, optional): Year Founded.
</Accordion>
<Accordion title="HUBSPOT_CREATE_RECORD_CONTACTS">
**Description:** Create a new contact record in HubSpot.
**Parameters:**
- `email` (string, required): Email address of the contact.
- `firstname` (string, optional): First Name.
- `lastname` (string, optional): Last Name.
- `phone` (string, optional): Phone Number.
- `hubspot_owner_id` (string, optional): Contact owner.
- `lifecyclestage` (string, optional): Lifecycle Stage. Available values: `subscriber`, `lead`, `marketingqualifiedlead`, `salesqualifiedlead`, `opportunity`, `customer`, `evangelist`, `other`.
- `hs_lead_status` (string, optional): Lead Status. Available values: `NEW`, `OPEN`, `IN_PROGRESS`, `OPEN_DEAL`, `UNQUALIFIED`, `ATTEMPTED_TO_CONTACT`, `CONNECTED`, `BAD_TIMING`.
- `annualrevenue` (string, optional): Annual Revenue.
- `hs_buying_role` (string, optional): Buying Role.
- `cc_emails` (string, optional): CC Emails.
- `ch_customer_id` (string, optional): Chargify Customer ID.
- `ch_customer_reference` (string, optional): Chargify Customer Reference.
- `chargify_sites` (string, optional): Chargify Site(s).
- `city` (string, optional): City.
- `hs_facebook_ad_clicked` (boolean, optional): Clicked Facebook ad.
- `hs_linkedin_ad_clicked` (string, optional): Clicked LinkedIn Ad.
- `hs_clicked_linkedin_ad` (string, optional): Clicked on a LinkedIn Ad.
- `closedate` (string, optional): Close Date.
- `company` (string, optional): Company Name.
- `company_size` (string, optional): Company size.
- `country` (string, optional): Country/Region.
- `hs_country_region_code` (string, optional): Country/Region Code.
- `date_of_birth` (string, optional): Date of birth.
- `degree` (string, optional): Degree.
- `hs_email_customer_quarantined_reason` (string, optional): Email address quarantine reason.
- `hs_role` (string, optional): Employment Role. Must be one of the predefined values.
- `hs_seniority` (string, optional): Employment Seniority. Must be one of the predefined values.
- `hs_sub_role` (string, optional): Employment Sub Role. Must be one of the predefined values.
- `hs_employment_change_detected_date` (string, optional): Employment change detected date.
- `hs_enriched_email_bounce_detected` (boolean, optional): Enriched Email Bounce Detected.
- `hs_facebookid` (string, optional): Facebook ID.
- `hs_facebook_click_id` (string, optional): Facebook click id.
- `fax` (string, optional): Fax Number.
- `field_of_study` (string, optional): Field of study.
- `followercount` (number, optional): Follower Count.
- `gender` (string, optional): Gender.
- `hs_google_click_id` (string, optional): Google ad click id.
- `graduation_date` (string, optional): Graduation date.
- `owneremail` (string, optional): HubSpot Owner Email (legacy).
- `ownername` (string, optional): HubSpot Owner Name (legacy).
- `industry` (string, optional): Industry.
- `hs_inferred_language_codes` (string, optional): Inferred Language Codes. Must be one of the predefined values.
- `jobtitle` (string, optional): Job Title.
- `hs_job_change_detected_date` (string, optional): Job change detected date.
- `job_function` (string, optional): Job function.
- `hs_journey_stage` (string, optional): Journey Stage. Must be one of the predefined values.
- `kloutscoregeneral` (number, optional): Klout Score.
- `hs_last_metered_enrichment_timestamp` (string, optional): Last Metered Enrichment Timestamp.
- `hs_latest_source` (string, optional): Latest Traffic Source.
- `hs_latest_source_timestamp` (string, optional): Latest Traffic Source Date.
- `hs_legal_basis` (string, optional): Legal basis for processing contact's data.
- `linkedinbio` (string, optional): LinkedIn Bio.
- `linkedinconnections` (number, optional): LinkedIn Connections.
- `hs_linkedin_url` (string, optional): LinkedIn URL.
- `hs_linkedinid` (string, optional): Linkedin ID.
- `hs_live_enrichment_deadline` (string, optional): Live enrichment deadline.
- `marital_status` (string, optional): Marital Status.
- `hs_content_membership_email` (string, optional): Member email.
- `hs_content_membership_notes` (string, optional): Membership Notes.
- `message` (string, optional): Message.
- `military_status` (string, optional): Military status.
- `mobilephone` (string, optional): Mobile Phone Number.
- `numemployees` (string, optional): Number of Employees.
- `hs_analytics_source` (string, optional): Original Traffic Source.
- `photo` (string, optional): Photo.
- `hs_pinned_engagement_id` (number, optional): Pinned engagement ID.
- `zip` (string, optional): Postal Code.
- `hs_language` (string, optional): Preferred language. Must be one of the predefined values.
- `associatedcompanyid` (number, optional): Primary Associated Company ID.
- `hs_email_optout_survey_reason` (string, optional): Reason for opting out of email.
- `relationship_status` (string, optional): Relationship Status.
- `hs_returning_to_office_detected_date` (string, optional): Returning to office detected date.
- `salutation` (string, optional): Salutation.
- `school` (string, optional): School.
- `seniority` (string, optional): Seniority.
- `hs_feedback_show_nps_web_survey` (boolean, optional): Should be shown an NPS web survey.
- `start_date` (string, optional): Start date.
- `state` (string, optional): State/Region.
- `hs_state_code` (string, optional): State/Region Code.
- `hs_content_membership_status` (string, optional): Status.
- `address` (string, optional): Street Address.
- `tax_exempt` (string, optional): Tax Exempt.
- `hs_timezone` (string, optional): Time Zone. Must be one of the predefined values.
- `twitterbio` (string, optional): Twitter Bio.
- `hs_twitterid` (string, optional): Twitter ID.
- `twitterprofilephoto` (string, optional): Twitter Profile Photo.
- `twitterhandle` (string, optional): Twitter Username.
- `vat_number` (string, optional): VAT Number.
- `ch_verified` (string, optional): Verified for ACH/eCheck Payments.
- `website` (string, optional): Website URL.
- `hs_whatsapp_phone_number` (string, optional): WhatsApp Phone Number.
- `work_email` (string, optional): Work email.
- `hs_googleplusid` (string, optional): googleplus ID.
</Accordion>
<Accordion title="HUBSPOT_CREATE_RECORD_DEALS">
**Description:** Create a new deal record in HubSpot.
**Parameters:**
- `dealname` (string, required): Name of the deal.
- `amount` (number, optional): The value of the deal.
- `dealstage` (string, optional): The pipeline stage of the deal.
- `pipeline` (string, optional): The pipeline the deal belongs to.
- `closedate` (string, optional): The date the deal is expected to close.
- `hubspot_owner_id` (string, optional): The owner of the deal.
- `dealtype` (string, optional): The type of deal. Available values: `newbusiness`, `existingbusiness`.
- `description` (string, optional): A description of the deal.
- `hs_priority` (string, optional): The priority of the deal. Available values: `low`, `medium`, `high`.
</Accordion>
<Accordion title="HUBSPOT_CREATE_RECORD_ENGAGEMENTS">
**Description:** Create a new engagement (e.g., note, email, call, meeting, task) in HubSpot.
**Parameters:**
- `engagementType` (string, required): The type of engagement. Available values: `NOTE`, `EMAIL`, `CALL`, `MEETING`, `TASK`.
- `hubspot_owner_id` (string, optional): The user the activity is assigned to.
- `hs_timestamp` (string, optional): The date and time of the activity.
- `hs_note_body` (string, optional): The body of the note. (Used for `NOTE`)
- `hs_task_subject` (string, optional): The title of the task. (Used for `TASK`)
- `hs_task_body` (string, optional): The notes for the task. (Used for `TASK`)
- `hs_task_status` (string, optional): The status of the task. (Used for `TASK`)
- `hs_meeting_title` (string, optional): The title of the meeting. (Used for `MEETING`)
- `hs_meeting_body` (string, optional): The description for the meeting. (Used for `MEETING`)
- `hs_meeting_start_time` (string, optional): The start time of the meeting. (Used for `MEETING`)
- `hs_meeting_end_time` (string, optional): The end time of the meeting. (Used for `MEETING`)
</Accordion>
<Accordion title="HUBSPOT_UPDATE_RECORD_COMPANIES">
**Description:** Update an existing company record in HubSpot.
**Parameters:**
- `recordId` (string, required): The ID of the company to update.
- `name` (string, optional): Name of the company.
- `domain` (string, optional): Company Domain Name.
- `industry` (string, optional): Industry.
- `phone` (string, optional): Phone Number.
- `city` (string, optional): City.
- `state` (string, optional): State/Region.
- `zip` (string, optional): Postal Code.
- `numberofemployees` (number, optional): Number of Employees.
- `annualrevenue` (number, optional): Annual Revenue.
- `description` (string, optional): Description.
</Accordion>
<Accordion title="HUBSPOT_CREATE_RECORD_ANY">
**Description:** Create a record for a specified object type in HubSpot.
**Parameters:**
- `recordType` (string, required): The object type ID of the custom object.
- Additional parameters depend on the custom object's schema.
</Accordion>
<Accordion title="HUBSPOT_UPDATE_RECORD_CONTACTS">
**Description:** Update an existing contact record in HubSpot.
**Parameters:**
- `recordId` (string, required): The ID of the contact to update.
- `firstname` (string, optional): First Name.
- `lastname` (string, optional): Last Name.
- `email` (string, optional): Email address.
- `phone` (string, optional): Phone Number.
- `company` (string, optional): Company Name.
- `jobtitle` (string, optional): Job Title.
- `lifecyclestage` (string, optional): Lifecycle Stage.
</Accordion>
<Accordion title="HUBSPOT_UPDATE_RECORD_DEALS">
**Description:** Update an existing deal record in HubSpot.
**Parameters:**
- `recordId` (string, required): The ID of the deal to update.
- `dealname` (string, optional): Name of the deal.
- `amount` (number, optional): The value of the deal.
- `dealstage` (string, optional): The pipeline stage of the deal.
- `pipeline` (string, optional): The pipeline the deal belongs to.
- `closedate` (string, optional): The date the deal is expected to close.
- `dealtype` (string, optional): The type of deal.
</Accordion>
<Accordion title="HUBSPOT_UPDATE_RECORD_ENGAGEMENTS">
**Description:** Update an existing engagement in HubSpot.
**Parameters:**
- `recordId` (string, required): The ID of the engagement to update.
- `hs_note_body` (string, optional): The body of the note.
- `hs_task_subject` (string, optional): The title of the task.
- `hs_task_body` (string, optional): The notes for the task.
- `hs_task_status` (string, optional): The status of the task.
</Accordion>
<Accordion title="HUBSPOT_UPDATE_RECORD_ANY">
**Description:** Update a record for a specified object type in HubSpot.
**Parameters:**
- `recordId` (string, required): The ID of the record to update.
- `recordType` (string, required): The object type ID of the custom object.
- Additional parameters depend on the custom object's schema.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORDS_COMPANIES">
**Description:** Get a list of company records from HubSpot.
**Parameters:**
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORDS_CONTACTS">
**Description:** Get a list of contact records from HubSpot.
**Parameters:**
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORDS_DEALS">
**Description:** Get a list of deal records from HubSpot.
**Parameters:**
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORDS_ENGAGEMENTS">
**Description:** Get a list of engagement records from HubSpot.
**Parameters:**
- `objectName` (string, required): The type of engagement to fetch (e.g., "notes").
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORDS_ANY">
**Description:** Get a list of records for any specified object type in HubSpot.
**Parameters:**
- `recordType` (string, required): The object type ID of the custom object.
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORD_BY_ID_COMPANIES">
**Description:** Get a single company record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the company to retrieve.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORD_BY_ID_CONTACTS">
**Description:** Get a single contact record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the contact to retrieve.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORD_BY_ID_DEALS">
**Description:** Get a single deal record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the deal to retrieve.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORD_BY_ID_ENGAGEMENTS">
**Description:** Get a single engagement record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the engagement to retrieve.
</Accordion>
<Accordion title="HUBSPOT_GET_RECORD_BY_ID_ANY">
**Description:** Get a single record of any specified object type by its ID.
**Parameters:**
- `recordType` (string, required): The object type ID of the custom object.
- `recordId` (string, required): The ID of the record to retrieve.
</Accordion>
<Accordion title="HUBSPOT_SEARCH_RECORDS_COMPANIES">
**Description:** Search for company records in HubSpot using a filter formula.
**Parameters:**
- `filterFormula` (object, optional): A filter in disjunctive normal form (OR of ANDs).
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_SEARCH_RECORDS_CONTACTS">
**Description:** Search for contact records in HubSpot using a filter formula.
**Parameters:**
- `filterFormula` (object, optional): A filter in disjunctive normal form (OR of ANDs).
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_SEARCH_RECORDS_DEALS">
**Description:** Search for deal records in HubSpot using a filter formula.
**Parameters:**
- `filterFormula` (object, optional): A filter in disjunctive normal form (OR of ANDs).
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_SEARCH_RECORDS_ENGAGEMENTS">
**Description:** Search for engagement records in HubSpot using a filter formula.
**Parameters:**
- `engagementFilterFormula` (object, optional): A filter for engagements.
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_SEARCH_RECORDS_ANY">
**Description:** Search for records of any specified object type in HubSpot.
**Parameters:**
- `recordType` (string, required): The object type ID to search.
- `filterFormula` (string, optional): The filter formula to apply.
- `paginationParameters` (object, optional): Use `pageCursor` to fetch subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_DELETE_RECORD_COMPANIES">
**Description:** Delete a company record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the company to delete.
</Accordion>
<Accordion title="HUBSPOT_DELETE_RECORD_CONTACTS">
**Description:** Delete a contact record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the contact to delete.
</Accordion>
<Accordion title="HUBSPOT_DELETE_RECORD_DEALS">
**Description:** Delete a deal record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the deal to delete.
</Accordion>
<Accordion title="HUBSPOT_DELETE_RECORD_ENGAGEMENTS">
**Description:** Delete an engagement record by its ID.
**Parameters:**
- `recordId` (string, required): The ID of the engagement to delete.
</Accordion>
<Accordion title="HUBSPOT_DELETE_RECORD_ANY">
**Description:** Delete a record of any specified object type by its ID.
**Parameters:**
- `recordType` (string, required): The object type ID of the custom object.
- `recordId` (string, required): The ID of the record to delete.
</Accordion>
<Accordion title="HUBSPOT_GET_CONTACTS_BY_LIST_ID">
**Description:** Get contacts from a specific list by its ID.
**Parameters:**
- `listId` (string, required): The ID of the list to get contacts from.
- `paginationParameters` (object, optional): Use `pageCursor` for subsequent pages.
</Accordion>
<Accordion title="HUBSPOT_DESCRIBE_ACTION_SCHEMA">
**Description:** Get the expected schema for a given object type and operation.
**Parameters:**
- `recordType` (string, required): The object type ID (e.g., 'companies').
- `operation` (string, required): The operation type (e.g., 'CREATE_RECORD').
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic HubSpot Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (HubSpot tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with HubSpot capabilities
hubspot_agent = Agent(
role="CRM Manager",
goal="Manage company and contact records in HubSpot",
backstory="An AI assistant specialized in CRM management.",
tools=[enterprise_tools]
)
# Task to create a new company
create_company_task = Task(
description="Create a new company in HubSpot with name 'Innovate Corp' and domain 'innovatecorp.com'.",
agent=hubspot_agent,
expected_output="Company created successfully with confirmation"
)
# Run the task
crew = Crew(
agents=[hubspot_agent],
tasks=[create_company_task]
)
crew.kickoff()
```
### Filtering Specific HubSpot Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only the tool to create contacts
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["hubspot_create_record_contacts"]
)
contact_creator = Agent(
role="Contact Creator",
goal="Create new contacts in HubSpot",
backstory="An AI assistant that focuses on creating new contact entries in the CRM.",
tools=[enterprise_tools]
)
# Task to create a contact
create_contact = Task(
description="Create a new contact for 'John Doe' with email 'john.doe@example.com'.",
agent=contact_creator,
expected_output="Contact created successfully in HubSpot."
)
crew = Crew(
agents=[contact_creator],
tasks=[create_contact]
)
crew.kickoff()
```
### Contact Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
crm_manager = Agent(
role="CRM Manager",
goal="Manage and organize HubSpot contacts efficiently.",
backstory="An experienced CRM manager who maintains an organized contact database.",
tools=[enterprise_tools]
)
# Task to manage contacts
contact_task = Task(
description="Create a new contact for 'Jane Smith' at 'Global Tech Inc.' with email 'jane.smith@globaltech.com'.",
agent=crm_manager,
expected_output="Contact database updated with the new contact."
)
crew = Crew(
agents=[crm_manager],
tasks=[contact_task]
)
crew.kickoff()
```
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with HubSpot integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,394 @@
---
title: Jira Integration
description: "Issue tracking and project management with Jira integration for CrewAI."
icon: "bug"
---
## Overview
Enable your agents to manage issues, projects, and workflows through Jira. Create and update issues, track project progress, manage assignments, and streamline your project management with AI-powered automation.
## Prerequisites
Before using the Jira integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Jira account with appropriate project permissions
- Connected your Jira account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Jira Integration
### 1. Connect Your Jira Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Jira** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for issue and project management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="JIRA_CREATE_ISSUE">
**Description:** Create an issue in Jira.
**Parameters:**
- `summary` (string, required): Summary - A brief one-line summary of the issue. (example: "The printer stopped working").
- `project` (string, optional): Project - The project which the issue belongs to. Defaults to the user's first project if not provided. Use Connect Portal Workflow Settings to allow users to select a Project.
- `issueType` (string, optional): Issue type - Defaults to Task if not provided.
- `jiraIssueStatus` (string, optional): Status - Defaults to the project's first status if not provided.
- `assignee` (string, optional): Assignee - Defaults to the authenticated user if not provided.
- `descriptionType` (string, optional): Description Type - Select the Description Type.
- Options: `description`, `descriptionJSON`
- `description` (string, optional): Description - A detailed description of the issue. This field appears only when 'descriptionType' = 'description'.
- `additionalFields` (string, optional): Additional Fields - Specify any other fields that should be included in JSON format. Use Connect Portal Workflow Settings to allow users to select which Issue Fields to update.
```json
{
"customfield_10001": "value"
}
```
</Accordion>
<Accordion title="JIRA_UPDATE_ISSUE">
**Description:** Update an issue in Jira.
**Parameters:**
- `issueKey` (string, required): Issue Key (example: "TEST-1234").
- `summary` (string, optional): Summary - A brief one-line summary of the issue. (example: "The printer stopped working").
- `issueType` (string, optional): Issue type - Use Connect Portal Workflow Settings to allow users to select an Issue Type.
- `jiraIssueStatus` (string, optional): Status - Use Connect Portal Workflow Settings to allow users to select a Status.
- `assignee` (string, optional): Assignee - Use Connect Portal Workflow Settings to allow users to select an Assignee.
- `descriptionType` (string, optional): Description Type - Select the Description Type.
- Options: `description`, `descriptionJSON`
- `description` (string, optional): Description - A detailed description of the issue. This field appears only when 'descriptionType' = 'description'.
- `additionalFields` (string, optional): Additional Fields - Specify any other fields that should be included in JSON format.
</Accordion>
<Accordion title="JIRA_GET_ISSUE_BY_KEY">
**Description:** Get an issue by key in Jira.
**Parameters:**
- `issueKey` (string, required): Issue Key (example: "TEST-1234").
</Accordion>
<Accordion title="JIRA_FILTER_ISSUES">
**Description:** Search issues in Jira using filters.
**Parameters:**
- `jqlQuery` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "status",
"operator": "$stringExactlyMatches",
"value": "Open"
}
]
}
]
}
```
Available operators: `$stringExactlyMatches`, `$stringDoesNotExactlyMatch`, `$stringIsIn`, `$stringIsNotIn`, `$stringContains`, `$stringDoesNotContain`, `$stringGreaterThan`, `$stringLessThan`
- `limit` (string, optional): Limit results - Limit the maximum number of issues to return. Defaults to 10 if left blank.
</Accordion>
<Accordion title="JIRA_SEARCH_BY_JQL">
**Description:** Search issues by JQL in Jira.
**Parameters:**
- `jqlQuery` (string, required): JQL Query (example: "project = PROJECT").
- `paginationParameters` (object, optional): Pagination parameters for paginated results.
```json
{
"pageCursor": "cursor_string"
}
```
</Accordion>
<Accordion title="JIRA_UPDATE_ISSUE_ANY">
**Description:** Update any issue in Jira. Use DESCRIBE_ACTION_SCHEMA to get properties schema for this function.
**Parameters:** No specific parameters - use JIRA_DESCRIBE_ACTION_SCHEMA first to get the expected schema.
</Accordion>
<Accordion title="JIRA_DESCRIBE_ACTION_SCHEMA">
**Description:** Get the expected schema for an issue type. Use this function first if no other function matches the issue type you want to operate on.
**Parameters:**
- `issueTypeId` (string, required): Issue Type ID.
- `projectKey` (string, required): Project key.
- `operation` (string, required): Operation Type value, for example CREATE_ISSUE or UPDATE_ISSUE.
</Accordion>
<Accordion title="JIRA_GET_PROJECTS">
**Description:** Get Projects in Jira.
**Parameters:**
- `paginationParameters` (object, optional): Pagination Parameters.
```json
{
"pageCursor": "cursor_string"
}
```
</Accordion>
<Accordion title="JIRA_GET_ISSUE_TYPES_BY_PROJECT">
**Description:** Get Issue Types by project in Jira.
**Parameters:**
- `project` (string, required): Project key.
</Accordion>
<Accordion title="JIRA_GET_ISSUE_TYPES">
**Description:** Get all Issue Types in Jira.
**Parameters:** None required.
</Accordion>
<Accordion title="JIRA_GET_ISSUE_STATUS_BY_PROJECT">
**Description:** Get issue statuses for a given project.
**Parameters:**
- `project` (string, required): Project key.
</Accordion>
<Accordion title="JIRA_GET_ALL_ASSIGNEES_BY_PROJECT">
**Description:** Get assignees for a given project.
**Parameters:**
- `project` (string, required): Project key.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Jira Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Jira tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Jira capabilities
jira_agent = Agent(
role="Issue Manager",
goal="Manage Jira issues and track project progress efficiently",
backstory="An AI assistant specialized in issue tracking and project management.",
tools=[enterprise_tools]
)
# Task to create a bug report
create_bug_task = Task(
description="Create a bug report for the login functionality with high priority and assign it to the development team",
agent=jira_agent,
expected_output="Bug report created successfully with issue key"
)
# Run the task
crew = Crew(
agents=[jira_agent],
tasks=[create_bug_task]
)
crew.kickoff()
```
### Filtering Specific Jira Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Jira tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["jira_create_issue", "jira_update_issue", "jira_search_by_jql"]
)
issue_coordinator = Agent(
role="Issue Coordinator",
goal="Create and manage Jira issues efficiently",
backstory="An AI assistant that focuses on issue creation and management.",
tools=enterprise_tools
)
# Task to manage issue workflow
issue_workflow = Task(
description="Create a feature request issue and update the status of related issues",
agent=issue_coordinator,
expected_output="Feature request created and related issues updated"
)
crew = Crew(
agents=[issue_coordinator],
tasks=[issue_workflow]
)
crew.kickoff()
```
### Project Analysis and Reporting
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
project_analyst = Agent(
role="Project Analyst",
goal="Analyze project data and generate insights from Jira",
backstory="An experienced project analyst who extracts insights from project management data.",
tools=[enterprise_tools]
)
# Task to analyze project status
analysis_task = Task(
description="""
1. Get all projects and their issue types
2. Search for all open issues across projects
3. Analyze issue distribution by status and assignee
4. Create a summary report issue with findings
""",
agent=project_analyst,
expected_output="Project analysis completed with summary report created"
)
crew = Crew(
agents=[project_analyst],
tasks=[analysis_task]
)
crew.kickoff()
```
### Automated Issue Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
automation_manager = Agent(
role="Automation Manager",
goal="Automate issue management and workflow processes",
backstory="An AI assistant that automates repetitive issue management tasks.",
tools=[enterprise_tools]
)
# Task to automate issue management
automation_task = Task(
description="""
1. Search for all unassigned issues using JQL
2. Get available assignees for each project
3. Automatically assign issues based on workload and expertise
4. Update issue priorities based on age and type
5. Create weekly sprint planning issues
""",
agent=automation_manager,
expected_output="Issues automatically assigned and sprint planning issues created"
)
crew = Crew(
agents=[automation_manager],
tasks=[automation_task]
)
crew.kickoff()
```
### Advanced Schema-Based Operations
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
schema_specialist = Agent(
role="Schema Specialist",
goal="Handle complex Jira operations using dynamic schemas",
backstory="An AI assistant that can work with dynamic Jira schemas and custom issue types.",
tools=[enterprise_tools]
)
# Task using schema-based operations
schema_task = Task(
description="""
1. Get all projects and their custom issue types
2. For each custom issue type, describe the action schema
3. Create issues using the dynamic schema for complex custom fields
4. Update issues with custom field values based on business rules
""",
agent=schema_specialist,
expected_output="Custom issues created and updated using dynamic schemas"
)
crew = Crew(
agents=[schema_specialist],
tasks=[schema_task]
)
crew.kickoff()
```
## Troubleshooting
### Common Issues
**Permission Errors**
- Ensure your Jira account has necessary permissions for the target projects
- Verify that the OAuth connection includes required scopes for Jira API
- Check if you have create/edit permissions for issues in the specified projects
**Invalid Project or Issue Keys**
- Double-check project keys and issue keys for correct format (e.g., "PROJ-123")
- Ensure projects exist and are accessible to your account
- Verify that issue keys reference existing issues
**Issue Type and Status Issues**
- Use JIRA_GET_ISSUE_TYPES_BY_PROJECT to get valid issue types for a project
- Use JIRA_GET_ISSUE_STATUS_BY_PROJECT to get valid statuses
- Ensure issue types and statuses are available in the target project
**JQL Query Problems**
- Test JQL queries in Jira's issue search before using in API calls
- Ensure field names in JQL are spelled correctly and exist in your Jira instance
- Use proper JQL syntax for complex queries
**Custom Fields and Schema Issues**
- Use JIRA_DESCRIBE_ACTION_SCHEMA to get the correct schema for complex issue types
- Ensure custom field IDs are correct (e.g., "customfield_10001")
- Verify that custom fields are available in the target project and issue type
**Filter Formula Issues**
- Ensure filter formulas follow the correct JSON structure for disjunctive normal form
- Use valid field names that exist in your Jira configuration
- Test simple filters before building complex multi-condition queries
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Jira integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,453 @@
---
title: Linear Integration
description: "Software project and bug tracking with Linear integration for CrewAI."
icon: "list-check"
---
## Overview
Enable your agents to manage issues, projects, and development workflows through Linear. Create and update issues, manage project timelines, organize teams, and streamline your software development process with AI-powered automation.
## Prerequisites
Before using the Linear integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Linear account with appropriate workspace permissions
- Connected your Linear account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Linear Integration
### 1. Connect Your Linear Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Linear** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for issue and project management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="LINEAR_CREATE_ISSUE">
**Description:** Create a new issue in Linear.
**Parameters:**
- `teamId` (string, required): Team ID - Specify the Team ID of the parent for this new issue. Use Connect Portal Workflow Settings to allow users to select a Team ID. (example: "a70bdf0f-530a-4887-857d-46151b52b47c").
- `title` (string, required): Title - Specify a title for this issue.
- `description` (string, optional): Description - Specify a description for this issue.
- `statusId` (string, optional): Status - Specify the state or status of this issue.
- `priority` (string, optional): Priority - Specify the priority of this issue as an integer.
- `dueDate` (string, optional): Due Date - Specify the due date of this issue in ISO 8601 format.
- `cycleId` (string, optional): Cycle ID - Specify the cycle associated with this issue.
- `additionalFields` (object, optional): Additional Fields.
```json
{
"assigneeId": "a70bdf0f-530a-4887-857d-46151b52b47c",
"labelIds": ["a70bdf0f-530a-4887-857d-46151b52b47c"]
}
```
</Accordion>
<Accordion title="LINEAR_UPDATE_ISSUE">
**Description:** Update an issue in Linear.
**Parameters:**
- `issueId` (string, required): Issue ID - Specify the Issue ID of the issue to update. (example: "90fbc706-18cd-42c9-ae66-6bd344cc8977").
- `title` (string, optional): Title - Specify a title for this issue.
- `description` (string, optional): Description - Specify a description for this issue.
- `statusId` (string, optional): Status - Specify the state or status of this issue.
- `priority` (string, optional): Priority - Specify the priority of this issue as an integer.
- `dueDate` (string, optional): Due Date - Specify the due date of this issue in ISO 8601 format.
- `cycleId` (string, optional): Cycle ID - Specify the cycle associated with this issue.
- `additionalFields` (object, optional): Additional Fields.
```json
{
"assigneeId": "a70bdf0f-530a-4887-857d-46151b52b47c",
"labelIds": ["a70bdf0f-530a-4887-857d-46151b52b47c"]
}
```
</Accordion>
<Accordion title="LINEAR_GET_ISSUE_BY_ID">
**Description:** Get an issue by ID in Linear.
**Parameters:**
- `issueId` (string, required): Issue ID - Specify the record ID of the issue to fetch. (example: "90fbc706-18cd-42c9-ae66-6bd344cc8977").
</Accordion>
<Accordion title="LINEAR_GET_ISSUE_BY_ISSUE_IDENTIFIER">
**Description:** Get an issue by issue identifier in Linear.
**Parameters:**
- `externalId` (string, required): External ID - Specify the human-readable Issue identifier of the issue to fetch. (example: "ABC-1").
</Accordion>
<Accordion title="LINEAR_SEARCH_ISSUE">
**Description:** Search issues in Linear.
**Parameters:**
- `queryTerm` (string, required): Query Term - The search term to look for.
- `issueFilterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "title",
"operator": "$stringContains",
"value": "bug"
}
]
}
]
}
```
Available fields: `title`, `number`, `project`, `createdAt`
Available operators: `$stringExactlyMatches`, `$stringDoesNotExactlyMatch`, `$stringIsIn`, `$stringIsNotIn`, `$stringStartsWith`, `$stringDoesNotStartWith`, `$stringEndsWith`, `$stringDoesNotEndWith`, `$stringContains`, `$stringDoesNotContain`, `$stringGreaterThan`, `$stringLessThan`, `$numberGreaterThanOrEqualTo`, `$numberLessThanOrEqualTo`, `$numberGreaterThan`, `$numberLessThan`, `$dateTimeAfter`, `$dateTimeBefore`
</Accordion>
<Accordion title="LINEAR_DELETE_ISSUE">
**Description:** Delete an issue in Linear.
**Parameters:**
- `issueId` (string, required): Issue ID - Specify the record ID of the issue to delete. (example: "90fbc706-18cd-42c9-ae66-6bd344cc8977").
</Accordion>
<Accordion title="LINEAR_ARCHIVE_ISSUE">
**Description:** Archive an issue in Linear.
**Parameters:**
- `issueId` (string, required): Issue ID - Specify the record ID of the issue to archive. (example: "90fbc706-18cd-42c9-ae66-6bd344cc8977").
</Accordion>
<Accordion title="LINEAR_CREATE_SUB_ISSUE">
**Description:** Create a sub-issue in Linear.
**Parameters:**
- `parentId` (string, required): Parent ID - Specify the Issue ID for the parent of this new issue.
- `teamId` (string, required): Team ID - Specify the Team ID of the parent for this new sub-issue. Use Connect Portal Workflow Settings to allow users to select a Team ID. (example: "a70bdf0f-530a-4887-857d-46151b52b47c").
- `title` (string, required): Title - Specify a title for this issue.
- `description` (string, optional): Description - Specify a description for this issue.
- `additionalFields` (object, optional): Additional Fields.
```json
{
"lead": "linear_user_id"
}
```
</Accordion>
<Accordion title="LINEAR_CREATE_PROJECT">
**Description:** Create a new project in Linear.
**Parameters:**
- `teamIds` (object, required): Team ID - Specify the team ID(s) this project is associated with as a string or a JSON array. Use Connect Portal User Settings to allow your user to select a Team ID.
```json
[
"a70bdf0f-530a-4887-857d-46151b52b47c",
"4ac7..."
]
```
- `projectName` (string, required): Project Name - Specify the name of the project. (example: "My Linear Project").
- `description` (string, optional): Project Description - Specify a description for this project.
- `additionalFields` (object, optional): Additional Fields.
```json
{
"state": "planned",
"description": ""
}
```
</Accordion>
<Accordion title="LINEAR_UPDATE_PROJECT">
**Description:** Update a project in Linear.
**Parameters:**
- `projectId` (string, required): Project ID - Specify the ID of the project to update. (example: "a6634484-6061-4ac7-9739-7dc5e52c796b").
- `projectName` (string, optional): Project Name - Specify the name of the project to update. (example: "My Linear Project").
- `description` (string, optional): Project Description - Specify a description for this project.
- `additionalFields` (object, optional): Additional Fields.
```json
{
"state": "planned",
"description": ""
}
```
</Accordion>
<Accordion title="LINEAR_GET_PROJECT_BY_ID">
**Description:** Get a project by ID in Linear.
**Parameters:**
- `projectId` (string, required): Project ID - Specify the Project ID of the project to fetch. (example: "a6634484-6061-4ac7-9739-7dc5e52c796b").
</Accordion>
<Accordion title="LINEAR_DELETE_PROJECT">
**Description:** Delete a project in Linear.
**Parameters:**
- `projectId` (string, required): Project ID - Specify the Project ID of the project to delete. (example: "a6634484-6061-4ac7-9739-7dc5e52c796b").
</Accordion>
<Accordion title="LINEAR_SEARCH_TEAMS">
**Description:** Search teams in Linear.
**Parameters:**
- `teamFilterFormula` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "name",
"operator": "$stringContains",
"value": "Engineering"
}
]
}
]
}
```
Available fields: `id`, `name`
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Linear Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Linear tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Linear capabilities
linear_agent = Agent(
role="Development Manager",
goal="Manage Linear issues and track development progress efficiently",
backstory="An AI assistant specialized in software development project management.",
tools=[enterprise_tools]
)
# Task to create a bug report
create_bug_task = Task(
description="Create a high-priority bug report for the authentication system and assign it to the backend team",
agent=linear_agent,
expected_output="Bug report created successfully with issue ID"
)
# Run the task
crew = Crew(
agents=[linear_agent],
tasks=[create_bug_task]
)
crew.kickoff()
```
### Filtering Specific Linear Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Linear tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["linear_create_issue", "linear_update_issue", "linear_search_issue"]
)
issue_manager = Agent(
role="Issue Manager",
goal="Create and manage Linear issues efficiently",
backstory="An AI assistant that focuses on issue creation and lifecycle management.",
tools=enterprise_tools
)
# Task to manage issue workflow
issue_workflow = Task(
description="Create a feature request issue and update the status of related issues to reflect current progress",
agent=issue_manager,
expected_output="Feature request created and related issues updated"
)
crew = Crew(
agents=[issue_manager],
tasks=[issue_workflow]
)
crew.kickoff()
```
### Project and Team Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
project_coordinator = Agent(
role="Project Coordinator",
goal="Coordinate projects and teams in Linear efficiently",
backstory="An experienced project coordinator who manages development cycles and team workflows.",
tools=[enterprise_tools]
)
# Task to coordinate project setup
project_coordination = Task(
description="""
1. Search for engineering teams in Linear
2. Create a new project for Q2 feature development
3. Associate the project with relevant teams
4. Create initial project milestones as issues
""",
agent=project_coordinator,
expected_output="Q2 project created with teams assigned and initial milestones established"
)
crew = Crew(
agents=[project_coordinator],
tasks=[project_coordination]
)
crew.kickoff()
```
### Issue Hierarchy and Sub-task Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
task_organizer = Agent(
role="Task Organizer",
goal="Organize complex issues into manageable sub-tasks",
backstory="An AI assistant that breaks down complex development work into organized sub-tasks.",
tools=[enterprise_tools]
)
# Task to create issue hierarchy
hierarchy_task = Task(
description="""
1. Search for large feature issues that need to be broken down
2. For each complex issue, create sub-issues for different components
3. Update the parent issues with proper descriptions and links to sub-issues
4. Assign sub-issues to appropriate team members based on expertise
""",
agent=task_organizer,
expected_output="Complex issues broken down into manageable sub-tasks with proper assignments"
)
crew = Crew(
agents=[task_organizer],
tasks=[hierarchy_task]
)
crew.kickoff()
```
### Automated Development Workflow
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
workflow_automator = Agent(
role="Workflow Automator",
goal="Automate development workflow processes in Linear",
backstory="An AI assistant that automates repetitive development workflow tasks.",
tools=[enterprise_tools]
)
# Complex workflow automation task
automation_task = Task(
description="""
1. Search for issues that have been in progress for more than 7 days
2. Update their priorities based on due dates and project importance
3. Create weekly sprint planning issues for each team
4. Archive completed issues from the previous cycle
5. Generate project status reports as new issues
""",
agent=workflow_automator,
expected_output="Development workflow automated with updated priorities, sprint planning, and status reports"
)
crew = Crew(
agents=[workflow_automator],
tasks=[automation_task]
)
crew.kickoff()
```
## Troubleshooting
### Common Issues
**Permission Errors**
- Ensure your Linear account has necessary permissions for the target workspace
- Verify that the OAuth connection includes required scopes for Linear API
- Check if you have create/edit permissions for issues and projects in the workspace
**Invalid IDs and References**
- Double-check team IDs, issue IDs, and project IDs for correct UUID format
- Ensure referenced entities (teams, projects, cycles) exist and are accessible
- Verify that issue identifiers follow the correct format (e.g., "ABC-1")
**Team and Project Association Issues**
- Use LINEAR_SEARCH_TEAMS to get valid team IDs before creating issues or projects
- Ensure teams exist and are active in your workspace
- Verify that team IDs are properly formatted as UUIDs
**Issue Status and Priority Problems**
- Check that status IDs reference valid workflow states for the team
- Ensure priority values are within the valid range for your Linear configuration
- Verify that custom fields and labels exist before referencing them
**Date and Time Format Issues**
- Use ISO 8601 format for due dates and timestamps
- Ensure time zones are handled correctly for due date calculations
- Verify that date values are valid and in the future for due dates
**Search and Filter Issues**
- Ensure search queries are properly formatted and not empty
- Use valid field names in filter formulas: `title`, `number`, `project`, `createdAt`
- Test simple filters before building complex multi-condition queries
- Verify that operator types match the data types of the fields being filtered
**Sub-issue Creation Problems**
- Ensure parent issue IDs are valid and accessible
- Verify that the team ID for sub-issues matches or is compatible with the parent issue's team
- Check that parent issues are not already archived or deleted
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Linear integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,509 @@
---
title: Notion Integration
description: "Page and database management with Notion integration for CrewAI."
icon: "book"
---
## Overview
Enable your agents to manage pages, databases, and content through Notion. Create and update pages, manage content blocks, organize knowledge bases, and streamline your documentation workflows with AI-powered automation.
## Prerequisites
Before using the Notion integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Notion account with appropriate workspace permissions
- Connected your Notion account through the [Integrations page](https://app.crewai.com/crewai_plus/connectors)
## Setting Up Notion Integration
### 1. Connect Your Notion Account
1. Navigate to [CrewAI Enterprise Integrations](https://app.crewai.com/crewai_plus/connectors)
2. Find **Notion** in the Authentication Integrations section
3. Click **Connect** and complete the OAuth flow
4. Grant the necessary permissions for page and database management
5. Copy your Enterprise Token from [Account Settings](https://app.crewai.com/crewai_plus/settings/account)
### 2. Install Required Package
```bash
uv add crewai-tools
```
## Available Actions
<AccordionGroup>
<Accordion title="NOTION_CREATE_PAGE">
**Description:** Create a page in Notion.
**Parameters:**
- `parent` (object, required): Parent - The parent page or database where the new page is inserted, represented as a JSON object with a page_id or database_id key.
```json
{
"database_id": "DATABASE_ID"
}
```
- `properties` (object, required): Properties - The values of the page's properties. If the parent is a database, then the schema must match the parent database's properties.
```json
{
"title": [
{
"text": {
"content": "My Page"
}
}
]
}
```
- `icon` (object, required): Icon - The page icon.
```json
{
"emoji": "🥬"
}
```
- `children` (object, optional): Children - Content blocks to add to the page.
```json
[
{
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Lacinato kale"
}
}
]
}
}
]
```
- `cover` (object, optional): Cover - The page cover image.
```json
{
"external": {
"url": "https://upload.wikimedia.org/wikipedia/commons/6/62/Tuscankale.jpg"
}
}
```
</Accordion>
<Accordion title="NOTION_UPDATE_PAGE">
**Description:** Update a page in Notion.
**Parameters:**
- `pageId` (string, required): Page ID - Specify the ID of the Page to Update. (example: "59833787-2cf9-4fdf-8782-e53db20768a5").
- `icon` (object, required): Icon - The page icon.
```json
{
"emoji": "🥬"
}
```
- `archived` (boolean, optional): Archived - Whether the page is archived (deleted). Set to true to archive a page. Set to false to un-archive (restore) a page.
- `properties` (object, optional): Properties - The property values to update for the page.
```json
{
"title": [
{
"text": {
"content": "My Updated Page"
}
}
]
}
```
- `cover` (object, optional): Cover - The page cover image.
```json
{
"external": {
"url": "https://upload.wikimedia.org/wikipedia/commons/6/62/Tuscankale.jpg"
}
}
```
</Accordion>
<Accordion title="NOTION_GET_PAGE_BY_ID">
**Description:** Get a page by ID in Notion.
**Parameters:**
- `pageId` (string, required): Page ID - Specify the ID of the Page to Get. (example: "59833787-2cf9-4fdf-8782-e53db20768a5").
</Accordion>
<Accordion title="NOTION_ARCHIVE_PAGE">
**Description:** Archive a page in Notion.
**Parameters:**
- `pageId` (string, required): Page ID - Specify the ID of the Page to Archive. (example: "59833787-2cf9-4fdf-8782-e53db20768a5").
</Accordion>
<Accordion title="NOTION_SEARCH_PAGES">
**Description:** Search pages in Notion using filters.
**Parameters:**
- `searchByTitleFilterSearch` (object, optional): A filter in disjunctive normal form - OR of AND groups of single conditions.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "query",
"operator": "$stringExactlyMatches",
"value": "meeting notes"
}
]
}
]
}
```
Available fields: `query`, `filter.value`, `direction`, `page_size`
</Accordion>
<Accordion title="NOTION_GET_PAGE_CONTENT">
**Description:** Get page content (blocks) in Notion.
**Parameters:**
- `blockId` (string, required): Page ID - Specify a Block or Page ID to receive all of its block's children in order. (example: "59833787-2cf9-4fdf-8782-e53db20768a5").
</Accordion>
<Accordion title="NOTION_UPDATE_BLOCK">
**Description:** Update a block in Notion.
**Parameters:**
- `blockId` (string, required): Block ID - Specify the ID of the Block to Update. (example: "9bc30ad4-9373-46a5-84ab-0a7845ee52e6").
- `archived` (boolean, optional): Archived - Set to true to archive (delete) a block. Set to false to un-archive (restore) a block.
- `paragraph` (object, optional): Paragraph content.
```json
{
"rich_text": [
{
"type": "text",
"text": {
"content": "Lacinato kale",
"link": null
}
}
],
"color": "default"
}
```
- `image` (object, optional): Image block.
```json
{
"type": "external",
"external": {
"url": "https://website.domain/images/image.png"
}
}
```
- `bookmark` (object, optional): Bookmark block.
```json
{
"caption": [],
"url": "https://companywebsite.com"
}
```
- `code` (object, optional): Code block.
```json
{
"rich_text": [
{
"type": "text",
"text": {
"content": "const a = 3"
}
}
],
"language": "javascript"
}
```
- `pdf` (object, optional): PDF block.
```json
{
"type": "external",
"external": {
"url": "https://website.domain/files/doc.pdf"
}
}
```
- `table` (object, optional): Table block.
```json
{
"table_width": 2,
"has_column_header": false,
"has_row_header": false
}
```
- `tableOfContent` (object, optional): Table of Contents block.
```json
{
"color": "default"
}
```
- `additionalFields` (object, optional): Additional block types.
```json
{
"child_page": {
"title": "Lacinato kale"
},
"child_database": {
"title": "My database"
}
}
```
</Accordion>
<Accordion title="NOTION_GET_BLOCK_BY_ID">
**Description:** Get a block by ID in Notion.
**Parameters:**
- `blockId` (string, required): Block ID - Specify the ID of the Block to Get. (example: "9bc30ad4-9373-46a5-84ab-0a7845ee52e6").
</Accordion>
<Accordion title="NOTION_DELETE_BLOCK">
**Description:** Delete a block in Notion.
**Parameters:**
- `blockId` (string, required): Block ID - Specify the ID of the Block to Delete. (example: "9bc30ad4-9373-46a5-84ab-0a7845ee52e6").
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Notion Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Notion tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Notion capabilities
notion_agent = Agent(
role="Documentation Manager",
goal="Manage documentation and knowledge base in Notion efficiently",
backstory="An AI assistant specialized in content management and documentation.",
tools=[enterprise_tools]
)
# Task to create a meeting notes page
create_notes_task = Task(
description="Create a new meeting notes page in the team database with today's date and agenda items",
agent=notion_agent,
expected_output="Meeting notes page created successfully with structured content"
)
# Run the task
crew = Crew(
agents=[notion_agent],
tasks=[create_notes_task]
)
crew.kickoff()
```
### Filtering Specific Notion Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Notion tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["notion_create_page", "notion_update_block", "notion_search_pages"]
)
content_manager = Agent(
role="Content Manager",
goal="Create and manage content pages efficiently",
backstory="An AI assistant that focuses on content creation and management.",
tools=enterprise_tools
)
# Task to manage content workflow
content_workflow = Task(
description="Create a new project documentation page and add structured content blocks for requirements and specifications",
agent=content_manager,
expected_output="Project documentation created with organized content sections"
)
crew = Crew(
agents=[content_manager],
tasks=[content_workflow]
)
crew.kickoff()
```
### Knowledge Base Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
knowledge_curator = Agent(
role="Knowledge Curator",
goal="Curate and organize knowledge base content in Notion",
backstory="An experienced knowledge manager who organizes and maintains comprehensive documentation.",
tools=[enterprise_tools]
)
# Task to curate knowledge base
curation_task = Task(
description="""
1. Search for existing documentation pages related to our new product feature
2. Create a comprehensive feature documentation page with proper structure
3. Add code examples, images, and links to related resources
4. Update existing pages with cross-references to the new documentation
""",
agent=knowledge_curator,
expected_output="Feature documentation created and integrated with existing knowledge base"
)
crew = Crew(
agents=[knowledge_curator],
tasks=[curation_task]
)
crew.kickoff()
```
### Content Structure and Organization
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
content_organizer = Agent(
role="Content Organizer",
goal="Organize and structure content blocks for optimal readability",
backstory="An AI assistant that specializes in content structure and user experience.",
tools=[enterprise_tools]
)
# Task to organize content structure
organization_task = Task(
description="""
1. Get content from existing project pages
2. Analyze the structure and identify improvement opportunities
3. Update content blocks to use proper headings, tables, and formatting
4. Add table of contents and improve navigation between related pages
5. Create templates for future documentation consistency
""",
agent=content_organizer,
expected_output="Content reorganized with improved structure and navigation"
)
crew = Crew(
agents=[content_organizer],
tasks=[organization_task]
)
crew.kickoff()
```
### Automated Documentation Workflows
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
doc_automator = Agent(
role="Documentation Automator",
goal="Automate documentation workflows and maintenance",
backstory="An AI assistant that automates repetitive documentation tasks.",
tools=[enterprise_tools]
)
# Complex documentation automation task
automation_task = Task(
description="""
1. Search for pages that haven't been updated in the last 30 days
2. Review and update outdated content blocks
3. Create weekly team update pages with consistent formatting
4. Add status indicators and progress tracking to project pages
5. Generate monthly documentation health reports
6. Archive completed project pages and organize them in archive sections
""",
agent=doc_automator,
expected_output="Documentation automated with updated content, weekly reports, and organized archives"
)
crew = Crew(
agents=[doc_automator],
tasks=[automation_task]
)
crew.kickoff()
```
## Troubleshooting
### Common Issues
**Permission Errors**
- Ensure your Notion account has edit access to the target workspace
- Verify that the OAuth connection includes required scopes for Notion API
- Check that pages and databases are shared with the authenticated integration
**Invalid Page and Block IDs**
- Double-check page IDs and block IDs for correct UUID format
- Ensure referenced pages and blocks exist and are accessible
- Verify that parent page or database IDs are valid when creating new pages
**Property Schema Issues**
- Ensure page properties match the database schema when creating pages in databases
- Verify that property names and types are correct for the target database
- Check that required properties are included when creating or updating pages
**Content Block Structure**
- Ensure block content follows Notion's rich text format specifications
- Verify that nested block structures are properly formatted
- Check that media URLs are accessible and properly formatted
**Search and Filter Issues**
- Ensure search queries are properly formatted and not empty
- Use valid field names in filter formulas: `query`, `filter.value`, `direction`, `page_size`
- Test simple searches before building complex filter conditions
**Parent-Child Relationships**
- Verify that parent page or database exists before creating child pages
- Ensure proper permissions exist for the parent container
- Check that database schemas allow the properties you're trying to set
**Rich Text and Media Content**
- Ensure URLs for external images, PDFs, and bookmarks are accessible
- Verify that rich text formatting follows Notion's API specifications
- Check that code block language types are supported by Notion
**Archive and Deletion Operations**
- Understand the difference between archiving (reversible) and deleting (permanent)
- Verify that you have permissions to archive or delete the target content
- Be cautious with bulk operations that might affect multiple pages or blocks
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Notion integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,632 @@
---
title: Salesforce Integration
description: "CRM and sales automation with Salesforce integration for CrewAI."
icon: "salesforce"
---
## Overview
Enable your agents to manage customer relationships, sales processes, and data through Salesforce. Create and update records, manage leads and opportunities, execute SOQL queries, and streamline your CRM workflows with AI-powered automation.
## Prerequisites
Before using the Salesforce integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Salesforce account with appropriate permissions
- Connected your Salesforce account through the [Integrations page](https://app.crewai.com/integrations)
## Available Tools
### **Record Management**
<AccordionGroup>
<Accordion title="SALESFORCE_CREATE_RECORD_CONTACT">
**Description:** Create a new Contact record in Salesforce.
**Parameters:**
- `FirstName` (string, optional): First Name
- `LastName` (string, required): Last Name - This field is required
- `accountId` (string, optional): Account ID - The Account that the Contact belongs to
- `Email` (string, optional): Email address
- `Title` (string, optional): Title of the contact, such as CEO or Vice President
- `Description` (string, optional): A description of the Contact
- `additionalFields` (object, optional): Additional fields in JSON format for custom Contact fields
</Accordion>
<Accordion title="SALESFORCE_CREATE_RECORD_LEAD">
**Description:** Create a new Lead record in Salesforce.
**Parameters:**
- `FirstName` (string, optional): First Name
- `LastName` (string, required): Last Name - This field is required
- `Company` (string, required): Company - This field is required
- `Email` (string, optional): Email address
- `Phone` (string, optional): Phone number
- `Website` (string, optional): Website URL
- `Title` (string, optional): Title of the contact, such as CEO or Vice President
- `Status` (string, optional): Lead Status - Use Connect Portal Workflow Settings to select Lead Status
- `Description` (string, optional): A description of the Lead
- `additionalFields` (object, optional): Additional fields in JSON format for custom Lead fields
</Accordion>
<Accordion title="SALESFORCE_CREATE_RECORD_OPPORTUNITY">
**Description:** Create a new Opportunity record in Salesforce.
**Parameters:**
- `Name` (string, required): The Opportunity name - This field is required
- `StageName` (string, optional): Opportunity Stage - Use Connect Portal Workflow Settings to select stage
- `CloseDate` (string, optional): Close Date in YYYY-MM-DD format - Defaults to 30 days from current date
- `AccountId` (string, optional): The Account that the Opportunity belongs to
- `Amount` (string, optional): Estimated total sale amount
- `Description` (string, optional): A description of the Opportunity
- `OwnerId` (string, optional): The Salesforce user assigned to work on this Opportunity
- `NextStep` (string, optional): Description of next task in closing Opportunity
- `additionalFields` (object, optional): Additional fields in JSON format for custom Opportunity fields
</Accordion>
<Accordion title="SALESFORCE_CREATE_RECORD_TASK">
**Description:** Create a new Task record in Salesforce.
**Parameters:**
- `whatId` (string, optional): Related to ID - The ID of the Account or Opportunity this Task is related to
- `whoId` (string, optional): Name ID - The ID of the Contact or Lead this Task is related to
- `subject` (string, required): Subject of the task
- `activityDate` (string, optional): Activity Date in YYYY-MM-DD format
- `description` (string, optional): A description of the Task
- `taskSubtype` (string, required): Task Subtype - Options: task, email, listEmail, call
- `Status` (string, optional): Status - Options: Not Started, In Progress, Completed
- `ownerId` (string, optional): Assigned To ID - The Salesforce user assigned to this Task
- `callDurationInSeconds` (string, optional): Call Duration in seconds
- `isReminderSet` (boolean, optional): Whether reminder is set
- `reminderDateTime` (string, optional): Reminder Date/Time in ISO format
- `additionalFields` (object, optional): Additional fields in JSON format for custom Task fields
</Accordion>
<Accordion title="SALESFORCE_CREATE_RECORD_ACCOUNT">
**Description:** Create a new Account record in Salesforce.
**Parameters:**
- `Name` (string, required): The Account name - This field is required
- `OwnerId` (string, optional): The Salesforce user assigned to this Account
- `Website` (string, optional): Website URL
- `Phone` (string, optional): Phone number
- `Description` (string, optional): Account description
- `additionalFields` (object, optional): Additional fields in JSON format for custom Account fields
</Accordion>
<Accordion title="SALESFORCE_CREATE_RECORD_ANY">
**Description:** Create a record of any object type in Salesforce.
**Note:** This is a flexible tool for creating records of custom or unknown object types.
</Accordion>
</AccordionGroup>
### **Record Updates**
<AccordionGroup>
<Accordion title="SALESFORCE_UPDATE_RECORD_CONTACT">
**Description:** Update an existing Contact record in Salesforce.
**Parameters:**
- `recordId` (string, required): The ID of the record to update
- `FirstName` (string, optional): First Name
- `LastName` (string, optional): Last Name
- `accountId` (string, optional): Account ID - The Account that the Contact belongs to
- `Email` (string, optional): Email address
- `Title` (string, optional): Title of the contact
- `Description` (string, optional): A description of the Contact
- `additionalFields` (object, optional): Additional fields in JSON format for custom Contact fields
</Accordion>
<Accordion title="SALESFORCE_UPDATE_RECORD_LEAD">
**Description:** Update an existing Lead record in Salesforce.
**Parameters:**
- `recordId` (string, required): The ID of the record to update
- `FirstName` (string, optional): First Name
- `LastName` (string, optional): Last Name
- `Company` (string, optional): Company name
- `Email` (string, optional): Email address
- `Phone` (string, optional): Phone number
- `Website` (string, optional): Website URL
- `Title` (string, optional): Title of the contact
- `Status` (string, optional): Lead Status
- `Description` (string, optional): A description of the Lead
- `additionalFields` (object, optional): Additional fields in JSON format for custom Lead fields
</Accordion>
<Accordion title="SALESFORCE_UPDATE_RECORD_OPPORTUNITY">
**Description:** Update an existing Opportunity record in Salesforce.
**Parameters:**
- `recordId` (string, required): The ID of the record to update
- `Name` (string, optional): The Opportunity name
- `StageName` (string, optional): Opportunity Stage
- `CloseDate` (string, optional): Close Date in YYYY-MM-DD format
- `AccountId` (string, optional): The Account that the Opportunity belongs to
- `Amount` (string, optional): Estimated total sale amount
- `Description` (string, optional): A description of the Opportunity
- `OwnerId` (string, optional): The Salesforce user assigned to work on this Opportunity
- `NextStep` (string, optional): Description of next task in closing Opportunity
- `additionalFields` (object, optional): Additional fields in JSON format for custom Opportunity fields
</Accordion>
<Accordion title="SALESFORCE_UPDATE_RECORD_TASK">
**Description:** Update an existing Task record in Salesforce.
**Parameters:**
- `recordId` (string, required): The ID of the record to update
- `whatId` (string, optional): Related to ID - The ID of the Account or Opportunity this Task is related to
- `whoId` (string, optional): Name ID - The ID of the Contact or Lead this Task is related to
- `subject` (string, optional): Subject of the task
- `activityDate` (string, optional): Activity Date in YYYY-MM-DD format
- `description` (string, optional): A description of the Task
- `Status` (string, optional): Status - Options: Not Started, In Progress, Completed
- `ownerId` (string, optional): Assigned To ID - The Salesforce user assigned to this Task
- `callDurationInSeconds` (string, optional): Call Duration in seconds
- `isReminderSet` (boolean, optional): Whether reminder is set
- `reminderDateTime` (string, optional): Reminder Date/Time in ISO format
- `additionalFields` (object, optional): Additional fields in JSON format for custom Task fields
</Accordion>
<Accordion title="SALESFORCE_UPDATE_RECORD_ACCOUNT">
**Description:** Update an existing Account record in Salesforce.
**Parameters:**
- `recordId` (string, required): The ID of the record to update
- `Name` (string, optional): The Account name
- `OwnerId` (string, optional): The Salesforce user assigned to this Account
- `Website` (string, optional): Website URL
- `Phone` (string, optional): Phone number
- `Description` (string, optional): Account description
- `additionalFields` (object, optional): Additional fields in JSON format for custom Account fields
</Accordion>
<Accordion title="SALESFORCE_UPDATE_RECORD_ANY">
**Description:** Update a record of any object type in Salesforce.
**Note:** This is a flexible tool for updating records of custom or unknown object types.
</Accordion>
</AccordionGroup>
### **Record Retrieval**
<AccordionGroup>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_CONTACT">
**Description:** Get a Contact record by its ID.
**Parameters:**
- `recordId` (string, required): Record ID of the Contact
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_LEAD">
**Description:** Get a Lead record by its ID.
**Parameters:**
- `recordId` (string, required): Record ID of the Lead
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_OPPORTUNITY">
**Description:** Get an Opportunity record by its ID.
**Parameters:**
- `recordId` (string, required): Record ID of the Opportunity
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_TASK">
**Description:** Get a Task record by its ID.
**Parameters:**
- `recordId` (string, required): Record ID of the Task
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_ACCOUNT">
**Description:** Get an Account record by its ID.
**Parameters:**
- `recordId` (string, required): Record ID of the Account
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_ID_ANY">
**Description:** Get a record of any object type by its ID.
**Parameters:**
- `recordType` (string, required): Record Type (e.g., "CustomObject__c")
- `recordId` (string, required): Record ID
</Accordion>
</AccordionGroup>
### **Record Search**
<AccordionGroup>
<Accordion title="SALESFORCE_SEARCH_RECORDS_CONTACT">
**Description:** Search for Contact records with advanced filtering.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `sortBy` (string, optional): Sort field (e.g., "CreatedDate")
- `sortDirection` (string, optional): Sort direction - Options: ASC, DESC
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_SEARCH_RECORDS_LEAD">
**Description:** Search for Lead records with advanced filtering.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `sortBy` (string, optional): Sort field (e.g., "CreatedDate")
- `sortDirection` (string, optional): Sort direction - Options: ASC, DESC
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_SEARCH_RECORDS_OPPORTUNITY">
**Description:** Search for Opportunity records with advanced filtering.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `sortBy` (string, optional): Sort field (e.g., "CreatedDate")
- `sortDirection` (string, optional): Sort direction - Options: ASC, DESC
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_SEARCH_RECORDS_TASK">
**Description:** Search for Task records with advanced filtering.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `sortBy` (string, optional): Sort field (e.g., "CreatedDate")
- `sortDirection` (string, optional): Sort direction - Options: ASC, DESC
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_SEARCH_RECORDS_ACCOUNT">
**Description:** Search for Account records with advanced filtering.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `sortBy` (string, optional): Sort field (e.g., "CreatedDate")
- `sortDirection` (string, optional): Sort direction - Options: ASC, DESC
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_SEARCH_RECORDS_ANY">
**Description:** Search for records of any object type.
**Parameters:**
- `recordType` (string, required): Record Type to search
- `filterFormula` (string, optional): Filter search criteria
- `includeAllFields` (boolean, optional): Include all fields in results
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
</AccordionGroup>
### **List View Retrieval**
<AccordionGroup>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_CONTACT">
**Description:** Get Contact records from a specific List View.
**Parameters:**
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_LEAD">
**Description:** Get Lead records from a specific List View.
**Parameters:**
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_OPPORTUNITY">
**Description:** Get Opportunity records from a specific List View.
**Parameters:**
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_TASK">
**Description:** Get Task records from a specific List View.
**Parameters:**
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_ACCOUNT">
**Description:** Get Account records from a specific List View.
**Parameters:**
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
<Accordion title="SALESFORCE_GET_RECORD_BY_VIEW_ID_ANY">
**Description:** Get records of any object type from a specific List View.
**Parameters:**
- `recordType` (string, required): Record Type
- `listViewId` (string, required): List View ID
- `paginationParameters` (object, optional): Pagination settings with pageCursor
</Accordion>
</AccordionGroup>
### **Custom Fields**
<AccordionGroup>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_CONTACT">
**Description:** Deploy custom fields for Contact objects.
**Parameters:**
- `label` (string, required): Field Label for displays and internal reference
- `type` (string, required): Field Type - Options: Checkbox, Currency, Date, Email, Number, Percent, Phone, Picklist, MultiselectPicklist, Text, TextArea, LongTextArea, Html, Time, Url
- `defaultCheckboxValue` (boolean, optional): Default value for checkbox fields
- `length` (string, required): Length for numeric/text fields
- `decimalPlace` (string, required): Decimal places for numeric fields
- `pickListValues` (string, required): Values for picklist fields (separated by new lines)
- `visibleLines` (string, required): Visible lines for multiselect/text area fields
- `description` (string, optional): Field description
- `helperText` (string, optional): Helper text shown on hover
- `defaultFieldValue` (string, optional): Default field value
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_LEAD">
**Description:** Deploy custom fields for Lead objects.
**Parameters:**
- `label` (string, required): Field Label for displays and internal reference
- `type` (string, required): Field Type - Options: Checkbox, Currency, Date, Email, Number, Percent, Phone, Picklist, MultiselectPicklist, Text, TextArea, LongTextArea, Html, Time, Url
- `defaultCheckboxValue` (boolean, optional): Default value for checkbox fields
- `length` (string, required): Length for numeric/text fields
- `decimalPlace` (string, required): Decimal places for numeric fields
- `pickListValues` (string, required): Values for picklist fields (separated by new lines)
- `visibleLines` (string, required): Visible lines for multiselect/text area fields
- `description` (string, optional): Field description
- `helperText` (string, optional): Helper text shown on hover
- `defaultFieldValue` (string, optional): Default field value
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_OPPORTUNITY">
**Description:** Deploy custom fields for Opportunity objects.
**Parameters:**
- `label` (string, required): Field Label for displays and internal reference
- `type` (string, required): Field Type - Options: Checkbox, Currency, Date, Email, Number, Percent, Phone, Picklist, MultiselectPicklist, Text, TextArea, LongTextArea, Html, Time, Url
- `defaultCheckboxValue` (boolean, optional): Default value for checkbox fields
- `length` (string, required): Length for numeric/text fields
- `decimalPlace` (string, required): Decimal places for numeric fields
- `pickListValues` (string, required): Values for picklist fields (separated by new lines)
- `visibleLines` (string, required): Visible lines for multiselect/text area fields
- `description` (string, optional): Field description
- `helperText` (string, optional): Helper text shown on hover
- `defaultFieldValue` (string, optional): Default field value
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_TASK">
**Description:** Deploy custom fields for Task objects.
**Parameters:**
- `label` (string, required): Field Label for displays and internal reference
- `type` (string, required): Field Type - Options: Checkbox, Currency, Date, Email, Number, Percent, Phone, Picklist, MultiselectPicklist, Text, TextArea, Time, Url
- `defaultCheckboxValue` (boolean, optional): Default value for checkbox fields
- `length` (string, required): Length for numeric/text fields
- `decimalPlace` (string, required): Decimal places for numeric fields
- `pickListValues` (string, required): Values for picklist fields (separated by new lines)
- `visibleLines` (string, required): Visible lines for multiselect fields
- `description` (string, optional): Field description
- `helperText` (string, optional): Helper text shown on hover
- `defaultFieldValue` (string, optional): Default field value
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_ACCOUNT">
**Description:** Deploy custom fields for Account objects.
**Parameters:**
- `label` (string, required): Field Label for displays and internal reference
- `type` (string, required): Field Type - Options: Checkbox, Currency, Date, Email, Number, Percent, Phone, Picklist, MultiselectPicklist, Text, TextArea, LongTextArea, Html, Time, Url
- `defaultCheckboxValue` (boolean, optional): Default value for checkbox fields
- `length` (string, required): Length for numeric/text fields
- `decimalPlace` (string, required): Decimal places for numeric fields
- `pickListValues` (string, required): Values for picklist fields (separated by new lines)
- `visibleLines` (string, required): Visible lines for multiselect/text area fields
- `description` (string, optional): Field description
- `helperText` (string, optional): Helper text shown on hover
- `defaultFieldValue` (string, optional): Default field value
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_FIELD_ANY">
**Description:** Deploy custom fields for any object type.
**Note:** This is a flexible tool for creating custom fields on custom or unknown object types.
</Accordion>
</AccordionGroup>
### **Advanced Operations**
<AccordionGroup>
<Accordion title="SALESFORCE_WRITE_SOQL_QUERY">
**Description:** Execute custom SOQL queries against your Salesforce data.
**Parameters:**
- `query` (string, required): SOQL Query (e.g., "SELECT Id, Name FROM Account WHERE Name = 'Example'")
</Accordion>
<Accordion title="SALESFORCE_CREATE_CUSTOM_OBJECT">
**Description:** Deploy a new custom object in Salesforce.
**Parameters:**
- `label` (string, required): Object Label for tabs, page layouts, and reports
- `pluralLabel` (string, required): Plural Label (e.g., "Accounts")
- `description` (string, optional): A description of the Custom Object
- `recordName` (string, required): Record Name that appears in layouts and searches (e.g., "Account Name")
</Accordion>
<Accordion title="SALESFORCE_DESCRIBE_ACTION_SCHEMA">
**Description:** Get the expected schema for operations on specific object types.
**Parameters:**
- `recordType` (string, required): Record Type to describe
- `operation` (string, required): Operation Type (e.g., "CREATE_RECORD" or "UPDATE_RECORD")
**Note:** Use this function first when working with custom objects to understand their schema before performing operations.
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Salesforce Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Salesforce tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Salesforce capabilities
salesforce_agent = Agent(
role="CRM Manager",
goal="Manage customer relationships and sales processes efficiently",
backstory="An AI assistant specialized in CRM operations and sales automation.",
tools=[enterprise_tools]
)
# Task to create a new lead
create_lead_task = Task(
description="Create a new lead for John Doe from Example Corp with email john.doe@example.com",
agent=salesforce_agent,
expected_output="Lead created successfully with lead ID"
)
# Run the task
crew = Crew(
agents=[salesforce_agent],
tasks=[create_lead_task]
)
crew.kickoff()
```
### Filtering Specific Salesforce Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Salesforce tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["salesforce_create_record_lead", "salesforce_update_record_opportunity", "salesforce_search_records_contact"]
)
sales_manager = Agent(
role="Sales Manager",
goal="Manage leads and opportunities in the sales pipeline",
backstory="An experienced sales manager who handles lead qualification and opportunity management.",
tools=enterprise_tools
)
# Task to manage sales pipeline
pipeline_task = Task(
description="Create a qualified lead and convert it to an opportunity with $50,000 value",
agent=sales_manager,
expected_output="Lead created and opportunity established successfully"
)
crew = Crew(
agents=[sales_manager],
tasks=[pipeline_task]
)
crew.kickoff()
```
### Contact and Account Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
account_manager = Agent(
role="Account Manager",
goal="Manage customer accounts and maintain strong relationships",
backstory="An AI assistant that specializes in account management and customer relationship building.",
tools=[enterprise_tools]
)
# Task to manage customer accounts
account_task = Task(
description="""
1. Create a new account for TechCorp Inc.
2. Add John Doe as the primary contact for this account
3. Create a follow-up task for next week to check on their project status
""",
agent=account_manager,
expected_output="Account, contact, and follow-up task created successfully"
)
crew = Crew(
agents=[account_manager],
tasks=[account_task]
)
crew.kickoff()
```
### Advanced SOQL Queries and Reporting
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
data_analyst = Agent(
role="Sales Data Analyst",
goal="Generate insights from Salesforce data using SOQL queries",
backstory="An analytical AI that excels at extracting meaningful insights from CRM data.",
tools=[enterprise_tools]
)
# Complex task involving SOQL queries and data analysis
analysis_task = Task(
description="""
1. Execute a SOQL query to find all opportunities closing this quarter
2. Search for contacts at companies with opportunities over $100K
3. Create a summary report of the sales pipeline status
4. Update high-value opportunities with next steps
""",
agent=data_analyst,
expected_output="Comprehensive sales pipeline analysis with actionable insights"
)
crew = Crew(
agents=[data_analyst],
tasks=[analysis_task]
)
crew.kickoff()
```
This comprehensive documentation covers all the Salesforce tools organized by functionality, making it easy for users to find the specific operations they need for their CRM automation tasks.
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Salesforce integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,382 @@
---
title: Shopify Integration
description: "E-commerce and online store management with Shopify integration for CrewAI."
icon: "shopify"
---
## Overview
Enable your agents to manage e-commerce operations through Shopify. Handle customers, orders, products, inventory, and store analytics to streamline your online business with AI-powered automation.
## Prerequisites
Before using the Shopify integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Shopify store with appropriate admin permissions
- Connected your Shopify store through the [Integrations page](https://app.crewai.com/integrations)
## Available Tools
### **Customer Management**
<AccordionGroup>
<Accordion title="SHOPIFY_GET_CUSTOMERS">
**Description:** Retrieve a list of customers from your Shopify store.
**Parameters:**
- `customerIds` (string, optional): Comma-separated list of customer IDs to filter by (example: "207119551, 207119552")
- `createdAtMin` (string, optional): Only return customers created after this date (ISO or Unix timestamp)
- `createdAtMax` (string, optional): Only return customers created before this date (ISO or Unix timestamp)
- `updatedAtMin` (string, optional): Only return customers updated after this date (ISO or Unix timestamp)
- `updatedAtMax` (string, optional): Only return customers updated before this date (ISO or Unix timestamp)
- `limit` (string, optional): Maximum number of customers to return (defaults to 250)
</Accordion>
<Accordion title="SHOPIFY_SEARCH_CUSTOMERS">
**Description:** Search for customers using advanced filtering criteria.
**Parameters:**
- `filterFormula` (object, optional): Advanced filter in disjunctive normal form with field-specific operators
- `limit` (string, optional): Maximum number of customers to return (defaults to 250)
</Accordion>
<Accordion title="SHOPIFY_CREATE_CUSTOMER">
**Description:** Create a new customer in your Shopify store.
**Parameters:**
- `firstName` (string, required): Customer's first name
- `lastName` (string, required): Customer's last name
- `email` (string, required): Customer's email address
- `company` (string, optional): Company name
- `streetAddressLine1` (string, optional): Street address
- `streetAddressLine2` (string, optional): Street address line 2
- `city` (string, optional): City
- `state` (string, optional): State or province code
- `country` (string, optional): Country
- `zipCode` (string, optional): Zip code
- `phone` (string, optional): Phone number
- `tags` (string, optional): Tags as array or comma-separated list
- `note` (string, optional): Customer note
- `sendEmailInvite` (boolean, optional): Whether to send email invitation
- `metafields` (object, optional): Additional metafields in JSON format
</Accordion>
<Accordion title="SHOPIFY_UPDATE_CUSTOMER">
**Description:** Update an existing customer in your Shopify store.
**Parameters:**
- `customerId` (string, required): The ID of the customer to update
- `firstName` (string, optional): Customer's first name
- `lastName` (string, optional): Customer's last name
- `email` (string, optional): Customer's email address
- `company` (string, optional): Company name
- `streetAddressLine1` (string, optional): Street address
- `streetAddressLine2` (string, optional): Street address line 2
- `city` (string, optional): City
- `state` (string, optional): State or province code
- `country` (string, optional): Country
- `zipCode` (string, optional): Zip code
- `phone` (string, optional): Phone number
- `tags` (string, optional): Tags as array or comma-separated list
- `note` (string, optional): Customer note
- `sendEmailInvite` (boolean, optional): Whether to send email invitation
- `metafields` (object, optional): Additional metafields in JSON format
</Accordion>
</AccordionGroup>
### **Order Management**
<AccordionGroup>
<Accordion title="SHOPIFY_GET_ORDERS">
**Description:** Retrieve a list of orders from your Shopify store.
**Parameters:**
- `orderIds` (string, optional): Comma-separated list of order IDs to filter by (example: "450789469, 450789470")
- `createdAtMin` (string, optional): Only return orders created after this date (ISO or Unix timestamp)
- `createdAtMax` (string, optional): Only return orders created before this date (ISO or Unix timestamp)
- `updatedAtMin` (string, optional): Only return orders updated after this date (ISO or Unix timestamp)
- `updatedAtMax` (string, optional): Only return orders updated before this date (ISO or Unix timestamp)
- `limit` (string, optional): Maximum number of orders to return (defaults to 250)
</Accordion>
<Accordion title="SHOPIFY_CREATE_ORDER">
**Description:** Create a new order in your Shopify store.
**Parameters:**
- `email` (string, required): Customer email address
- `lineItems` (object, required): Order line items in JSON format with title, price, quantity, and variant_id
- `sendReceipt` (boolean, optional): Whether to send order receipt
- `fulfillmentStatus` (string, optional): Fulfillment status - Options: fulfilled, null, partial, restocked
- `financialStatus` (string, optional): Financial status - Options: pending, authorized, partially_paid, paid, partially_refunded, refunded, voided
- `inventoryBehaviour` (string, optional): Inventory behavior - Options: bypass, decrement_ignoring_policy, decrement_obeying_policy
- `note` (string, optional): Order note
</Accordion>
<Accordion title="SHOPIFY_UPDATE_ORDER">
**Description:** Update an existing order in your Shopify store.
**Parameters:**
- `orderId` (string, required): The ID of the order to update
- `email` (string, optional): Customer email address
- `lineItems` (object, optional): Updated order line items in JSON format
- `sendReceipt` (boolean, optional): Whether to send order receipt
- `fulfillmentStatus` (string, optional): Fulfillment status - Options: fulfilled, null, partial, restocked
- `financialStatus` (string, optional): Financial status - Options: pending, authorized, partially_paid, paid, partially_refunded, refunded, voided
- `inventoryBehaviour` (string, optional): Inventory behavior - Options: bypass, decrement_ignoring_policy, decrement_obeying_policy
- `note` (string, optional): Order note
</Accordion>
<Accordion title="SHOPIFY_GET_ABANDONED_CARTS">
**Description:** Retrieve abandoned carts from your Shopify store.
**Parameters:**
- `createdWithInLast` (string, optional): Restrict results to checkouts created within specified time
- `createdAfterId` (string, optional): Restrict results to after the specified ID
- `status` (string, optional): Show checkouts with given status - Options: open, closed (defaults to open)
- `createdAtMin` (string, optional): Only return carts created after this date (ISO or Unix timestamp)
- `createdAtMax` (string, optional): Only return carts created before this date (ISO or Unix timestamp)
- `limit` (string, optional): Maximum number of carts to return (defaults to 250)
</Accordion>
</AccordionGroup>
### **Product Management (REST API)**
<AccordionGroup>
<Accordion title="SHOPIFY_GET_PRODUCTS">
**Description:** Retrieve a list of products from your Shopify store using REST API.
**Parameters:**
- `productIds` (string, optional): Comma-separated list of product IDs to filter by (example: "632910392, 632910393")
- `title` (string, optional): Filter by product title
- `productType` (string, optional): Filter by product type
- `vendor` (string, optional): Filter by vendor
- `status` (string, optional): Filter by status - Options: active, archived, draft
- `createdAtMin` (string, optional): Only return products created after this date (ISO or Unix timestamp)
- `createdAtMax` (string, optional): Only return products created before this date (ISO or Unix timestamp)
- `updatedAtMin` (string, optional): Only return products updated after this date (ISO or Unix timestamp)
- `updatedAtMax` (string, optional): Only return products updated before this date (ISO or Unix timestamp)
- `limit` (string, optional): Maximum number of products to return (defaults to 250)
</Accordion>
<Accordion title="SHOPIFY_CREATE_PRODUCT">
**Description:** Create a new product in your Shopify store using REST API.
**Parameters:**
- `title` (string, required): Product title
- `productType` (string, required): Product type/category
- `vendor` (string, required): Product vendor
- `productDescription` (string, optional): Product description (accepts plain text or HTML)
- `tags` (string, optional): Product tags as array or comma-separated list
- `price` (string, optional): Product price
- `inventoryPolicy` (string, optional): Inventory policy - Options: deny, continue
- `imageUrl` (string, optional): Product image URL
- `isPublished` (boolean, optional): Whether product is published
- `publishToPointToSale` (boolean, optional): Whether to publish to point of sale
</Accordion>
<Accordion title="SHOPIFY_UPDATE_PRODUCT">
**Description:** Update an existing product in your Shopify store using REST API.
**Parameters:**
- `productId` (string, required): The ID of the product to update
- `title` (string, optional): Product title
- `productType` (string, optional): Product type/category
- `vendor` (string, optional): Product vendor
- `productDescription` (string, optional): Product description (accepts plain text or HTML)
- `tags` (string, optional): Product tags as array or comma-separated list
- `price` (string, optional): Product price
- `inventoryPolicy` (string, optional): Inventory policy - Options: deny, continue
- `imageUrl` (string, optional): Product image URL
- `isPublished` (boolean, optional): Whether product is published
- `publishToPointToSale` (boolean, optional): Whether to publish to point of sale
</Accordion>
</AccordionGroup>
### **Product Management (GraphQL)**
<AccordionGroup>
<Accordion title="SHOPIFY_GET_PRODUCTS_GRAPHQL">
**Description:** Retrieve products using advanced GraphQL filtering capabilities.
**Parameters:**
- `productFilterFormula` (object, optional): Advanced filter in disjunctive normal form with support for fields like id, title, vendor, status, handle, tag, created_at, updated_at, published_at
</Accordion>
<Accordion title="SHOPIFY_CREATE_PRODUCT_GRAPHQL">
**Description:** Create a new product using GraphQL API with enhanced media support.
**Parameters:**
- `title` (string, required): Product title
- `productType` (string, required): Product type/category
- `vendor` (string, required): Product vendor
- `productDescription` (string, optional): Product description (accepts plain text or HTML)
- `tags` (string, optional): Product tags as array or comma-separated list
- `media` (object, optional): Media objects with alt text, content type, and source URL
- `additionalFields` (object, optional): Additional product fields like status, requiresSellingPlan, giftCard
</Accordion>
<Accordion title="SHOPIFY_UPDATE_PRODUCT_GRAPHQL">
**Description:** Update an existing product using GraphQL API with enhanced media support.
**Parameters:**
- `productId` (string, required): The GraphQL ID of the product to update (e.g., "gid://shopify/Product/913144112")
- `title` (string, optional): Product title
- `productType` (string, optional): Product type/category
- `vendor` (string, optional): Product vendor
- `productDescription` (string, optional): Product description (accepts plain text or HTML)
- `tags` (string, optional): Product tags as array or comma-separated list
- `media` (object, optional): Updated media objects with alt text, content type, and source URL
- `additionalFields` (object, optional): Additional product fields like status, requiresSellingPlan, giftCard
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Shopify Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Shopify tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Shopify capabilities
shopify_agent = Agent(
role="E-commerce Manager",
goal="Manage online store operations and customer relationships efficiently",
backstory="An AI assistant specialized in e-commerce operations and online store management.",
tools=[enterprise_tools]
)
# Task to create a new customer
create_customer_task = Task(
description="Create a new VIP customer Jane Smith with email jane.smith@example.com and phone +1-555-0123",
agent=shopify_agent,
expected_output="Customer created successfully with customer ID"
)
# Run the task
crew = Crew(
agents=[shopify_agent],
tasks=[create_customer_task]
)
crew.kickoff()
```
### Filtering Specific Shopify Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Shopify tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["shopify_create_customer", "shopify_create_order", "shopify_get_products"]
)
store_manager = Agent(
role="Store Manager",
goal="Manage customer orders and product catalog",
backstory="An experienced store manager who handles customer relationships and inventory management.",
tools=enterprise_tools
)
# Task to manage store operations
store_task = Task(
description="Create a new customer and process their order for 2 Premium Coffee Mugs",
agent=store_manager,
expected_output="Customer created and order processed successfully"
)
crew = Crew(
agents=[store_manager],
tasks=[store_task]
)
crew.kickoff()
```
### Product Management with GraphQL
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
product_manager = Agent(
role="Product Manager",
goal="Manage product catalog and inventory with advanced GraphQL capabilities",
backstory="An AI assistant that specializes in product management and catalog optimization.",
tools=[enterprise_tools]
)
# Task to manage product catalog
catalog_task = Task(
description="""
1. Create a new product "Premium Coffee Mug" from Coffee Co vendor
2. Add high-quality product images and descriptions
3. Search for similar products from the same vendor
4. Update product tags and pricing strategy
""",
agent=product_manager,
expected_output="Product created and catalog optimized successfully"
)
crew = Crew(
agents=[product_manager],
tasks=[catalog_task]
)
crew.kickoff()
```
### Order and Customer Analytics
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
analytics_agent = Agent(
role="E-commerce Analyst",
goal="Analyze customer behavior and order patterns to optimize store performance",
backstory="An analytical AI that excels at extracting insights from e-commerce data.",
tools=[enterprise_tools]
)
# Complex task involving multiple operations
analytics_task = Task(
description="""
1. Retrieve recent customer data and order history
2. Identify abandoned carts from the last 7 days
3. Analyze product performance and inventory levels
4. Generate recommendations for customer retention
""",
agent=analytics_agent,
expected_output="Comprehensive e-commerce analytics report with actionable insights"
)
crew = Crew(
agents=[analytics_agent],
tasks=[analytics_task]
)
crew.kickoff()
```
### Getting Help
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Shopify integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,293 @@
---
title: Slack Integration
description: "Team communication and collaboration with Slack integration for CrewAI."
icon: "slack"
---
## Overview
Enable your agents to manage team communication through Slack. Send messages, search conversations, manage channels, and coordinate team activities to streamline your collaboration workflows with AI-powered automation.
## Prerequisites
Before using the Slack integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Slack workspace with appropriate permissions
- Connected your Slack workspace through the [Integrations page](https://app.crewai.com/integrations)
## Available Tools
### **User Management**
<AccordionGroup>
<Accordion title="SLACK_LIST_MEMBERS">
**Description:** List all members in a Slack channel.
**Parameters:**
- No parameters required - retrieves all channel members
</Accordion>
<Accordion title="SLACK_GET_USER_BY_EMAIL">
**Description:** Find a user in your Slack workspace by their email address.
**Parameters:**
- `email` (string, required): The email address of a user in the workspace
</Accordion>
<Accordion title="SLACK_GET_USERS_BY_NAME">
**Description:** Search for users by their name or display name.
**Parameters:**
- `name` (string, required): User's real name to search for
- `displayName` (string, required): User's display name to search for
- `paginationParameters` (object, optional): Pagination settings
- `pageCursor` (string, optional): Page cursor for pagination
</Accordion>
</AccordionGroup>
### **Channel Management**
<AccordionGroup>
<Accordion title="SLACK_LIST_CHANNELS">
**Description:** List all channels in your Slack workspace.
**Parameters:**
- No parameters required - retrieves all accessible channels
</Accordion>
</AccordionGroup>
### **Messaging**
<AccordionGroup>
<Accordion title="SLACK_SEND_MESSAGE">
**Description:** Send a message to a Slack channel.
**Parameters:**
- `channel` (string, required): Channel name or ID - Use Connect Portal Workflow Settings to allow users to select a channel, or enter a channel name to create a new channel
- `message` (string, required): The message text to send
- `botName` (string, required): The name of the bot that sends this message
- `botIcon` (string, required): Bot icon - Can be either an image URL or an emoji (e.g., ":dog:")
- `blocks` (object, optional): Slack Block Kit JSON for rich message formatting with attachments and interactive elements
- `authenticatedUser` (boolean, optional): If true, message appears to come from your authenticated Slack user instead of the application (defaults to false)
</Accordion>
<Accordion title="SLACK_SEND_DIRECT_MESSAGE">
**Description:** Send a direct message to a specific user in Slack.
**Parameters:**
- `memberId` (string, required): Recipient user ID - Use Connect Portal Workflow Settings to allow users to select a workspace member
- `message` (string, required): The message text to send
- `botName` (string, required): The name of the bot that sends this message
- `botIcon` (string, required): Bot icon - Can be either an image URL or an emoji (e.g., ":dog:")
- `blocks` (object, optional): Slack Block Kit JSON for rich message formatting with attachments and interactive elements
- `authenticatedUser` (boolean, optional): If true, message appears to come from your authenticated Slack user instead of the application (defaults to false)
</Accordion>
</AccordionGroup>
### **Search & Discovery**
<AccordionGroup>
<Accordion title="SLACK_SEARCH_MESSAGES">
**Description:** Search for messages across your Slack workspace.
**Parameters:**
- `query` (string, required): Search query using Slack search syntax to find messages that match specified criteria
**Search Query Examples:**
- `"project update"` - Search for messages containing "project update"
- `from:@john in:#general` - Search for messages from John in the #general channel
- `has:link after:2023-01-01` - Search for messages with links after January 1, 2023
- `in:@channel before:yesterday` - Search for messages in a specific channel before yesterday
</Accordion>
</AccordionGroup>
## Block Kit Integration
Slack's Block Kit allows you to create rich, interactive messages. Here are some examples of how to use the `blocks` parameter:
### Simple Text with Attachment
```json
[
{
"text": "I am a test message",
"attachments": [
{
"text": "And here's an attachment!"
}
]
}
]
```
### Rich Formatting with Sections
```json
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Project Update*\nStatus: ✅ Complete"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "All tasks have been completed successfully."
}
}
]
```
## Usage Examples
### Basic Slack Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Slack tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Slack capabilities
slack_agent = Agent(
role="Team Communication Manager",
goal="Facilitate team communication and coordinate collaboration efficiently",
backstory="An AI assistant specialized in team communication and workspace coordination.",
tools=[enterprise_tools]
)
# Task to send project updates
update_task = Task(
description="Send a project status update to the #general channel with current progress",
agent=slack_agent,
expected_output="Project update message sent successfully to team channel"
)
# Run the task
crew = Crew(
agents=[slack_agent],
tasks=[update_task]
)
crew.kickoff()
```
### Filtering Specific Slack Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Slack tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["slack_send_message", "slack_send_direct_message", "slack_search_messages"]
)
communication_manager = Agent(
role="Communication Coordinator",
goal="Manage team communications and ensure important messages reach the right people",
backstory="An experienced communication coordinator who handles team messaging and notifications.",
tools=enterprise_tools
)
# Task to coordinate team communication
coordination_task = Task(
description="Send task completion notifications to team members and update project channels",
agent=communication_manager,
expected_output="Team notifications sent and project channels updated successfully"
)
crew = Crew(
agents=[communication_manager],
tasks=[coordination_task]
)
crew.kickoff()
```
### Advanced Messaging with Block Kit
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
notification_agent = Agent(
role="Notification Manager",
goal="Create rich, interactive notifications and manage workspace communication",
backstory="An AI assistant that specializes in creating engaging team notifications and updates.",
tools=[enterprise_tools]
)
# Task to send rich notifications
notification_task = Task(
description="""
1. Send a formatted project completion message to #general with progress charts
2. Send direct messages to team leads with task summaries
3. Create interactive notification with action buttons for team feedback
""",
agent=notification_agent,
expected_output="Rich notifications sent with interactive elements and formatted content"
)
crew = Crew(
agents=[notification_agent],
tasks=[notification_task]
)
crew.kickoff()
```
### Message Search and Analytics
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
analytics_agent = Agent(
role="Communication Analyst",
goal="Analyze team communication patterns and extract insights from conversations",
backstory="An analytical AI that excels at understanding team dynamics through communication data.",
tools=[enterprise_tools]
)
# Complex task involving search and analysis
analysis_task = Task(
description="""
1. Search for recent project-related messages across all channels
2. Find users by email to identify team members
3. Analyze communication patterns and response times
4. Generate weekly team communication summary
""",
agent=analytics_agent,
expected_output="Comprehensive communication analysis with team insights and recommendations"
)
crew = Crew(
agents=[analytics_agent],
tasks=[analysis_task]
)
crew.kickoff()
```
## Contact Support
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with Slack integration setup or troubleshooting.
</Card>

View File

@@ -0,0 +1,305 @@
---
title: Stripe Integration
description: "Payment processing and subscription management with Stripe integration for CrewAI."
icon: "stripe"
---
## Overview
Enable your agents to manage payments, subscriptions, and customer billing through Stripe. Handle customer data, process subscriptions, manage products, and track financial transactions to streamline your payment workflows with AI-powered automation.
## Prerequisites
Before using the Stripe integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Stripe account with appropriate API permissions
- Connected your Stripe account through the [Integrations page](https://app.crewai.com/integrations)
## Available Tools
### **Customer Management**
<AccordionGroup>
<Accordion title="STRIPE_CREATE_CUSTOMER">
**Description:** Create a new customer in your Stripe account.
**Parameters:**
- `emailCreateCustomer` (string, required): Customer's email address
- `name` (string, optional): Customer's full name
- `description` (string, optional): Customer description for internal reference
- `metadataCreateCustomer` (object, optional): Additional metadata as key-value pairs (e.g., `{"field1": 1, "field2": 2}`)
</Accordion>
<Accordion title="STRIPE_GET_CUSTOMER_BY_ID">
**Description:** Retrieve a specific customer by their Stripe customer ID.
**Parameters:**
- `idGetCustomer` (string, required): The Stripe customer ID to retrieve
</Accordion>
<Accordion title="STRIPE_GET_CUSTOMERS">
**Description:** Retrieve a list of customers with optional filtering.
**Parameters:**
- `emailGetCustomers` (string, optional): Filter customers by email address
- `createdAfter` (string, optional): Filter customers created after this date (Unix timestamp)
- `createdBefore` (string, optional): Filter customers created before this date (Unix timestamp)
- `limitGetCustomers` (string, optional): Maximum number of customers to return (defaults to 10)
</Accordion>
<Accordion title="STRIPE_UPDATE_CUSTOMER">
**Description:** Update an existing customer's information.
**Parameters:**
- `customerId` (string, required): The ID of the customer to update
- `emailUpdateCustomer` (string, optional): Updated email address
- `name` (string, optional): Updated customer name
- `description` (string, optional): Updated customer description
- `metadataUpdateCustomer` (object, optional): Updated metadata as key-value pairs
</Accordion>
</AccordionGroup>
### **Subscription Management**
<AccordionGroup>
<Accordion title="STRIPE_CREATE_SUBSCRIPTION">
**Description:** Create a new subscription for a customer.
**Parameters:**
- `customerIdCreateSubscription` (string, required): The customer ID for whom the subscription will be created
- `plan` (string, required): The plan ID for the subscription - Use Connect Portal Workflow Settings to allow users to select a plan
- `metadataCreateSubscription` (object, optional): Additional metadata for the subscription
</Accordion>
<Accordion title="STRIPE_GET_SUBSCRIPTIONS">
**Description:** Retrieve subscriptions with optional filtering.
**Parameters:**
- `customerIdGetSubscriptions` (string, optional): Filter subscriptions by customer ID
- `subscriptionStatus` (string, optional): Filter by subscription status - Options: incomplete, incomplete_expired, trialing, active, past_due, canceled, unpaid
- `limitGetSubscriptions` (string, optional): Maximum number of subscriptions to return (defaults to 10)
</Accordion>
</AccordionGroup>
### **Product Management**
<AccordionGroup>
<Accordion title="STRIPE_CREATE_PRODUCT">
**Description:** Create a new product in your Stripe catalog.
**Parameters:**
- `productName` (string, required): The product name
- `description` (string, optional): Product description
- `metadataProduct` (object, optional): Additional product metadata as key-value pairs
</Accordion>
<Accordion title="STRIPE_GET_PRODUCT_BY_ID">
**Description:** Retrieve a specific product by its Stripe product ID.
**Parameters:**
- `productId` (string, required): The Stripe product ID to retrieve
</Accordion>
<Accordion title="STRIPE_GET_PRODUCTS">
**Description:** Retrieve a list of products with optional filtering.
**Parameters:**
- `createdAfter` (string, optional): Filter products created after this date (Unix timestamp)
- `createdBefore` (string, optional): Filter products created before this date (Unix timestamp)
- `limitGetProducts` (string, optional): Maximum number of products to return (defaults to 10)
</Accordion>
</AccordionGroup>
### **Financial Operations**
<AccordionGroup>
<Accordion title="STRIPE_GET_BALANCE_TRANSACTIONS">
**Description:** Retrieve balance transactions from your Stripe account.
**Parameters:**
- `balanceTransactionType` (string, optional): Filter by transaction type - Options: charge, refund, payment, payment_refund
- `paginationParameters` (object, optional): Pagination settings
- `pageCursor` (string, optional): Page cursor for pagination
</Accordion>
<Accordion title="STRIPE_GET_PLANS">
**Description:** Retrieve subscription plans from your Stripe account.
**Parameters:**
- `isPlanActive` (boolean, optional): Filter by plan status - true for active plans, false for inactive plans
- `paginationParameters` (object, optional): Pagination settings
- `pageCursor` (string, optional): Page cursor for pagination
</Accordion>
</AccordionGroup>
## Usage Examples
### Basic Stripe Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Stripe tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Stripe capabilities
stripe_agent = Agent(
role="Payment Manager",
goal="Manage customer payments, subscriptions, and billing operations efficiently",
backstory="An AI assistant specialized in payment processing and subscription management.",
tools=[enterprise_tools]
)
# Task to create a new customer
create_customer_task = Task(
description="Create a new premium customer John Doe with email john.doe@example.com",
agent=stripe_agent,
expected_output="Customer created successfully with customer ID"
)
# Run the task
crew = Crew(
agents=[stripe_agent],
tasks=[create_customer_task]
)
crew.kickoff()
```
### Filtering Specific Stripe Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Stripe tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["stripe_create_customer", "stripe_create_subscription", "stripe_get_balance_transactions"]
)
billing_manager = Agent(
role="Billing Manager",
goal="Handle customer billing, subscriptions, and payment processing",
backstory="An experienced billing manager who handles subscription lifecycle and payment operations.",
tools=enterprise_tools
)
# Task to manage billing operations
billing_task = Task(
description="Create a new customer and set up their premium subscription plan",
agent=billing_manager,
expected_output="Customer created and subscription activated successfully"
)
crew = Crew(
agents=[billing_manager],
tasks=[billing_task]
)
crew.kickoff()
```
### Subscription Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
subscription_manager = Agent(
role="Subscription Manager",
goal="Manage customer subscriptions and optimize recurring revenue",
backstory="An AI assistant that specializes in subscription lifecycle management and customer retention.",
tools=[enterprise_tools]
)
# Task to manage subscription operations
subscription_task = Task(
description="""
1. Create a new product "Premium Service Plan" with advanced features
2. Set up subscription plans with different tiers
3. Create customers and assign them to appropriate plans
4. Monitor subscription status and handle billing issues
""",
agent=subscription_manager,
expected_output="Subscription management system configured with customers and active plans"
)
crew = Crew(
agents=[subscription_manager],
tasks=[subscription_task]
)
crew.kickoff()
```
### Financial Analytics and Reporting
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
financial_analyst = Agent(
role="Financial Analyst",
goal="Analyze payment data and generate financial insights",
backstory="An analytical AI that excels at extracting insights from payment and subscription data.",
tools=[enterprise_tools]
)
# Complex task involving financial analysis
analytics_task = Task(
description="""
1. Retrieve balance transactions for the current month
2. Analyze customer payment patterns and subscription trends
3. Identify high-value customers and subscription performance
4. Generate monthly financial performance report
""",
agent=financial_analyst,
expected_output="Comprehensive financial analysis with payment insights and recommendations"
)
crew = Crew(
agents=[financial_analyst],
tasks=[analytics_task]
)
crew.kickoff()
```
## Subscription Status Reference
Understanding subscription statuses:
- **incomplete** - Subscription requires payment method or payment confirmation
- **incomplete_expired** - Subscription expired before payment was confirmed
- **trialing** - Subscription is in trial period
- **active** - Subscription is active and current
- **past_due** - Payment failed but subscription is still active
- **canceled** - Subscription has been canceled
- **unpaid** - Payment failed and subscription is no longer active
## Metadata Usage
Metadata allows you to store additional information about customers, subscriptions, and products:
```json
{
"customer_segment": "enterprise",
"acquisition_source": "google_ads",
"lifetime_value": "high",
"custom_field_1": "value1"
}
```
This integration enables comprehensive payment and subscription management automation, allowing your AI agents to handle billing operations seamlessly within your Stripe ecosystem.

View File

@@ -0,0 +1,343 @@
---
title: Zendesk Integration
description: "Customer support and helpdesk management with Zendesk integration for CrewAI."
icon: "headset"
---
## Overview
Enable your agents to manage customer support operations through Zendesk. Create and update tickets, manage users, track support metrics, and streamline your customer service workflows with AI-powered automation.
## Prerequisites
Before using the Zendesk integration, ensure you have:
- A [CrewAI Enterprise](https://app.crewai.com) account with an active subscription
- A Zendesk account with appropriate API permissions
- Connected your Zendesk account through the [Integrations page](https://app.crewai.com/integrations)
## Available Tools
### **Ticket Management**
<AccordionGroup>
<Accordion title="ZENDESK_CREATE_TICKET">
**Description:** Create a new support ticket in Zendesk.
**Parameters:**
- `ticketSubject` (string, required): Ticket subject line (e.g., "Help, my printer is on fire!")
- `ticketDescription` (string, required): First comment that appears on the ticket (e.g., "The smoke is very colorful.")
- `requesterName` (string, required): Name of the user requesting support (e.g., "Jane Customer")
- `requesterEmail` (string, required): Email of the user requesting support (e.g., "jane@example.com")
- `assigneeId` (string, optional): Zendesk Agent ID assigned to this ticket - Use Connect Portal Workflow Settings to allow users to select an assignee
- `ticketType` (string, optional): Ticket type - Options: problem, incident, question, task
- `ticketPriority` (string, optional): Priority level - Options: urgent, high, normal, low
- `ticketStatus` (string, optional): Ticket status - Options: new, open, pending, hold, solved, closed
- `ticketDueAt` (string, optional): Due date for task-type tickets (ISO 8601 timestamp)
- `ticketTags` (string, optional): Array of tags to apply (e.g., `["enterprise", "other_tag"]`)
- `ticketExternalId` (string, optional): External ID to link tickets to local records
- `ticketCustomFields` (object, optional): Custom field values in JSON format
</Accordion>
<Accordion title="ZENDESK_UPDATE_TICKET">
**Description:** Update an existing support ticket in Zendesk.
**Parameters:**
- `ticketId` (string, required): ID of the ticket to update (e.g., "35436")
- `ticketSubject` (string, optional): Updated ticket subject
- `requesterName` (string, required): Name of the user who requested this ticket
- `requesterEmail` (string, required): Email of the user who requested this ticket
- `assigneeId` (string, optional): Updated assignee ID - Use Connect Portal Workflow Settings
- `ticketType` (string, optional): Updated ticket type - Options: problem, incident, question, task
- `ticketPriority` (string, optional): Updated priority - Options: urgent, high, normal, low
- `ticketStatus` (string, optional): Updated status - Options: new, open, pending, hold, solved, closed
- `ticketDueAt` (string, optional): Updated due date (ISO 8601 timestamp)
- `ticketTags` (string, optional): Updated tags array
- `ticketExternalId` (string, optional): Updated external ID
- `ticketCustomFields` (object, optional): Updated custom field values
</Accordion>
<Accordion title="ZENDESK_GET_TICKET_BY_ID">
**Description:** Retrieve a specific ticket by its ID.
**Parameters:**
- `ticketId` (string, required): The ticket ID to retrieve (e.g., "35436")
</Accordion>
<Accordion title="ZENDESK_ADD_COMMENT_TO_TICKET">
**Description:** Add a comment or internal note to an existing ticket.
**Parameters:**
- `ticketId` (string, required): ID of the ticket to add comment to (e.g., "35436")
- `commentBody` (string, required): Comment message (accepts plain text or HTML, e.g., "Thanks for your help!")
- `isInternalNote` (boolean, optional): Set to true for internal notes instead of public replies (defaults to false)
- `isPublic` (boolean, optional): True for public comments, false for internal notes
</Accordion>
<Accordion title="ZENDESK_SEARCH_TICKETS">
**Description:** Search for tickets using various filters and criteria.
**Parameters:**
- `ticketSubject` (string, optional): Filter by text in ticket subject
- `ticketDescription` (string, optional): Filter by text in ticket description and comments
- `ticketStatus` (string, optional): Filter by status - Options: new, open, pending, hold, solved, closed
- `ticketType` (string, optional): Filter by type - Options: problem, incident, question, task, no_type
- `ticketPriority` (string, optional): Filter by priority - Options: urgent, high, normal, low, no_priority
- `requesterId` (string, optional): Filter by requester user ID
- `assigneeId` (string, optional): Filter by assigned agent ID
- `recipientEmail` (string, optional): Filter by original recipient email address
- `ticketTags` (string, optional): Filter by ticket tags
- `ticketExternalId` (string, optional): Filter by external ID
- `createdDate` (object, optional): Filter by creation date with operator (EQUALS, LESS_THAN_EQUALS, GREATER_THAN_EQUALS) and value
- `updatedDate` (object, optional): Filter by update date with operator and value
- `dueDate` (object, optional): Filter by due date with operator and value
- `sort_by` (string, optional): Sort field - Options: created_at, updated_at, priority, status, ticket_type
- `sort_order` (string, optional): Sort direction - Options: asc, desc
</Accordion>
</AccordionGroup>
### **User Management**
<AccordionGroup>
<Accordion title="ZENDESK_CREATE_USER">
**Description:** Create a new user in Zendesk.
**Parameters:**
- `name` (string, required): User's full name
- `email` (string, optional): User's email address (e.g., "jane@example.com")
- `phone` (string, optional): User's phone number
- `role` (string, optional): User role - Options: admin, agent, end-user
- `externalId` (string, optional): Unique identifier from another system
- `details` (string, optional): Additional user details
- `notes` (string, optional): Internal notes about the user
</Accordion>
<Accordion title="ZENDESK_UPDATE_USER">
**Description:** Update an existing user's information.
**Parameters:**
- `userId` (string, required): ID of the user to update
- `name` (string, optional): Updated user name
- `email` (string, optional): Updated email (adds as secondary email on update)
- `phone` (string, optional): Updated phone number
- `role` (string, optional): Updated role - Options: admin, agent, end-user
- `externalId` (string, optional): Updated external ID
- `details` (string, optional): Updated user details
- `notes` (string, optional): Updated internal notes
</Accordion>
<Accordion title="ZENDESK_GET_USER_BY_ID">
**Description:** Retrieve a specific user by their ID.
**Parameters:**
- `userId` (string, required): The user ID to retrieve
</Accordion>
<Accordion title="ZENDESK_SEARCH_USERS">
**Description:** Search for users using various criteria.
**Parameters:**
- `name` (string, optional): Filter by user name
- `email` (string, optional): Filter by user email (e.g., "jane@example.com")
- `role` (string, optional): Filter by role - Options: admin, agent, end-user
- `externalId` (string, optional): Filter by external ID
- `sort_by` (string, optional): Sort field - Options: created_at, updated_at
- `sort_order` (string, optional): Sort direction - Options: asc, desc
</Accordion>
</AccordionGroup>
### **Administrative Tools**
<AccordionGroup>
<Accordion title="ZENDESK_GET_TICKET_FIELDS">
**Description:** Retrieve all standard and custom fields available for tickets.
**Parameters:**
- `paginationParameters` (object, optional): Pagination settings
- `pageCursor` (string, optional): Page cursor for pagination
</Accordion>
<Accordion title="ZENDESK_GET_TICKET_AUDITS">
**Description:** Get audit records (read-only history) for tickets.
**Parameters:**
- `ticketId` (string, optional): Get audits for specific ticket (if empty, retrieves audits for all non-archived tickets, e.g., "1234")
- `paginationParameters` (object, optional): Pagination settings
- `pageCursor` (string, optional): Page cursor for pagination
</Accordion>
</AccordionGroup>
## Custom Fields
Custom fields allow you to store additional information specific to your organization:
```json
[
{ "id": 27642, "value": "745" },
{ "id": 27648, "value": "yes" }
]
```
## Ticket Priority Levels
Understanding priority levels:
- **urgent** - Critical issues requiring immediate attention
- **high** - Important issues that should be addressed quickly
- **normal** - Standard priority for most tickets
- **low** - Minor issues that can be addressed when convenient
## Ticket Status Workflow
Standard ticket status progression:
- **new** - Recently created, not yet assigned
- **open** - Actively being worked on
- **pending** - Waiting for customer response or external action
- **hold** - Temporarily paused
- **solved** - Issue resolved, awaiting customer confirmation
- **closed** - Ticket completed and closed
## Usage Examples
### Basic Zendesk Agent Setup
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
# Get enterprise tools (Zendesk tools will be included)
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
# Create an agent with Zendesk capabilities
zendesk_agent = Agent(
role="Support Manager",
goal="Manage customer support tickets and provide excellent customer service",
backstory="An AI assistant specialized in customer support operations and ticket management.",
tools=[enterprise_tools]
)
# Task to create a new support ticket
create_ticket_task = Task(
description="Create a high-priority support ticket for John Smith who is unable to access his account after password reset",
agent=zendesk_agent,
expected_output="Support ticket created successfully with ticket ID"
)
# Run the task
crew = Crew(
agents=[zendesk_agent],
tasks=[create_ticket_task]
)
crew.kickoff()
```
### Filtering Specific Zendesk Tools
```python
from crewai_tools import CrewaiEnterpriseTools
# Get only specific Zendesk tools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token",
actions_list=["zendesk_create_ticket", "zendesk_update_ticket", "zendesk_add_comment_to_ticket"]
)
support_agent = Agent(
role="Customer Support Agent",
goal="Handle customer inquiries and resolve support issues efficiently",
backstory="An experienced support agent who specializes in ticket resolution and customer communication.",
tools=enterprise_tools
)
# Task to manage support workflow
support_task = Task(
description="Create a ticket for login issues, add troubleshooting comments, and update status to resolved",
agent=support_agent,
expected_output="Support ticket managed through complete resolution workflow"
)
crew = Crew(
agents=[support_agent],
tasks=[support_task]
)
crew.kickoff()
```
### Advanced Ticket Management
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
ticket_manager = Agent(
role="Ticket Manager",
goal="Manage support ticket workflows and ensure timely resolution",
backstory="An AI assistant that specializes in support ticket triage and workflow optimization.",
tools=[enterprise_tools]
)
# Task to manage ticket lifecycle
ticket_workflow = Task(
description="""
1. Create a new support ticket for account access issues
2. Add internal notes with troubleshooting steps
3. Update ticket priority based on customer tier
4. Add resolution comments and close the ticket
""",
agent=ticket_manager,
expected_output="Complete ticket lifecycle managed from creation to resolution"
)
crew = Crew(
agents=[ticket_manager],
tasks=[ticket_workflow]
)
crew.kickoff()
```
### Support Analytics and Reporting
```python
from crewai import Agent, Task, Crew
from crewai_tools import CrewaiEnterpriseTools
enterprise_tools = CrewaiEnterpriseTools(
enterprise_token="your_enterprise_token"
)
support_analyst = Agent(
role="Support Analyst",
goal="Analyze support metrics and generate insights for team performance",
backstory="An analytical AI that excels at extracting insights from support data and ticket patterns.",
tools=[enterprise_tools]
)
# Complex task involving analytics and reporting
analytics_task = Task(
description="""
1. Search for all open tickets from the last 30 days
2. Analyze ticket resolution times and customer satisfaction
3. Identify common issues and support patterns
4. Generate weekly support performance report
""",
agent=support_analyst,
expected_output="Comprehensive support analytics report with performance insights and recommendations"
)
crew = Crew(
agents=[support_analyst],
tasks=[analytics_task]
)
crew.kickoff()
```

View File

@@ -277,22 +277,23 @@ This pattern allows you to combine direct data passing with state updates for ma
One of CrewAI's most powerful features is the ability to persist flow state across executions. This enables workflows that can be paused, resumed, and even recovered after failures.
### The @persist Decorator
### The @persist() Decorator
The `@persist` decorator automates state persistence, saving your flow's state at key points in execution.
The `@persist()` decorator automates state persistence, saving your flow's state at key points in execution.
#### Class-Level Persistence
When applied at the class level, `@persist` saves state after every method execution:
When applied at the class level, `@persist()` saves state after every method execution:
```python
from crewai.flow.flow import Flow, listen, persist, start
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
from pydantic import BaseModel
class CounterState(BaseModel):
value: int = 0
@persist # Apply to the entire flow class
@persist() # Apply to the entire flow class
class PersistentCounterFlow(Flow[CounterState]):
@start()
def increment(self):
@@ -319,10 +320,11 @@ print(f"Second run result: {result2}") # Will be higher due to persisted state
#### Method-Level Persistence
For more granular control, you can apply `@persist` to specific methods:
For more granular control, you can apply `@persist()` to specific methods:
```python
from crewai.flow.flow import Flow, listen, persist, start
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
class SelectivePersistFlow(Flow):
@start()
@@ -330,7 +332,7 @@ class SelectivePersistFlow(Flow):
self.state["count"] = 1
return "First step"
@persist # Only persist after this method
@persist() # Only persist after this method
@listen(first_step)
def important_step(self, prev_result):
self.state["count"] += 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -22,7 +22,7 @@ Watch this video tutorial for a step-by-step demonstration of the installation p
<Note>
**Python Version Requirements**
CrewAI requires `Python >=3.10 and <=3.13`. Here's how to check your version:
CrewAI requires `Python >=3.10 and <3.14`. Here's how to check your version:
```bash
python3 --version
```

View File

@@ -30,18 +30,29 @@ Set your Langfuse API keys and configure OpenTelemetry export settings to send t
```python
import os
import base64
# Get keys for your project from the project settings page: https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 EU region
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 US region
# Your OpenAI key
os.environ["OPENAI_API_KEY"] = "sk-proj-..."
```
With the environment variables set, we can now initialize the Langfuse client. get_client() initializes the Langfuse client using the credentials provided in the environment variables.
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_AUTH=base64.b64encode(f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()).decode()
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://cloud.langfuse.com/api/public/otel" # EU data region
# os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://us.cloud.langfuse.com/api/public/otel" # US data region
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
# your openai key
os.environ["OPENAI_API_KEY"] = "sk-..."
```python
from langfuse import get_client
langfuse = get_client()
# Verify connection
if langfuse.auth_check():
print("Langfuse client is authenticated and ready!")
else:
print("Authentication failed. Please check your credentials and host.")
```
### Step 3: Initialize OpenLit

View File

@@ -0,0 +1,152 @@
---
title: Maxim Integration
description: Start Agent monitoring, evaluation, and observability
icon: bars-staggered
---
# Maxim Integration
Maxim AI provides comprehensive agent monitoring, evaluation, and observability for your CrewAI applications. With Maxim's one-line integration, you can easily trace and analyse agent interactions, performance metrics, and more.
## Features: One Line Integration
- **End-to-End Agent Tracing**: Monitor the complete lifecycle of your agents
- **Performance Analytics**: Track latency, tokens consumed, and costs
- **Hyperparameter Monitoring**: View the configuration details of your agent runs
- **Tool Call Tracking**: Observe when and how agents use their tools
- **Advanced Visualisation**: Understand agent trajectories through intuitive dashboards
## Getting Started
### Prerequisites
- Python version >=3.10
- A Maxim account ([sign up here](https://getmaxim.ai/))
- A CrewAI project
### Installation
Install the Maxim SDK via pip:
```python
pip install maxim-py>=3.6.2
```
Or add it to your `requirements.txt`:
```
maxim-py>=3.6.2
```
### Basic Setup
### 1. Set up environment variables
```python
### Environment Variables Setup
# Create a `.env` file in your project root:
# Maxim API Configuration
MAXIM_API_KEY=your_api_key_here
MAXIM_LOG_REPO_ID=your_repo_id_here
```
### 2. Import the required packages
```python
from crewai import Agent, Task, Crew, Process
from maxim import Maxim
from maxim.logger.crewai import instrument_crewai
```
### 3. Initialise Maxim with your API key
```python
# Initialize Maxim logger
logger = Maxim().logger()
# Instrument CrewAI with just one line
instrument_crewai(logger)
```
### 4. Create and run your CrewAI application as usual
```python
# Create your agent
researcher = Agent(
role='Senior Research Analyst',
goal='Uncover cutting-edge developments in AI',
backstory="You are an expert researcher at a tech think tank...",
verbose=True,
llm=llm
)
# Define the task
research_task = Task(
description="Research the latest AI advancements...",
expected_output="",
agent=researcher
)
# Configure and run the crew
crew = Crew(
agents=[researcher],
tasks=[research_task],
verbose=True
)
try:
result = crew.kickoff()
finally:
maxim.cleanup() # Ensure cleanup happens even if errors occur
```
That's it! All your CrewAI agent interactions will now be logged and available in your Maxim dashboard.
Check this Google Colab Notebook for a quick reference - [Notebook](https://colab.research.google.com/drive/1ZKIZWsmgQQ46n8TH9zLsT1negKkJA6K8?usp=sharing)
## Viewing Your Traces
After running your CrewAI application:
![Example trace in Maxim showing agent interactions](https://raw.githubusercontent.com/maximhq/maxim-docs/master/images/Screenshot2025-05-14at12.10.58PM.png)
1. Log in to your [Maxim Dashboard](https://getmaxim.ai/dashboard)
2. Navigate to your repository
3. View detailed agent traces, including:
- Agent conversations
- Tool usage patterns
- Performance metrics
- Cost analytics
## Troubleshooting
### Common Issues
- **No traces appearing**: Ensure your API key and repository ID are correc
- Ensure you've **called `instrument_crewai()`** ***before*** running your crew. This initializes logging hooks correctly.
- Set `debug=True` in your `instrument_crewai()` call to surface any internal errors:
```python
instrument_crewai(logger, debug=True)
```
- Configure your agents with `verbose=True` to capture detailed logs:
```python
agent = CrewAgent(..., verbose=True)
```
- Double-check that `instrument_crewai()` is called **before** creating or executing agents. This might be obvious, but it's a common oversight.
### Support
If you encounter any issues:
- Check the [Maxim Documentation](https://getmaxim.ai/docs)
- Maxim Github [Link](https://github.com/maximhq)

View File

@@ -212,7 +212,7 @@ Follow the steps below to get Crewing! 🚣‍♂️
1. Log in to your CrewAI Enterprise account (create a free account at [app.crewai.com](https://app.crewai.com))
2. Open Crew Studio
3. Type what is the automation you're tryign to build
3. Type what is the automation you're trying to build
4. Create your tasks visually and connect them in sequence
5. Configure your inputs and click "Download Code" or "Deploy"

View File

@@ -1,216 +0,0 @@
site_name: crewAI
site_author: crewAI, Inc
site_description: Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.
repo_name: crewAI
repo_url: https://github.com/crewAIInc/crewAI
site_url: https://docs.crewai.com
edit_uri: edit/main/docs/
copyright: Copyright &copy; 2024 crewAI, Inc
markdown_extensions:
- abbr
- admonition
- pymdownx.details
- attr_list
- def_list
- footnotes
- md_in_html
- toc:
permalink: true
- pymdownx.arithmatex:
generic: true
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.emoji:
emoji_generator: !!python/name:material.extensions.emoji.to_svg
emoji_index: !!python/name:material.extensions.emoji.twemoji
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.magiclink:
normalize_issue_symbols: true
repo_url_shorthand: true
user: joaomdmoura
repo: crewAI
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.snippets:
auto_append:
- includes/mkdocs.md
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
combine_header_slug: true
slugify: !!python/object/apply:pymdownx.slugs.slugify
kwds:
case: lower
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde
theme:
name: material
language: en
icon:
repo: fontawesome/brands/github
edit: material/pencil
view: material/eye
admonition:
note: octicons/light-bulb-16
abstract: octicons/checklist-16
info: octicons/info-16
tip: octicons/squirrel-16
success: octicons/check-16
question: octicons/question-16
warning: octicons/alert-16
failure: octicons/x-circle-16
danger: octicons/zap-16
bug: octicons/bug-16
example: octicons/beaker-16
quote: octicons/quote-16
palette:
- scheme: default
primary: deep orange
accent: deep orange
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: deep orange
accent: deep orange
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- announce.dismiss
- content.action.edit
- content.action.view
- content.code.annotate
- content.code.copy
- content.code.select
- content.tabs.link
- content.tooltips
- header.autohide
- navigation.footer
- navigation.indexes
# - navigation.prune
# - navigation.sections
# - navigation.tabs
- search.suggest
- navigation.instant
- navigation.instant.progress
- navigation.instant.prefetch
- navigation.tracking
# - navigation.expand
- navigation.path
- navigation.top
- toc.follow
- toc.integrate
- search.highlight
- search.share
nav:
- Home: '/'
- Getting Started:
- Installing CrewAI: 'getting-started/Installing-CrewAI.md'
- Starting a new CrewAI project: 'getting-started/Start-a-New-CrewAI-Project-Template-Method.md'
- Core Concepts:
- Agents: 'core-concepts/Agents.md'
- Tasks: 'core-concepts/Tasks.md'
- Tools: 'core-concepts/Tools.md'
- Processes: 'core-concepts/Processes.md'
- Crews: 'core-concepts/Crews.md'
- Collaboration: 'core-concepts/Collaboration.md'
- Training: 'core-concepts/Training-Crew.md'
- Memory: 'core-concepts/Memory.md'
- Planning: 'core-concepts/Planning.md'
- Testing: 'core-concepts/Testing.md'
- Using LangChain Tools: 'core-concepts/Using-LangChain-Tools.md'
- Using LlamaIndex Tools: 'core-concepts/Using-LlamaIndex-Tools.md'
- How to Guides:
- Create Custom Tools: 'how-to/Create-Custom-Tools.md'
- Using Sequential Process: 'how-to/Sequential.md'
- Using Hierarchical Process: 'how-to/Hierarchical.md'
- Create your own Manager Agent: 'how-to/Your-Own-Manager-Agent.md'
- Connecting to any LLM: 'how-to/LLM-Connections.md'
- Customizing Agents: 'how-to/Customizing-Agents.md'
- Coding Agents: 'how-to/Coding-Agents.md'
- Forcing Tool Output as Result: 'how-to/Force-Tool-Ouput-as-Result.md'
- Human Input on Execution: 'how-to/Human-Input-on-Execution.md'
- Kickoff a Crew Asynchronously: 'how-to/Kickoff-async.md'
- Kickoff a Crew for a List: 'how-to/Kickoff-for-each.md'
- Replay from a specific task from a kickoff: 'how-to/Replay-tasks-from-latest-Crew-Kickoff.md'
- Conditional Tasks: 'how-to/Conditional-Tasks.md'
- Agent Monitoring with AgentOps: 'how-to/AgentOps-Observability.md'
- Agent Monitoring with LangTrace: 'how-to/Langtrace-Observability.md'
- Agent Monitoring with OpenLIT: 'how-to/openlit-Observability.md'
- Agent Monitoring with MLflow: 'how-to/mlflow-Observability.md'
- Tools Docs:
- Browserbase Web Loader: 'tools/BrowserbaseLoadTool.md'
- Code Docs RAG Search: 'tools/CodeDocsSearchTool.md'
- Code Interpreter: 'tools/CodeInterpreterTool.md'
- Composio Tools: 'tools/ComposioTool.md'
- CSV RAG Search: 'tools/CSVSearchTool.md'
- DALL-E Tool: 'tools/DALL-ETool.md'
- Directory RAG Search: 'tools/DirectorySearchTool.md'
- Directory Read: 'tools/DirectoryReadTool.md'
- Docx Rag Search: 'tools/DOCXSearchTool.md'
- EXA Search Web Loader: 'tools/EXASearchTool.md'
- File Read: 'tools/FileReadTool.md'
- File Write: 'tools/FileWriteTool.md'
- Firecrawl Crawl Website Tool: 'tools/FirecrawlCrawlWebsiteTool.md'
- Firecrawl Scrape Website Tool: 'tools/FirecrawlScrapeWebsiteTool.md'
- Firecrawl Search Tool: 'tools/FirecrgstawlSearchTool.md'
- Github RAG Search: 'tools/GitHubSearchTool.md'
- Google Serper Search: 'tools/SerperDevTool.md'
- JSON RAG Search: 'tools/JSONSearchTool.md'
- MDX RAG Search: 'tools/MDXSearchTool.md'
- MySQL Tool: 'tools/MySQLTool.md'
- NL2SQL Tool: 'tools/NL2SQLTool.md'
- PDF RAG Search: 'tools/PDFSearchTool.md'
- PG RAG Search: 'tools/PGSearchTool.md'
- Scrape Website: 'tools/ScrapeWebsiteTool.md'
- Selenium Scraper: 'tools/SeleniumScrapingTool.md'
- Spider Scraper: 'tools/SpiderTool.md'
- TXT RAG Search: 'tools/TXTSearchTool.md'
- Vision Tool: 'tools/VisionTool.md'
- Website RAG Search: 'tools/WebsiteSearchTool.md'
- XML RAG Search: 'tools/XMLSearchTool.md'
- Youtube Channel RAG Search: 'tools/YoutubeChannelSearchTool.md'
- Youtube Video RAG Search: 'tools/YoutubeVideoSearchTool.md'
- Examples:
- Trip Planner Crew: https://github.com/joaomdmoura/crewAI-examples/tree/main/trip_planner"
- Create Instagram Post: https://github.com/joaomdmoura/crewAI-examples/tree/main/instagram_post"
- Stock Analysis: https://github.com/joaomdmoura/crewAI-examples/tree/main/stock_analysis"
- Game Generator: https://github.com/joaomdmoura/crewAI-examples/tree/main/game-builder-crew"
- Drafting emails with LangGraph: https://github.com/joaomdmoura/crewAI-examples/tree/main/CrewAI-LangGraph"
- Landing Page Generator: https://github.com/joaomdmoura/crewAI-examples/tree/main/landing_page_generator"
- Prepare for meetings: https://github.com/joaomdmoura/crewAI-examples/tree/main/prep-for-a-meeting"
- Telemetry: 'telemetry/Telemetry.md'
- Change Log: 'https://github.com/crewAIInc/crewAI/releases'
extra_css:
- stylesheets/output.css
- stylesheets/extra.css
plugins:
- social
- search
extra:
analytics:
provider: google
property: G-N3Q505TMQ6
social:
- icon: fontawesome/brands/x-twitter
link: https://x.com/crewAIInc
- icon: fontawesome/brands/github
link: https://github.com/crewAIInc/crewAI

View File

@@ -1,6 +1,6 @@
[project]
name = "crewai"
version = "0.126.0"
version = "0.130.0"
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
readme = "README.md"
requires-python = ">=3.10,<3.14"
@@ -11,7 +11,7 @@ dependencies = [
# Core Dependencies
"pydantic>=2.4.2",
"openai>=1.13.3",
"litellm==1.68.0",
"litellm==1.72.0",
"instructor>=1.3.3",
# Text Processing
"pdfplumber>=0.11.4",
@@ -47,7 +47,7 @@ Documentation = "https://docs.crewai.com"
Repository = "https://github.com/crewAIInc/crewAI"
[project.optional-dependencies]
tools = ["crewai-tools~=0.46.0"]
tools = ["crewai-tools~=0.47.1"]
embeddings = [
"tiktoken~=0.8.0"
]
@@ -74,11 +74,6 @@ dev-dependencies = [
"ruff>=0.8.2",
"mypy>=1.10.0",
"pre-commit>=3.6.0",
"mkdocs>=1.4.3",
"mkdocstrings>=0.22.0",
"mkdocstrings-python>=1.1.2",
"mkdocs-material>=9.5.7",
"mkdocs-material-extensions>=1.3.1",
"pillow>=10.2.0",
"cairosvg>=2.7.1",
"pytest>=8.0.0",

View File

@@ -18,7 +18,7 @@ warnings.filterwarnings(
category=UserWarning,
module="pydantic.main",
)
__version__ = "0.126.0"
__version__ = "0.130.0"
__all__ = [
"Agent",
"Crew",

View File

@@ -1,6 +1,6 @@
import shutil
import subprocess
from typing import Any, Dict, List, Literal, Optional, Sequence, Type, Union
from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple, Type, Union
from pydantic import Field, InstanceOf, PrivateAttr, model_validator
@@ -155,6 +155,13 @@ class Agent(BaseAgent):
default=None,
description="The Agent's role to be used from your repository.",
)
guardrail: Optional[Union[Callable[[Any], Tuple[bool, Any]], str]] = Field(
default=None,
description="Function or string description of a guardrail to validate agent output"
)
guardrail_max_retries: int = Field(
default=3, description="Maximum number of retries when guardrail fails"
)
@model_validator(mode="before")
def validate_from_repository(cls, v):
@@ -780,6 +787,8 @@ class Agent(BaseAgent):
response_format=response_format,
i18n=self.i18n,
original_agent=self,
guardrail=self.guardrail,
guardrail_max_retries=self.guardrail_max_retries,
)
return lite_agent.kickoff(messages)

View File

@@ -7,6 +7,7 @@ from crewai.utilities import I18N
from crewai.utilities.converter import ConverterError
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
from crewai.utilities.printer import Printer
from crewai.utilities.events.event_listener import event_listener
if TYPE_CHECKING:
from crewai.agents.agent_builder.base_agent import BaseAgent
@@ -125,33 +126,38 @@ class CrewAgentExecutorMixin:
def _ask_human_input(self, final_answer: str) -> str:
"""Prompt human input with mode-appropriate messaging."""
self._printer.print(
content=f"\033[1m\033[95m ## Final Result:\033[00m \033[92m{final_answer}\033[00m"
)
# Training mode prompt (single iteration)
if self.crew and getattr(self.crew, "_train", False):
prompt = (
"\n\n=====\n"
"## TRAINING MODE: Provide feedback to improve the agent's performance.\n"
"This will be used to train better versions of the agent.\n"
"Please provide detailed feedback about the result quality and reasoning process.\n"
"=====\n"
)
# Regular human-in-the-loop prompt (multiple iterations)
else:
prompt = (
"\n\n=====\n"
"## HUMAN FEEDBACK: Provide feedback on the Final Result and Agent's actions.\n"
"Please follow these guidelines:\n"
" - If you are happy with the result, simply hit Enter without typing anything.\n"
" - Otherwise, provide specific improvement requests.\n"
" - You can provide multiple rounds of feedback until satisfied.\n"
"=====\n"
event_listener.formatter.pause_live_updates()
try:
self._printer.print(
content=f"\033[1m\033[95m ## Final Result:\033[00m \033[92m{final_answer}\033[00m"
)
self._printer.print(content=prompt, color="bold_yellow")
response = input()
if response.strip() != "":
self._printer.print(content="\nProcessing your feedback...", color="cyan")
return response
# Training mode prompt (single iteration)
if self.crew and getattr(self.crew, "_train", False):
prompt = (
"\n\n=====\n"
"## TRAINING MODE: Provide feedback to improve the agent's performance.\n"
"This will be used to train better versions of the agent.\n"
"Please provide detailed feedback about the result quality and reasoning process.\n"
"=====\n"
)
# Regular human-in-the-loop prompt (multiple iterations)
else:
prompt = (
"\n\n=====\n"
"## HUMAN FEEDBACK: Provide feedback on the Final Result and Agent's actions.\n"
"Please follow these guidelines:\n"
" - If you are happy with the result, simply hit Enter without typing anything.\n"
" - Otherwise, provide specific improvement requests.\n"
" - You can provide multiple rounds of feedback until satisfied.\n"
"=====\n"
)
self._printer.print(content=prompt, color="bold_yellow")
response = input()
if response.strip() != "":
self._printer.print(content="\nProcessing your feedback...", color="cyan")
return response
finally:
event_listener.formatter.resume_live_updates()

View File

@@ -25,12 +25,16 @@ from crewai.utilities.agent_utils import (
has_reached_max_iterations,
is_context_length_exceeded,
process_llm_response,
show_agent_logs,
)
from crewai.utilities.constants import MAX_LLM_RETRY, TRAINING_DATA_FILE
from crewai.utilities.logger import Logger
from crewai.utilities.tool_utils import execute_tool_and_check_finality
from crewai.utilities.training_handler import CrewTrainingHandler
from crewai.utilities.events.agent_events import (
AgentLogsStartedEvent,
AgentLogsExecutionEvent,
)
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
class CrewAgentExecutor(CrewAgentExecutorMixin):
@@ -263,26 +267,32 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
"""Show logs for the start of agent execution."""
if self.agent is None:
raise ValueError("Agent cannot be None")
show_agent_logs(
printer=self._printer,
agent_role=self.agent.role,
task_description=(
getattr(self.task, "description") if self.task else "Not Found"
crewai_event_bus.emit(
self.agent,
AgentLogsStartedEvent(
agent_role=self.agent.role,
task_description=(
getattr(self.task, "description") if self.task else "Not Found"
),
verbose=self.agent.verbose
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
),
verbose=self.agent.verbose
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
)
def _show_logs(self, formatted_answer: Union[AgentAction, AgentFinish]):
"""Show logs for the agent's execution."""
if self.agent is None:
raise ValueError("Agent cannot be None")
show_agent_logs(
printer=self._printer,
agent_role=self.agent.role,
formatted_answer=formatted_answer,
verbose=self.agent.verbose
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
crewai_event_bus.emit(
self.agent,
AgentLogsExecutionEvent(
agent_role=self.agent.role,
formatted_answer=formatted_answer,
verbose=self.agent.verbose
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
),
)
def _summarize_messages(self) -> None:

View File

@@ -16,6 +16,7 @@ from .deploy.main import DeployCommand
from .evaluate_crew import evaluate_crew
from .install_crew import install_crew
from .kickoff_flow import kickoff_flow
from .organization.main import OrganizationCommand
from .plot_flow import plot_flow
from .replay_from_task import replay_task_command
from .reset_memories_command import reset_memories_command
@@ -353,5 +354,33 @@ def chat():
run_chat()
@crewai.group(invoke_without_command=True)
def org():
"""Organization management commands."""
pass
@org.command()
def list():
"""List available organizations."""
org_command = OrganizationCommand()
org_command.list()
@org.command()
@click.argument("id")
def switch(id):
"""Switch to a specific organization."""
org_command = OrganizationCommand()
org_command.switch(id)
@org.command()
def current():
"""Show current organization when 'crewai org' is called without subcommands."""
org_command = OrganizationCommand()
org_command.current()
if __name__ == "__main__":
crewai()

View File

@@ -14,6 +14,12 @@ class Settings(BaseModel):
tool_repository_password: Optional[str] = Field(
None, description="Password for interacting with the Tool Repository"
)
org_name: Optional[str] = Field(
None, description="Name of the currently active organization"
)
org_uuid: Optional[str] = Field(
None, description="UUID of the currently active organization"
)
config_path: Path = Field(default=DEFAULT_CONFIG_PATH, exclude=True)
def __init__(self, config_path: Path = DEFAULT_CONFIG_PATH, **data):

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,76 @@
from rich.console import Console
from rich.table import Table
from requests import HTTPError
from crewai.cli.command import BaseCommand, PlusAPIMixin
from crewai.cli.config import Settings
console = Console()
class OrganizationCommand(BaseCommand, PlusAPIMixin):
def __init__(self):
BaseCommand.__init__(self)
PlusAPIMixin.__init__(self, telemetry=self._telemetry)
def list(self):
try:
response = self.plus_api_client.get_organizations()
response.raise_for_status()
orgs = response.json()
if not orgs:
console.print("You don't belong to any organizations yet.", style="yellow")
return
table = Table(title="Your Organizations")
table.add_column("Name", style="cyan")
table.add_column("ID", style="green")
for org in orgs:
table.add_row(org["name"], org["uuid"])
console.print(table)
except HTTPError as e:
if e.response.status_code == 401:
console.print("You are not logged in to any organization. Use 'crewai login' to login.", style="bold red")
return
console.print(f"Failed to retrieve organization list: {str(e)}", style="bold red")
raise SystemExit(1)
except Exception as e:
console.print(f"Failed to retrieve organization list: {str(e)}", style="bold red")
raise SystemExit(1)
def switch(self, org_id):
try:
response = self.plus_api_client.get_organizations()
response.raise_for_status()
orgs = response.json()
org = next((o for o in orgs if o["uuid"] == org_id), None)
if not org:
console.print(f"Organization with id '{org_id}' not found.", style="bold red")
return
settings = Settings()
settings.org_name = org["name"]
settings.org_uuid = org["uuid"]
settings.dump()
console.print(f"Successfully switched to {org['name']} ({org['uuid']})", style="bold green")
except HTTPError as e:
if e.response.status_code == 401:
console.print("You are not logged in to any organization. Use 'crewai login' to login.", style="bold red")
return
console.print(f"Failed to retrieve organization list: {str(e)}", style="bold red")
raise SystemExit(1)
except Exception as e:
console.print(f"Failed to switch organization: {str(e)}", style="bold red")
raise SystemExit(1)
def current(self):
settings = Settings()
if settings.org_uuid:
console.print(f"Currently logged in to organization {settings.org_name} ({settings.org_uuid})", style="bold green")
else:
console.print("You're not currently logged in to any organization.", style="yellow")
console.print("Use 'crewai org list' to see available organizations.", style="yellow")
console.print("Use 'crewai org switch <id>' to switch to an organization.", style="yellow")

View File

@@ -4,6 +4,7 @@ from urllib.parse import urljoin
import requests
from crewai.cli.config import Settings
from crewai.cli.version import get_crewai_version
@@ -13,6 +14,7 @@ class PlusAPI:
"""
TOOLS_RESOURCE = "/crewai_plus/api/v1/tools"
ORGANIZATIONS_RESOURCE = "/crewai_plus/api/v1/me/organizations"
CREWS_RESOURCE = "/crewai_plus/api/v1/crews"
AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
@@ -24,6 +26,9 @@ class PlusAPI:
"User-Agent": f"CrewAI-CLI/{get_crewai_version()}",
"X-Crewai-Version": get_crewai_version(),
}
settings = Settings()
if settings.org_uuid:
self.headers["X-Crewai-Organization-Id"] = settings.org_uuid
self.base_url = getenv("CREWAI_BASE_URL", "https://app.crewai.com")
def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
@@ -103,3 +108,7 @@ class PlusAPI:
def create_crew(self, payload) -> requests.Response:
return self._make_request("POST", self.CREWS_RESOURCE, json=payload)
def get_organizations(self) -> requests.Response:
return self._make_request("GET", self.ORGANIZATIONS_RESOURCE)

View File

@@ -1,3 +1,5 @@
import os
import certifi
import json
import time
from collections import defaultdict
@@ -163,8 +165,10 @@ def fetch_provider_data(cache_file):
Returns:
- dict or None: The fetched provider data or None if the operation fails.
"""
ssl_config = os.environ['SSL_CERT_FILE'] = certifi.where()
try:
response = requests.get(JSON_URL, stream=True, timeout=60)
response = requests.get(JSON_URL, stream=True, timeout=60, verify=ssl_config)
response.raise_for_status()
data = download_data(response)
with open(cache_file, "w") as f:

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]>=0.126.0,<1.0.0"
"crewai[tools]>=0.130.0,<1.0.0"
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]>=0.126.0,<1.0.0",
"crewai[tools]>=0.130.0,<1.0.0",
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
readme = "README.md"
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]>=0.126.0"
"crewai[tools]>=0.130.0"
]
[tool.crewai]

View File

@@ -91,6 +91,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
console.print(
f"[green]Found these tools to publish: {', '.join([e['name'] for e in available_exports])}[/green]"
)
self._print_current_organization()
with tempfile.TemporaryDirectory() as temp_build_dir:
subprocess.run(
@@ -136,6 +137,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
)
def install(self, handle: str):
self._print_current_organization()
get_response = self.plus_api_client.get_tool(handle)
if get_response.status_code == 404:
@@ -173,10 +175,16 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
settings.tool_repository_password = login_response_json["credential"][
"password"
]
settings.org_uuid = login_response_json["current_organization"][
"uuid"
]
settings.org_name = login_response_json["current_organization"][
"name"
]
settings.dump()
console.print(
"Successfully authenticated to the tool repository.", style="bold green"
f"Successfully authenticated to the tool repository as {settings.org_name} ({settings.org_uuid}).", style="bold green"
)
def _add_package(self, tool_details: dict[str, Any]):
@@ -234,3 +242,10 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
)
return env
def _print_current_organization(self):
settings = Settings()
if settings.org_uuid:
console.print(f"Current organization: {settings.org_name} ({settings.org_uuid})", style="bold blue")
else:
console.print("No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.", style="yellow")

View File

@@ -655,8 +655,6 @@ class Crew(FlowTrackable, BaseModel):
if self.planning:
self._handle_crew_planning()
metrics: List[UsageMetrics] = []
if self.process == Process.sequential:
result = self._run_sequential_process()
elif self.process == Process.hierarchical:
@@ -669,11 +667,8 @@ class Crew(FlowTrackable, BaseModel):
for after_callback in self.after_kickoff_callbacks:
result = after_callback(result)
metrics += [agent._token_process.get_summary() for agent in self.agents]
self.usage_metrics = self.calculate_usage_metrics()
self.usage_metrics = UsageMetrics()
for metric in metrics:
self.usage_metrics.add_usage_metrics(metric)
return result
except Exception as e:
crewai_event_bus.emit(

View File

@@ -20,7 +20,8 @@ class FlowTrackable(BaseModel):
)
@model_validator(mode="after")
def _set_parent_flow(self, max_depth: int = 5) -> "FlowTrackable":
def _set_parent_flow(self) -> "FlowTrackable":
max_depth = 5
frame = inspect.currentframe()
try:

View File

@@ -17,7 +17,7 @@ Example
import ast
import inspect
from typing import Any, Dict, List, Optional, Tuple, Union
from typing import Any, Dict, List, Tuple, Union
from .utils import (
build_ancestor_dict,
@@ -140,7 +140,7 @@ def compute_positions(
flow: Any,
node_levels: Dict[str, int],
y_spacing: float = 150,
x_spacing: float = 150
x_spacing: float = 300
) -> Dict[str, Tuple[float, float]]:
"""
Compute the (x, y) positions for each node in the flow graph.
@@ -154,7 +154,7 @@ def compute_positions(
y_spacing : float, optional
Vertical spacing between levels, by default 150.
x_spacing : float, optional
Horizontal spacing between nodes, by default 150.
Horizontal spacing between nodes, by default 300.
Returns
-------

View File

@@ -1,9 +1,33 @@
import asyncio
import inspect
import uuid
from datetime import datetime
from typing import Any, Callable, Dict, List, Optional, Type, Union, cast
from typing import (
Any,
Callable,
Dict,
List,
Optional,
Tuple,
Type,
Union,
cast,
get_args,
get_origin,
)
from pydantic import BaseModel, Field, InstanceOf, PrivateAttr, model_validator
try:
from typing import Self
except ImportError:
from typing_extensions import Self
from pydantic import (
BaseModel,
Field,
InstanceOf,
PrivateAttr,
model_validator,
field_validator,
)
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
@@ -18,6 +42,7 @@ from crewai.llm import LLM
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities import I18N
from crewai.utilities.guardrail import process_guardrail
from crewai.utilities.agent_utils import (
enforce_rpm_limit,
format_message_for_llm,
@@ -33,10 +58,10 @@ from crewai.utilities.agent_utils import (
parse_tools,
process_llm_response,
render_text_description_and_args,
show_agent_logs,
)
from crewai.utilities.converter import convert_to_model, generate_model_description
from crewai.utilities.converter import generate_model_description
from crewai.utilities.events.agent_events import (
AgentLogsExecutionEvent,
LiteAgentExecutionCompletedEvent,
LiteAgentExecutionErrorEvent,
LiteAgentExecutionStartedEvent,
@@ -146,6 +171,17 @@ class LiteAgent(FlowTrackable, BaseModel):
default=[], description="Callbacks to be used for the agent"
)
# Guardrail Properties
guardrail: Optional[Union[Callable[[LiteAgentOutput], Tuple[bool, Any]], str]] = (
Field(
default=None,
description="Function or string description of a guardrail to validate agent output",
)
)
guardrail_max_retries: int = Field(
default=3, description="Maximum number of retries when guardrail fails"
)
# State and Results
tools_results: List[Dict[str, Any]] = Field(
default=[], description="Results of the tools used by the agent."
@@ -163,6 +199,8 @@ class LiteAgent(FlowTrackable, BaseModel):
_messages: List[Dict[str, str]] = PrivateAttr(default_factory=list)
_iterations: int = PrivateAttr(default=0)
_printer: Printer = PrivateAttr(default_factory=Printer)
_guardrail: Optional[Callable] = PrivateAttr(default=None)
_guardrail_retry_count: int = PrivateAttr(default=0)
@model_validator(mode="after")
def setup_llm(self):
@@ -184,6 +222,61 @@ class LiteAgent(FlowTrackable, BaseModel):
return self
@model_validator(mode="after")
def ensure_guardrail_is_callable(self) -> Self:
if callable(self.guardrail):
self._guardrail = self.guardrail
elif isinstance(self.guardrail, str):
from crewai.tasks.llm_guardrail import LLMGuardrail
assert isinstance(self.llm, LLM)
self._guardrail = LLMGuardrail(description=self.guardrail, llm=self.llm)
return self
@field_validator("guardrail", mode="before")
@classmethod
def validate_guardrail_function(
cls, v: Optional[Union[Callable, str]]
) -> Optional[Union[Callable, str]]:
"""Validate that the guardrail function has the correct signature.
If v is a callable, validate that it has the correct signature.
If v is a string, return it as is.
Args:
v: The guardrail function to validate or a string describing the guardrail task
Returns:
The validated guardrail function or a string describing the guardrail task
"""
if v is None or isinstance(v, str):
return v
# Check function signature
sig = inspect.signature(v)
if len(sig.parameters) != 1:
raise ValueError(
f"Guardrail function must accept exactly 1 parameter (LiteAgentOutput), "
f"but it accepts {len(sig.parameters)}"
)
# Check return annotation if present
if sig.return_annotation is not sig.empty:
if sig.return_annotation == Tuple[bool, Any]:
return v
origin = get_origin(sig.return_annotation)
args = get_args(sig.return_annotation)
if origin is not tuple or len(args) != 2 or args[0] is not bool:
raise ValueError(
"If return type is annotated, it must be Tuple[bool, Any]"
)
return v
@property
def key(self) -> str:
"""Get the unique key for this agent instance."""
@@ -223,54 +316,7 @@ class LiteAgent(FlowTrackable, BaseModel):
# Format messages for the LLM
self._messages = self._format_messages(messages)
# Emit event for agent execution start
crewai_event_bus.emit(
self,
event=LiteAgentExecutionStartedEvent(
agent_info=agent_info,
tools=self._parsed_tools,
messages=messages,
),
)
# Execute the agent using invoke loop
agent_finish = self._invoke_loop()
formatted_result: Optional[BaseModel] = None
if self.response_format:
try:
# Cast to BaseModel to ensure type safety
result = self.response_format.model_validate_json(
agent_finish.output
)
if isinstance(result, BaseModel):
formatted_result = result
except Exception as e:
self._printer.print(
content=f"Failed to parse output into response format: {str(e)}",
color="yellow",
)
# Calculate token usage metrics
usage_metrics = self._token_process.get_summary()
# Create output
output = LiteAgentOutput(
raw=agent_finish.output,
pydantic=formatted_result,
agent_role=self.role,
usage_metrics=usage_metrics.model_dump() if usage_metrics else None,
)
# Emit completion event
crewai_event_bus.emit(
self,
event=LiteAgentExecutionCompletedEvent(
agent_info=agent_info,
output=agent_finish.output,
),
)
return output
return self._execute_core(agent_info=agent_info)
except Exception as e:
self._printer.print(
@@ -288,6 +334,95 @@ class LiteAgent(FlowTrackable, BaseModel):
)
raise e
def _execute_core(self, agent_info: Dict[str, Any]) -> LiteAgentOutput:
# Emit event for agent execution start
crewai_event_bus.emit(
self,
event=LiteAgentExecutionStartedEvent(
agent_info=agent_info,
tools=self._parsed_tools,
messages=self._messages,
),
)
# Execute the agent using invoke loop
agent_finish = self._invoke_loop()
formatted_result: Optional[BaseModel] = None
if self.response_format:
try:
# Cast to BaseModel to ensure type safety
result = self.response_format.model_validate_json(agent_finish.output)
if isinstance(result, BaseModel):
formatted_result = result
except Exception as e:
self._printer.print(
content=f"Failed to parse output into response format: {str(e)}",
color="yellow",
)
# Calculate token usage metrics
usage_metrics = self._token_process.get_summary()
# Create output
output = LiteAgentOutput(
raw=agent_finish.output,
pydantic=formatted_result,
agent_role=self.role,
usage_metrics=usage_metrics.model_dump() if usage_metrics else None,
)
# Process guardrail if set
if self._guardrail is not None:
guardrail_result = process_guardrail(
output=output,
guardrail=self._guardrail,
retry_count=self._guardrail_retry_count,
)
if not guardrail_result.success:
if self._guardrail_retry_count >= self.guardrail_max_retries:
raise Exception(
f"Agent's guardrail failed validation after {self.guardrail_max_retries} retries. "
f"Last error: {guardrail_result.error}"
)
self._guardrail_retry_count += 1
if self.verbose:
self._printer.print(
f"Guardrail failed. Retrying ({self._guardrail_retry_count}/{self.guardrail_max_retries})..."
f"\n{guardrail_result.error}"
)
self._messages.append(
{
"role": "user",
"content": guardrail_result.error
or "Guardrail validation failed",
}
)
return self._execute_core(agent_info=agent_info)
# Apply guardrail result if available
if guardrail_result.result is not None:
if isinstance(guardrail_result.result, str):
output.raw = guardrail_result.result
elif isinstance(guardrail_result.result, BaseModel):
output.pydantic = guardrail_result.result
usage_metrics = self._token_process.get_summary()
output.usage_metrics = usage_metrics.model_dump() if usage_metrics else None
# Emit completion event
crewai_event_bus.emit(
self,
event=LiteAgentExecutionCompletedEvent(
agent_info=agent_info,
output=agent_finish.output,
),
)
return output
async def kickoff_async(
self, messages: Union[str, List[Dict[str, str]]]
) -> LiteAgentOutput:
@@ -467,11 +602,13 @@ class LiteAgent(FlowTrackable, BaseModel):
def _show_logs(self, formatted_answer: Union[AgentAction, AgentFinish]):
"""Show logs for the agent's execution."""
show_agent_logs(
printer=self._printer,
agent_role=self.role,
formatted_answer=formatted_answer,
verbose=self.verbose,
crewai_event_bus.emit(
self,
AgentLogsExecutionEvent(
agent_role=self.role,
formatted_answer=formatted_answer,
verbose=self.verbose,
),
)
def _append_message(self, text: str, role: str = "assistant") -> None:

View File

@@ -4,6 +4,9 @@ from typing import Any, Dict, List
from mem0 import Memory, MemoryClient
from crewai.memory.storage.interface import Storage
from crewai.utilities.chromadb import sanitize_collection_name
MAX_AGENT_ID_LENGTH_MEM0 = 255
class Mem0Storage(Storage):
@@ -134,7 +137,7 @@ class Mem0Storage(Storage):
agents = self.crew.agents
agents = [self._sanitize_role(agent.role) for agent in agents]
agents = "_".join(agents)
return agents
return sanitize_collection_name(name=agents,max_collection_length=MAX_AGENT_ID_LENGTH_MEM0)
def _get_config(self) -> Dict[str, Any]:
return self.config or getattr(self, "memory_config", {}).get("config", {}) or {}

View File

@@ -35,12 +35,12 @@ from pydantic_core import PydanticCustomError
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.security import Fingerprint, SecurityConfig
from crewai.tasks.guardrail_result import GuardrailResult
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
from crewai.tools.base_tool import BaseTool
from crewai.utilities.config import process_config
from crewai.utilities.constants import NOT_SPECIFIED
from crewai.utilities.constants import NOT_SPECIFIED, _NotSpecified
from crewai.utilities.guardrail import process_guardrail, GuardrailResult
from crewai.utilities.converter import Converter, convert_to_model
from crewai.utilities.events import (
TaskCompletedEvent,
@@ -95,9 +95,9 @@ class Task(BaseModel):
agent: Optional[BaseAgent] = Field(
description="Agent responsible for execution the task.", default=None
)
context: Optional[List["Task"]] = Field(
context: Union[List["Task"], None, _NotSpecified] = Field(
description="Other tasks that will have their output used as context for this task.",
default=NOT_SPECIFIED,
default=NOT_SPECIFIED
)
async_execution: Optional[bool] = Field(
description="Whether the task should be executed asynchronously or not.",
@@ -158,6 +158,9 @@ class Task(BaseModel):
end_time: Optional[datetime.datetime] = Field(
default=None, description="End time of the task execution"
)
model_config = {
"arbitrary_types_allowed": True
}
@field_validator("guardrail")
@classmethod
@@ -431,7 +434,11 @@ class Task(BaseModel):
)
if self._guardrail:
guardrail_result = self._process_guardrail(task_output)
guardrail_result = process_guardrail(
output=task_output,
guardrail=self._guardrail,
retry_count=self.retry_count
)
if not guardrail_result.success:
if self.retry_count >= self.max_retries:
raise Exception(
@@ -527,10 +534,10 @@ class Task(BaseModel):
def prompt(self) -> str:
"""Generates the task prompt with optional markdown formatting.
When the markdown attribute is True, instructions for formatting the
response in Markdown syntax will be added to the prompt.
Returns:
str: The formatted prompt string containing the task description,
expected output, and optional markdown formatting instructions.
@@ -541,7 +548,7 @@ class Task(BaseModel):
expected_output=self.expected_output
)
tasks_slices = [self.description, output]
if self.markdown:
markdown_instruction = """Your final answer MUST be formatted in Markdown syntax.
Follow these guidelines:

View File

@@ -8,7 +8,7 @@ import platform
import warnings
from contextlib import contextmanager
from importlib.metadata import version
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, Callable, Optional
import threading
from opentelemetry import trace
@@ -73,11 +73,16 @@ class Telemetry:
with cls._lock:
if cls._instance is None:
cls._instance = super(Telemetry, cls).__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self) -> None:
if hasattr(self, '_initialized') and self._initialized:
return
self.ready: bool = False
self.trace_set: bool = False
self._initialized: bool = True
if self._is_telemetry_disabled():
return
@@ -113,6 +118,10 @@ class Telemetry:
or os.getenv("CREWAI_DISABLE_TELEMETRY", "false").lower() == "true"
)
def _should_execute_telemetry(self) -> bool:
"""Check if telemetry operations should be executed."""
return self.ready and not self._is_telemetry_disabled()
def set_tracer(self):
if self.ready and not self.trace_set:
try:
@@ -123,8 +132,9 @@ class Telemetry:
self.ready = False
self.trace_set = False
def _safe_telemetry_operation(self, operation):
if not self.ready:
def _safe_telemetry_operation(self, operation: Callable[[], None]) -> None:
"""Execute telemetry operation safely, checking both readiness and environment variables."""
if not self._should_execute_telemetry():
return
try:
operation()
@@ -423,7 +433,8 @@ class Telemetry:
return span
return self._safe_telemetry_operation(operation)
self._safe_telemetry_operation(operation)
return None
def task_ended(self, span: Span, task: Task, crew: Crew):
"""Records the completion of a task execution in a crew.
@@ -773,7 +784,8 @@ class Telemetry:
return span
if crew.share_crew:
return self._safe_telemetry_operation(operation)
self._safe_telemetry_operation(operation)
return operation()
return None
def end_crew(self, crew, final_string_output):

View File

@@ -64,7 +64,7 @@ class BaseTool(BaseModel, ABC):
},
},
)
@field_validator("max_usage_count", mode="before")
@classmethod
def validate_max_usage_count(cls, v: int | None) -> int | None:
@@ -88,11 +88,11 @@ class BaseTool(BaseModel, ABC):
# If _run is async, we safely run it
if asyncio.iscoroutine(result):
result = asyncio.run(result)
self.current_usage_count += 1
return result
def reset_usage_count(self) -> None:
"""Reset the current usage count to zero."""
self.current_usage_count = 0
@@ -279,7 +279,7 @@ def to_langchain(
def tool(*args, result_as_answer: bool = False, max_usage_count: int | None = None) -> Callable:
"""
Decorator to create a tool from a function.
Args:
*args: Positional arguments, either the function to decorate or the tool name.
result_as_answer: Flag to indicate if the tool result should be used as the final agent answer.

View File

@@ -1,5 +1,7 @@
from __future__ import annotations
import asyncio
import inspect
import textwrap
from typing import Any, Callable, Optional, Union, get_type_hints
@@ -239,7 +241,17 @@ class CrewStructuredTool:
) -> Any:
"""Main method for tool execution."""
parsed_args = self._parse_args(input)
return self.func(**parsed_args, **kwargs)
if inspect.iscoroutinefunction(self.func):
result = asyncio.run(self.func(**parsed_args, **kwargs))
return result
result = self.func(**parsed_args, **kwargs)
if asyncio.iscoroutine(result):
return asyncio.run(result)
return result
@property
def args(self) -> dict:

View File

@@ -20,7 +20,10 @@ from crewai.utilities.errors import AgentRepositoryError
from crewai.utilities.exceptions.context_window_exceeding_exception import (
LLMContextLengthExceededException,
)
from rich.console import Console
from crewai.cli.config import Settings
console = Console()
def parse_tools(tools: List[BaseTool]) -> List[CrewStructuredTool]:
"""Parse tools to be used for the task."""
@@ -215,9 +218,6 @@ def handle_agent_action_core(
if show_logs:
show_logs(formatted_answer)
if messages is not None:
messages.append({"role": "assistant", "content": tool_result.result})
return formatted_answer
@@ -438,6 +438,13 @@ def show_agent_logs(
)
def _print_current_organization():
settings = Settings()
if settings.org_uuid:
console.print(f"Fetching agent from organization: {settings.org_name} ({settings.org_uuid})", style="bold blue")
else:
console.print("No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.", style="yellow")
def load_agent_from_repository(from_repository: str) -> Dict[str, Any]:
attributes: Dict[str, Any] = {}
if from_repository:
@@ -447,15 +454,18 @@ def load_agent_from_repository(from_repository: str) -> Dict[str, Any]:
from crewai.cli.plus_api import PlusAPI
client = PlusAPI(api_key=get_auth_token())
_print_current_organization()
response = client.get_agent(from_repository)
if response.status_code == 404:
raise AgentRepositoryError(
f"Agent {from_repository} does not exist, make sure the name is correct or the agent is available on your organization"
f"Agent {from_repository} does not exist, make sure the name is correct or the agent is available on your organization."
f"\nIf you are using the wrong organization, switch to the correct one using `crewai org switch <org_id>` command.",
)
if response.status_code != 200:
raise AgentRepositoryError(
f"Agent {from_repository} could not be loaded: {response.text}"
f"\nIf you are using the wrong organization, switch to the correct one using `crewai org switch <org_id>` command.",
)
agent = response.json()
@@ -466,7 +476,14 @@ def load_agent_from_repository(from_repository: str) -> Dict[str, Any]:
try:
module = importlib.import_module(tool["module"])
tool_class = getattr(module, tool["name"])
attributes[key].append(tool_class())
tool_value = tool_class(**tool["init_params"])
if isinstance(tool_value, list):
attributes[key].extend(tool_value)
else:
attributes[key].append(tool_value)
except Exception as e:
raise AgentRepositoryError(
f"Tool {tool['name']} could not be loaded: {e}"

View File

@@ -23,7 +23,7 @@ def is_ipv4_pattern(name: str) -> bool:
return bool(IPV4_PATTERN.match(name))
def sanitize_collection_name(name: Optional[str]) -> str:
def sanitize_collection_name(name: Optional[str], max_collection_length: int = MAX_COLLECTION_LENGTH) -> str:
"""
Sanitize a collection name to meet ChromaDB requirements:
1. 3-63 characters long
@@ -54,8 +54,8 @@ def sanitize_collection_name(name: Optional[str]) -> str:
if len(sanitized) < MIN_COLLECTION_LENGTH:
sanitized = sanitized + "x" * (MIN_COLLECTION_LENGTH - len(sanitized))
if len(sanitized) > MAX_COLLECTION_LENGTH:
sanitized = sanitized[:MAX_COLLECTION_LENGTH]
if len(sanitized) > max_collection_length:
sanitized = sanitized[:max_collection_length]
if not sanitized[-1].isalnum():
sanitized = sanitized[:-1] + "z"

View File

@@ -102,3 +102,24 @@ class LiteAgentExecutionErrorEvent(BaseEvent):
agent_info: Dict[str, Any]
error: str
type: str = "lite_agent_execution_error"
# New logging events
class AgentLogsStartedEvent(BaseEvent):
"""Event emitted when agent logs should be shown at start"""
agent_role: str
task_description: Optional[str] = None
verbose: bool = False
type: str = "agent_logs_started"
class AgentLogsExecutionEvent(BaseEvent):
"""Event emitted when agent logs should be shown during execution"""
agent_role: str
formatted_answer: Any
verbose: bool = False
type: str = "agent_logs_execution"
model_config = {"arbitrary_types_allowed": True}

View File

@@ -27,6 +27,8 @@ from crewai.utilities.events.utils.console_formatter import ConsoleFormatter
from .agent_events import (
AgentExecutionCompletedEvent,
AgentExecutionStartedEvent,
AgentLogsStartedEvent,
AgentLogsExecutionEvent,
LiteAgentExecutionCompletedEvent,
LiteAgentExecutionErrorEvent,
LiteAgentExecutionStartedEvent,
@@ -108,6 +110,7 @@ class EventListener(BaseEventListener):
event.crew_name or "Crew",
source.id,
"completed",
final_string_output,
)
@crewai_event_bus.on(CrewKickoffFailedEvent)
@@ -286,13 +289,14 @@ class EventListener(BaseEventListener):
if isinstance(source, LLM):
self.formatter.handle_llm_tool_usage_started(
event.tool_name,
event.tool_args,
)
else:
self.formatter.handle_tool_usage_started(
self.formatter.current_agent_branch,
event.tool_name,
self.formatter.current_crew_tree,
)
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(ToolUsageFinishedEvent)
def on_tool_usage_finished(source, event: ToolUsageFinishedEvent):
@@ -320,16 +324,20 @@ class EventListener(BaseEventListener):
event.tool_name,
event.error,
self.formatter.current_crew_tree,
)
)
# ----------- LLM EVENTS -----------
@crewai_event_bus.on(LLMCallStartedEvent)
def on_llm_call_started(source, event: LLMCallStartedEvent):
self.formatter.handle_llm_call_started(
# Capture the returned tool branch and update the current_tool_branch reference
thinking_branch = self.formatter.handle_llm_call_started(
self.formatter.current_agent_branch,
self.formatter.current_crew_tree,
)
# Update the formatter's current_tool_branch to ensure proper cleanup
if thinking_branch is not None:
self.formatter.current_tool_branch = thinking_branch
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_call_completed(source, event: LLMCallCompletedEvent):
@@ -462,5 +470,23 @@ class EventListener(BaseEventListener):
self.formatter.current_crew_tree,
)
# ----------- AGENT LOGGING EVENTS -----------
@crewai_event_bus.on(AgentLogsStartedEvent)
def on_agent_logs_started(source, event: AgentLogsStartedEvent):
self.formatter.handle_agent_logs_started(
event.agent_role,
event.task_description,
event.verbose,
)
@crewai_event_bus.on(AgentLogsExecutionEvent)
def on_agent_logs_execution(source, event: AgentLogsExecutionEvent):
self.formatter.handle_agent_logs_execution(
event.agent_role,
event.formatted_answer,
event.verbose,
)
event_listener = EventListener()

View File

@@ -5,6 +5,7 @@ from rich.panel import Panel
from rich.text import Text
from rich.tree import Tree
from rich.live import Live
from rich.syntax import Syntax
class ConsoleFormatter:
@@ -17,6 +18,7 @@ class ConsoleFormatter:
current_lite_agent_branch: Optional[Tree] = None
tool_usage_counts: Dict[str, int] = {}
current_reasoning_branch: Optional[Tree] = None # Track reasoning status
_live_paused: bool = False
current_llm_tool_tree: Optional[Tree] = None
def __init__(self, verbose: bool = False):
@@ -39,7 +41,12 @@ class ConsoleFormatter:
)
def create_status_content(
self, title: str, name: str, status_style: str = "blue", **fields
self,
title: str,
name: str,
status_style: str = "blue",
tool_args: Dict[str, Any] | str = "",
**fields,
) -> Text:
"""Create standardized status content with consistent formatting."""
content = Text()
@@ -52,6 +59,8 @@ class ConsoleFormatter:
content.append(
f"{value}\n", style=fields.get(f"{label}_style", status_style)
)
content.append("Tool Args: ", style="white")
content.append(f"{tool_args}\n", style=status_style)
return content
@@ -119,6 +128,19 @@ class ConsoleFormatter:
# Finally, pass through to the regular Console.print implementation
self.console.print(*args, **kwargs)
def pause_live_updates(self) -> None:
"""Pause Live session updates to allow for human input without interference."""
if not self._live_paused:
if self._live:
self._live.stop()
self._live = None
self._live_paused = True
def resume_live_updates(self) -> None:
"""Resume Live session updates after human input is complete."""
if self._live_paused:
self._live_paused = False
def print_panel(
self, content: Text, title: str, style: str = "blue", is_flow: bool = False
) -> None:
@@ -138,6 +160,7 @@ class ConsoleFormatter:
crew_name: str,
source_id: str,
status: str = "completed",
final_string_output: str = "",
) -> None:
"""Handle crew tree updates with consistent formatting."""
if not self.verbose or tree is None:
@@ -169,6 +192,7 @@ class ConsoleFormatter:
style,
ID=source_id,
)
content.append(f"Final Output: {final_string_output}\n", style="white")
self.print_panel(content, title, style)
@@ -441,12 +465,19 @@ class ConsoleFormatter:
def handle_llm_tool_usage_started(
self,
tool_name: str,
tool_args: Dict[str, Any] | str,
):
tree = self.get_llm_tree(tool_name)
self.add_tree_node(tree, "🔄 Tool Usage Started", "green")
self.print(tree)
# Create status content for the tool usage
content = self.create_status_content(
"Tool Usage Started", tool_name, Status="In Progress", tool_args=tool_args
)
# Create and print the panel
self.print_panel(content, "Tool Usage", "green")
self.print()
return tree
# Still return the tree for compatibility with existing code
return self.get_llm_tree(tool_name)
def handle_llm_tool_usage_finished(
self,
@@ -477,6 +508,7 @@ class ConsoleFormatter:
agent_branch: Optional[Tree],
tool_name: str,
crew_tree: Optional[Tree],
tool_args: Dict[str, Any] | str = "",
) -> Optional[Tree]:
"""Handle tool usage started event."""
if not self.verbose:
@@ -484,9 +516,7 @@ class ConsoleFormatter:
# Parent for tool usage: LiteAgent > Agent > Task
branch_to_use = (
self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
self.current_lite_agent_branch or agent_branch or self.current_task_branch
)
# Render full crew tree when available for consistent live updates
@@ -595,9 +625,7 @@ class ConsoleFormatter:
# Parent for tool usage: LiteAgent > Agent > Task
branch_to_use = (
self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
self.current_lite_agent_branch or agent_branch or self.current_task_branch
)
# Render full crew tree when available for consistent live updates
@@ -611,14 +639,21 @@ class ConsoleFormatter:
return None
# Only add thinking status if we don't have a current tool branch
if self.current_tool_branch is None:
# or if the current tool branch is not a thinking node
should_add_thinking = self.current_tool_branch is None or "Thinking" not in str(
self.current_tool_branch.label
)
if should_add_thinking:
tool_branch = branch_to_use.add("")
self.update_tree_label(tool_branch, "🧠", "Thinking...", "blue")
self.current_tool_branch = tool_branch
self.print(tree_to_use)
self.print()
return tool_branch
return None
# Return the existing tool branch if it's already a thinking node
return self.current_tool_branch
def handle_llm_call_completed(
self,
@@ -627,7 +662,7 @@ class ConsoleFormatter:
crew_tree: Optional[Tree],
) -> None:
"""Handle LLM call completed event."""
if not self.verbose or tool_branch is None:
if not self.verbose:
return
# Decide which tree to render: prefer full crew tree, else parent branch
@@ -635,23 +670,50 @@ class ConsoleFormatter:
if tree_to_use is None:
return
# Remove the thinking status node when complete
if "Thinking" in str(tool_branch.label):
# Try to remove the thinking status node - first try the provided tool_branch
thinking_branch_to_remove = None
removed = False
# Method 1: Use the provided tool_branch if it's a thinking node
if tool_branch is not None and "Thinking" in str(tool_branch.label):
thinking_branch_to_remove = tool_branch
# Method 2: Fallback - search for any thinking node if tool_branch is None or not thinking
if thinking_branch_to_remove is None:
parents = [
self.current_lite_agent_branch,
self.current_agent_branch,
self.current_task_branch,
tree_to_use,
]
removed = False
for parent in parents:
if isinstance(parent, Tree) and tool_branch in parent.children:
parent.children.remove(tool_branch)
if isinstance(parent, Tree):
for child in parent.children:
if "Thinking" in str(child.label):
thinking_branch_to_remove = child
break
if thinking_branch_to_remove:
break
# Remove the thinking node if found
if thinking_branch_to_remove:
parents = [
self.current_lite_agent_branch,
self.current_agent_branch,
self.current_task_branch,
tree_to_use,
]
for parent in parents:
if (
isinstance(parent, Tree)
and thinking_branch_to_remove in parent.children
):
parent.children.remove(thinking_branch_to_remove)
removed = True
break
# Clear pointer if we just removed the current_tool_branch
if self.current_tool_branch is tool_branch:
if self.current_tool_branch is thinking_branch_to_remove:
self.current_tool_branch = None
if removed:
@@ -668,9 +730,36 @@ class ConsoleFormatter:
# Decide which tree to render: prefer full crew tree, else parent branch
tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch
# Update tool branch if it exists
if tool_branch:
tool_branch.label = Text("❌ LLM Failed", style="red bold")
# Find the thinking branch to update (similar to completion logic)
thinking_branch_to_update = None
# Method 1: Use the provided tool_branch if it's a thinking node
if tool_branch is not None and "Thinking" in str(tool_branch.label):
thinking_branch_to_update = tool_branch
# Method 2: Fallback - search for any thinking node if tool_branch is None or not thinking
if thinking_branch_to_update is None:
parents = [
self.current_lite_agent_branch,
self.current_agent_branch,
self.current_task_branch,
tree_to_use,
]
for parent in parents:
if isinstance(parent, Tree):
for child in parent.children:
if "Thinking" in str(child.label):
thinking_branch_to_update = child
break
if thinking_branch_to_update:
break
# Update the thinking branch to show failure
if thinking_branch_to_update:
thinking_branch_to_update.label = Text("❌ LLM Failed", style="red bold")
# Clear the current_tool_branch reference
if self.current_tool_branch is thinking_branch_to_update:
self.current_tool_branch = None
if tree_to_use:
self.print(tree_to_use)
self.print()
@@ -1113,9 +1202,7 @@ class ConsoleFormatter:
# Prefer LiteAgent > Agent > Task branch as the parent for reasoning
branch_to_use = (
self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
self.current_lite_agent_branch or agent_branch or self.current_task_branch
)
# We always want to render the full crew tree when possible so the
@@ -1162,7 +1249,9 @@ class ConsoleFormatter:
)
style = "green" if ready else "yellow"
status_text = "Reasoning Completed" if ready else "Reasoning Completed (Not Ready)"
status_text = (
"Reasoning Completed" if ready else "Reasoning Completed (Not Ready)"
)
if reasoning_branch is not None:
self.update_tree_label(reasoning_branch, "", status_text, style)
@@ -1219,3 +1308,149 @@ class ConsoleFormatter:
# Clear stored branch after failure
self.current_reasoning_branch = None
# ----------- AGENT LOGGING EVENTS -----------
def handle_agent_logs_started(
self,
agent_role: str,
task_description: Optional[str] = None,
verbose: bool = False,
) -> None:
"""Handle agent logs started event."""
if not verbose:
return
agent_role = agent_role.split("\n")[0]
# Create panel content
content = Text()
content.append("Agent: ", style="white")
content.append(f"{agent_role}", style="bright_green bold")
if task_description:
content.append("\n\nTask: ", style="white")
content.append(f"{task_description}", style="bright_green")
# Create and display the panel
agent_panel = Panel(
content,
title="🤖 Agent Started",
border_style="magenta",
padding=(1, 2),
)
self.print(agent_panel)
self.print()
def handle_agent_logs_execution(
self,
agent_role: str,
formatted_answer: Any,
verbose: bool = False,
) -> None:
"""Handle agent logs execution event."""
if not verbose:
return
from crewai.agents.parser import AgentAction, AgentFinish
import json
import re
agent_role = agent_role.split("\n")[0]
if isinstance(formatted_answer, AgentAction):
thought = re.sub(r"\n+", "\n", formatted_answer.thought)
formatted_json = json.dumps(
formatted_answer.tool_input,
indent=2,
ensure_ascii=False,
)
# Create content for the action panel
content = Text()
content.append("Agent: ", style="white")
content.append(f"{agent_role}\n\n", style="bright_green bold")
if thought and thought != "":
content.append("Thought: ", style="white")
content.append(f"{thought}\n\n", style="bright_green")
content.append("Using Tool: ", style="white")
content.append(f"{formatted_answer.tool}\n\n", style="bright_green bold")
content.append("Tool Input:\n", style="white")
# Create a syntax-highlighted JSON code block
json_syntax = Syntax(
formatted_json,
"json",
theme="monokai",
line_numbers=False,
background_color="default",
)
content.append("\n")
# Create separate panels for better organization
main_content = Text()
main_content.append("Agent: ", style="white")
main_content.append(f"{agent_role}\n\n", style="bright_green bold")
if thought and thought != "":
main_content.append("Thought: ", style="white")
main_content.append(f"{thought}\n\n", style="bright_green")
main_content.append("Using Tool: ", style="white")
main_content.append(f"{formatted_answer.tool}", style="bright_green bold")
# Create the main action panel
action_panel = Panel(
main_content,
title="🔧 Agent Tool Execution",
border_style="magenta",
padding=(1, 2),
)
# Create the JSON input panel
input_panel = Panel(
json_syntax,
title="Tool Input",
border_style="blue",
padding=(1, 2),
)
# Create tool output content with better formatting
output_text = str(formatted_answer.result)
if len(output_text) > 2000:
output_text = output_text[:1997] + "..."
output_panel = Panel(
Text(output_text, style="bright_green"),
title="Tool Output",
border_style="green",
padding=(1, 2),
)
# Print all panels
self.print(action_panel)
self.print(input_panel)
self.print(output_panel)
self.print()
elif isinstance(formatted_answer, AgentFinish):
# Create content for the finish panel
content = Text()
content.append("Agent: ", style="white")
content.append(f"{agent_role}\n\n", style="bright_green bold")
content.append("Final Answer:\n", style="white")
content.append(f"{formatted_answer.output}", style="bright_green")
# Create and display the finish panel
finish_panel = Panel(
content,
title="✅ Agent Final Answer",
border_style="green",
padding=(1, 2),
)
self.print(finish_panel)
self.print()

View File

@@ -1,5 +1,5 @@
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, List, Union
from crewai.utilities.constants import _NotSpecified
if TYPE_CHECKING:
from crewai.task import Task
@@ -15,7 +15,7 @@ def aggregate_raw_outputs_from_task_outputs(task_outputs: List["TaskOutput"]) ->
return context
def aggregate_raw_outputs_from_tasks(tasks: List["Task"]) -> str:
def aggregate_raw_outputs_from_tasks(tasks: Union[List["Task"],_NotSpecified]) -> str:
"""Generate string context from the tasks."""
task_outputs = (

View File

@@ -1,15 +1,7 @@
"""
Module for handling task guardrail validation results.
This module provides the GuardrailResult class which standardizes
the way task guardrails return their validation results.
"""
from typing import Any, Optional, Tuple, Union
from typing import Any, Callable, Optional, Tuple, Union
from pydantic import BaseModel, field_validator
class GuardrailResult(BaseModel):
"""Result from a task guardrail execution.
@@ -54,3 +46,48 @@ class GuardrailResult(BaseModel):
result=data if success else None,
error=data if not success else None
)
def process_guardrail(output: Any, guardrail: Callable, retry_count: int) -> GuardrailResult:
"""Process the guardrail for the agent output.
Args:
output: The output to validate with the guardrail
Returns:
GuardrailResult: The result of the guardrail validation
"""
from crewai.task import TaskOutput
from crewai.lite_agent import LiteAgentOutput
assert isinstance(output, TaskOutput) or isinstance(output, LiteAgentOutput), "Output must be a TaskOutput or LiteAgentOutput"
assert guardrail is not None
from crewai.utilities.events import (
LLMGuardrailCompletedEvent,
LLMGuardrailStartedEvent,
)
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
crewai_event_bus.emit(
None,
LLMGuardrailStartedEvent(
guardrail=guardrail, retry_count=retry_count
),
)
result = guardrail(output)
guardrail_result = GuardrailResult.from_tuple(result)
crewai_event_bus.emit(
None,
LLMGuardrailCompletedEvent(
success=guardrail_result.success,
result=guardrail_result.result,
error=guardrail_result.error,
retry_count=retry_count,
),
)
return guardrail_result

View File

@@ -501,8 +501,7 @@ def test_agent_custom_max_iterations():
def test_agent_repeated_tool_usage(capsys):
@tool
def get_final_answer() -> float:
"""Get the final answer but don't give it yet, just re-use this
tool non-stop."""
"""Get the final answer but don't give it yet, just re-use this tool non-stop."""
return 42
agent = Agent(
@@ -527,12 +526,42 @@ def test_agent_repeated_tool_usage(capsys):
)
captured = capsys.readouterr()
assert (
"I tried reusing the same input, I must stop using this action input. I'll try something else instead."
in captured.out
output = (
captured.out.replace("\n", " ")
.replace(" ", " ")
.strip()
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("[", "")
.replace("]", "")
.replace("bold", "")
.replace("blue", "")
.replace("yellow", "")
.replace("green", "")
.replace("red", "")
.replace("dim", "")
.replace("🤖", "")
.replace("🔧", "")
.replace("", "")
.replace("\x1b[93m", "")
.replace("\x1b[00m", "")
.replace("\\", "")
.replace('"', "")
.replace("'", "")
)
# Look for the message in the normalized output, handling the apostrophe difference
expected_message = (
"I tried reusing the same input, I must stop using this action input."
)
assert (
expected_message in output
), f"Expected message not found in output. Output was: {output}"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys):
@@ -564,11 +593,43 @@ def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys):
)
captured = capsys.readouterr()
assert (
"I tried reusing the same input, I must stop using this action input. I'll try something else instead."
in captured.out
output = (
captured.out.replace("\n", " ")
.replace(" ", " ")
.strip()
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("", "")
.replace("[", "")
.replace("]", "")
.replace("bold", "")
.replace("blue", "")
.replace("yellow", "")
.replace("green", "")
.replace("red", "")
.replace("dim", "")
.replace("🤖", "")
.replace("🔧", "")
.replace("", "")
.replace("\x1b[93m", "")
.replace("\x1b[00m", "")
.replace("\\", "")
.replace('"', "")
.replace("'", "")
)
# Look for the message in the normalized output, handling the apostrophe difference
expected_message = (
"I tried reusing the same input, I must stop using this action input"
)
assert (
expected_message in output
), f"Expected message not found in output. Output was: {output}"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_moved_on_after_max_iterations():
@@ -2038,7 +2099,7 @@ def mock_get_auth_token():
@patch("crewai.cli.plus_api.PlusAPI.get_agent")
def test_agent_from_repository(mock_get_agent, mock_get_auth_token):
from crewai_tools import SerperDevTool, XMLSearchTool
from crewai_tools import SerperDevTool, XMLSearchTool, CSVSearchTool, EnterpriseActionTool
mock_get_response = MagicMock()
mock_get_response.status_code = 200
@@ -2047,19 +2108,42 @@ def test_agent_from_repository(mock_get_agent, mock_get_auth_token):
"goal": "test goal",
"backstory": "test backstory",
"tools": [
{"module": "crewai_tools", "name": "SerperDevTool"},
{"module": "crewai_tools", "name": "XMLSearchTool"},
{"module": "crewai_tools", "name": "SerperDevTool", "init_params": {"n_results": 30}},
{"module": "crewai_tools", "name": "XMLSearchTool", "init_params": {"summarize": True}},
{"module": "crewai_tools", "name": "CSVSearchTool", "init_params": {}},
# using a tools that returns a list of BaseTools
{"module": "crewai_tools", "name": "CrewaiEnterpriseTools", "init_params": {"actions_list": [], "enterprise_token": "test_key"}},
],
}
mock_get_agent.return_value = mock_get_response
agent = Agent(from_repository="test_agent")
tool_action = EnterpriseActionTool(
name="test_name",
description="test_description",
enterprise_action_token="test_token",
action_name="test_action_name",
action_schema={"test": "test"},
)
with patch("crewai_tools.CrewaiEnterpriseTools", return_value=[tool_action]):
agent = Agent(from_repository="test_agent")
assert agent.role == "test role"
assert agent.goal == "test goal"
assert agent.backstory == "test backstory"
assert len(agent.tools) == 2
assert len(agent.tools) == 4
assert isinstance(agent.tools[0], SerperDevTool)
assert agent.tools[0].n_results == 30
assert isinstance(agent.tools[1], XMLSearchTool)
assert agent.tools[1].summarize
assert isinstance(agent.tools[2], CSVSearchTool)
assert not agent.tools[2].summarize
assert isinstance(agent.tools[3], EnterpriseActionTool)
assert agent.tools[3].name == "test_name"
@patch("crewai.cli.plus_api.PlusAPI.get_agent")
@@ -2072,7 +2156,7 @@ def test_agent_from_repository_override_attributes(mock_get_agent, mock_get_auth
"role": "test role",
"goal": "test goal",
"backstory": "test backstory",
"tools": [{"name": "SerperDevTool", "module": "crewai_tools"}],
"tools": [{"name": "SerperDevTool", "module": "crewai_tools", "init_params": {}}],
}
mock_get_agent.return_value = mock_get_response
agent = Agent(from_repository="test_agent", role="Custom Role")
@@ -2092,7 +2176,12 @@ def test_agent_from_repository_with_invalid_tools(mock_get_agent, mock_get_auth_
"role": "test role",
"goal": "test goal",
"backstory": "test backstory",
"tools": [{"name": "DoesNotExist", "module": "crewai_tools",}],
"tools": [
{
"name": "DoesNotExist",
"module": "crewai_tools",
}
],
}
mock_get_agent.return_value = mock_get_response
with pytest.raises(
@@ -2126,3 +2215,64 @@ def test_agent_from_repository_agent_not_found(mock_get_agent, mock_get_auth_tok
match="Agent test_agent does not exist, make sure the name is correct or the agent is available on your organization",
):
Agent(from_repository="test_agent")
@patch("crewai.cli.plus_api.PlusAPI.get_agent")
@patch("crewai.utilities.agent_utils.Settings")
@patch("crewai.utilities.agent_utils.console")
def test_agent_from_repository_displays_org_info(
mock_console, mock_settings, mock_get_agent, mock_get_auth_token
):
mock_settings_instance = MagicMock()
mock_settings_instance.org_uuid = "test-org-uuid"
mock_settings_instance.org_name = "Test Organization"
mock_settings.return_value = mock_settings_instance
mock_get_response = MagicMock()
mock_get_response.status_code = 200
mock_get_response.json.return_value = {
"role": "test role",
"goal": "test goal",
"backstory": "test backstory",
"tools": [],
}
mock_get_agent.return_value = mock_get_response
agent = Agent(from_repository="test_agent")
mock_console.print.assert_any_call(
"Fetching agent from organization: Test Organization (test-org-uuid)",
style="bold blue",
)
assert agent.role == "test role"
assert agent.goal == "test goal"
assert agent.backstory == "test backstory"
@patch("crewai.cli.plus_api.PlusAPI.get_agent")
@patch("crewai.utilities.agent_utils.Settings")
@patch("crewai.utilities.agent_utils.console")
def test_agent_from_repository_without_org_set(
mock_console, mock_settings, mock_get_agent, mock_get_auth_token
):
mock_settings_instance = MagicMock()
mock_settings_instance.org_uuid = None
mock_settings_instance.org_name = None
mock_settings.return_value = mock_settings_instance
mock_get_response = MagicMock()
mock_get_response.status_code = 401
mock_get_response.text = "Unauthorized access"
mock_get_agent.return_value = mock_get_response
with pytest.raises(
AgentRepositoryError,
match="Agent test_agent could not be loaded: Unauthorized access",
):
Agent(from_repository="test_agent")
mock_console.print.assert_any_call(
"No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.",
style="yellow",
)

View File

@@ -0,0 +1,137 @@
interactions:
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '694'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//nFfNchtHDr7rKVBz0a6KVJGUZMm6SVrJcSw6Ktmb7NY6pQJ7wBlEPd1T
6B5S3JTP+yw55AVy9T7YFnr4Jy7pRLmwioP+wfcB+Br4eQ8g4zw7h8yUGE1V2+5l0dh/xOvXo5MT
ufDvJk//lNvJm+9HvR9+errPOrrDj34iExe7Do2vakuRvWvNRggj6an90+PXJ69OX50dJUPlc7K6
rahj99h3K3bcHfQGx93eabd/Nt9dejYUsnP41x4AwM/pV/10OT1l59DrLL5UFAIWlJ0vFwFk4q1+
yTAEDhFdzDoro/Eukkuufyx9U5TxHN6C81Mw6KDgCQFCof4DujAlAfjkbtihhYv0/xw+lgRjb62f
siuAAyCEKI2JjVAOfkIyYZqCH0MsCUwjQi5C9DX0exC8MSRQW5yRBGCXFk292BxGGPSA9IkFapKx
lwqdodABNCXThCpyUf+59ia0Friq0cT5PiiwIsCg139noh+RwKA3ODr/5D65/iEcHNyyd2RhSCHw
wQH85a2LJDBkrPivnxwAdOHg4M4H1ngeHJzDjZcpSr60XfnGRZmp6UIKcpEdLo0Xa27qilO4RGu9
g3z/OwHUg0IHqsZGri3B369vLuCqxKpm7wLcEhYNQeRoFbMlzJXj5TUQvaLpw5WvES6qL78IG0xs
DHqDAdy8vbmAHxKZV00NEzbRy+xQsQ8U+7uZZXQwHGFdf/lF0d+hcIAPyC5235BUyO7FLNyIxmgn
BRtOTdk5Eo38oNc/64BrKhLfBLhlxd5fon90fupg7AVKDhBqorwDufBoZNkVbQ4UHm03GC9KUy1+
SiEkuEcK91p0JXyDaNHlCneIzpQUNOJXHGcvhvpeTbPdUDFECnGe3hotITTlCqP6m7J+a+A7aaO6
jGCkMYwWtJp1w4bn+wGi0MhSV/nULYEweNfyOhioqhwlJo5T4GnCDv5GcCnNzNEfpmLI+ZjJ5iTb
2LgkW3BT7aTjHc0SogofSVIkNy5dq4Q7oYpJNktAg/w8EejJUK3+oYUJB/YuLapV7pS5EVuObc6f
KPQr4RAZnYd73ZN7BX9hu+8xBHlxAtx5iU2Bdifmk60Fj9Z2I1e0LGlNBNDEbUtBlWtHSszrxY9X
XNl1jnT7tSs0wzvwoUZ2LWtvI9qWhldKw3uaVSjwrRzO8X/DFu2L8V8K/pt3o3/3LFRjiyzJmfDI
1nagJCgxwNS7jWpvQ6hVkwPChONa5rdX7gdwOA97JKwgNMZQCB1gZ2yTSF2UgrI5V0hSgUwsnCoL
9/ogRLilKbrcT8NjegIuUQxZ7/BPpIPyvpOOe1I+KE+MPNOqeZp2Ehfqb1LJSxWPIbn9AHethKQE
mhd1byH0/Q7oOy48aqIeVhJO2M5Uby51l4Nh49iU+2HBEgUY0dgLQeUniSJdOked6DlLb2PziDD0
ufB//6PE3BNaGGIunP8ZfbgSj5F3P476ADwrlzbXNaTaUeg6tAp+zY/9sOW9FG6qumyzaFFh88sV
qfI71h4mLLqSdPPyTUoEvFYChr7EinL4gBZLZeCWJyS19y+vlOtiVsfflca5Li6v0Rqx9TyJKyUk
+buhjorz662DrljqxRtvc3Jw6X2cKxJsPTfx0O8pEd+z+/Kr4SbAt19+c+xlazp8lY+vSMf2YuEk
4CGibEg+FqlY1pVkqRU1T/y6WvxOqsxbogV+LaZuap3a5zMx8LGkQMsWtcQJablpM00u2hnkZDVc
lAM9RUEvOTuU2bOGddGOpupIjpfe5hC4cDxmgy4Cu7FtyBmCKcfyWSfsx/NGeaPQ21xmSQoY9teq
OzVDKI6SurDLecJ5gxbQGG8xp3C4PgYIjZuAOoq4xto1AzrnY9LZNID8OLd8Xo4c1he1+FHY2JqN
2XEoHyTRqONFiL7OkvXzHsCPabRpnk0rWS2+quND9I+UrusPBu152WqiWllPjxbWqBFfGc5Ojjtb
DnzIKSLbsDYdZQZNSflq62qUwiZnv2bYW4P9/+5sO7uFzq74I8evDEbbGcofaqGczXPIq2VCOnHu
WrakOTmcBR3BDD1EJtFQ5DTGxrZzYBZmIVL1MGZXkNTC7TA4rh9eDXBwhGd9Gmd7n/f+BwAA//8D
AMMI9CsaDwAA
headers:
CF-RAY:
- 94d9be627c40f260-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 15:02:05 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=qYkxv9nLxeWAtPBvECxNw8fLnoBHLorJdRI8.xVEVEA-1749567725-1.0.1.1-75sp4gwHGJocK1MFkSgRcB4xJUiCwz31VRD4LAmQGEmfYB0BMQZ5sgWS8e_UMbjCaEhaPNO88q5XdbLOCWA85_rO0vYTb4hp6tmIiaerhsM;
path=/; expires=Tue, 10-Jun-25 15:32:05 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=HRKCwkyTqSXpCj9_i_T5lDtlr_INA290o0b3k.26oi8-1749567725794-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '42674'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '42684'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999859'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_d92e6f33fa5e0fbe43349afee8f55921
status:
code: 200
message: OK
version: 1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,130 @@
interactions:
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 1 best players
in the world?"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '693'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//TFPRbttGEHzPVwz04taQBFt1nFZvsgsnRlvYqI0aafOyPK7IjY97xO1R
CpMv6nf0x4qlZLcvBHh3Mzs7s/vtDTCTerbGLLRUQtfHxVUzPF6udsaXHx/Cn7//8fG8e9rf/XxF
X+vqaTZ3RKo+cygvqGVIXR+5SNLDdchMhZ31/N3FT28vL99enE0XXao5Oqzpy+IiLTpRWazOVheL
s3eL8x+P6DZJYJut8dcbAPg2fV2n1vxltsbENZ10bEYNz9avj4BZTtFPZmQmVkjLbP7fZUhaWCfp
j20amrascQtNewRSNLJjEBrXD1LbcwY+6Y0oRWym/zUeW0ZJPfpII2eIorSMfcqxnoMMaYu7UFLF
Gauz1Q9ziOFXScoRv7GZLPEkNccRmRvKNdcTSNmBzjRVZyuwFALnY52Jl2JEkY7nBya0ZGipBikk
xsFKljQYAmXmjNBSplA4y1euUY3gLyVTyrUo5RH2LDHaHDsxSToHaY2Q1E1jDePSO/+kv/CITWiF
d9yxFlv78QKnp9dxqOz0dH2UYj1rmfR39DllKaMLbuVVDRXcXOOKcuCYlObYt5wZLaPiQB1P2BCH
6sS8z4X3ichUizawkDLnJT5MzxQlk9qWc+YaJeGeshgeSLQs3nPuSBTf3T+8/97TWZ2tzpcHzbda
OCv5pFJ07R+8RI1NbliLKDnZTkJJeXwJ1uG4Tj1h0/3zd5ZAk1PHqxVubm82ePL0cT30cxiHIbtm
7z1yQ2H0gAkvddFyTsuji5s95fp/Nnqi+6Tohlikj4wrijEp6pO7DJoez9FK00Zp2vJSxgqVwbxM
mfy08jKdTUwVxTgiKYx3nCkihUAeuS094PtIo/M8lDHylO5BiRieNe0V25SnIqIhcy1VZNRZqio6
iiqJUsY5+sxBjNH72mlzGKc+pyhbCWgSxYWHKNos8cGd8ZVjz8PnpMm085HxZveGVjpPoiPlYccZ
pc2+qyjeNGreshobjKmLbBbHOTp6PrjRgaYx9s13oK9yOkS5FY71chrrW93GgTUcOr7iMWk92ShW
JNhxwU4M0vUUXhka8uU7uHEk8CuyXqbMj7t66H5Kpk+5WEdqrfRo6V8AAAD//4xVy24CIRTdz1cQ
1m1Tx+nCr+gXGHKFO85NGSDAaF347w2ggtUmXR84931O0hcdLJP5mlC14yNzPfmJQlrB0qkkWWQW
VMyhH62fIUVNApUXgWk8oGZH1JqRiTYzzqRe1+8hDRFYEhOY83kWVKEimbcx55mFoLTlM2+IvuoL
fuPs0gQxsOMEkVFkM4IJiWmHD9vWaiGLVsHprRVfj+MSIBmAWbRuADDGxpxQlv3tBTnfhF7bvfN2
F3595SMZCpPwCMGaJOohWsczeu4Y22ZDWe48gjtvZxdFtF+Yw636vvDx6mMV7TebCxptBF2BoV+9
PCEUCiOQDo0ncQlyQlW/VgODRZFtgK4p+zGdZ9yldDL7/9BXQEp0EZVw6aTlfcn1mcfk8389u7U5
J8wD+gNJFJHQp1EoHGHRxX15OIWIsxjJ7NE7T8WCRyfWA3wMgJu15N25+wEAAP//AwDdzCHTkAgA
AA==
headers:
CF-RAY:
- 94d9a27f5dc000f9-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:42:51 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=7hq1JYlSmmLvjUR7npK1vcLJYOvCPn947S.EYBtvTcQ-1749566571-1.0.1.1-11XCSwdUqYCYC3zE9DZk20c_BHXTPqEi6YMhVtX9dekgrj0J3a4EHGdHvcnhBNkIxYzhM4zzQsetx2sxisMk62ywkO8Tzo3rlYdo__Kov7w;
path=/; expires=Tue, 10-Jun-25 15:12:51 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=bhxj6kzt6diFCyNbiiw60v4lKiUKaoHjQ3Yc4KWW4OI-1749566571331-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '30419'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '30424'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999859'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_b5983a9572e28ded39da7b12e678e2b7
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,643 @@
interactions:
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '694'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
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
BF+cIJ0em8Hel71fAQAA//8DAICIe4nBDwAA
headers:
CF-RAY:
- 94d9947e9abcf1fe-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:33:27 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=UscrLNUFPQp17ivpT2nYqbIb8GsK9e0GpWC7sIKWwnU-1749566007-1.0.1.1-fCm5jdN02Agxc9.Ep4aAnKukyBp9S3iOLK9NY51NLG1zib3MnfjyCm5HhsWtUvr2lIjQpD_EWosVk4JuLbGxrKYZBa4WTendGsY9lCU9naU;
path=/; expires=Tue, 10-Jun-25 15:03:27 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=CGbpwQsAGlm5OcWs4NP0JuyqJh.mIqHyUZjIdKm8_.I-1749566007688-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '40370'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '40375'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999859'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_94fb13dc93d3bc9714811ff4ede4c08f
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Guardrail Agent. You
are a expert at validating the output of a task. By providing effective feedback
if the output is not valid.\nYour personal goal is: Validate the output of the
task\n\nTo give my best complete final answer to the task respond using the
exact following format:\n\nThought: I now can give a great answer\nFinal Answer:
Your final answer must be the great and the most complete as possible, it must
be outcome described.\n\nI MUST use these formats, my job depends on it!\nIMPORTANT:
Your final answer MUST contain all the information requested in the following
format: {\n \"valid\": bool,\n \"feedback\": str | None\n}\n\nIMPORTANT: Ensure
the final output does not include any code block markers like ```json or ```python."},
{"role": "user", "content": "\n Ensure the following task result complies
with the given guardrail.\n\n Task result:\n Here are the top
10 best soccer players in the world as of October 2023, based on their performance,
impact, and overall contributions to the game:\n\n1. **Lionel Messi** (Inter
Miami)\n - Position: Forward\n - Country: Argentina\n - Highlights: Messi
continues to showcase his exceptional dribbling, vision, and playmaking ability,
leading Argentina to victory in the 2022 FIFA World Cup and significantly contributing
to his club''s successes.\n\n2. **Kylian Mbapp\u00e9** (Paris Saint-Germain)\n -
Position: Forward\n - Country: France\n - Highlights: Known for his blistering
speed and clinical finishing, Mbapp\u00e9 is a key player for both PSG and the
French national team, known for his performances in Ligue 1 and international
tournaments.\n\n3. **Erling Haaland** (Manchester City)\n - Position: Forward\n -
Country: Norway\n - Highlights: Haaland''s goal-scoring prowess and strength
have made him one of the most feared strikers in Europe, helping Manchester
City secure several titles including the UEFA Champions League.\n\n4. **Kevin
De Bruyne** (Manchester City)\n - Position: Midfielder\n - Country: Belgium\n -
Highlights: De Bruyne\u2019s exceptional passing, control, and vision make him
one of the best playmakers in the world, instrumental in Manchester City\u2019s
dominance in domestic and European competitions.\n\n5. **Cristiano Ronaldo**
(Al Nassr)\n - Position: Forward\n - Country: Portugal\n - Highlights:
Despite moving to the Saudi Pro League, Ronaldo''s extraordinary talent and
goal-scoring ability keep him in the conversation among the best, having had
a legendary career across multiple leagues.\n\n6. **Neymar Jr.** (Al Hilal)\n -
Position: Forward\n - Country: Brazil\n - Highlights: Neymar''s agility,
creativity, and skill have kept him as a top performer in soccer, now showcasing
his talents in the Saudi Pro League while maintaining a strong national team
presence.\n\n7. **Robert Lewandowski** (Barcelona)\n - Position: Forward\n -
Country: Poland\n - Highlights: The prolific striker continues to score consistently
in La Liga, known for his finishing, positioning, and aerial ability, contributing
to Barcelona\u2019s successes.\n\n8. **Karim Benzema** (Al Ittihad)\n - Position:
Forward\n - Country: France\n - Highlights: The former Real Madrid star
remains a top talent, displaying leadership and technical skills, now continuing
his career in the Saudi Pro League.\n\n9. **Luka Modri\u0107** (Real Madrid)\n -
Position: Midfielder\n - Country: Croatia\n - Highlights: A master of midfield
control and passing, Modri\u0107 remains an influential figure at Real Madrid,
showcasing his experience and football intelligence.\n\n10. **Mohamed Salah**
(Liverpool)\n - Position: Forward\n - Country: Egypt\n - Highlights:
Salah''s explosive pace and goal-scoring ability keep him as a central figure
for Liverpool in the Premier League and European competitions, maintaining his
status as one of the elite forwards.\n\nThese players have demonstrated exceptional
skill, consistency, and impact in their respective teams and leagues, solidifying
their positions among the best in the world.\n\n Guardrail:\n Only
include Brazilian players, both women and men\n \n Your task:\n -
Confirm if the Task result complies with the guardrail.\n - If not, provide
clear feedback explaining what is wrong (e.g., by how much it violates the rule,
or what specific part fails).\n - Focus only on identifying issues \u2014
do not propose corrections.\n - If the Task result complies with the
guardrail, saying that is valid\n "}], "model": "gpt-4o-mini", "stop":
["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '4676'
content-type:
- application/json
cookie:
- __cf_bm=UscrLNUFPQp17ivpT2nYqbIb8GsK9e0GpWC7sIKWwnU-1749566007-1.0.1.1-fCm5jdN02Agxc9.Ep4aAnKukyBp9S3iOLK9NY51NLG1zib3MnfjyCm5HhsWtUvr2lIjQpD_EWosVk4JuLbGxrKYZBa4WTendGsY9lCU9naU;
_cfuvid=CGbpwQsAGlm5OcWs4NP0JuyqJh.mIqHyUZjIdKm8_.I-1749566007688-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAA4xUwWobMRC9+ysGnVqwjZ3YjuNbQim0hULBtIc6mLE0uzuxVtpqtHbckH8v2o29
TpNCLwuapzfzZvRmH3sAio1agNIFRl1WdnCb119+LIvZh+/+h9zM55Op3Wzv5dt4rKei+onhN/ek
45E11L6sLEX2roV1IIyUso6vJtfT2Ww0mjdA6Q3ZRMurOJj4QcmOBxeji8lgdDUYz5/ZhWdNohbw
swcA8Nh8k05n6EEtYNQ/RkoSwZzU4nQJQAVvU0ShCEtEF1W/A7V3kVwjfVn4Oi/iAj6B83vQ6CDn
HQFCnvQDOtlTAFi5j+zQwk1zXsDjygGs1A4tm5VaQIZWqN8GMyKzQb1N8ZVaFgQRZQuBpLYRjCcB
5yM0AzvAnmMBsSDIawwmIFtAAY7ATtvakEBl8UBBIAu+hB0G9rWA9rWLgUkAnQHv7AFKcmn8AnHv
4Tbgb7aM7kR/95UOJQb4HIYNpz2+h33BloAeUjV2+Rlz70vq+Cl7IHsYwvKFWqlIc8YarT1AoF81
SRSI/thAK+6Vnj5sfCyeayQ9Jbl+EqML4HZCWW0ztpbMcKVW7un8CQNltWCykautPQPQOR+xmUMy
z90z8nSyi/V5FfxG/qKqjB1LsQ6E4l2yhkRfqQZ96gHcNbasXzhNVcGXVVxHv6Wm3PV01uZT3TZ0
6PwIRh/RdvHxaHLRfyPh2lBEtnLmbKVRF2Q6brcGWBv2Z0DvrO3Xct7K3bbOLv+f9B2gNVWRzLoK
ZFi/bLm7Fij9Lf517TTmRrASCjvWtI5MIT2FoQxr2+6wkoNEKtcZu5xCFbhd5KxaX05wOkG6vtSq
99T7AwAA//8DABZK9w/WBAAA
headers:
CF-RAY:
- 94d9957c4b46f1fe-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:33:46 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '18280'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '18287'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149998889'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_853da6b9ca8f386ff1427c837346290d
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}, {"role": "assistant", "content": "Thought: I now can give a
great answer \nFinal Answer: Here are the top 10 best soccer players in the
world as of October 2023, based on their performance, impact, and overall contributions
to the game:\n\n1. **Lionel Messi** (Inter Miami)\n - Position: Forward\n -
Country: Argentina\n - Highlights: Messi continues to showcase his exceptional
dribbling, vision, and playmaking ability, leading Argentina to victory in the
2022 FIFA World Cup and significantly contributing to his club''s successes.\n\n2.
**Kylian Mbapp\u00e9** (Paris Saint-Germain)\n - Position: Forward\n - Country:
France\n - Highlights: Known for his blistering speed and clinical finishing,
Mbapp\u00e9 is a key player for both PSG and the French national team, known
for his performances in Ligue 1 and international tournaments.\n\n3. **Erling
Haaland** (Manchester City)\n - Position: Forward\n - Country: Norway\n -
Highlights: Haaland''s goal-scoring prowess and strength have made him one of
the most feared strikers in Europe, helping Manchester City secure several titles
including the UEFA Champions League.\n\n4. **Kevin De Bruyne** (Manchester City)\n -
Position: Midfielder\n - Country: Belgium\n - Highlights: De Bruyne\u2019s
exceptional passing, control, and vision make him one of the best playmakers
in the world, instrumental in Manchester City\u2019s dominance in domestic and
European competitions.\n\n5. **Cristiano Ronaldo** (Al Nassr)\n - Position:
Forward\n - Country: Portugal\n - Highlights: Despite moving to the Saudi
Pro League, Ronaldo''s extraordinary talent and goal-scoring ability keep him
in the conversation among the best, having had a legendary career across multiple
leagues.\n\n6. **Neymar Jr.** (Al Hilal)\n - Position: Forward\n - Country:
Brazil\n - Highlights: Neymar''s agility, creativity, and skill have kept
him as a top performer in soccer, now showcasing his talents in the Saudi Pro
League while maintaining a strong national team presence.\n\n7. **Robert Lewandowski**
(Barcelona)\n - Position: Forward\n - Country: Poland\n - Highlights:
The prolific striker continues to score consistently in La Liga, known for his
finishing, positioning, and aerial ability, contributing to Barcelona\u2019s
successes.\n\n8. **Karim Benzema** (Al Ittihad)\n - Position: Forward\n -
Country: France\n - Highlights: The former Real Madrid star remains a top
talent, displaying leadership and technical skills, now continuing his career
in the Saudi Pro League.\n\n9. **Luka Modri\u0107** (Real Madrid)\n - Position:
Midfielder\n - Country: Croatia\n - Highlights: A master of midfield control
and passing, Modri\u0107 remains an influential figure at Real Madrid, showcasing
his experience and football intelligence.\n\n10. **Mohamed Salah** (Liverpool)\n -
Position: Forward\n - Country: Egypt\n - Highlights: Salah''s explosive
pace and goal-scoring ability keep him as a central figure for Liverpool in
the Premier League and European competitions, maintaining his status as one
of the elite forwards.\n\nThese players have demonstrated exceptional skill,
consistency, and impact in their respective teams and leagues, solidifying their
positions among the best in the world."}, {"role": "user", "content": "The task
result does not comply with the guardrail as it includes players from various
countries and only mentions two Brazilian players (Neymar Jr. and Neymar) while
excluding Brazilian women players entirely. The guardrail specifically requests
to include only Brazilian players, both women and men, which is not fulfilled."}],
"model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '4338'
content-type:
- application/json
cookie:
- __cf_bm=UscrLNUFPQp17ivpT2nYqbIb8GsK9e0GpWC7sIKWwnU-1749566007-1.0.1.1-fCm5jdN02Agxc9.Ep4aAnKukyBp9S3iOLK9NY51NLG1zib3MnfjyCm5HhsWtUvr2lIjQpD_EWosVk4JuLbGxrKYZBa4WTendGsY9lCU9naU;
_cfuvid=CGbpwQsAGlm5OcWs4NP0JuyqJh.mIqHyUZjIdKm8_.I-1749566007688-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
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
headers:
CF-RAY:
- 94d995f0ca0cf1fe-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:34:22 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '35941'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '35946'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149998983'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_c874fed664f962f5ee90decb8ebad875
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Guardrail Agent. You
are a expert at validating the output of a task. By providing effective feedback
if the output is not valid.\nYour personal goal is: Validate the output of the
task\n\nTo give my best complete final answer to the task respond using the
exact following format:\n\nThought: I now can give a great answer\nFinal Answer:
Your final answer must be the great and the most complete as possible, it must
be outcome described.\n\nI MUST use these formats, my job depends on it!\nIMPORTANT:
Your final answer MUST contain all the information requested in the following
format: {\n \"valid\": bool,\n \"feedback\": str | None\n}\n\nIMPORTANT: Ensure
the final output does not include any code block markers like ```json or ```python."},
{"role": "user", "content": "\n Ensure the following task result complies
with the given guardrail.\n\n Task result:\n Here are the top
10 best soccer players in the world, focusing exclusively on Brazilian players,
both women and men, based on their performance and impact in the game as of
October 2023:\n\n1. **Neymar Jr.** \n - Position: Forward \n - Club: Al
Hilal \n - Highlights: One of the most skilled forwards globally, Neymar
continues to dazzle with his dribbling, playmaking, and goal-scoring ability,
having a significant impact on both his club and the Brazilian national team.\n\n2.
**Vin\u00edcius J\u00fanior** \n - Position: Forward \n - Club: Real Madrid \n -
Highlights: Vin\u00edcius has emerged as a key player for Real Madrid, noted
for his speed, technical skills, and crucial goals in important matches, showcasing
his talent on both club and international levels.\n\n3. **Richarlison** \n -
Position: Forward \n - Club: Tottenham Hotspur \n - Highlights: Known
for his versatility and aerial ability, Richarlison has become a vital member
of the national team and has the capability to change the game with his pace
and scoring ability.\n\n4. **Marta** \n - Position: Forward \n - Club:
Orlando Pride \n - Highlights: A true legend of women''s soccer, Marta has
consistently showcased her skill, leadership, and goal-scoring prowess, earning
numerous awards and accolades throughout her legendary career.\n\n5. **Andressa
Alves** \n - Position: Midfielder \n - Club: Roma \n - Highlights:
A pivotal player in women''s soccer, Andressa has displayed her exceptional
skills and tactical awareness both in club play and for the Brazilian national
team.\n\n6. **Alana Santos** \n - Position: Defender \n - Club: Benfica \n -
Highlights: Alana is recognized for her defensive prowess and ability to contribute
to the attack, establishing herself as a key player for both her club and the
national team.\n\n7. **Gabriel Jesus** \n - Position: Forward \n - Club:
Arsenal \n - Highlights: With a flair for scoring and assisting, Gabriel
Jesus is an essential part of the national team, known for his work rate and
intelligence on the field.\n\n8. **Ta\u00eds Ara\u00fajo** \n - Position:
Midfielder \n - Club: S\u00e3o Paulo \n - Highlights: A rising star in
Brazilian women''s soccer, Ta\u00eds has gained recognition for her strong performances
in midfield, showcasing both skill and creativity.\n\n9. **Thiago Silva** \n -
Position: Defender \n - Club: Chelsea \n - Highlights: An experienced
and reliable center-back, Silva\u2019s leadership and defensive abilities have
made him a cornerstone for Chelsea and the Brazilian national team.\n\n10. **Bia
Zaneratto** \n - Position: Forward \n - Club: Palmeiras \n - Highlights:
A talented forward, Bia has become known for her goal-scoring capabilities and
playmaking skills, contributing significantly to both her club and the national
team.\n\nThis list highlights the incredible talent and contributions of Brazilian
players in soccer, showcasing their skills across both men''s and women''s games,
thus representing Brazil''s rich soccer legacy.\n\n Guardrail:\n Only
include Brazilian players, both women and men\n \n Your task:\n -
Confirm if the Task result complies with the guardrail.\n - If not, provide
clear feedback explaining what is wrong (e.g., by how much it violates the rule,
or what specific part fails).\n - Focus only on identifying issues \u2014
do not propose corrections.\n - If the Task result complies with the
guardrail, saying that is valid\n "}], "model": "gpt-4o-mini", "stop":
["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '4571'
content-type:
- application/json
cookie:
- __cf_bm=UscrLNUFPQp17ivpT2nYqbIb8GsK9e0GpWC7sIKWwnU-1749566007-1.0.1.1-fCm5jdN02Agxc9.Ep4aAnKukyBp9S3iOLK9NY51NLG1zib3MnfjyCm5HhsWtUvr2lIjQpD_EWosVk4JuLbGxrKYZBa4WTendGsY9lCU9naU;
_cfuvid=CGbpwQsAGlm5OcWs4NP0JuyqJh.mIqHyUZjIdKm8_.I-1749566007688-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAA4ySTW/bMAyG7/4VhM72kLj5WHxrDwEKtNilGDYshcFItK1WlgRJTjYE+e+D7DR2
tw7YxYD58KX4kjwlAEwKVgDjDQbeWpXd1d3Dl+5Yfb3dPD6K7bri26cw899f5vruG0ujwuxfiIc3
1SduWqsoSKMHzB1hoFh1vl5slqvVbJX3oDWCVJTVNmQLk7VSyyyf5Ytsts7mny/qxkhOnhXwIwEA
OPXf2KcW9JMVMEvfIi15jzWx4poEwJxRMcLQe+kD6sDSEXKjA+m+9afGdHUTCrgHbY7AUUMtDwQI
dewfUPsjOYCd3kqNCm77/wJOOw2wYwdUUuxYAcF1lA6xikjskb/GsO6U2unz9HFHVedRXeAEoNYm
YBxgb/v5Qs5Xo8rU1pm9/0PKKqmlb0pH6I2OpnwwlvX0nAA89wPt3s2IWWdaG8pgXql/bpMvh3ps
3ONI8/UFBhNQTVTLPP2gXikooFR+shLGkTckRum4P+yENBOQTFz/3c1HtQfnUtf/U34EnJMNJErr
SEj+3vGY5iie+b/SrlPuG2ae3EFyKoMkFzchqMJODcfH/C8fqC0rqWty1snhAitb3ixwuUDa3HCW
nJPfAAAA//8DAOHOvQuPAwAA
headers:
CF-RAY:
- 94d996d2b877f1fe-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:34:38 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '15341'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '15343'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149998917'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_22419b91d42cf1e03278a29f3093ed3d
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,726 @@
interactions:
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '694'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAA5yXzVIbORDH7zxF11xiqLHLNp/xDUhMssEJm49N1S4pqq1pz/SikYaWxo6Tynmf
ZQ/7AnvNPtiWZIMNGAK5UHhaLfVPrW799XUNIOEs6UGiCvSqrHTzIK9HH/yzi6Mv44uT3z93X5fn
Fxefs49bR3vya5IGDzv8k5S/9GopW1aaPFszMysh9BRm7exuPd3e2d1s70VDaTPSwS2vfHPLNks2
3Oy2u1vN9m6zszf3LiwrckkP/lgDAPga/4Y4TUafkx6008svJTmHOSW9q0EAiVgdviToHDuPxifp
wqis8WRi6O8LW+eF78FLMHYCCg3kPCZAyEP8gMZNSABOTZ8NatiPv3vwviDwtoJOG4bkPDirFAlU
GqckDtiALwgmVnSWAjqwI3ijvB2SQLfd3UzjSkMCzsh4HjFlMERHGdjoyQJCioyHimRkpUSjyKXg
zllrlwKXFSofBudYBgOaDOyYBLWGgCc8rEMuHHg7n9ATlq4FL0gI2MX4nJda+VooA83O907Nqem0
YGPjmK0hDQNyjqHx0ngSGDCWDIf99Y2NUwMATTixjsMiPehbmaBk8++vaAr7fhYDuR48Ex4ONZs8
hTE7tiaF3KJuOmWFTQ44ZM1+2pq776uCaUwlGe96MKi150oTHKDW1kD25I3AhI0hSeHQVgj7JQkr
BFVgWcXZP4Z9h8O6uvoGjW67211vBcJuIHw11YwGBkOsqu9/Q+MEhR28Qza+eURSIpvHg76riLIU
PKnC8EVNKYzYsCvY5Kvh+i/7+3dE29lbT+GY85qgA569DmkuLzcjsyU5zwpUXblItRmonkvYZniB
qMOJaAzQqIJcSN8h++njiU7sJOyzm4Fdyxob59kov5rsoDYZOc05xjoJTmGiD8/7+3A4x3RwTBgA
Z+mMOdpcT+FEqGSSS+sMPkJuxdTRmA08IziQemroQZQDzkZMOiO5CzR0iuUTGvsXj398LldGm0Lg
rCv3IOTIth3ZULiEAzJfqERo7OvmS++5wOzxqTtaSlYaO1OJ5/F/j8qzQg1sPGnNORlFqylD1ays
vKuTeJPMwYR9AW8JNQwwE84i3U6ge03TEgV+kVZAgxesUT8erK+RJYVs0VUenKurstHXk3UrPVIH
zGZdxeB3Q/BvQ/f2cEwTNJmduHOGRv8QDlAUaWvwJ0Aum0MK1dwl/kASRn1/W4yd4yBcPQMyTxyc
xJtnKS/LBTinPMbQUPB6U5yfvr2AOLAFlpTBO9RYQOOYxySVtfqnO+FoQXiVrtU8N6po0ctndXR/
GUWCp4HgNzbf/1FcO/jl+7+GrUBj6ST+NMcjztoPy727fn8mOu14A9fnCAObCf/310qG+9rbQ/rb
PU3g7vq5xXV5tG63iPkdFpHeF+yiwgjCIwhF1rcFj6pFguIJcieFCt1N8RNutDu0jZCrSPmg3KLM
CQJsaH2xKPjgHEjFYPBFHeMgHzfQteb1A4Im9EgX9dkYZbqIsiJx0dFWbELOwpRhWR6Fe1jYh7KF
OgwfWQEao67jYikMax8idXSlEAscU6AJ8pSM11MYEhkQylEyyoJmdLakIByDUosic1lVrhCVrWWN
KzSqHQadbWqtlwxojPUxrKiuP80t3670tLZ5JXbobrgms1o+E0JnTdDOztsqidZvawCfom6vr0nx
pBJbVv7M23OKy3W63dl8yeK5sLDubG3Ord561AvD7s52umLCs4w8snZL0j9RqArKFq6LdwLWGdsl
w9oS9u1wVs09Q2eTP2T6hUEpqjxlZ5VQxuo68mKYUHhO3TXsaptjwIkjGbOiM88kIRUZjbDWs0dO
4qbOU3k2YpOTVMKzl86oOtvcwu0tpKebKln7tvY/AAAA//8DAJ67Vp33DQAA
headers:
CF-RAY:
- 94d9b5400dcd624b-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:55:42 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=8Yv8F0ZCFAo2lf.qoqxao70yxyjVvIV90zQqVF6bVzQ-1749567342-1.0.1.1-fZgnv3RDfunvCO1koxwwFJrHnxSx_rwS_FHvQ6xxDPpKHwYr7dTqIQLZrNgSX5twGyK4F22rUmkuiS6KMVogcinChk8lmHtJBTUVTFjr2KU;
path=/; expires=Tue, 10-Jun-25 15:25:42 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=wzh8YnmXvLq1G0RcIVijtzboQtCZyIe2uZiochkBLqE-1749567342267-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '33288'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '33292'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999859'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_6a587ea22edef774ecdada790a320cab
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}, {"role": "assistant", "content": "Thought: I now can give a
great answer \nFinal Answer: The top 10 best soccer players in the world, as
of October 2023, can be identified based on their recent performances, skills,
impact on games, and overall contributions to their teams. Here is the structured
list:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), Ligue 1 titles, multiple domestic cups.\n\n3. **Erling Haaland
(Manchester City)**\n - Position: Forward\n - Key Attributes: Power, speed,
goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA Champions
League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne (Manchester
City)**\n - Position: Midfielder\n - Key Attributes: Passing, vision, creativity.\n -
Achievements: Multiple Premier League titles, FA Cups, UEFA Champions League
winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n - Position: Forward\n -
Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n - Achievements:
2022 Ballon d''Or winner, multiple Champions Leagues with Real Madrid.\n\n6.
**Neymar Jr. (Al Hilal)**\n - Position: Forward\n - Key Attributes: Flair,
dribbling, creativity.\n - Achievements: Multiple domestic league titles,
Champions League runner-up.\n\n7. **Robert Lewandowski (FC Barcelona)**\n -
Position: Forward\n - Key Attributes: Finishing, positioning, aerial ability.\n -
Achievements: FIFA Best Men''s Player, multiple Bundesliga titles, La Liga champion
(2023).\n\n8. **Mohamed Salah (Liverpool)**\n - Position: Forward\n - Key
Attributes: Speed, finishing, dribbling.\n - Achievements: Premier League
champion, FA Cup, UEFA Champions League winner.\n\n9. **Vin\u00edcius J\u00fanior
(Real Madrid)**\n - Position: Forward\n - Key Attributes: Speed, dribbling,
creativity.\n - Achievements: UEFA Champions League winner (2022), La Liga
champion (2023).\n\n10. **Luka Modri\u0107 (Real Madrid)**\n - Position:
Midfielder\n - Key Attributes: Passing, vision, tactical intelligence.\n -
Achievements: Multiple Champions League titles, Ballon d''Or winner (2018).\n\nThis
list is compiled based on their current form, past performances, and contributions
to their respective teams in both domestic and international competitions. Player
rankings can vary based on personal opinion and specific criteria used for evaluation,
but these players have consistently been regarded as some of the best in the
world as of October 2023."}, {"role": "user", "content": "You are not allowed
to include Brazilian players"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '3594'
content-type:
- application/json
cookie:
- __cf_bm=8Yv8F0ZCFAo2lf.qoqxao70yxyjVvIV90zQqVF6bVzQ-1749567342-1.0.1.1-fZgnv3RDfunvCO1koxwwFJrHnxSx_rwS_FHvQ6xxDPpKHwYr7dTqIQLZrNgSX5twGyK4F22rUmkuiS6KMVogcinChk8lmHtJBTUVTFjr2KU;
_cfuvid=wzh8YnmXvLq1G0RcIVijtzboQtCZyIe2uZiochkBLqE-1749567342267-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//nJfNctpIEMfvfoouXYJdwgXYjm1u4JjEsam4kuzuYZ1yNaNG6njUo50Z
QUgq5zzLHvYF9pp9sK0ZwOAEx3EuFKjVTf+mP/TXpy2AhLOkC4kq0Kuy0s1+Xucvb06e+X7ndDzp
DflscN6yRBfng75O0uBhRu9J+aXXrjJlpcmzkblZWUJPIWr7cP/44Onh3sFxNJQmIx3c8so3902z
ZOFmp9XZb7YOm+2jhXdhWJFLuvDnFgDAp/gZ8pSMPiRdaKXLKyU5hzkl3dubABJrdLiSoHPsPIpP
0pVRGfEkMfW3hanzwnfhDMRMQaFAzhMChDzkDyhuShbgSgYsqKEXf3fhBVkCdoACdZUFUNDsPJgx
+ILAmwraLRiR8+CMUmSh0jgj64Al3jE1VmeALni8Ut6MyEKn1dlLgT4oXWcsOfQtfmTNKEvn7pVc
SXsXdnYu2AhpGJJzDI0z8WRhyFgynAy2d3auBACacGkch4p0YWDsFG22uH5OM+h5b3lUe3JdeGZ5
NNIseQoTdmwkhdygbjplbEgER6zZz3YX7j1VME2oJPGuC8Nae640QR+1NgLZk1cWpixCNoUTUyH0
SrKsEFSBZRWj/xHpT+rq9ho0Oq1OZ3s3EHYC4fkskg9HWFVf/4bGJVp28AZZfPM52RJZHg/6piLK
UvCkCuG/akphzMKuYMk3ww3OBr17sm0fbadwwXlN0AbPXpNLoVweRmZKcp4VqLpykWovUJ3acMzw
AlGjZNAYoqiCXCjfCfvZ44kuzTScs5uD3akai/Msym8m69eSkdOcY+zW4BQC/XY66MHJAtPBBWEA
nJcz1mhvO4VLSyWTXVrn8BFyP5aOJizwjKBv65nQT1EOORsz6YzsfaBhlNc7NC4YnjzclxuzTSFw
1pX7KeTIdhDZ0HIJfZKPVCI0erp55j0XmD2+dM/XipXGES/xJn73qDwr1MDiSWvOSRRtpgxTs3Hy
bjvxWzIHU/YFvCbUMMTMchbpnga612EPebigKUpmpu6GoTE4gT5aRdoIPh5ysJyvFKqFS/yBZBn1
jzdLHL5+2KFDkicOLuMWXENb7+FFVS8wzCTe3SuLAh4GxKEpsKQM3qDGAhoXPCFbGaN/eZmMV4TZ
co9u5vmmEVfrcN6KP+7ESHAUCH5n+fqP4trBy6//ChsLjbVi/jJHtnoIPDRaD05MZ/vHlTiOz7D6
BmFoMsv/fXkQ4fH74RFDNLxvVm7b6vsJWzwCIk67FXheoLUzOMew8fqhUwWGtbAqljz31uTB5bD2
wFrtid2l712Y50ZnJNA3xt8ug3tWYKzjaW1NRSgr+IIrsHXwbNZVBHxbsJsLnAIdjIgEMHtfu6B7
vFlIFvpesEB4yI2Nqh05MEH5GEcwLQwUOCEoMSNwnAuPWaF44LJC5ZciiS0oXY/mUcxcN4ViWsFw
hqjBecxpg4rahVNUxSKLoNMsKZMLf6SQjl0Epw+KqmWkG9bapVCRHRtboqhwQOGPce10d9dlpKVx
7TBIWam1XjOgiPExxShg3y0sn28lqzZ5Zc3IfeOazFfJtSV0RoI8dd5USbR+3gJ4F6VxfUftJpU1
ZeWvvbmh+HeHR+15vGSlyFfWp+2FcE688ahXhvbe8dLvTsTrjDyydmvyOlGoCspWvistjnXGZs2w
tcb9fT6bYs/ZWfKfCb8yqFBJyq4rSxmru8yr2yyFV5b7brs955hw4shOWNG1Z7KhFhmNsdbzF4nE
zZyn8nrMkpOtLM/fJsbV9d4+HuwjHe+pZOvz1v8AAAD//wMAVoKTVVsNAAA=
headers:
CF-RAY:
- 94d9b6782db84d3b-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:56:30 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '31484'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '31490'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999166'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_aa737cf40bb76af9f458bfd35f7a77a1
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}, {"role": "assistant", "content": "Thought: I now can give a
great answer \nFinal Answer: The top 10 best soccer players in the world, as
of October 2023, can be identified based on their recent performances, skills,
impact on games, and overall contributions to their teams. Here is the structured
list:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), Ligue 1 titles, multiple domestic cups.\n\n3. **Erling Haaland
(Manchester City)**\n - Position: Forward\n - Key Attributes: Power, speed,
goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA Champions
League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne (Manchester
City)**\n - Position: Midfielder\n - Key Attributes: Passing, vision, creativity.\n -
Achievements: Multiple Premier League titles, FA Cups, UEFA Champions League
winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n - Position: Forward\n -
Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n - Achievements:
2022 Ballon d''Or winner, multiple Champions Leagues with Real Madrid.\n\n6.
**Neymar Jr. (Al Hilal)**\n - Position: Forward\n - Key Attributes: Flair,
dribbling, creativity.\n - Achievements: Multiple domestic league titles,
Champions League runner-up.\n\n7. **Robert Lewandowski (FC Barcelona)**\n -
Position: Forward\n - Key Attributes: Finishing, positioning, aerial ability.\n -
Achievements: FIFA Best Men''s Player, multiple Bundesliga titles, La Liga champion
(2023).\n\n8. **Mohamed Salah (Liverpool)**\n - Position: Forward\n - Key
Attributes: Speed, finishing, dribbling.\n - Achievements: Premier League
champion, FA Cup, UEFA Champions League winner.\n\n9. **Vin\u00edcius J\u00fanior
(Real Madrid)**\n - Position: Forward\n - Key Attributes: Speed, dribbling,
creativity.\n - Achievements: UEFA Champions League winner (2022), La Liga
champion (2023).\n\n10. **Luka Modri\u0107 (Real Madrid)**\n - Position:
Midfielder\n - Key Attributes: Passing, vision, tactical intelligence.\n -
Achievements: Multiple Champions League titles, Ballon d''Or winner (2018).\n\nThis
list is compiled based on their current form, past performances, and contributions
to their respective teams in both domestic and international competitions. Player
rankings can vary based on personal opinion and specific criteria used for evaluation,
but these players have consistently been regarded as some of the best in the
world as of October 2023."}, {"role": "user", "content": "You are not allowed
to include Brazilian players"}, {"role": "assistant", "content": "Thought: I
now can give a great answer \nFinal Answer: Here is an updated list of the
top 10 best soccer players in the world as of October 2023, excluding Brazilian
players:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), Ligue 1 titles, multiple domestic cups.\n\n3. **Erling Haaland
(Manchester City)**\n - Position: Forward\n - Key Attributes: Power, speed,
goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA Champions
League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne (Manchester
City)**\n - Position: Midfielder\n - Key Attributes: Passing, vision, creativity.\n -
Achievements: Multiple Premier League titles, FA Cups, UEFA Champions League
winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n - Position: Forward\n -
Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n - Achievements:
2022 Ballon d''Or winner, multiple Champions Leagues with Real Madrid.\n\n6.
**Robert Lewandowski (FC Barcelona)**\n - Position: Forward\n - Key Attributes:
Finishing, positioning, aerial ability.\n - Achievements: FIFA Best Men''s
Player, multiple Bundesliga titles, La Liga champion (2023).\n\n7. **Mohamed
Salah (Liverpool)**\n - Position: Forward\n - Key Attributes: Speed, finishing,
dribbling.\n - Achievements: Premier League champion, FA Cup, UEFA Champions
League winner.\n\n8. **Vin\u00edcius J\u00fanior (Real Madrid)**\n - Position:
Forward\n - Key Attributes: Speed, dribbling, creativity.\n - Achievements:
UEFA Champions League winner (2022), La Liga champion (2023).\n\n9. **Luka Modri\u0107
(Real Madrid)**\n - Position: Midfielder\n - Key Attributes: Passing, vision,
tactical intelligence.\n - Achievements: Multiple Champions League titles,
Ballon d''Or winner (2018).\n\n10. **Harry Kane (Bayern Munich)**\n - Position:
Forward\n - Key Attributes: Goal-scoring, technique, playmaking.\n - Achievements:
Golden Boot winner, Premier League titles, UEFA European Championship runner-up.\n\nThis
list has been adjusted to exclude Brazilian players and focuses on those who
have made significant impacts in their clubs and on the international stage
as of October 2023. Each player is recognized for their exceptional skills,
performances, and achievements."}, {"role": "user", "content": "You are not
allowed to include Brazilian players"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '6337'
content-type:
- application/json
cookie:
- __cf_bm=8Yv8F0ZCFAo2lf.qoqxao70yxyjVvIV90zQqVF6bVzQ-1749567342-1.0.1.1-fZgnv3RDfunvCO1koxwwFJrHnxSx_rwS_FHvQ6xxDPpKHwYr7dTqIQLZrNgSX5twGyK4F22rUmkuiS6KMVogcinChk8lmHtJBTUVTFjr2KU;
_cfuvid=wzh8YnmXvLq1G0RcIVijtzboQtCZyIe2uZiochkBLqE-1749567342267-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//rJfNbhs3EMfvforBXiIHK0OSv3WTHCsxYiFunKJo6sAYkaPdqbkkS3Il
K0HOfZY+R/tgBSnJkhPZtdteDC+HQ82P87H//bIFkLHMupCJEoOorGr2i7rwvWp2tt+qfzi4tCf2
6Pjjx5/36FX74nOWRw8z+pVEWHrtCFNZRYGNnpuFIwwUT20f7h3vHxzuHu8lQ2UkqehW2NDcM82K
NTc7rc5es3XYbB8tvEvDgnzWhV+2AAC+pL8xTi3pNutCK1+uVOQ9FpR17zYBZM6ouJKh9+wD6pDl
K6MwOpBOoX8oTV2UoQtnoM0UBGooeEKAUMT4AbWfkgO40gPWqKCXnrvwhhwBewglgaMJe5Kg2Acw
47QWjIV2C0bkA3gjBDmwCmfkPLBOO6bGKQnoo8c7EcyIHHRand0c6NYqFhzUDOhWqFqyLqDv8DMr
Rr08p3ulr3R7B16+PGejScGQvGdonOlADoaMFcPJYPvlyysNAE24MJ5jdrowMG6KTi7W39IMeiE4
HtWBfBdeOR6NFOsihwl7NjqHwqBqemFcDARHrDjMdhbuPVEyTagiHXwXhrUKbBVBH5UyGuSLdw6m
rDW5HE6MRehV5FggiBIrm07/KV3ESW3v1qDRaXU62zuRsBMJ384S+XCE1v75BzQu0LGHS2Qdmq/J
Vcj6+aCXlkjmEEiUmn+rKYcxa/Yl62Iz3OBs0Hsg2vbRdg7VEv6ci5qgDYGDIp8DagkTdGxqD9JU
5AMLELX1iXA3Ep66eOXwBlHF3Y0halGSj6k84TB7Pt2FmcY793PIexlk7QNrETZT9mstySsuMBVx
dIoH/Xg66MHJAtnDOWFknKc25Wt3O4cLRxWTW1rn/AlyL6WRJqzhFUHf1TNNT6IcshwzKUnuIdDY
4uvVmgYPT/65RjdGm0PkrK1/EnJi209s6LiCPunPVCE0eqp5FgKXKJ+futdrycpTu1d4k/4PKAIL
VMA6kFJckBa0mTJ20MYuvKvSb8k8TDmU8J5QwRClY5noDiLd+zieApzTFLU0U3/D0BicQB+dIGU0
Ph9ysOy1HOzCJT0gOUb1+JRJjdiPo3VI+oWHizQR19DWa3iR1XOMbYn3Z8wigYcRcWhKrEjCJSos
oXHOE3LWGPWvB8t4RSiXM3UzzzeFuBqN81J8vBITwVF6D9Q3CEMjHf/1OzTW8vj/9NUzim/4UI3d
peP7ylyM0YRzHHHeoHMzeItxUPRjgjUMa82i/K8dtTbxV821GeS1UZI09I0Jdw30wNhIOTqtnbGE
egVesgVXR89mbRNcuzXvKOn4wQn4WKo2vbNpTNpH3eJvWCmfg11mb9lcsaem6EiT9zvLcx4tw3tg
TxiDH0r2cw3EHkTtovqDacmKAGVJ6dUTzEIx+eBYxLii/HlE5ezAKYpy8QSs40aSUKIHSZXRPsx/
h24F2QXn/A7SW9dRhe4GR4rAkhsbV6EWEcobxZLHsxRUSezABwy1j4LMm4qWOi4JuH9QbDvr2tLR
uPYY9a2ulVozoNYmYAwxqdpPC8vXOx2rTGGdGflvXLP5HLl2hN7oqFl9MDZL1q9bAJ+SXq7vSeDM
OlPZcB3MDaWfa++1DucHZiudvjIftPYX1mACqpWh02p38g1HXksKyMqvie5MoChJrnxXCh1ryWbN
sLUG/n08m86ew7MunnL8yiBiUZC8to7kvNw2bXMUP2Qe2nZ30SngzJObsKDrwORiMiSNsVbzz4vM
z3yg6nrMuiBnHc+/Mcb2+qCDnV08atM42/q69TcAAAD//wMAvqcBwHENAAA=
headers:
CF-RAY:
- 94d9b7561f204d3b-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:56:46 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '12189'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '12193'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149998513'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_1098f5a5384f4a26aecf0c9e4e4d1fc0
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Sports Analyst. You
are an expert at gathering and organizing information. You carefully collect
details and present them in a structured way.\nYour personal goal is: Gather
information about the best soccer players\n\nTo give my best complete final
answer to the task respond using the exact following format:\n\nThought: I now
can give a great answer\nFinal Answer: Your final answer must be the great and
the most complete as possible, it must be outcome described.\n\nI MUST use these
formats, my job depends on it!"}, {"role": "user", "content": "Top 10 best players
in the world?"}, {"role": "assistant", "content": "Thought: I now can give a
great answer \nFinal Answer: The top 10 best soccer players in the world, as
of October 2023, can be identified based on their recent performances, skills,
impact on games, and overall contributions to their teams. Here is the structured
list:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), Ligue 1 titles, multiple domestic cups.\n\n3. **Erling Haaland
(Manchester City)**\n - Position: Forward\n - Key Attributes: Power, speed,
goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA Champions
League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne (Manchester
City)**\n - Position: Midfielder\n - Key Attributes: Passing, vision, creativity.\n -
Achievements: Multiple Premier League titles, FA Cups, UEFA Champions League
winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n - Position: Forward\n -
Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n - Achievements:
2022 Ballon d''Or winner, multiple Champions Leagues with Real Madrid.\n\n6.
**Neymar Jr. (Al Hilal)**\n - Position: Forward\n - Key Attributes: Flair,
dribbling, creativity.\n - Achievements: Multiple domestic league titles,
Champions League runner-up.\n\n7. **Robert Lewandowski (FC Barcelona)**\n -
Position: Forward\n - Key Attributes: Finishing, positioning, aerial ability.\n -
Achievements: FIFA Best Men''s Player, multiple Bundesliga titles, La Liga champion
(2023).\n\n8. **Mohamed Salah (Liverpool)**\n - Position: Forward\n - Key
Attributes: Speed, finishing, dribbling.\n - Achievements: Premier League
champion, FA Cup, UEFA Champions League winner.\n\n9. **Vin\u00edcius J\u00fanior
(Real Madrid)**\n - Position: Forward\n - Key Attributes: Speed, dribbling,
creativity.\n - Achievements: UEFA Champions League winner (2022), La Liga
champion (2023).\n\n10. **Luka Modri\u0107 (Real Madrid)**\n - Position:
Midfielder\n - Key Attributes: Passing, vision, tactical intelligence.\n -
Achievements: Multiple Champions League titles, Ballon d''Or winner (2018).\n\nThis
list is compiled based on their current form, past performances, and contributions
to their respective teams in both domestic and international competitions. Player
rankings can vary based on personal opinion and specific criteria used for evaluation,
but these players have consistently been regarded as some of the best in the
world as of October 2023."}, {"role": "user", "content": "You are not allowed
to include Brazilian players"}, {"role": "assistant", "content": "Thought: I
now can give a great answer \nFinal Answer: Here is an updated list of the
top 10 best soccer players in the world as of October 2023, excluding Brazilian
players:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), Ligue 1 titles, multiple domestic cups.\n\n3. **Erling Haaland
(Manchester City)**\n - Position: Forward\n - Key Attributes: Power, speed,
goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA Champions
League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne (Manchester
City)**\n - Position: Midfielder\n - Key Attributes: Passing, vision, creativity.\n -
Achievements: Multiple Premier League titles, FA Cups, UEFA Champions League
winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n - Position: Forward\n -
Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n - Achievements:
2022 Ballon d''Or winner, multiple Champions Leagues with Real Madrid.\n\n6.
**Robert Lewandowski (FC Barcelona)**\n - Position: Forward\n - Key Attributes:
Finishing, positioning, aerial ability.\n - Achievements: FIFA Best Men''s
Player, multiple Bundesliga titles, La Liga champion (2023).\n\n7. **Mohamed
Salah (Liverpool)**\n - Position: Forward\n - Key Attributes: Speed, finishing,
dribbling.\n - Achievements: Premier League champion, FA Cup, UEFA Champions
League winner.\n\n8. **Vin\u00edcius J\u00fanior (Real Madrid)**\n - Position:
Forward\n - Key Attributes: Speed, dribbling, creativity.\n - Achievements:
UEFA Champions League winner (2022), La Liga champion (2023).\n\n9. **Luka Modri\u0107
(Real Madrid)**\n - Position: Midfielder\n - Key Attributes: Passing, vision,
tactical intelligence.\n - Achievements: Multiple Champions League titles,
Ballon d''Or winner (2018).\n\n10. **Harry Kane (Bayern Munich)**\n - Position:
Forward\n - Key Attributes: Goal-scoring, technique, playmaking.\n - Achievements:
Golden Boot winner, Premier League titles, UEFA European Championship runner-up.\n\nThis
list has been adjusted to exclude Brazilian players and focuses on those who
have made significant impacts in their clubs and on the international stage
as of October 2023. Each player is recognized for their exceptional skills,
performances, and achievements."}, {"role": "user", "content": "You are not
allowed to include Brazilian players"}, {"role": "assistant", "content": "Thought:
I now can give a great answer \nFinal Answer: Here is the revised list of the
top 10 best soccer players in the world as of October 2023, explicitly excluding
Brazilian players:\n\n1. **Lionel Messi (Inter Miami CF)**\n - Position: Forward\n -
Key Attributes: Dribbling, vision, goal-scoring ability.\n - Achievements:
Multiple Ballon d''Or winner, Copa America champion, World Cup champion (2022).\n\n2.
**Kylian Mbapp\u00e9 (Paris Saint-Germain)**\n - Position: Forward\n - Key
Attributes: Speed, technique, finishing.\n - Achievements: FIFA World Cup
champion (2018), multiple Ligue 1 titles, and various domestic cups.\n\n3. **Erling
Haaland (Manchester City)**\n - Position: Forward\n - Key Attributes: Power,
speed, goal-scoring instinct.\n - Achievements: Bundesliga top scorer, UEFA
Champions League winner (2023), Premier League titles.\n\n4. **Kevin De Bruyne
(Manchester City)**\n - Position: Midfielder\n - Key Attributes: Passing,
vision, creativity.\n - Achievements: Multiple Premier League titles, FA Cups,
UEFA Champions League winner (2023).\n\n5. **Karim Benzema (Al-Ittihad)**\n -
Position: Forward\n - Key Attributes: Goal-scoring, playmaking, tactical intelligence.\n -
Achievements: 2022 Ballon d''Or winner, multiple Champions Leagues with Real
Madrid.\n\n6. **Robert Lewandowski (FC Barcelona)**\n - Position: Forward\n -
Key Attributes: Finishing, positioning, aerial ability.\n - Achievements:
FIFA Best Men''s Player, multiple Bundesliga titles, La Liga champion (2023).\n\n7.
**Mohamed Salah (Liverpool)**\n - Position: Forward\n - Key Attributes:
Speed, finishing, dribbling.\n - Achievements: Premier League champion, FA
Cup, UEFA Champions League winner.\n\n8. **Luka Modri\u0107 (Real Madrid)**\n -
Position: Midfielder\n - Key Attributes: Passing, vision, tactical intelligence.\n -
Achievements: Multiple Champions League titles, Ballon d''Or winner (2018).\n\n9.
**Harry Kane (Bayern Munich)**\n - Position: Forward\n - Key Attributes:
Goal-scoring, technique, playmaking.\n - Achievements: Golden Boot winner,
Premier League titles, UEFA European Championship runner-up.\n\n10. **Rodri
(Manchester City)**\n - Position: Midfielder\n - Key Attributes: Defensive
skills, passing, positional awareness.\n - Achievements: Premier League titles,
UEFA Champions League winner (2023).\n\nThis list is curated while adhering
to the restriction of excluding Brazilian players. Each player included has
demonstrated exceptional skills and remarkable performances, solidifying their
status as some of the best in the world as of October 2023."}, {"role": "user",
"content": "You are not allowed to include Brazilian players"}], "model": "gpt-4o-mini",
"stop": ["\nObservation:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '9093'
content-type:
- application/json
cookie:
- __cf_bm=8Yv8F0ZCFAo2lf.qoqxao70yxyjVvIV90zQqVF6bVzQ-1749567342-1.0.1.1-fZgnv3RDfunvCO1koxwwFJrHnxSx_rwS_FHvQ6xxDPpKHwYr7dTqIQLZrNgSX5twGyK4F22rUmkuiS6KMVogcinChk8lmHtJBTUVTFjr2KU;
_cfuvid=wzh8YnmXvLq1G0RcIVijtzboQtCZyIe2uZiochkBLqE-1749567342267-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//rJfNThtJEMfvPEVpLjFojGxjEuIbJhCi4ASFRKvVJkLl7vJMLT3Vk+4e
O06U8z7LPsfug626bbAhJgm7e0F4aqqmfl0f858vWwAZ62wAmSoxqKo27WHRlEc0edUdv+q9pl/f
2P2jsnz38dXw7J3GLI8edvw7qXDttatsVRsKbGVhVo4wUIzafdJ/uv/4Sb/bT4bKajLRrahDu2/b
FQu3e51ev9150u4eLL1Ly4p8NoDftgAAvqS/MU/R9CkbQCe/vlKR91hQNri5CSBz1sQrGXrPPqCE
LF8ZlZVAklJ/W9qmKMMAXoDYGSgUKHhKgFDE/AHFz8gBvJcTFjRwmH4P4JQcAXtAcDRhIQ2GfQA7
gVASBFtDtwNj8gG8VYoc1Abn5DywpDtm1hkN6KPHaxXsmBz0Or29HEh841gKCCUGEAtDh5/ZMMpN
DIzPFmUaTXrwXt5Ldxd2ds7YChkYkfcMrRcSyMGIsWI4Otne2XkvANCGc+s5FmkAJ9bN0Onl9Zc0
h8MQHI+bQH4AzxyPx4alyGHKnq3kUFg0ba9syg7HbDjMd5fuh6pkmlJFEvwARo0JXBuCIRpjBfSj
1w5mLEIuhyNbIxxW5FghqBKrOkX/JZ3IUVPfXINWr9Prbe9Gwl4kfDlPxzAaY13/9Se0ztGxhwtk
Ce3n5CpkeTjoRU2kcwikSuGPDeUwYWFfshSb4U5enBzek233YDuH6hr+jIuGoAuBgyGfwxQd28aD
thX5wApUU/tEtxfpjl08bjhFNCgaWiMUVZKPZTziMH842bmdxfP2C8Bb1WPxgUWFzYTDRjR5wwWm
To5OMdC745NDOFriejgjjHyLsqZa7W3ncO6oYnLX1gV7guynEtKUBZ4RDF0zF/opyhHrCZPR5O4D
jVO+3qlp9/D0x/25MdscImdT+59CTmz7iQ0dVzAk+UwVQuvQtF+EwCXqh5fu+Vqx8jT3FV6l/wOq
wAoNsAQyhgsSRZsp4/RsnMCbDr1L5mHGoYQ3hAZGqB3rRPc40r2JOyrAGc1QtJ35K4bWyREM0Sky
VvDhkCfXc5ZDvXRJP5Aco/n+hklDOIz7dUTyyMN5Wo1raOs9vKzqGcaRxNv7ZVnAJxFxZEusSMMF
GiyhdcZTcrW15l8vlcmKUF/v0808dxpxtRYXrfj9TkwEB+kd0FwhjKx2/Pcf0Fqr4/8zVw9ovtF9
PXZTjm87c7lCE87TiHOKzs3hJcZFMYwFFhg1wqr8rxO1tu1Xw7UZ5Lk1mgSG1oZvB+ie/ZGKddw4
WxPK6gRKrsE1MUS7qRNltxMxL6zAKTVSRC0Erbc2BJISKzi1wdeNu6a9F/enOvAu6I968Lvg6++w
9SX/tmS/kEIlehgTCSh0NGmMmYOjKXvSECzQpyRfAI3ZIHBmJRuCkovScFGG+MbytqJreVVZHyCg
IQmkgUXzlHWDJqmrpd76VlrtwjGqcvmMlJ4v7UxhzMhRhe4Kx4aAJhNSgack5D3EN7G/YmNyiKox
HW9KhwvhCSuUYOYRKJTEDgJh5cEKjG0oV4cUo8SRcYKxeGjAByzI766r0nhKHqMylsaYNQOK2JAc
kx7+sLR8vVHAxha1s2N/xzVbFP/SEXorUe36YOssWb9uAXxISru5JZ6z2tmqDpfBXlF6XK/TO1gE
zFYKf2V+vBD1AFmwAc2a3+N+L98Q8lJTQDZ+Ta5nClVJeuXb7R2s5D02mu3K1tlaY/82pU3hF/ws
xVqUe8OvDEpRHUhf1o40q9vYq9scxa+g+267OeuUcObJTVnRZWBysR6aJtiYxbdJ5uc+UHU5YSnI
1Y4XHyiT+nKvj/t9pKd7Ktv6uvUPAAAA//8DAMc1SFGuDQAA
headers:
CF-RAY:
- 94d9b7d24d991d2c-GRU
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Tue, 10 Jun 2025 14:57:29 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '35291'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '35294'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149997855'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_4676152d4227ac1825d1240ddef231d6
status:
code: 200
message: OK
version: 1

File diff suppressed because one or more lines are too long

View File

@@ -199,4 +199,833 @@ interactions:
- req_2ac1e3cef69e9b09b7ade0e1d010fc08
http_version: HTTP/1.1
status_code: 200
- request:
body: '{"input": ["I now can give a great answer. Final Answer: **Topic**: Basic
Addition **Explanation**: Addition is a fundamental concept in math that means
combining two or more numbers to get a new total. It''s like putting together
pieces of a puzzle to see the whole picture. When we add, we take two or more
groups of things and count them all together. **Angle**: Use relatable and
engaging real-life scenarios to illustrate addition, making it fun and easier
for a 6-year-old to understand and apply. **Examples**: 1. **Counting Apples**: Let''s
say you have 2 apples and your friend gives you 3 more apples. How many apples
do you have in total? - You start with 2 apples. - Your friend gives you
3 more apples. - Now, you count all the apples together: 2 + 3 = 5. -
So, you have 5 apples in total. 2. **Toy Cars**: Imagine you have 4 toy
cars and you find 2 more toy cars in your room. How many toy cars do you have
now? - You start with 4 toy cars. - You find 2 more toy cars. - You
count them all together: 4 + 2 = 6. - So, you have 6 toy cars in total. 3.
**Drawing Pictures**: If you draw 3 pictures today and 2 pictures tomorrow,
how many pictures will you have drawn in total? - You draw 3 pictures today. -
You draw 2 pictures tomorrow. - You add them together: 3 + 2 = 5. - So,
you will have drawn 5 pictures in total. 4. **Using Fingers**: Let''s use
your fingers to practice addition. Show 3 fingers on one hand and 1 finger on
the other hand. How many fingers are you holding up? - 3 fingers on one hand. -
1 finger on the other hand. - Put them together and count: 3 + 1 = 4. -
So, you are holding up 4 fingers. By using objects that kids are familiar with,
such as apples, toy cars, drawings, and even their own fingers, we can make
the concept of addition relatable and enjoyable. Practicing with real items
helps children visualize the math and understand that addition is simply combining
groups to find out how many there are altogether."], "model": "text-embedding-3-small",
"encoding_format": "base64"}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '2092'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-read-timeout:
- '600'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/embeddings
response:
body:
string: !!binary |
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
AAA=
headers:
CF-RAY:
- 94f4c6967c75fb3c-SJC
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Fri, 13 Jun 2025 21:45:35 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=I11FOm0L.OsIzhpXscunzNqcbLqgXE1LlRgqtNWazAs-1749851135-1.0.1.1-R2n01WWaBpL_jzTwLFUo8WNM2u_1OD78QDkM9lSQoM9Av669lg1O9RgxK.Eew7KQ1MP_oJ9WkD408NuNCPwFcLRzX8TqwMNow5Gb_N1LChY;
path=/; expires=Fri, 13-Jun-25 22:15:35 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=TSYyYzslZUaIRAy.2QeTpFy6uf5Di7f.JM5ndpSEYhs-1749851135188-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-allow-origin:
- '*'
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-model:
- text-embedding-3-small
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '76'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
via:
- envoy-router-86bb9759f6-ksq4x
x-envoy-upstream-service-time:
- '80'
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '10000000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '9999496'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 3ms
x-request-id:
- req_335974d1e85d40d312fb1989e3b2ade5
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "user", "content": "Assess the quality of the task
completed based on the description, expected output, and actual results.\n\nTask
Description:\nResearch a topic to teach a kid aged 6 about math.\n\nExpected
Output:\nA topic, explanation, angle, and examples.\n\nActual Output:\nI now
can give a great answer.\nFinal Answer: \n**Topic**: Basic Addition\n\n**Explanation**:\nAddition
is a fundamental concept in math that means combining two or more numbers to
get a new total. It''s like putting together pieces of a puzzle to see the whole
picture. When we add, we take two or more groups of things and count them all
together.\n\n**Angle**:\nUse relatable and engaging real-life scenarios to illustrate
addition, making it fun and easier for a 6-year-old to understand and apply.\n\n**Examples**:\n\n1.
**Counting Apples**:\n Let''s say you have 2 apples and your friend gives
you 3 more apples. How many apples do you have in total?\n - You start with
2 apples.\n - Your friend gives you 3 more apples.\n - Now, you count all
the apples together: 2 + 3 = 5.\n - So, you have 5 apples in total.\n\n2.
**Toy Cars**:\n Imagine you have 4 toy cars and you find 2 more toy cars in
your room. How many toy cars do you have now?\n - You start with 4 toy cars.\n -
You find 2 more toy cars.\n - You count them all together: 4 + 2 = 6.\n -
So, you have 6 toy cars in total.\n\n3. **Drawing Pictures**:\n If you draw
3 pictures today and 2 pictures tomorrow, how many pictures will you have drawn
in total?\n - You draw 3 pictures today.\n - You draw 2 pictures tomorrow.\n -
You add them together: 3 + 2 = 5.\n - So, you will have drawn 5 pictures in
total.\n\n4. **Using Fingers**:\n Let''s use your fingers to practice addition.
Show 3 fingers on one hand and 1 finger on the other hand. How many fingers
are you holding up?\n - 3 fingers on one hand.\n - 1 finger on the other
hand.\n - Put them together and count: 3 + 1 = 4.\n - So, you are holding
up 4 fingers.\n\nBy using objects that kids are familiar with, such as apples,
toy cars, drawings, and even their own fingers, we can make the concept of addition
relatable and enjoyable. Practicing with real items helps children visualize
the math and understand that addition is simply combining groups to find out
how many there are altogether.\n\nPlease provide:\n- Bullet points suggestions
to improve future similar tasks\n- A score from 0 to 10 evaluating on completion,
quality, and overall performance- Entities extracted from the task output, if
any, their type, description, and relationships"}], "model": "gpt-4o-mini",
"tool_choice": {"type": "function", "function": {"name": "TaskEvaluation"}},
"tools": [{"type": "function", "function": {"name": "TaskEvaluation", "description":
"Correctly extracted `TaskEvaluation` with all the required parameters with
correct types", "parameters": {"$defs": {"Entity": {"properties": {"name": {"description":
"The name of the entity.", "title": "Name", "type": "string"}, "type": {"description":
"The type of the entity.", "title": "Type", "type": "string"}, "description":
{"description": "Description of the entity.", "title": "Description", "type":
"string"}, "relationships": {"description": "Relationships of the entity.",
"items": {"type": "string"}, "title": "Relationships", "type": "array"}}, "required":
["name", "type", "description", "relationships"], "title": "Entity", "type":
"object"}}, "properties": {"suggestions": {"description": "Suggestions to improve
future similar tasks.", "items": {"type": "string"}, "title": "Suggestions",
"type": "array"}, "quality": {"description": "A score from 0 to 10 evaluating
on completion, quality, and overall performance, all taking into account the
task description, expected output, and the result of the task.", "title": "Quality",
"type": "number"}, "entities": {"description": "Entities extracted from the
task output.", "items": {"$ref": "#/$defs/Entity"}, "title": "Entities", "type":
"array"}}, "required": ["entities", "quality", "suggestions"], "type": "object"}}}]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '4092'
content-type:
- application/json
cookie:
- _cfuvid=63wmKMTuFamkLN8FBI4fP8JZWbjWiRxWm7wb3kz.z_A-1735447412038-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAA6xW32/bNhB+z19x4Mte7CJu0jb2W5q2WJt2KLZsw1AFxpk6S2wpHkdSdtQg//tA
0rKU1gaKbX4wBB7v1/fdHe/+BECoUixAyBqDbKyevlTP//ig/vy8+iu8b+qvZ7Puan6+wnc3+v1v
nZhEDV59Jhl6rSeSG6spKDZZLB1hoGh19uJ8fvFsNjt7ngQNl6SjWmXD9JynjTJq+vT06fn09MV0
drHTrllJ8mIBn04AAO7Tf4zTlHQnFnA66U8a8h4rEov9JQDhWMcTgd4rH9AEMRmEkk0gE0M3rdYj
QWDWS4laD47z7370PYCFWi9Pb66v52x/veDrd9cb/uDo7rO7vvhl5C+b7mwKaN0auQdpJN+fL75x
BiAMNkn3Bv2X1xvULR6wACDQVW1DJsToxX0hfFtV5ONdX4jFp0K8NVK3JUHDjkCZQA5lUBsCNCWQ
qbBSpoJ0poIiD2t2EGoCWStdPinEpBCXZQkb5VvUgKr0wA5Kh1tlKg+BoSZtQWnd+uAwUNZmI8kG
nw28NZKd5SStsKFkYt0akDVqTaaiZMiRMmt2kkATOqNMldU/Ot6okgDLUsXUUINNaUjUQHcYq9BD
qDFAxbCijk0JklsTYm6BgUyNRhK0piQXa6PMtm8nhfi7Ra1CV4jFfFIIMiHBEMG7LxINhVgU4iV6
JeFyF0CKKtKbZB8w1HDDVsl0XpKXTtl8b1GIy5hpiZEl1D0woAw0US9FrcyG9YY8SG5WyqSotxxB
SrSZtlmRSxBVFADB0BYCB9QZH0c6lYevle1p94Cw5uh4h1jy5r8orRPFhE53GWdyPrOU/DdsdAcB
26oO0WOqA0cG0EVziV2sCHgNzwtx+zAZw/Q6k7GAqx79SxvJOQDY7uphyExPK7SeykThndWoDKwS
EX0lwKobiLZ151NJ5DHlJ4DJecQ61MqDRE9HEXtFDZtcwX5cwjHRvbtQO26rGhyhnmq1pt5X7hNr
tZK40pS7iFDWMbABwn39HMPthju4Qvc/ADZqyINoBe5AovNHAXnTulCTG7oyw7I31uPTY4KQjKT0
vSSDTnGy/TNp6wcQ8iRRX+kH4HiVxwx8VDK07r/UUeqz3F7kAV1IAzC34FBd++S2KtRgd16PQvTa
SG4dxvGV3r44QzvY1io67NnH8dB4VGXDEMNcOcn1rt5M1ff9cXh+9/HaG2Uq+jclM34OtthFIFLU
AwxtcrDODgD9uHxYHwfmo+OGU4p9T6anhuIQjO3Yz/dEQDJyxcbERsqUJPj3r9BPHnhrYMVllxpr
RSGQezzNI0i3D+LR+/hwcuj7dvT6O1q3HvX3awEawyFnFfeC253kYb+CaK6s45X/RlWslVG+XjpC
n1524QPbHFYMITkX7aPtRVjHjQ3LwF8ouXsxn2d7YtiwBunZ6dlOmp6AQTCbnT6dHLC4LCmgSgvO
fqeSKGsqB91ht8K2VDwSnIzy/j6eQ7Zz7spUP2J+EMg4TahcWkelko9zHq45ivP22LU9zilg4clt
lKRlUOQiFyWtsdV5MRS+84GaZS5u61TaDsXaLs/O8dk50vxMipOHk38AAAD//wMAfAt9/isLAAA=
headers:
CF-RAY:
- 94f4c6a1cf5afb44-SJC
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Fri, 13 Jun 2025 21:45:40 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=RNxOcvndh1ViOpBviaCq8vg2oE59_B32cF84QEfAM8M-1749851140-1.0.1.1-161vq6SqDcfIu41VKaJdjmGjwyhGQ3AyY0VDnfk1SUfufmIewYKYnNufCV49o2gCDVOzInyRnwwp3.Sk2rj9DoDtAbcdOdEHxpr34JvDa8w;
path=/; expires=Fri, 13-Jun-25 22:15:40 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=uF44YidguuLD6X0Fw3uiyzdru2Ad2jXf2Nx1M4V87qI-1749851140865-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '4149'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '4154'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999368'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_437679f0db60c9eec6bbdf32d399f960
status:
code: 200
message: OK
- request:
body: '{"input": ["Example: Counting Apples(Math Example): An example used to
explain basic addition by counting physical objects, apples in this case."],
"model": "text-embedding-3-small", "encoding_format": "base64"}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '211'
content-type:
- application/json
cookie:
- __cf_bm=I11FOm0L.OsIzhpXscunzNqcbLqgXE1LlRgqtNWazAs-1749851135-1.0.1.1-R2n01WWaBpL_jzTwLFUo8WNM2u_1OD78QDkM9lSQoM9Av669lg1O9RgxK.Eew7KQ1MP_oJ9WkD408NuNCPwFcLRzX8TqwMNow5Gb_N1LChY;
_cfuvid=TSYyYzslZUaIRAy.2QeTpFy6uf5Di7f.JM5ndpSEYhs-1749851135188-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-read-timeout:
- '600'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/embeddings
response:
body:
string: !!binary |
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
headers:
CF-RAY:
- 94f4c6bed82cfac6-SJC
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Fri, 13 Jun 2025 21:45:41 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-allow-origin:
- '*'
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-model:
- text-embedding-3-small
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '457'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
via:
- envoy-router-6c6c4dc575-7ps9v
x-envoy-upstream-service-time:
- '459'
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '10000000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '9999967'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_eba1229193b2c4c623e7661cac3bec77
status:
code: 200
message: OK
- request:
body: '{"input": ["Example: Drawing Pictures(Math Example): An example that combines
art and math to explain addition with pictures."], "model": "text-embedding-3-small",
"encoding_format": "base64"}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
content-length:
- '192'
content-type:
- application/json
cookie:
- __cf_bm=I11FOm0L.OsIzhpXscunzNqcbLqgXE1LlRgqtNWazAs-1749851135-1.0.1.1-R2n01WWaBpL_jzTwLFUo8WNM2u_1OD78QDkM9lSQoM9Av669lg1O9RgxK.Eew7KQ1MP_oJ9WkD408NuNCPwFcLRzX8TqwMNow5Gb_N1LChY;
_cfuvid=TSYyYzslZUaIRAy.2QeTpFy6uf5Di7f.JM5ndpSEYhs-1749851135188-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.78.0
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.78.0
x-stainless-read-timeout:
- '600'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.12.9
method: POST
uri: https://api.openai.com/v1/embeddings
response:
body:
string: !!binary |
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
AAAA//8DAIV5SL/gIAAA
headers:
CF-RAY:
- 94f4c6c3be7efac6-SJC
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Fri, 13 Jun 2025 21:45:42 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
X-Content-Type-Options:
- nosniff
access-control-allow-origin:
- '*'
access-control-expose-headers:
- X-Request-ID
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
openai-model:
- text-embedding-3-small
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '128'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
via:
- envoy-router-675c5bcfc8-v6fpw
x-envoy-upstream-service-time:
- '131'
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '10000000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '9999972'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_7f9985e9b536960af27b96d0fd747fbb
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,244 @@
import unittest
from unittest.mock import MagicMock, patch, call
import pytest
from click.testing import CliRunner
import requests
from crewai.cli.organization.main import OrganizationCommand
from crewai.cli.cli import list, switch, current
@pytest.fixture
def runner():
return CliRunner()
@pytest.fixture
def org_command():
with patch.object(OrganizationCommand, '__init__', return_value=None):
command = OrganizationCommand()
yield command
@pytest.fixture
def mock_settings():
with patch('crewai.cli.organization.main.Settings') as mock_settings_class:
mock_settings_instance = MagicMock()
mock_settings_class.return_value = mock_settings_instance
yield mock_settings_instance
@patch('crewai.cli.cli.OrganizationCommand')
def test_org_list_command(mock_org_command_class, runner):
mock_org_instance = MagicMock()
mock_org_command_class.return_value = mock_org_instance
result = runner.invoke(list)
assert result.exit_code == 0
mock_org_command_class.assert_called_once()
mock_org_instance.list.assert_called_once()
@patch('crewai.cli.cli.OrganizationCommand')
def test_org_switch_command(mock_org_command_class, runner):
mock_org_instance = MagicMock()
mock_org_command_class.return_value = mock_org_instance
result = runner.invoke(switch, ['test-id'])
assert result.exit_code == 0
mock_org_command_class.assert_called_once()
mock_org_instance.switch.assert_called_once_with('test-id')
@patch('crewai.cli.cli.OrganizationCommand')
def test_org_current_command(mock_org_command_class, runner):
mock_org_instance = MagicMock()
mock_org_command_class.return_value = mock_org_instance
result = runner.invoke(current)
assert result.exit_code == 0
mock_org_command_class.assert_called_once()
mock_org_instance.current.assert_called_once()
class TestOrganizationCommand(unittest.TestCase):
def setUp(self):
with patch.object(OrganizationCommand, '__init__', return_value=None):
self.org_command = OrganizationCommand()
self.org_command.plus_api_client = MagicMock()
@patch('crewai.cli.organization.main.console')
@patch('crewai.cli.organization.main.Table')
def test_list_organizations_success(self, mock_table, mock_console):
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json.return_value = [
{"name": "Org 1", "uuid": "org-123"},
{"name": "Org 2", "uuid": "org-456"}
]
self.org_command.plus_api_client = MagicMock()
self.org_command.plus_api_client.get_organizations.return_value = mock_response
mock_console.print = MagicMock()
self.org_command.list()
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_table.assert_called_once_with(title="Your Organizations")
mock_table.return_value.add_column.assert_has_calls([
call("Name", style="cyan"),
call("ID", style="green")
])
mock_table.return_value.add_row.assert_has_calls([
call("Org 1", "org-123"),
call("Org 2", "org-456")
])
@patch('crewai.cli.organization.main.console')
def test_list_organizations_empty(self, mock_console):
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json.return_value = []
self.org_command.plus_api_client = MagicMock()
self.org_command.plus_api_client.get_organizations.return_value = mock_response
self.org_command.list()
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_console.print.assert_called_once_with(
"You don't belong to any organizations yet.",
style="yellow"
)
@patch('crewai.cli.organization.main.console')
def test_list_organizations_api_error(self, mock_console):
self.org_command.plus_api_client = MagicMock()
self.org_command.plus_api_client.get_organizations.side_effect = requests.exceptions.RequestException("API Error")
with pytest.raises(SystemExit):
self.org_command.list()
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_console.print.assert_called_once_with(
"Failed to retrieve organization list: API Error",
style="bold red"
)
@patch('crewai.cli.organization.main.console')
@patch('crewai.cli.organization.main.Settings')
def test_switch_organization_success(self, mock_settings_class, mock_console):
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json.return_value = [
{"name": "Org 1", "uuid": "org-123"},
{"name": "Test Org", "uuid": "test-id"}
]
self.org_command.plus_api_client = MagicMock()
self.org_command.plus_api_client.get_organizations.return_value = mock_response
mock_settings_instance = MagicMock()
mock_settings_class.return_value = mock_settings_instance
self.org_command.switch("test-id")
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_settings_instance.dump.assert_called_once()
assert mock_settings_instance.org_name == "Test Org"
assert mock_settings_instance.org_uuid == "test-id"
mock_console.print.assert_called_once_with(
"Successfully switched to Test Org (test-id)",
style="bold green"
)
@patch('crewai.cli.organization.main.console')
def test_switch_organization_not_found(self, mock_console):
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json.return_value = [
{"name": "Org 1", "uuid": "org-123"},
{"name": "Org 2", "uuid": "org-456"}
]
self.org_command.plus_api_client = MagicMock()
self.org_command.plus_api_client.get_organizations.return_value = mock_response
self.org_command.switch("non-existent-id")
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_console.print.assert_called_once_with(
"Organization with id 'non-existent-id' not found.",
style="bold red"
)
@patch('crewai.cli.organization.main.console')
@patch('crewai.cli.organization.main.Settings')
def test_current_organization_with_org(self, mock_settings_class, mock_console):
mock_settings_instance = MagicMock()
mock_settings_instance.org_name = "Test Org"
mock_settings_instance.org_uuid = "test-id"
mock_settings_class.return_value = mock_settings_instance
self.org_command.current()
self.org_command.plus_api_client.get_organizations.assert_not_called()
mock_console.print.assert_called_once_with(
"Currently logged in to organization Test Org (test-id)",
style="bold green"
)
@patch('crewai.cli.organization.main.console')
@patch('crewai.cli.organization.main.Settings')
def test_current_organization_without_org(self, mock_settings_class, mock_console):
mock_settings_instance = MagicMock()
mock_settings_instance.org_uuid = None
mock_settings_class.return_value = mock_settings_instance
self.org_command.current()
assert mock_console.print.call_count == 3
mock_console.print.assert_any_call(
"You're not currently logged in to any organization.",
style="yellow"
)
@patch('crewai.cli.organization.main.console')
def test_list_organizations_unauthorized(self, mock_console):
mock_response = MagicMock()
mock_http_error = requests.exceptions.HTTPError(
"401 Client Error: Unauthorized",
response=MagicMock(status_code=401)
)
mock_response.raise_for_status.side_effect = mock_http_error
self.org_command.plus_api_client.get_organizations.return_value = mock_response
self.org_command.list()
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_console.print.assert_called_once_with(
"You are not logged in to any organization. Use 'crewai login' to login.",
style="bold red"
)
@patch('crewai.cli.organization.main.console')
def test_switch_organization_unauthorized(self, mock_console):
mock_response = MagicMock()
mock_http_error = requests.exceptions.HTTPError(
"401 Client Error: Unauthorized",
response=MagicMock(status_code=401)
)
mock_response.raise_for_status.side_effect = mock_http_error
self.org_command.plus_api_client.get_organizations.return_value = mock_response
self.org_command.switch("test-id")
self.org_command.plus_api_client.get_organizations.assert_called_once()
mock_console.print.assert_called_once_with(
"You are not logged in to any organization. Use 'crewai login' to login.",
style="bold red"
)

View File

@@ -1,6 +1,6 @@
import os
import unittest
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, patch, ANY
from crewai.cli.plus_api import PlusAPI
@@ -9,6 +9,7 @@ class TestPlusAPI(unittest.TestCase):
def setUp(self):
self.api_key = "test_api_key"
self.api = PlusAPI(self.api_key)
self.org_uuid = "test-org-uuid"
def test_init(self):
self.assertEqual(self.api.api_key, self.api_key)
@@ -29,17 +30,96 @@ class TestPlusAPI(unittest.TestCase):
)
self.assertEqual(response, mock_response)
def assert_request_with_org_id(self, mock_make_request, method: str, endpoint: str, **kwargs):
mock_make_request.assert_called_once_with(
method, f"https://app.crewai.com{endpoint}", headers={'Authorization': ANY, 'Content-Type': ANY, 'User-Agent': ANY, 'X-Crewai-Version': ANY, 'X-Crewai-Organization-Id': self.org_uuid}, **kwargs
)
@patch("crewai.cli.plus_api.Settings")
@patch("requests.Session.request")
def test_login_to_tool_repository_with_org_uuid(self, mock_make_request, mock_settings_class):
mock_settings = MagicMock()
mock_settings.org_uuid = self.org_uuid
mock_settings_class.return_value = mock_settings
# re-initialize Client
self.api = PlusAPI(self.api_key)
mock_response = MagicMock()
mock_make_request.return_value = mock_response
response = self.api.login_to_tool_repository()
self.assert_request_with_org_id(
mock_make_request,
'POST',
'/crewai_plus/api/v1/tools/login'
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.PlusAPI._make_request")
def test_get_agent(self, mock_make_request):
mock_response = MagicMock()
mock_make_request.return_value = mock_response
response = self.api.get_agent("test_agent_handle")
mock_make_request.assert_called_once_with(
"GET", "/crewai_plus/api/v1/agents/test_agent_handle"
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.Settings")
@patch("requests.Session.request")
def test_get_agent_with_org_uuid(self, mock_make_request, mock_settings_class):
mock_settings = MagicMock()
mock_settings.org_uuid = self.org_uuid
mock_settings_class.return_value = mock_settings
# re-initialize Client
self.api = PlusAPI(self.api_key)
mock_response = MagicMock()
mock_make_request.return_value = mock_response
response = self.api.get_agent("test_agent_handle")
self.assert_request_with_org_id(
mock_make_request,
"GET",
"/crewai_plus/api/v1/agents/test_agent_handle"
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.PlusAPI._make_request")
def test_get_tool(self, mock_make_request):
mock_response = MagicMock()
mock_make_request.return_value = mock_response
response = self.api.get_tool("test_tool_handle")
mock_make_request.assert_called_once_with(
"GET", "/crewai_plus/api/v1/tools/test_tool_handle"
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.Settings")
@patch("requests.Session.request")
def test_get_tool_with_org_uuid(self, mock_make_request, mock_settings_class):
mock_settings = MagicMock()
mock_settings.org_uuid = self.org_uuid
mock_settings_class.return_value = mock_settings
# re-initialize Client
self.api = PlusAPI(self.api_key)
# Set up mock response
mock_response = MagicMock()
mock_make_request.return_value = mock_response
response = self.api.get_tool("test_tool_handle")
self.assert_request_with_org_id(
mock_make_request,
"GET",
"/crewai_plus/api/v1/tools/test_tool_handle"
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.PlusAPI._make_request")
def test_publish_tool(self, mock_make_request):
@@ -67,6 +147,47 @@ class TestPlusAPI(unittest.TestCase):
"POST", "/crewai_plus/api/v1/tools", json=params
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.Settings")
@patch("requests.Session.request")
def test_publish_tool_with_org_uuid(self, mock_make_request, mock_settings_class):
mock_settings = MagicMock()
mock_settings.org_uuid = self.org_uuid
mock_settings_class.return_value = mock_settings
# re-initialize Client
self.api = PlusAPI(self.api_key)
# Set up mock response
mock_response = MagicMock()
mock_make_request.return_value = mock_response
handle = "test_tool_handle"
public = True
version = "1.0.0"
description = "Test tool description"
encoded_file = "encoded_test_file"
response = self.api.publish_tool(
handle, public, version, description, encoded_file
)
# Expected params including organization_uuid
expected_params = {
"handle": handle,
"public": public,
"version": version,
"file": encoded_file,
"description": description,
"available_exports": None,
}
self.assert_request_with_org_id(
mock_make_request,
"POST",
"/crewai_plus/api/v1/tools",
json=expected_params
)
self.assertEqual(response, mock_response)
@patch("crewai.cli.plus_api.PlusAPI._make_request")
def test_publish_tool_without_description(self, mock_make_request):

View File

@@ -56,7 +56,8 @@ def test_create_success(mock_subprocess, capsys, tool_command):
@patch("crewai.cli.tools.main.subprocess.run")
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
def test_install_success(mock_get, mock_subprocess_run, capsys, tool_command):
@patch("crewai.cli.tools.main.ToolCommand._print_current_organization")
def test_install_success(mock_print_org, mock_get, mock_subprocess_run, capsys, tool_command):
mock_get_response = MagicMock()
mock_get_response.status_code = 200
mock_get_response.json.return_value = {
@@ -85,6 +86,9 @@ def test_install_success(mock_get, mock_subprocess_run, capsys, tool_command):
env=unittest.mock.ANY,
)
# Verify _print_current_organization was called
mock_print_org.assert_called_once()
@patch("crewai.cli.tools.main.subprocess.run")
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
def test_install_success_from_pypi(mock_get, mock_subprocess_run, capsys, tool_command):
@@ -166,7 +170,9 @@ def test_publish_when_not_in_sync(mock_is_synced, capsys, tool_command):
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=False)
@patch("crewai.cli.tools.main.extract_available_exports", return_value=[{"name": "SampleTool"}])
@patch("crewai.cli.tools.main.ToolCommand._print_current_organization")
def test_publish_when_not_in_sync_and_force(
mock_print_org,
mock_available_exports,
mock_is_synced,
mock_publish,
@@ -202,6 +208,7 @@ def test_publish_when_not_in_sync_and_force(
encoded_file=unittest.mock.ANY,
available_exports=[{"name": "SampleTool"}],
)
mock_print_org.assert_called_once()
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
@@ -329,3 +336,27 @@ def test_publish_api_error(
assert "Request to Enterprise API failed" in output
mock_publish.assert_called_once()
@patch("crewai.cli.tools.main.Settings")
def test_print_current_organization_with_org(mock_settings, capsys, tool_command):
mock_settings_instance = MagicMock()
mock_settings_instance.org_uuid = "test-org-uuid"
mock_settings_instance.org_name = "Test Organization"
mock_settings.return_value = mock_settings_instance
tool_command._print_current_organization()
output = capsys.readouterr().out
assert "Current organization: Test Organization (test-org-uuid)" in output
@patch("crewai.cli.tools.main.Settings")
def test_print_current_organization_without_org(mock_settings, capsys, tool_command):
mock_settings_instance = MagicMock()
mock_settings_instance.org_uuid = None
mock_settings_instance.org_name = None
mock_settings.return_value = mock_settings_instance
tool_command._print_current_organization()
output = capsys.readouterr().out
assert "No organization currently set" in output
assert "org switch <org_id>" in output

View File

@@ -856,18 +856,22 @@ def test_crew_verbose_output(researcher, writer, capsys):
)
expected_strings = [
"\x1b[1m\x1b[95m# Agent:\x1b[00m \x1b[1m\x1b[92mResearcher",
"\x1b[00m\n\x1b[95m## Task:\x1b[00m \x1b[92mResearch AI advancements.",
"\x1b[1m\x1b[95m# Agent:\x1b[00m \x1b[1m\x1b[92mSenior Writer",
"\x1b[95m## Task:\x1b[00m \x1b[92mWrite about AI in healthcare.",
"\n\n\x1b[1m\x1b[95m# Agent:\x1b[00m \x1b[1m\x1b[92mResearcher",
"\x1b[00m\n\x1b[95m## Final Answer:",
"\n\n\x1b[1m\x1b[95m# Agent:\x1b[00m \x1b[1m\x1b[92mSenior Writer",
"\x1b[00m\n\x1b[95m## Final Answer:",
"🤖 Agent Started",
"Agent: Researcher",
"Task: Research AI advancements.",
"✅ Agent Final Answer",
"Agent: Researcher",
"🤖 Agent Started",
"Agent: Senior Writer",
"Task: Write about AI in healthcare.",
"✅ Agent Final Answer",
"Agent: Senior Writer",
]
for expected_string in expected_strings:
assert expected_string in filtered_output
assert (
expected_string in filtered_output
), f"Expected '{expected_string}' in output, but it was not found."
# Now test with verbose set to False
crew.verbose = False
@@ -1765,6 +1769,76 @@ def test_agent_usage_metrics_are_captured_for_hierarchical_process():
)
def test_hierarchical_kickoff_usage_metrics_include_manager(researcher):
"""Ensure Crew.kickoff() sums UsageMetrics from both regular and manager agents."""
# ── 1. Build the manager and a simple task ──────────────────────────────────
manager = Agent(
role="Manager",
goal="Coordinate everything.",
backstory="Keeps the project on track.",
allow_delegation=False,
)
task = Task(
description="Say hello",
expected_output="Hello",
agent=researcher, # *regular* agent
)
# ── 2. Stub out each agents _token_process.get_summary() ───────────────────
researcher_metrics = UsageMetrics(
total_tokens=120, prompt_tokens=80, completion_tokens=40, successful_requests=2
)
manager_metrics = UsageMetrics(
total_tokens=30, prompt_tokens=20, completion_tokens=10, successful_requests=1
)
# Replace the internal _token_process objects with simple mocks
researcher._token_process = MagicMock(
get_summary=MagicMock(return_value=researcher_metrics)
)
manager._token_process = MagicMock(
get_summary=MagicMock(return_value=manager_metrics)
)
# ── 3. Create the crew (hierarchical!) and kick it off ──────────────────────
crew = Crew(
agents=[researcher], # regular agents
manager_agent=manager, # manager to be included
tasks=[task],
process=Process.hierarchical,
)
# We dont care about LLM output here; patch execute_sync to avoid network
with patch.object(
Task,
"execute_sync",
return_value=TaskOutput(
description="dummy", raw="Hello", agent=researcher.role
),
):
crew.kickoff()
# ── 4. Assert the aggregated numbers are the *sum* of both agents ───────────
assert (
crew.usage_metrics.total_tokens
== researcher_metrics.total_tokens + manager_metrics.total_tokens
)
assert (
crew.usage_metrics.prompt_tokens
== researcher_metrics.prompt_tokens + manager_metrics.prompt_tokens
)
assert (
crew.usage_metrics.completion_tokens
== researcher_metrics.completion_tokens + manager_metrics.completion_tokens
)
assert (
crew.usage_metrics.successful_requests
== researcher_metrics.successful_requests + manager_metrics.successful_requests
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_hierarchical_crew_creation_tasks_with_agents(researcher, writer):
"""
@@ -4406,27 +4480,29 @@ def test_sets_parent_flow_when_inside_flow(researcher, writer):
assert result.parent_flow is flow
def test_reset_knowledge_with_no_crew_knowledge(researcher,writer):
def test_reset_knowledge_with_no_crew_knowledge(researcher, writer):
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
]
],
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='knowledge')
crew.reset_memories(command_type="knowledge")
# Optionally, you can also check the error message
assert "Crew Knowledge and Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
assert "Crew Knowledge and Agent Knowledge memory system is not initialized" in str(
excinfo.value
) # Replace with the expected message
def test_reset_knowledge_with_only_crew_knowledge(researcher,writer):
def test_reset_knowledge_with_only_crew_knowledge(researcher, writer):
mock_ks = MagicMock(spec=Knowledge)
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
with patch.object(Crew, "reset_knowledge") as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4434,14 +4510,14 @@ def test_reset_knowledge_with_only_crew_knowledge(researcher,writer):
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks
knowledge=mock_ks,
)
crew.reset_memories(command_type='knowledge')
crew.reset_memories(command_type="knowledge")
mock_reset_agent_knowledge.assert_called_once_with([mock_ks])
def test_reset_knowledge_with_crew_and_agent_knowledge(researcher,writer):
def test_reset_knowledge_with_crew_and_agent_knowledge(researcher, writer):
mock_ks_crew = MagicMock(spec=Knowledge)
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
@@ -4449,7 +4525,7 @@ def test_reset_knowledge_with_crew_and_agent_knowledge(researcher,writer):
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
with patch.object(Crew, "reset_knowledge") as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4457,21 +4533,23 @@ def test_reset_knowledge_with_crew_and_agent_knowledge(researcher,writer):
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks_crew
knowledge=mock_ks_crew,
)
crew.reset_memories(command_type='knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_crew,mock_ks_research,mock_ks_writer])
crew.reset_memories(command_type="knowledge")
mock_reset_agent_knowledge.assert_called_once_with(
[mock_ks_crew, mock_ks_research, mock_ks_writer]
)
def test_reset_knowledge_with_only_agent_knowledge(researcher,writer):
def test_reset_knowledge_with_only_agent_knowledge(researcher, writer):
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
with patch.object(Crew, "reset_knowledge") as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4481,11 +4559,13 @@ def test_reset_knowledge_with_only_agent_knowledge(researcher,writer):
],
)
crew.reset_memories(command_type='knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])
crew.reset_memories(command_type="knowledge")
mock_reset_agent_knowledge.assert_called_once_with(
[mock_ks_research, mock_ks_writer]
)
def test_reset_agent_knowledge_with_no_agent_knowledge(researcher,writer):
def test_reset_agent_knowledge_with_no_agent_knowledge(researcher, writer):
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4496,13 +4576,15 @@ def test_reset_agent_knowledge_with_no_agent_knowledge(researcher,writer):
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='agent_knowledge')
crew.reset_memories(command_type="agent_knowledge")
# Optionally, you can also check the error message
assert "Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
assert "Agent Knowledge memory system is not initialized" in str(
excinfo.value
) # Replace with the expected message
def test_reset_agent_knowledge_with_only_crew_knowledge(researcher,writer):
def test_reset_agent_knowledge_with_only_crew_knowledge(researcher, writer):
mock_ks = MagicMock(spec=Knowledge)
crew = Crew(
@@ -4512,17 +4594,19 @@ def test_reset_agent_knowledge_with_only_crew_knowledge(researcher,writer):
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks
knowledge=mock_ks,
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='agent_knowledge')
crew.reset_memories(command_type="agent_knowledge")
# Optionally, you can also check the error message
assert "Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
assert "Agent Knowledge memory system is not initialized" in str(
excinfo.value
) # Replace with the expected message
def test_reset_agent_knowledge_with_crew_and_agent_knowledge(researcher,writer):
def test_reset_agent_knowledge_with_crew_and_agent_knowledge(researcher, writer):
mock_ks_crew = MagicMock(spec=Knowledge)
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
@@ -4530,7 +4614,7 @@ def test_reset_agent_knowledge_with_crew_and_agent_knowledge(researcher,writer):
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
with patch.object(Crew, "reset_knowledge") as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4538,21 +4622,23 @@ def test_reset_agent_knowledge_with_crew_and_agent_knowledge(researcher,writer):
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks_crew
knowledge=mock_ks_crew,
)
crew.reset_memories(command_type='agent_knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])
crew.reset_memories(command_type="agent_knowledge")
mock_reset_agent_knowledge.assert_called_once_with(
[mock_ks_research, mock_ks_writer]
)
def test_reset_agent_knowledge_with_only_agent_knowledge(researcher,writer):
def test_reset_agent_knowledge_with_only_agent_knowledge(researcher, writer):
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
with patch.object(Crew, "reset_knowledge") as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
@@ -4562,7 +4648,7 @@ def test_reset_agent_knowledge_with_only_agent_knowledge(researcher,writer):
],
)
crew.reset_memories(command_type='agent_knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])
crew.reset_memories(command_type="agent_knowledge")
mock_reset_agent_knowledge.assert_called_once_with(
[mock_ks_research, mock_ks_writer]
)

View File

@@ -9,6 +9,14 @@ from crewai.telemetry import Telemetry
from opentelemetry import trace
@pytest.fixture(autouse=True)
def cleanup_telemetry():
"""Automatically clean up Telemetry singleton between tests."""
Telemetry._instance = None
yield
Telemetry._instance = None
@pytest.mark.parametrize(
"env_var,value,expected_ready",
[

View File

@@ -1,11 +1,19 @@
import os
from unittest.mock import patch
from unittest.mock import patch, MagicMock
import pytest
from crewai.telemetry import Telemetry
@pytest.fixture(autouse=True)
def cleanup_telemetry():
"""Automatically clean up Telemetry singleton between tests."""
Telemetry._instance = None
yield
Telemetry._instance = None
@pytest.mark.parametrize("env_var,value,expected_ready", [
("OTEL_SDK_DISABLED", "true", False),
("OTEL_SDK_DISABLED", "TRUE", False),
@@ -28,3 +36,59 @@ def test_telemetry_enabled_by_default():
with patch("crewai.telemetry.telemetry.TracerProvider"):
telemetry = Telemetry()
assert telemetry.ready is True
def test_telemetry_disable_after_singleton_creation():
"""Test that telemetry operations are disabled when env var is set after singleton creation."""
with patch.dict(os.environ, {}, clear=True):
with patch("crewai.telemetry.telemetry.TracerProvider"):
telemetry = Telemetry()
assert telemetry.ready is True
mock_operation = MagicMock()
telemetry._safe_telemetry_operation(mock_operation)
mock_operation.assert_called_once()
mock_operation.reset_mock()
os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
telemetry._safe_telemetry_operation(mock_operation)
mock_operation.assert_not_called()
def test_telemetry_disable_with_multiple_instances():
"""Test that multiple telemetry instances respect dynamically changed env vars."""
with patch.dict(os.environ, {}, clear=True):
with patch("crewai.telemetry.telemetry.TracerProvider"):
telemetry1 = Telemetry()
assert telemetry1.ready is True
os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
telemetry2 = Telemetry()
assert telemetry2 is telemetry1
assert telemetry2.ready is True
mock_operation = MagicMock()
telemetry2._safe_telemetry_operation(mock_operation)
mock_operation.assert_not_called()
def test_telemetry_otel_sdk_disabled_after_creation():
"""Test that OTEL_SDK_DISABLED also works when set after singleton creation."""
with patch.dict(os.environ, {}, clear=True):
with patch("crewai.telemetry.telemetry.TracerProvider"):
telemetry = Telemetry()
assert telemetry.ready is True
mock_operation = MagicMock()
telemetry._safe_telemetry_operation(mock_operation)
mock_operation.assert_called_once()
mock_operation.reset_mock()
os.environ['OTEL_SDK_DISABLED'] = 'true'
telemetry._safe_telemetry_operation(mock_operation)
mock_operation.assert_not_called()

View File

@@ -0,0 +1,167 @@
import pytest
from unittest.mock import patch, MagicMock
from crewai.utilities.events.event_listener import event_listener
class TestFlowHumanInputIntegration:
"""Test integration between Flow execution and human input functionality."""
def test_console_formatter_pause_resume_methods(self):
"""Test that ConsoleFormatter pause/resume methods work correctly."""
formatter = event_listener.formatter
original_paused_state = formatter._live_paused
try:
formatter._live_paused = False
formatter.pause_live_updates()
assert formatter._live_paused
formatter.resume_live_updates()
assert not formatter._live_paused
finally:
formatter._live_paused = original_paused_state
@patch('builtins.input', return_value='')
def test_human_input_pauses_flow_updates(self, mock_input):
"""Test that human input pauses Flow status updates."""
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
executor = CrewAgentExecutorMixin()
executor.crew = MagicMock()
executor.crew._train = False
executor._printer = MagicMock()
formatter = event_listener.formatter
original_paused_state = formatter._live_paused
try:
formatter._live_paused = False
with patch.object(formatter, 'pause_live_updates') as mock_pause, \
patch.object(formatter, 'resume_live_updates') as mock_resume:
result = executor._ask_human_input("Test result")
mock_pause.assert_called_once()
mock_resume.assert_called_once()
mock_input.assert_called_once()
assert result == ''
finally:
formatter._live_paused = original_paused_state
@patch('builtins.input', side_effect=['feedback', ''])
def test_multiple_human_input_rounds(self, mock_input):
"""Test multiple rounds of human input with Flow status management."""
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
executor = CrewAgentExecutorMixin()
executor.crew = MagicMock()
executor.crew._train = False
executor._printer = MagicMock()
formatter = event_listener.formatter
original_paused_state = formatter._live_paused
try:
pause_calls = []
resume_calls = []
def track_pause():
pause_calls.append(True)
def track_resume():
resume_calls.append(True)
with patch.object(formatter, 'pause_live_updates', side_effect=track_pause), \
patch.object(formatter, 'resume_live_updates', side_effect=track_resume):
result1 = executor._ask_human_input("Test result 1")
assert result1 == 'feedback'
result2 = executor._ask_human_input("Test result 2")
assert result2 == ''
assert len(pause_calls) == 2
assert len(resume_calls) == 2
finally:
formatter._live_paused = original_paused_state
def test_pause_resume_with_no_live_session(self):
"""Test pause/resume methods handle case when no Live session exists."""
formatter = event_listener.formatter
original_live = formatter._live
original_paused_state = formatter._live_paused
try:
formatter._live = None
formatter._live_paused = False
formatter.pause_live_updates()
formatter.resume_live_updates()
assert not formatter._live_paused
finally:
formatter._live = original_live
formatter._live_paused = original_paused_state
def test_pause_resume_exception_handling(self):
"""Test that resume is called even if exception occurs during human input."""
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
executor = CrewAgentExecutorMixin()
executor.crew = MagicMock()
executor.crew._train = False
executor._printer = MagicMock()
formatter = event_listener.formatter
original_paused_state = formatter._live_paused
try:
with patch.object(formatter, 'pause_live_updates') as mock_pause, \
patch.object(formatter, 'resume_live_updates') as mock_resume, \
patch('builtins.input', side_effect=KeyboardInterrupt("Test exception")):
with pytest.raises(KeyboardInterrupt):
executor._ask_human_input("Test result")
mock_pause.assert_called_once()
mock_resume.assert_called_once()
finally:
formatter._live_paused = original_paused_state
def test_training_mode_human_input(self):
"""Test human input in training mode."""
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
executor = CrewAgentExecutorMixin()
executor.crew = MagicMock()
executor.crew._train = True
executor._printer = MagicMock()
formatter = event_listener.formatter
original_paused_state = formatter._live_paused
try:
with patch.object(formatter, 'pause_live_updates') as mock_pause, \
patch.object(formatter, 'resume_live_updates') as mock_resume, \
patch('builtins.input', return_value='training feedback'):
result = executor._ask_human_input("Test result")
mock_pause.assert_called_once()
mock_resume.assert_called_once()
assert result == 'training feedback'
executor._printer.print.assert_called()
call_args = [call[1]['content'] for call in executor._printer.print.call_args_list]
training_prompt_found = any('TRAINING MODE' in content for content in call_args)
assert training_prompt_found
finally:
formatter._live_paused = original_paused_state

View File

@@ -1,4 +1,4 @@
import asyncio
from collections import defaultdict
from typing import cast
from unittest.mock import Mock
@@ -313,5 +313,108 @@ def test_sets_parent_flow_when_inside_flow():
nonlocal captured_agent
captured_agent = source
result = flow.kickoff()
flow.kickoff()
assert captured_agent.parent_flow is flow
@pytest.mark.vcr(filter_headers=["authorization"])
def test_guardrail_is_called_using_string():
guardrail_events = defaultdict(list)
from crewai.utilities.events import LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent
with crewai_event_bus.scoped_handlers():
@crewai_event_bus.on(LLMGuardrailStartedEvent)
def capture_guardrail_started(source, event):
guardrail_events["started"].append(event)
@crewai_event_bus.on(LLMGuardrailCompletedEvent)
def capture_guardrail_completed(source, event):
guardrail_events["completed"].append(event)
agent = Agent(
role="Sports Analyst",
goal="Gather information about the best soccer players",
backstory="""You are an expert at gathering and organizing information. You carefully collect details and present them in a structured way.""",
guardrail="""Only include Brazilian players, both women and men""",
)
result = agent.kickoff(messages="Top 10 best players in the world?")
assert len(guardrail_events['started']) == 2
assert len(guardrail_events['completed']) == 2
assert not guardrail_events['completed'][0].success
assert guardrail_events['completed'][1].success
assert "Here are the top 10 best soccer players in the world, focusing exclusively on Brazilian players" in result.raw
@pytest.mark.vcr(filter_headers=["authorization"])
def test_guardrail_is_called_using_callable():
guardrail_events = defaultdict(list)
from crewai.utilities.events import LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent
with crewai_event_bus.scoped_handlers():
@crewai_event_bus.on(LLMGuardrailStartedEvent)
def capture_guardrail_started(source, event):
guardrail_events["started"].append(event)
@crewai_event_bus.on(LLMGuardrailCompletedEvent)
def capture_guardrail_completed(source, event):
guardrail_events["completed"].append(event)
agent = Agent(
role="Sports Analyst",
goal="Gather information about the best soccer players",
backstory="""You are an expert at gathering and organizing information. You carefully collect details and present them in a structured way.""",
guardrail=lambda output: (True, "Pelé - Santos, 1958"),
)
result = agent.kickoff(messages="Top 1 best players in the world?")
assert len(guardrail_events['started']) == 1
assert len(guardrail_events['completed']) == 1
assert guardrail_events['completed'][0].success
assert "Pelé - Santos, 1958" in result.raw
@pytest.mark.vcr(filter_headers=["authorization"])
def test_guardrail_reached_attempt_limit():
guardrail_events = defaultdict(list)
from crewai.utilities.events import LLMGuardrailCompletedEvent, LLMGuardrailStartedEvent
with crewai_event_bus.scoped_handlers():
@crewai_event_bus.on(LLMGuardrailStartedEvent)
def capture_guardrail_started(source, event):
guardrail_events["started"].append(event)
@crewai_event_bus.on(LLMGuardrailCompletedEvent)
def capture_guardrail_completed(source, event):
guardrail_events["completed"].append(event)
agent = Agent(
role="Sports Analyst",
goal="Gather information about the best soccer players",
backstory="""You are an expert at gathering and organizing information. You carefully collect details and present them in a structured way.""",
guardrail=lambda output: (False, "You are not allowed to include Brazilian players"),
guardrail_max_retries=2,
)
with pytest.raises(Exception, match="Agent's guardrail failed validation after 2 retries"):
agent.kickoff(messages="Top 10 best players in the world?")
assert len(guardrail_events['started']) == 3 # 2 retries + 1 initial call
assert len(guardrail_events['completed']) == 3 # 2 retries + 1 initial call
assert not guardrail_events['completed'][0].success
assert not guardrail_events['completed'][1].success
assert not guardrail_events['completed'][2].success
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_output_when_guardrail_returns_base_model():
class Player(BaseModel):
name: str
country: str
agent = Agent(
role="Sports Analyst",
goal="Gather information about the best soccer players",
backstory="""You are an expert at gathering and organizing information. You carefully collect details and present them in a structured way.""",
guardrail=lambda output: (True, Player(name="Lionel Messi", country="Argentina")),
)
result = agent.kickoff(messages="Top 10 best players in the world?")
assert result.pydantic == Player(name="Lionel Messi", country="Argentina")

View File

@@ -25,122 +25,206 @@ def schema_class():
return TestSchema
class InternalCrewStructuredTool:
def test_initialization(self, basic_function, schema_class):
"""Test basic initialization of CrewStructuredTool"""
tool = CrewStructuredTool(
name="test_tool",
description="Test tool description",
func=basic_function,
args_schema=schema_class,
)
def test_initialization(basic_function, schema_class):
"""Test basic initialization of CrewStructuredTool"""
tool = CrewStructuredTool(
name="test_tool",
description="Test tool description",
func=basic_function,
args_schema=schema_class,
)
assert tool.name == "test_tool"
assert tool.description == "Test tool description"
assert tool.func == basic_function
assert tool.args_schema == schema_class
assert tool.name == "test_tool"
assert tool.description == "Test tool description"
assert tool.func == basic_function
assert tool.args_schema == schema_class
def test_from_function(self, basic_function):
"""Test creating tool from function"""
tool = CrewStructuredTool.from_function(
func=basic_function, name="test_tool", description="Test description"
)
def test_from_function(basic_function):
"""Test creating tool from function"""
tool = CrewStructuredTool.from_function(
func=basic_function, name="test_tool", description="Test description"
)
assert tool.name == "test_tool"
assert tool.description == "Test description"
assert tool.func == basic_function
assert isinstance(tool.args_schema, type(BaseModel))
assert tool.name == "test_tool"
assert tool.description == "Test description"
assert tool.func == basic_function
assert isinstance(tool.args_schema, type(BaseModel))
def test_validate_function_signature(self, basic_function, schema_class):
"""Test function signature validation"""
tool = CrewStructuredTool(
name="test_tool",
description="Test tool",
func=basic_function,
args_schema=schema_class,
)
def test_validate_function_signature(basic_function, schema_class):
"""Test function signature validation"""
tool = CrewStructuredTool(
name="test_tool",
description="Test tool",
func=basic_function,
args_schema=schema_class,
)
# Should not raise any exceptions
tool._validate_function_signature()
# Should not raise any exceptions
tool._validate_function_signature()
@pytest.mark.asyncio
async def test_ainvoke(self, basic_function):
"""Test asynchronous invocation"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
@pytest.mark.asyncio
async def test_ainvoke(basic_function):
"""Test asynchronous invocation"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
result = await tool.ainvoke(input={"param1": "test"})
assert result == "test 0"
result = await tool.ainvoke(input={"param1": "test"})
assert result == "test 0"
def test_parse_args_dict(self, basic_function):
"""Test parsing dictionary arguments"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
def test_parse_args_dict(basic_function):
"""Test parsing dictionary arguments"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
parsed = tool._parse_args({"param1": "test", "param2": 42})
assert parsed["param1"] == "test"
assert parsed["param2"] == 42
parsed = tool._parse_args({"param1": "test", "param2": 42})
assert parsed["param1"] == "test"
assert parsed["param2"] == 42
def test_parse_args_string(self, basic_function):
"""Test parsing string arguments"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
def test_parse_args_string(basic_function):
"""Test parsing string arguments"""
tool = CrewStructuredTool.from_function(func=basic_function, name="test_tool")
parsed = tool._parse_args('{"param1": "test", "param2": 42}')
assert parsed["param1"] == "test"
assert parsed["param2"] == 42
parsed = tool._parse_args('{"param1": "test", "param2": 42}')
assert parsed["param1"] == "test"
assert parsed["param2"] == 42
def test_complex_types(self):
"""Test handling of complex parameter types"""
def test_complex_types():
"""Test handling of complex parameter types"""
def complex_func(nested: dict, items: list) -> str:
"""Process complex types."""
return f"Processed {len(items)} items with {len(nested)} nested keys"
def complex_func(nested: dict, items: list) -> str:
"""Process complex types."""
return f"Processed {len(items)} items with {len(nested)} nested keys"
tool = CrewStructuredTool.from_function(
func=complex_func, name="test_tool", description="Test complex types"
)
result = tool.invoke({"nested": {"key": "value"}, "items": [1, 2, 3]})
assert result == "Processed 3 items with 1 nested keys"
tool = CrewStructuredTool.from_function(
func=complex_func, name="test_tool", description="Test complex types"
)
result = tool.invoke({"nested": {"key": "value"}, "items": [1, 2, 3]})
assert result == "Processed 3 items with 1 nested keys"
def test_schema_inheritance(self):
"""Test tool creation with inherited schema"""
def test_schema_inheritance():
"""Test tool creation with inherited schema"""
def extended_func(base_param: str, extra_param: int) -> str:
"""Test function with inherited schema."""
return f"{base_param} {extra_param}"
def extended_func(base_param: str, extra_param: int) -> str:
"""Test function with inherited schema."""
return f"{base_param} {extra_param}"
class BaseSchema(BaseModel):
base_param: str
class BaseSchema(BaseModel):
base_param: str
class ExtendedSchema(BaseSchema):
extra_param: int
class ExtendedSchema(BaseSchema):
extra_param: int
tool = CrewStructuredTool.from_function(
func=extended_func, name="test_tool", args_schema=ExtendedSchema
)
tool = CrewStructuredTool.from_function(
func=extended_func, name="test_tool", args_schema=ExtendedSchema
)
result = tool.invoke({"base_param": "test", "extra_param": 42})
assert result == "test 42"
result = tool.invoke({"base_param": "test", "extra_param": 42})
assert result == "test 42"
def test_default_values_in_schema(self):
"""Test handling of default values in schema"""
def test_default_values_in_schema():
"""Test handling of default values in schema"""
def default_func(
required_param: str,
optional_param: str = "default",
nullable_param: Optional[int] = None,
) -> str:
"""Test function with default values."""
return f"{required_param} {optional_param} {nullable_param}"
def default_func(
required_param: str,
optional_param: str = "default",
nullable_param: Optional[int] = None,
) -> str:
"""Test function with default values."""
return f"{required_param} {optional_param} {nullable_param}"
tool = CrewStructuredTool.from_function(
func=default_func, name="test_tool", description="Test defaults"
)
tool = CrewStructuredTool.from_function(
func=default_func, name="test_tool", description="Test defaults"
)
# Test with minimal parameters
result = tool.invoke({"required_param": "test"})
assert result == "test default None"
# Test with minimal parameters
result = tool.invoke({"required_param": "test"})
assert result == "test default None"
# Test with all parameters
result = tool.invoke(
{"required_param": "test", "optional_param": "custom", "nullable_param": 42}
)
assert result == "test custom 42"
# Test with all parameters
result = tool.invoke(
{"required_param": "test", "optional_param": "custom", "nullable_param": 42}
)
assert result == "test custom 42"
@pytest.fixture
def custom_tool_decorator():
from crewai.tools import tool
@tool("custom_tool", result_as_answer=True)
async def custom_tool():
"""This is a tool that does something"""
return "Hello World from Custom Tool"
return custom_tool
@pytest.fixture
def custom_tool():
from crewai.tools import BaseTool
class CustomTool(BaseTool):
name: str = "my_tool"
description: str = "This is a tool that does something"
result_as_answer: bool = True
async def _run(self):
return "Hello World from Custom Tool"
return CustomTool()
def build_simple_crew(tool):
from crewai import Agent, Task, Crew
agent1 = Agent(role="Simple role", goal="Simple goal", backstory="Simple backstory", tools=[tool])
say_hi_task = Task(
description="Use the custom tool result as answer.", agent=agent1, expected_output="Use the tool result"
)
crew = Crew(agents=[agent1], tasks=[say_hi_task])
return crew
@pytest.mark.vcr(filter_headers=["authorization"])
def test_async_tool_using_within_isolated_crew(custom_tool):
crew = build_simple_crew(custom_tool)
result = crew.kickoff()
assert result.raw == "Hello World from Custom Tool"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_async_tool_using_decorator_within_isolated_crew(custom_tool_decorator):
crew = build_simple_crew(custom_tool_decorator)
result = crew.kickoff()
assert result.raw == "Hello World from Custom Tool"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_async_tool_within_flow(custom_tool):
from crewai.flow.flow import Flow
class StructuredExampleFlow(Flow):
from crewai.flow.flow import start
@start()
async def start(self):
crew = build_simple_crew(custom_tool)
result = await crew.kickoff_async()
return result
flow = StructuredExampleFlow()
result = flow.kickoff()
assert result.raw == "Hello World from Custom Tool"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_async_tool_using_decorator_within_flow(custom_tool_decorator):
from crewai.flow.flow import Flow
class StructuredExampleFlow(Flow):
from crewai.flow.flow import start
@start()
async def start(self):
crew = build_simple_crew(custom_tool_decorator)
result = await crew.kickoff_async()
return result
flow = StructuredExampleFlow()
result = flow.kickoff()
assert result.raw == "Hello World from Custom Tool"

View File

@@ -0,0 +1,116 @@
from unittest.mock import MagicMock, patch
from rich.tree import Tree
from rich.live import Live
from crewai.utilities.events.utils.console_formatter import ConsoleFormatter
class TestConsoleFormatterPauseResume:
"""Test ConsoleFormatter pause/resume functionality."""
def test_pause_live_updates_with_active_session(self):
"""Test pausing when Live session is active."""
formatter = ConsoleFormatter()
mock_live = MagicMock(spec=Live)
formatter._live = mock_live
formatter._live_paused = False
formatter.pause_live_updates()
mock_live.stop.assert_called_once()
assert formatter._live_paused
def test_pause_live_updates_when_already_paused(self):
"""Test pausing when already paused does nothing."""
formatter = ConsoleFormatter()
mock_live = MagicMock(spec=Live)
formatter._live = mock_live
formatter._live_paused = True
formatter.pause_live_updates()
mock_live.stop.assert_not_called()
assert formatter._live_paused
def test_pause_live_updates_with_no_session(self):
"""Test pausing when no Live session exists."""
formatter = ConsoleFormatter()
formatter._live = None
formatter._live_paused = False
formatter.pause_live_updates()
assert formatter._live_paused
def test_resume_live_updates_when_paused(self):
"""Test resuming when paused."""
formatter = ConsoleFormatter()
formatter._live_paused = True
formatter.resume_live_updates()
assert not formatter._live_paused
def test_resume_live_updates_when_not_paused(self):
"""Test resuming when not paused does nothing."""
formatter = ConsoleFormatter()
formatter._live_paused = False
formatter.resume_live_updates()
assert not formatter._live_paused
def test_print_after_resume_restarts_live_session(self):
"""Test that printing a Tree after resume creates new Live session."""
formatter = ConsoleFormatter()
formatter._live_paused = True
formatter._live = None
formatter.resume_live_updates()
assert not formatter._live_paused
tree = Tree("Test")
with patch('crewai.utilities.events.utils.console_formatter.Live') as mock_live_class:
mock_live_instance = MagicMock()
mock_live_class.return_value = mock_live_instance
formatter.print(tree)
mock_live_class.assert_called_once()
mock_live_instance.start.assert_called_once()
assert formatter._live == mock_live_instance
def test_multiple_pause_resume_cycles(self):
"""Test multiple pause/resume cycles work correctly."""
formatter = ConsoleFormatter()
mock_live = MagicMock(spec=Live)
formatter._live = mock_live
formatter._live_paused = False
formatter.pause_live_updates()
assert formatter._live_paused
mock_live.stop.assert_called_once()
assert formatter._live is None # Live session should be cleared
formatter.resume_live_updates()
assert not formatter._live_paused
formatter.pause_live_updates()
assert formatter._live_paused
formatter.resume_live_updates()
assert not formatter._live_paused
def test_pause_resume_state_initialization(self):
"""Test that _live_paused is properly initialized."""
formatter = ConsoleFormatter()
assert hasattr(formatter, '_live_paused')
assert not formatter._live_paused

277
uv.lock generated
View File

@@ -337,15 +337,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/ee/3fd29bf416eb4f1c5579cf12bf393ae954099258abd7bde03c4f9716ef6b/autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840", size = 32483 },
]
[[package]]
name = "babel"
version = "2.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 },
]
[[package]]
name = "backoff"
version = "2.2.1"
@@ -725,7 +716,7 @@ wheels = [
[[package]]
name = "crewai"
version = "0.126.0"
version = "0.130.0"
source = { editable = "." }
dependencies = [
{ name = "appdirs" },
@@ -787,11 +778,6 @@ tools = [
[package.dev-dependencies]
dev = [
{ name = "cairosvg" },
{ name = "mkdocs" },
{ name = "mkdocs-material" },
{ name = "mkdocs-material-extensions" },
{ name = "mkdocstrings" },
{ name = "mkdocstrings-python" },
{ name = "mypy" },
{ name = "pillow" },
{ name = "pre-commit" },
@@ -814,13 +800,13 @@ requires-dist = [
{ name = "blinker", specifier = ">=1.9.0" },
{ name = "chromadb", specifier = ">=0.5.23" },
{ name = "click", specifier = ">=8.1.7" },
{ name = "crewai-tools", marker = "extra == 'tools'", specifier = "~=0.46.0" },
{ name = "crewai-tools", marker = "extra == 'tools'", specifier = "~=0.47.1" },
{ name = "docling", marker = "extra == 'docling'", specifier = ">=2.12.0" },
{ name = "instructor", specifier = ">=1.3.3" },
{ name = "json-repair", specifier = ">=0.25.2" },
{ name = "json5", specifier = ">=0.10.0" },
{ name = "jsonref", specifier = ">=1.1.0" },
{ name = "litellm", specifier = "==1.68.0" },
{ name = "litellm", specifier = "==1.72.0" },
{ name = "mem0ai", marker = "extra == 'mem0'", specifier = ">=0.1.94" },
{ name = "onnxruntime", specifier = "==1.22.0" },
{ name = "openai", specifier = ">=1.13.3" },
@@ -846,11 +832,6 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "cairosvg", specifier = ">=2.7.1" },
{ name = "mkdocs", specifier = ">=1.4.3" },
{ name = "mkdocs-material", specifier = ">=9.5.7" },
{ name = "mkdocs-material-extensions", specifier = ">=1.3.1" },
{ name = "mkdocstrings", specifier = ">=0.22.0" },
{ name = "mkdocstrings-python", specifier = ">=1.1.2" },
{ name = "mypy", specifier = ">=1.10.0" },
{ name = "pillow", specifier = ">=10.2.0" },
{ name = "pre-commit", specifier = ">=3.6.0" },
@@ -866,7 +847,7 @@ dev = [
[[package]]
name = "crewai-tools"
version = "0.46.0"
version = "0.47.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "chromadb" },
@@ -880,10 +861,11 @@ dependencies = [
{ name = "pyright" },
{ name = "pytube" },
{ name = "requests" },
{ name = "tiktoken" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/9e/69109f5d5b398b2edeccec1055e93cdceac3becd04407bcce97de6557180/crewai_tools-0.46.0.tar.gz", hash = "sha256:c8f89247199d528c77db4b450a1ca781b5d32405982467baf516ede4b2045bd1", size = 913715 }
sdist = { url = "https://files.pythonhosted.org/packages/0e/cd/fc5a96be8c108febcc2c767714e3ec9b70cca9be8e6b29bba7c1874fb6d2/crewai_tools-0.47.1.tar.gz", hash = "sha256:4de5ebf320aeae317ffabe2e4704b98b5d791f663196871fb5ad2e7bbea14a82", size = 921418 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/62/0b68637ce820fbb0385495bd6d75ceb27de53f060df5417f293419826481/crewai_tools-0.46.0-py3-none-any.whl", hash = "sha256:f8e60723869ca36ede7b43dcc1491ebefc93410a972d97b7b0ce59c4bd7a826b", size = 606190 },
{ url = "https://files.pythonhosted.org/packages/e3/2c/05d9fa584d9d814c0c8c4c3793df572222417695fe3d716f14bc274376d6/crewai_tools-0.47.1-py3-none-any.whl", hash = "sha256:4dc9bb0a08e3afa33c6b9efb163e47181801a7906be7241977426e6d3dec0a05", size = 606305 },
]
[[package]]
@@ -1427,18 +1409,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/b2/454d6e7f0158951d8a78c2e1eb4f69ae81beb8dca5fee9809c6c99e9d0d0/fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871", size = 179641 },
]
[[package]]
name = "ghp-import"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 },
]
[[package]]
name = "google-auth"
version = "2.35.0"
@@ -1530,18 +1500,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
]
[[package]]
name = "griffe"
version = "1.5.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/c9/8167810358ca129839156dc002526e7398b5fad4a9d7b6e88b875e802d0d/griffe-1.5.1.tar.gz", hash = "sha256:72964f93e08c553257706d6cd2c42d1c172213feb48b2be386f243380b405d4b", size = 384113 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/00/e693a155da0a2a72fd2df75b8fe338146cae59d590ad6f56800adde90cb5/griffe-1.5.1-py3-none-any.whl", hash = "sha256:ad6a7980f8c424c9102160aafa3bcdf799df0e75f7829d75af9ee5aef656f860", size = 127132 },
]
[[package]]
name = "grpcio"
version = "1.67.0"
@@ -2245,7 +2203,7 @@ wheels = [
[[package]]
name = "litellm"
version = "1.68.0"
version = "1.72.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -2260,9 +2218,9 @@ dependencies = [
{ name = "tiktoken" },
{ name = "tokenizers" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/22/138545b646303ca3f4841b69613c697b9d696322a1386083bb70bcbba60b/litellm-1.68.0.tar.gz", hash = "sha256:9fb24643db84dfda339b64bafca505a2eef857477afbc6e98fb56512c24dbbfa", size = 7314051 }
sdist = { url = "https://files.pythonhosted.org/packages/55/d3/f1a8c9c9ffdd3bab1bc410254c8140b1346f05a01b8c6b37c48b56abb4b0/litellm-1.72.0.tar.gz", hash = "sha256:135022b9b8798f712ffa84e71ac419aa4310f1d0a70f79dd2007f7ef3a381e43", size = 8082337 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/af/1e344bc8aee41445272e677d802b774b1f8b34bdc3bb5697ba30f0fb5d52/litellm-1.68.0-py3-none-any.whl", hash = "sha256:3bca38848b1a5236b11aa6b70afa4393b60880198c939e582273f51a542d4759", size = 7684460 },
{ url = "https://files.pythonhosted.org/packages/c2/98/bec08f5a3e504013db6f52b5fd68375bd92b463c91eb454d5a6460e957af/litellm-1.72.0-py3-none-any.whl", hash = "sha256:88360a7ae9aa9c96278ae1bb0a459226f909e711c5d350781296d0640386a824", size = 7979630 },
]
[[package]]
@@ -2359,15 +2317,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/48/22/bc14c6f02e6dccaafb3eba95764c8f096714260c2aa5f76f654fd16a23dd/Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a", size = 78557 },
]
[[package]]
name = "markdown"
version = "3.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
@@ -2498,131 +2447,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/fa/a2043c5a228077423678d9e7e0fe3aab89ab9a625a4f171a30967de1bbb3/mem0ai-0.1.94-py3-none-any.whl", hash = "sha256:862aaccf4ec41d65a5c935dc49c7b8175c96f6b60b29abeda338d8a335027e2c", size = 143077 },
]
[[package]]
name = "mergedeep"
version = "1.3.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 },
]
[[package]]
name = "mkdocs"
version = "1.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "colorama", marker = "platform_system == 'Windows'" },
{ name = "ghp-import" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "markupsafe" },
{ name = "mergedeep" },
{ name = "mkdocs-get-deps" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "pyyaml" },
{ name = "pyyaml-env-tag" },
{ name = "watchdog" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 },
]
[[package]]
name = "mkdocs-autorefs"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown" },
{ name = "markupsafe" },
{ name = "mkdocs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/ae/0f1154c614d6a8b8a36fff084e5b82af3a15f7d2060cf0dcdb1c53297a71/mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f", size = 40262 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f", size = 16522 },
]
[[package]]
name = "mkdocs-get-deps"
version = "0.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mergedeep" },
{ name = "platformdirs" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 },
]
[[package]]
name = "mkdocs-material"
version = "9.5.42"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "babel" },
{ name = "colorama" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "mkdocs" },
{ name = "mkdocs-material-extensions" },
{ name = "paginate" },
{ name = "pygments" },
{ name = "pymdown-extensions" },
{ name = "regex" },
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f9/33/b3343ed975fbbd6798b8d8a7c4f1bf8489cc321fc8fd426eba3d87b0242e/mkdocs_material-9.5.42.tar.gz", hash = "sha256:92779b5e9b5934540c574c11647131d217dc540dce72b05feeda088c8eb1b8f2", size = 3963891 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/55/55/ad3e6a60ac1e8e76025543c49c1f24ecd80fb38e8a57000403bf2f0a4293/mkdocs_material-9.5.42-py3-none-any.whl", hash = "sha256:452a7c5d21284b373f36b981a2cbebfff59263feebeede1bc28652e9c5bbe316", size = 8672619 },
]
[[package]]
name = "mkdocs-material-extensions"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 },
]
[[package]]
name = "mkdocstrings"
version = "0.26.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "markupsafe" },
{ name = "mkdocs" },
{ name = "mkdocs-autorefs" },
{ name = "platformdirs" },
{ name = "pymdown-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/76/0475d10d27f3384df3a6ddfdf4a4fdfef83766f77cd4e327d905dc956c15/mkdocstrings-0.26.2.tar.gz", hash = "sha256:34a8b50f1e6cfd29546c6c09fbe02154adfb0b361bb758834bf56aa284ba876e", size = 92512 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/80/b6/4ee320d7c313da3774eff225875eb278f7e6bb26a9cd8e680b8dbc38fdea/mkdocstrings-0.26.2-py3-none-any.whl", hash = "sha256:1248f3228464f3b8d1a15bd91249ce1701fe3104ac517a5f167a0e01ca850ba5", size = 29716 },
]
[[package]]
name = "mkdocstrings-python"
version = "1.12.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "griffe" },
{ name = "mkdocs-autorefs" },
{ name = "mkdocstrings" },
]
sdist = { url = "https://files.pythonhosted.org/packages/23/ec/cb6debe2db77f1ef42b25b21d93b5021474de3037cd82385e586aee72545/mkdocstrings_python-1.12.2.tar.gz", hash = "sha256:7a1760941c0b52a2cd87b960a9e21112ffe52e7df9d0b9583d04d47ed2e186f3", size = 168207 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/c1/ac524e1026d9580cbc654b5d19f5843c8b364a66d30f956372cd09fd2f92/mkdocstrings_python-1.12.2-py3-none-any.whl", hash = "sha256:7f7d40d6db3cb1f5d19dbcd80e3efe4d0ba32b073272c0c0de9de2e604eda62a", size = 111759 },
]
[[package]]
name = "mmh3"
version = "4.1.0"
@@ -3123,7 +2947,7 @@ wheels = [
[[package]]
name = "openai"
version = "1.68.2"
version = "1.78.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -3135,9 +2959,9 @@ dependencies = [
{ name = "tqdm" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/6b/6b002d5d38794645437ae3ddb42083059d556558493408d39a0fcea608bc/openai-1.68.2.tar.gz", hash = "sha256:b720f0a95a1dbe1429c0d9bb62096a0d98057bcda82516f6e8af10284bdd5b19", size = 413429 }
sdist = { url = "https://files.pythonhosted.org/packages/d1/7c/7c48bac9be52680e41e99ae7649d5da3a0184cd94081e028897f9005aa03/openai-1.78.0.tar.gz", hash = "sha256:254aef4980688468e96cbddb1f348ed01d274d02c64c6c69b0334bf001fb62b3", size = 442652 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/34/cebce15f64eb4a3d609a83ac3568d43005cc9a1cba9d7fde5590fd415423/openai-1.68.2-py3-none-any.whl", hash = "sha256:24484cb5c9a33b58576fdc5acf0e5f92603024a4e39d0b99793dfa1eb14c2b36", size = 606073 },
{ url = "https://files.pythonhosted.org/packages/cc/41/d64a6c56d0ec886b834caff7a07fc4d43e1987895594b144757e7a6b90d7/openai-1.78.0-py3-none-any.whl", hash = "sha256:1ade6a48cd323ad8a7715e7e1669bb97a17e1a5b8a916644261aaef4bf284778", size = 680407 },
]
[[package]]
@@ -3388,15 +3212,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 },
]
[[package]]
name = "paginate"
version = "0.5.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 },
]
[[package]]
name = "pandas"
version = "2.2.3"
@@ -3463,15 +3278,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 },
]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
]
[[package]]
name = "pdfminer-six"
version = "20231228"
@@ -4054,19 +3860,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 },
]
[[package]]
name = "pymdown-extensions"
version = "10.11.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f4/71/2730a20e9e3752393d78998347f8b1085ef9c417646ea9befbeef221e3c4/pymdown_extensions-10.11.2.tar.gz", hash = "sha256:bc8847ecc9e784a098efd35e20cba772bc5a1b529dfcef9dc1972db9021a1049", size = 830241 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/35/c0edf199257ef0a7d407d29cd51c4e70d1dad4370a5f44deb65a7a5475e2/pymdown_extensions-10.11.2-py3-none-any.whl", hash = "sha256:41cdde0a77290e480cf53892f5c5e50921a7ee3e5cd60ba91bf19837b33badcf", size = 259044 },
]
[[package]]
name = "pypdf"
version = "5.0.1"
@@ -4443,18 +4236,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
]
[[package]]
name = "pyyaml-env-tag"
version = "0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 },
]
[[package]]
name = "qdrant-client"
version = "1.12.0"
@@ -5665,38 +5446,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/15/828ec11907aee2349a9342fa71fba4ba7f3af938162a382dd7da339dea16/virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655", size = 3110969 },
]
[[package]]
name = "watchdog"
version = "5.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/48/a86139aaeab2db0a2482676f64798d8ac4d2dbb457523f50ab37bf02ce2c/watchdog-5.0.3.tar.gz", hash = "sha256:108f42a7f0345042a854d4d0ad0834b741d421330d5f575b81cb27b883500176", size = 129556 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/2b/dd2081aab6fc9e785c2eee7146d3c6de58e607f4e70049d715cd170cbf77/watchdog-5.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:85527b882f3facda0579bce9d743ff7f10c3e1e0db0a0d0e28170a7d0e5ce2ea", size = 96652 },
{ url = "https://files.pythonhosted.org/packages/9e/4f/f643c0a720d16ef7316aea06a79b96e229e59df4e0d83bec5e12713c1f29/watchdog-5.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:53adf73dcdc0ef04f7735066b4a57a4cd3e49ef135daae41d77395f0b5b692cb", size = 88651 },
{ url = "https://files.pythonhosted.org/packages/2b/72/acb22067d1f18161914c9b1087c703d63638131a9fde78090da290663407/watchdog-5.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e25adddab85f674acac303cf1f5835951345a56c5f7f582987d266679979c75b", size = 89289 },
{ url = "https://files.pythonhosted.org/packages/70/34/946f08602f8b8e6af45bc725e4a8013975a34883ab5570bd0d827a4c9829/watchdog-5.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f01f4a3565a387080dc49bdd1fefe4ecc77f894991b88ef927edbfa45eb10818", size = 96650 },
{ url = "https://files.pythonhosted.org/packages/96/2b/b84e35d49e8b0bad77e5d086fc1e2c6c833bbfe74d53144cfe8b26117eff/watchdog-5.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91b522adc25614cdeaf91f7897800b82c13b4b8ac68a42ca959f992f6990c490", size = 88653 },
{ url = "https://files.pythonhosted.org/packages/d5/3f/41b5d77c10f450b79921c17b7d0b416616048867bfe63acaa072a619a0cb/watchdog-5.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d52db5beb5e476e6853da2e2d24dbbbed6797b449c8bf7ea118a4ee0d2c9040e", size = 89286 },
{ url = "https://files.pythonhosted.org/packages/1c/9b/8b206a928c188fdeb7b12e1c795199534cd44bdef223b8470129016009dd/watchdog-5.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d11b07c64f63f49876e0ab8042ae034674c8653bfcdaa8c4b32e71cfff87e8", size = 96739 },
{ url = "https://files.pythonhosted.org/packages/e1/26/129ca9cd0f8016672f37000010c2fedc0b86816e894ebdc0af9bb04a6439/watchdog-5.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:349c9488e1d85d0a58e8cb14222d2c51cbc801ce11ac3936ab4c3af986536926", size = 88708 },
{ url = "https://files.pythonhosted.org/packages/8f/b3/5e10ec32f0c429cdb55b1369066d6e83faf9985b3a53a4e37bb5c5e29aa0/watchdog-5.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:53a3f10b62c2d569e260f96e8d966463dec1a50fa4f1b22aec69e3f91025060e", size = 89309 },
{ url = "https://files.pythonhosted.org/packages/54/c4/49af4ab00bcfb688e9962eace2edda07a2cf89b9699ea536da48e8585cff/watchdog-5.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:950f531ec6e03696a2414b6308f5c6ff9dab7821a768c9d5788b1314e9a46ca7", size = 96740 },
{ url = "https://files.pythonhosted.org/packages/96/a4/b24de77cc9ae424c1687c9d4fb15aa560d7d7b28ba559aca72f781d0202b/watchdog-5.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae6deb336cba5d71476caa029ceb6e88047fc1dc74b62b7c4012639c0b563906", size = 88711 },
{ url = "https://files.pythonhosted.org/packages/a4/71/3f2e9fe8403386b99d788868955b3a790f7a09721501a7e1eb58f514ffaa/watchdog-5.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1021223c08ba8d2d38d71ec1704496471ffd7be42cfb26b87cd5059323a389a1", size = 89319 },
{ url = "https://files.pythonhosted.org/packages/a2/d6/1d1ca81c75d903eca3fdb7061d93845485b58a5ba182d146843b88fc51c2/watchdog-5.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:90a67d7857adb1d985aca232cc9905dd5bc4803ed85cfcdcfcf707e52049eda7", size = 88172 },
{ url = "https://files.pythonhosted.org/packages/47/bb/d5e0abcfd6d729029a24766682e062526db8b59e9ae0c94aff509e0fd2b9/watchdog-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:720ef9d3a4f9ca575a780af283c8fd3a0674b307651c1976714745090da5a9e8", size = 88644 },
{ url = "https://files.pythonhosted.org/packages/60/33/7cb71c9df9a77b6927ee5f48d25e1de5562ce0fa7e0c56dcf2b0472e64a2/watchdog-5.0.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dd021efa85970bd4824acacbb922066159d0f9e546389a4743d56919b6758b91", size = 79335 },
{ url = "https://files.pythonhosted.org/packages/f6/91/320bc1496cf951a3cf93a7ffd18a581f0792c304be963d943e0e608c2919/watchdog-5.0.3-py3-none-manylinux2014_armv7l.whl", hash = "sha256:78864cc8f23dbee55be34cc1494632a7ba30263951b5b2e8fc8286b95845f82c", size = 79334 },
{ url = "https://files.pythonhosted.org/packages/8b/2c/567c5e042ed667d3544c43d48a65cf853450a2d2a9089d9523a65f195e94/watchdog-5.0.3-py3-none-manylinux2014_i686.whl", hash = "sha256:1e9679245e3ea6498494b3028b90c7b25dbb2abe65c7d07423ecfc2d6218ff7c", size = 79333 },
{ url = "https://files.pythonhosted.org/packages/c3/f0/64059fe162ef3274662e67bbdea6c45b3cd53e846d5bd1365fcdc3dc1d15/watchdog-5.0.3-py3-none-manylinux2014_ppc64.whl", hash = "sha256:9413384f26b5d050b6978e6fcd0c1e7f0539be7a4f1a885061473c5deaa57221", size = 79334 },
{ url = "https://files.pythonhosted.org/packages/f6/d9/19b7d02965be2801e2d0f6f4bde23e4ae172620071b65430fa0c2f8441ac/watchdog-5.0.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:294b7a598974b8e2c6123d19ef15de9abcd282b0fbbdbc4d23dfa812959a9e05", size = 79333 },
{ url = "https://files.pythonhosted.org/packages/cb/a1/5393ac6d0b095d3a44946b09258e9b5f22cb2fb67bcfa419dd868478826c/watchdog-5.0.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:26dd201857d702bdf9d78c273cafcab5871dd29343748524695cecffa44a8d97", size = 79332 },
{ url = "https://files.pythonhosted.org/packages/a0/58/edec25190b6403caf4426dd418234f2358a106634b7d6aa4aec6939b104f/watchdog-5.0.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:0f9332243355643d567697c3e3fa07330a1d1abf981611654a1f2bf2175612b7", size = 79334 },
{ url = "https://files.pythonhosted.org/packages/97/69/cfb2d17ba8aabc73be2e2d03c8c319b1f32053a02c4b571852983aa24ff2/watchdog-5.0.3-py3-none-win32.whl", hash = "sha256:c66f80ee5b602a9c7ab66e3c9f36026590a0902db3aea414d59a2f55188c1f49", size = 79320 },
{ url = "https://files.pythonhosted.org/packages/91/b4/2b5b59358dadfa2c8676322f955b6c22cde4937602f40490e2f7403e548e/watchdog-5.0.3-py3-none-win_amd64.whl", hash = "sha256:f00b4cf737f568be9665563347a910f8bdc76f88c2970121c86243c8cfdf90e9", size = 79325 },
{ url = "https://files.pythonhosted.org/packages/38/b8/0aa69337651b3005f161f7f494e59188a1d8d94171666900d26d29d10f69/watchdog-5.0.3-py3-none-win_ia64.whl", hash = "sha256:49f4d36cb315c25ea0d946e018c01bb028048023b9e103d3d3943f58e109dd45", size = 79324 },
]
[[package]]
name = "watchfiles"
version = "0.24.0"