From e1fd83e6a76bec489fc69e57869c3c1cc441c551 Mon Sep 17 00:00:00 2001 From: "Brandon Hancock (bhancock_ai)" <109994880+bhancockio@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:01:00 -0400 Subject: [PATCH] support unsafe code execution. add in docker install and running checks. (#1496) * support unsafe code execution. add in docker install and running checks. * Update return type --- docs/concepts/agents.mdx | 8 +++++--- src/crewai/agent.py | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/concepts/agents.mdx b/docs/concepts/agents.mdx index f7e97f186..6c939b321 100644 --- a/docs/concepts/agents.mdx +++ b/docs/concepts/agents.mdx @@ -31,16 +31,17 @@ Think of an agent as a member of a team, with specific skills and a particular j | **Max RPM** *(optional)* | `max_rpm` | Max RPM is the maximum number of requests per minute the agent can perform to avoid rate limits. It's optional and can be left unspecified, with a default value of `None`. | | **Max Execution Time** *(optional)* | `max_execution_time` | Max Execution Time is the maximum execution time for an agent to execute a task. It's optional and can be left unspecified, with a default value of `None`, meaning no max execution time. | | **Verbose** *(optional)* | `verbose` | Setting this to `True` configures the internal logger to provide detailed execution logs, aiding in debugging and monitoring. Default is `False`. | -| **Allow Delegation** *(optional)* | `allow_delegation` | Agents can delegate tasks or questions to one another, ensuring that each task is handled by the most suitable agent. Default is `False`. +| **Allow Delegation** *(optional)* | `allow_delegation` | Agents can delegate tasks or questions to one another, ensuring that each task is handled by the most suitable agent. Default is `False`. | | **Step Callback** *(optional)* | `step_callback` | A function that is called after each step of the agent. This can be used to log the agent's actions or to perform other operations. It will overwrite the crew `step_callback`. | | **Cache** *(optional)* | `cache` | Indicates if the agent should use a cache for tool usage. Default is `True`. | | **System Template** *(optional)* | `system_template` | Specifies the system format for the agent. Default is `None`. | | **Prompt Template** *(optional)* | `prompt_template` | Specifies the prompt format for the agent. Default is `None`. | | **Response Template** *(optional)* | `response_template` | Specifies the response format for the agent. Default is `None`. | | **Allow Code Execution** *(optional)* | `allow_code_execution` | Enable code execution for the agent. Default is `False`. | -| **Max Retry Limit** *(optional)* | `max_retry_limit` | Maximum number of retries for an agent to execute a task when an error occurs. Default is `2`. +| **Max Retry Limit** *(optional)* | `max_retry_limit` | Maximum number of retries for an agent to execute a task when an error occurs. Default is `2`. | | **Use System Prompt** *(optional)* | `use_system_prompt` | Adds the ability to not use system prompt (to support o1 models). Default is `True`. | | **Respect Context Window** *(optional)* | `respect_context_window` | Summary strategy to avoid overflowing the context window. Default is `True`. | +| **Code Execution Mode** *(optional)* | `code_execution_mode` | Determines the mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution on the host machine). Default is `safe`. | ## Creating an agent @@ -83,6 +84,7 @@ agent = Agent( max_retry_limit=2, # Optional use_system_prompt=True, # Optional respect_context_window=True, # Optional + code_execution_mode='safe', # Optional, defaults to 'safe' ) ``` @@ -156,4 +158,4 @@ crew = my_crew.kickoff(inputs={"input": "Mark Twain"}) ## Conclusion Agents are the building blocks of the CrewAI framework. By understanding how to define and interact with agents, -you can create sophisticated AI systems that leverage the power of collaborative intelligence. \ No newline at end of file +you can create sophisticated AI systems that leverage the power of collaborative intelligence. The `code_execution_mode` attribute provides flexibility in how agents execute code, allowing for both secure and direct execution options. diff --git a/src/crewai/agent.py b/src/crewai/agent.py index 165a40656..f68d6401b 100644 --- a/src/crewai/agent.py +++ b/src/crewai/agent.py @@ -1,6 +1,8 @@ import os +import shutil +import subprocess from inspect import signature -from typing import Any, List, Optional, Union +from typing import Any, List, Literal, Optional, Union from pydantic import Field, InstanceOf, PrivateAttr, model_validator @@ -112,6 +114,10 @@ class Agent(BaseAgent): default=2, description="Maximum number of retries for an agent to execute a task when an error occurs.", ) + code_execution_mode: Literal["safe", "unsafe"] = Field( + default="safe", + description="Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution).", + ) @model_validator(mode="after") def post_init_setup(self): @@ -173,6 +179,9 @@ class Agent(BaseAgent): if not self.agent_executor: self._setup_agent_executor() + if self.allow_code_execution: + self._validate_docker_installation() + return self def _setup_agent_executor(self): @@ -308,7 +317,9 @@ class Agent(BaseAgent): try: from crewai_tools import CodeInterpreterTool - return [CodeInterpreterTool()] + # Set the unsafe_mode based on the code_execution_mode attribute + unsafe_mode = self.code_execution_mode == "unsafe" + return [CodeInterpreterTool(unsafe_mode=unsafe_mode)] except ModuleNotFoundError: self._logger.log( "info", "Coding tools not available. Install crewai_tools. " @@ -408,6 +419,25 @@ class Agent(BaseAgent): return "\n".join(tool_strings) + def _validate_docker_installation(self) -> None: + """Check if Docker is installed and running.""" + if not shutil.which("docker"): + raise RuntimeError( + f"Docker is not installed. Please install Docker to use code execution with agent: {self.role}" + ) + + try: + subprocess.run( + ["docker", "info"], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except subprocess.CalledProcessError: + raise RuntimeError( + f"Docker is not running. Please start Docker to use code execution with agent: {self.role}" + ) + @staticmethod def __tools_names(tools) -> str: return ", ".join([t.name for t in tools])