diff --git a/docs/concepts/knowledge.mdx b/docs/concepts/knowledge.mdx index 2afb1b568..ce0203e13 100644 --- a/docs/concepts/knowledge.mdx +++ b/docs/concepts/knowledge.mdx @@ -1,6 +1,6 @@ --- title: Knowledge -description: What is knowledge in CrewAI and how to use it. +description: Understand what knowledge is in CrewAI and how to effectively use it. icon: book --- @@ -8,7 +8,14 @@ icon: book ## Introduction +Knowledge in CrewAI serves as a foundational component for enriching AI agents with contextual and relevant information. It enables agents to access and utilize structured data sources during their execution processes, making them more intelligent and responsive. + The Knowledge class in CrewAI provides a powerful way to manage and query knowledge sources for your AI agents. This guide will show you how to implement knowledge management in your CrewAI projects. + +## What is Knowledge? + +The `Knowledge` class in CrewAI manages various sources that store information, which can be queried and retrieved by AI agents. This modular approach allows you to integrate diverse data formats such as text, PDFs, spreadsheets, and more into your AI workflows. + Additionally, we have specific tools for generate knowledge sources for strings, text files, PDF's, and Spreadsheets. You can expand on any source type by extending the `KnowledgeSource` class. ## Basic Implementation @@ -25,17 +32,14 @@ string_source = StringKnowledgeSource( content=content, metadata={"preference": "personal"} ) - -llm = LLM(model="gpt-4o-mini", temperature=0) - # Create an agent with the knowledge store +# Create an agent with the knowledge store agent = Agent( role="About User", goal="You know everything about the user.", backstory="""You are a master at understanding people and their preferences.""", - verbose=True, - allow_delegation=False, - llm=llm, + verbose=True ) + task = Task( description="Answer the following questions about the user: {question}", expected_output="An answer to the question.", diff --git a/docs/how-to/before-and-after-kickoff-hooks.mdx b/docs/how-to/before-and-after-kickoff-hooks.mdx new file mode 100644 index 000000000..83058ce3c --- /dev/null +++ b/docs/how-to/before-and-after-kickoff-hooks.mdx @@ -0,0 +1,59 @@ +--- +title: Before and After Kickoff Hooks +description: Learn how to use before and after kickoff hooks in CrewAI +--- + +CrewAI provides hooks that allow you to execute code before and after a crew's kickoff. These hooks are useful for preprocessing inputs or post-processing results. + +## Before Kickoff Hook + +The before kickoff hook is executed before the crew starts its tasks. It receives the input dictionary and can modify it before passing it to the crew. You can use this hook to set up your environment, load necessary data, or preprocess your inputs. This is useful in scenarios where the input data might need enrichment or validation before being processed by the crew. + +Here's an example of defining a before kickoff function in your `crew.py`: + +```python +from crewai import CrewBase, before_kickoff + +@CrewBase +class MyCrew: + @before_kickoff + def prepare_data(self, inputs): + # Preprocess or modify inputs + inputs['processed'] = True + return inputs + +#... +``` + +In this example, the prepare_data function modifies the inputs by adding a new key-value pair indicating that the inputs have been processed. + +## After Kickoff Hook + +The after kickoff hook is executed after the crew has completed its tasks. It receives the result object, which contains the outputs of the crew's execution. This hook is ideal for post-processing results, such as logging, data transformation, or further analysis. + +Here's how you can define an after kickoff function in your `crew.py`: + +```python +from crewai import CrewBase, after_kickoff + +@CrewBase +class MyCrew: + @after_kickoff + def log_results(self, result): + # Log or modify the results + print("Crew execution completed with result:", result) + return result + +# ... +``` + + +In the `log_results` function, the results of the crew execution are simply printed out. You can extend this to perform more complex operations such as sending notifications or integrating with other services. + +## Utilizing Both Hooks + +Both hooks can be used together to provide a comprehensive setup and teardown process for your crew's execution. They are particularly useful in maintaining clean code architecture by separating concerns and enhancing the modularity of your CrewAI implementations. + +## Conclusion + +Before and after kickoff hooks in CrewAI offer powerful ways to interact with the lifecycle of a crew's execution. By understanding and utilizing these hooks, you can greatly enhance the robustness and flexibility of your AI agents. diff --git a/docs/mint.json b/docs/mint.json index 3ea9f5baf..d5aa8cb8f 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -68,6 +68,7 @@ "concepts/tasks", "concepts/crews", "concepts/flows", + "concepts/knowledge", "concepts/llms", "concepts/processes", "concepts/collaboration", diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index ef149bfcc..246bbdf84 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -8,7 +8,7 @@ icon: rocket Let's create a simple crew that will help us `research` and `report` on the `latest AI developments` for a given topic or subject. -Before we proceed, make sure you have `crewai` and `crewai-tools` installed. +Before we proceed, make sure you have `crewai` and `crewai-tools` installed. If you haven't installed them yet, you can do so by following the [installation guide](/installation). Follow the steps below to get crewing! 🚣‍♂️ @@ -23,7 +23,7 @@ Follow the steps below to get crewing! 🚣‍♂️ ``` - + You can also modify the agents as needed to fit your use case or copy and paste as is to your project. Any variable interpolated in your `agents.yaml` and `tasks.yaml` files like `{topic}` will be replaced by the value of the variable in the `main.py` file. @@ -39,7 +39,7 @@ Follow the steps below to get crewing! 🚣‍♂️ You're a seasoned researcher with a knack for uncovering the latest developments in {topic}. Known for your ability to find the most relevant information and present it in a clear and concise manner. - + reporting_analyst: role: > {topic} Reporting Analyst @@ -51,7 +51,7 @@ Follow the steps below to get crewing! 🚣‍♂️ it easy for others to understand and act on the information you provide. ``` - + ```yaml tasks.yaml # src/latest_ai_development/config/tasks.yaml research_task: @@ -73,8 +73,8 @@ Follow the steps below to get crewing! 🚣‍♂️ agent: reporting_analyst output_file: report.md ``` - - + + ```python crew.py # src/latest_ai_development/crew.py from crewai import Agent, Crew, Process, Task @@ -121,10 +121,34 @@ Follow the steps below to get crewing! 🚣‍♂️ tasks=self.tasks, # Automatically created by the @task decorator process=Process.sequential, verbose=True, - ) + ) ``` - + + ```python crew.py + # src/latest_ai_development/crew.py + from crewai import Agent, Crew, Process, Task + from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff + from crewai_tools import SerperDevTool + + @CrewBase + class LatestAiDevelopmentCrew(): + """LatestAiDevelopment crew""" + + @before_kickoff + def before_kickoff_function(self, inputs): + print(f"Before kickoff function with inputs: {inputs}") + return inputs # You can return the inputs or modify them as needed + + @after_kickoff + def after_kickoff_function(self, result): + print(f"After kickoff function with result: {result}") + return result # You can return the result or modify it as needed + + # ... remaining code + ``` + + For example, you can pass the `topic` input to your crew to customize the research and reporting. ```python main.py #!/usr/bin/env python @@ -237,14 +261,14 @@ Follow the steps below to get crewing! 🚣‍♂️ ### Note on Consistency in Naming The names you use in your YAML files (`agents.yaml` and `tasks.yaml`) should match the method names in your Python code. -For example, you can reference the agent for specific tasks from `tasks.yaml` file. +For example, you can reference the agent for specific tasks from `tasks.yaml` file. This naming consistency allows CrewAI to automatically link your configurations with your code; otherwise, your task won't recognize the reference properly. #### Example References Note how we use the same name for the agent in the `agents.yaml` (`email_summarizer`) file as the method name in the `crew.py` (`email_summarizer`) file. - + ```yaml agents.yaml email_summarizer: @@ -281,6 +305,8 @@ Use the annotations to properly reference the agent and task in the `crew.py` fi * `@task` * `@crew` * `@tool` +* `@before_kickoff` +* `@after_kickoff` * `@callback` * `@output_json` * `@output_pydantic` @@ -304,7 +330,7 @@ def email_summarizer_task(self) -> Task: In addition to the [sequential process](../how-to/sequential-process), you can use the [hierarchical process](../how-to/hierarchical-process), -which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. +which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. You can learn more about the core concepts [here](/concepts). diff --git a/pyproject.toml b/pyproject.toml index bac31842c..1d7d8cc43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "crewai" -version = "0.80.0" +version = "0.83.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.13" @@ -28,6 +28,8 @@ dependencies = [ "tomli-w>=1.1.0", "tomli>=2.0.2", "chromadb>=0.5.18", + "pdfplumber>=0.11.4", + "openpyxl>=3.1.5", ] [project.urls] diff --git a/src/crewai/__init__.py b/src/crewai/__init__.py index 6cfa381de..34d7f17c9 100644 --- a/src/crewai/__init__.py +++ b/src/crewai/__init__.py @@ -16,7 +16,7 @@ warnings.filterwarnings( category=UserWarning, module="pydantic.main", ) -__version__ = "0.80.0" +__version__ = "0.83.0" __all__ = [ "Agent", "Crew", diff --git a/src/crewai/cli/authentication/main.py b/src/crewai/cli/authentication/main.py index 543f06844..c64045801 100644 --- a/src/crewai/cli/authentication/main.py +++ b/src/crewai/cli/authentication/main.py @@ -7,6 +7,7 @@ from rich.console import Console from .constants import AUTH0_AUDIENCE, AUTH0_CLIENT_ID, AUTH0_DOMAIN from .utils import TokenManager, validate_token +from crewai.cli.tools.main import ToolCommand console = Console() @@ -63,7 +64,22 @@ class AuthenticationCommand: validate_token(token_data["id_token"]) expires_in = 360000 # Token expiration time in seconds self.token_manager.save_tokens(token_data["access_token"], expires_in) - console.print("\nWelcome to CrewAI+ !!", style="green") + + try: + ToolCommand().login() + except Exception: + console.print( + "\n[bold yellow]Warning:[/bold yellow] Authentication with the Tool Repository failed.", + style="yellow", + ) + console.print( + "Other features will work normally, but you may experience limitations " + "with downloading and publishing tools." + "\nRun [bold]crewai login[/bold] to try logging in again.\n", + style="yellow", + ) + + console.print("\n[bold green]Welcome to CrewAI Enterprise![/bold green]\n") return if token_data["error"] not in ("authorization_pending", "slow_down"): diff --git a/src/crewai/cli/authentication/token.py b/src/crewai/cli/authentication/token.py new file mode 100644 index 000000000..889fdc2eb --- /dev/null +++ b/src/crewai/cli/authentication/token.py @@ -0,0 +1,10 @@ +from .utils import TokenManager + +def get_auth_token() -> str: + """Get the authentication token.""" + access_token = TokenManager().get_token() + if not access_token: + raise Exception() + return access_token + + diff --git a/src/crewai/cli/command.py b/src/crewai/cli/command.py index f05fe237f..f2af92bf5 100644 --- a/src/crewai/cli/command.py +++ b/src/crewai/cli/command.py @@ -2,7 +2,7 @@ import requests from requests.exceptions import JSONDecodeError from rich.console import Console from crewai.cli.plus_api import PlusAPI -from crewai.cli.utils import get_auth_token +from crewai.cli.authentication.token import get_auth_token from crewai.telemetry.telemetry import Telemetry console = Console() diff --git a/src/crewai/cli/plus_api.py b/src/crewai/cli/plus_api.py index 04f6fb8ff..2fce0d6d8 100644 --- a/src/crewai/cli/plus_api.py +++ b/src/crewai/cli/plus_api.py @@ -1,7 +1,7 @@ from typing import Optional import requests from os import getenv -from crewai.cli.utils import get_crewai_version +from crewai.cli.version import get_crewai_version from urllib.parse import urljoin diff --git a/src/crewai/cli/run_crew.py b/src/crewai/cli/run_crew.py index 5450cf32b..95b560109 100644 --- a/src/crewai/cli/run_crew.py +++ b/src/crewai/cli/run_crew.py @@ -3,7 +3,8 @@ import subprocess import click from packaging import version -from crewai.cli.utils import get_crewai_version, read_toml +from crewai.cli.utils import read_toml +from crewai.cli.version import get_crewai_version def run_crew() -> None: diff --git a/src/crewai/cli/templates/crew/crew.py b/src/crewai/cli/templates/crew/crew.py index c47315415..6f8e66c4a 100644 --- a/src/crewai/cli/templates/crew/crew.py +++ b/src/crewai/cli/templates/crew/crew.py @@ -1,5 +1,5 @@ from crewai import Agent, Crew, Process, Task -from crewai.project import CrewBase, agent, crew, task +from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff # Uncomment the following line to use an example of a custom tool # from {{folder_name}}.tools.custom_tool import MyCustomTool @@ -14,6 +14,18 @@ class {{crew_name}}(): agents_config = 'config/agents.yaml' tasks_config = 'config/tasks.yaml' + @before_kickoff # Optional hook to be executed before the crew starts + def pull_data_example(self, inputs): + # Example of pulling data from an external API, dynamically changing the inputs + inputs['extra_data'] = "This is extra data" + return inputs + + @after_kickoff # Optional hook to be executed after the crew has finished + def log_results(self, output): + # Example of logging results, dynamically changing the output + print(f"Results: {output}") + return output + @agent def researcher(self) -> Agent: return Agent( diff --git a/src/crewai/cli/templates/crew/pyproject.toml b/src/crewai/cli/templates/crew/pyproject.toml index a5ab36877..1e456c725 100644 --- a/src/crewai/cli/templates/crew/pyproject.toml +++ b/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.80.0,<1.0.0" + "crewai[tools]>=0.83.0,<1.0.0" ] [project.scripts] diff --git a/src/crewai/cli/templates/flow/pyproject.toml b/src/crewai/cli/templates/flow/pyproject.toml index e863f20b7..575aaf086 100644 --- a/src/crewai/cli/templates/flow/pyproject.toml +++ b/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.80.0,<1.0.0", + "crewai[tools]>=0.83.0,<1.0.0", ] [project.scripts] diff --git a/src/crewai/cli/templates/pipeline/pyproject.toml b/src/crewai/cli/templates/pipeline/pyproject.toml index 60294740d..d12dccf11 100644 --- a/src/crewai/cli/templates/pipeline/pyproject.toml +++ b/src/crewai/cli/templates/pipeline/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = ">=3.10,<=3.13" -crewai = { extras = ["tools"], version = ">=0.80.0,<1.0.0" } +crewai = { extras = ["tools"], version = ">=0.83.0,<1.0.0" } asyncio = "*" [tool.poetry.scripts] diff --git a/src/crewai/cli/templates/pipeline_router/pyproject.toml b/src/crewai/cli/templates/pipeline_router/pyproject.toml index 7c022b1f7..06487bcfa 100644 --- a/src/crewai/cli/templates/pipeline_router/pyproject.toml +++ b/src/crewai/cli/templates/pipeline_router/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = ["Your Name "] requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.80.0,<1.0.0" + "crewai[tools]>=0.83.0,<1.0.0" ] [project.scripts] diff --git a/src/crewai/cli/templates/tool/pyproject.toml b/src/crewai/cli/templates/tool/pyproject.toml index 4142c325d..7c1afddfa 100644 --- a/src/crewai/cli/templates/tool/pyproject.toml +++ b/src/crewai/cli/templates/tool/pyproject.toml @@ -5,6 +5,6 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.80.0" + "crewai[tools]>=0.83.0" ] diff --git a/src/crewai/cli/utils.py b/src/crewai/cli/utils.py index 25da9e31a..2daba4111 100644 --- a/src/crewai/cli/utils.py +++ b/src/crewai/cli/utils.py @@ -1,4 +1,3 @@ -import importlib.metadata import os import shutil import sys @@ -9,7 +8,6 @@ import click import tomli from rich.console import Console -from crewai.cli.authentication.utils import TokenManager from crewai.cli.constants import ENV_VARS if sys.version_info >= (3, 11): @@ -137,11 +135,6 @@ def _get_nested_value(data: Dict[str, Any], keys: List[str]) -> Any: return reduce(dict.__getitem__, keys, data) -def get_crewai_version() -> str: - """Get the version number of CrewAI running the CLI""" - return importlib.metadata.version("crewai") - - def fetch_and_json_env_file(env_file_path: str = ".env") -> dict: """Fetch the environment variables from a .env file and return them as a dictionary.""" try: @@ -166,14 +159,6 @@ def fetch_and_json_env_file(env_file_path: str = ".env") -> dict: return {} -def get_auth_token() -> str: - """Get the authentication token.""" - access_token = TokenManager().get_token() - if not access_token: - raise Exception() - return access_token - - def tree_copy(source, destination): """Copies the entire directory structure from the source to the destination.""" for item in os.listdir(source): diff --git a/src/crewai/cli/version.py b/src/crewai/cli/version.py new file mode 100644 index 000000000..543be9c32 --- /dev/null +++ b/src/crewai/cli/version.py @@ -0,0 +1,6 @@ +import importlib.metadata + +def get_crewai_version() -> str: + """Get the version number of CrewAI running the CLI""" + return importlib.metadata.version("crewai") + diff --git a/tests/cli/authentication/test_auth_main.py b/tests/cli/authentication/test_auth_main.py index c56968aab..4466cc999 100644 --- a/tests/cli/authentication/test_auth_main.py +++ b/tests/cli/authentication/test_auth_main.py @@ -43,10 +43,11 @@ class TestAuthenticationCommand(unittest.TestCase): mock_print.assert_any_call("2. Enter the following code: ", "ABCDEF") mock_open.assert_called_once_with("https://example.com") + @patch("crewai.cli.authentication.main.ToolCommand") @patch("crewai.cli.authentication.main.requests.post") @patch("crewai.cli.authentication.main.validate_token") @patch("crewai.cli.authentication.main.console.print") - def test_poll_for_token_success(self, mock_print, mock_validate_token, mock_post): + def test_poll_for_token_success(self, mock_print, mock_validate_token, mock_post, mock_tool): mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = { @@ -55,10 +56,13 @@ class TestAuthenticationCommand(unittest.TestCase): } mock_post.return_value = mock_response + mock_instance = mock_tool.return_value + mock_instance.login.return_value = None + self.auth_command._poll_for_token({"device_code": "123456"}) mock_validate_token.assert_called_once_with("TOKEN") - mock_print.assert_called_once_with("\nWelcome to CrewAI+ !!", style="green") + mock_print.assert_called_once_with("\n[bold green]Welcome to CrewAI Enterprise![/bold green]\n") @patch("crewai.cli.authentication.main.requests.post") @patch("crewai.cli.authentication.main.console.print") diff --git a/tests/cli/deploy/test_deploy_main.py b/tests/cli/deploy/test_deploy_main.py index 385dbb8a5..bf1198a0b 100644 --- a/tests/cli/deploy/test_deploy_main.py +++ b/tests/cli/deploy/test_deploy_main.py @@ -260,6 +260,6 @@ class TestDeployCommand(unittest.TestCase): self.assertEqual(project_name, "test_project") def test_get_crewai_version(self): - from crewai.cli.utils import get_crewai_version + from crewai.cli.version import get_crewai_version assert isinstance(get_crewai_version(), str) diff --git a/uv.lock b/uv.lock index 667d609a6..050602e61 100644 --- a/uv.lock +++ b/uv.lock @@ -608,7 +608,7 @@ wheels = [ [[package]] name = "crewai" -version = "0.80.0" +version = "0.83.0" source = { editable = "." } dependencies = [ { name = "appdirs" }, @@ -621,9 +621,11 @@ dependencies = [ { name = "jsonref" }, { name = "litellm" }, { name = "openai" }, + { name = "openpyxl" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-sdk" }, + { name = "pdfplumber" }, { name = "pydantic" }, { name = "python-dotenv" }, { name = "pyvis" }, @@ -692,11 +694,13 @@ requires-dist = [ { name = "litellm", specifier = ">=1.44.22" }, { name = "mem0ai", marker = "extra == 'mem0'", specifier = ">=0.1.29" }, { name = "openai", specifier = ">=1.13.3" }, + { name = "openpyxl", specifier = ">=3.1.5" }, { name = "openpyxl", marker = "extra == 'openpyxl'", specifier = ">=3.1.5" }, { name = "opentelemetry-api", specifier = ">=1.22.0" }, { name = "opentelemetry-exporter-otlp-proto-http", specifier = ">=1.22.0" }, { name = "opentelemetry-sdk", specifier = ">=1.22.0" }, { name = "pandas", marker = "extra == 'pandas'", specifier = ">=2.2.3" }, + { name = "pdfplumber", specifier = ">=0.11.4" }, { name = "pdfplumber", marker = "extra == 'pdfplumber'", specifier = ">=0.11.4" }, { name = "pydantic", specifier = ">=2.4.2" }, { name = "python-dotenv", specifier = ">=1.0.0" },