mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
Merge pull request #59 from joaomdmoura/feat/code-interpreter
Feat: Code Interpreter tool
This commit is contained in:
@@ -1,24 +1,29 @@
|
|||||||
from .browserbase_load_tool.browserbase_load_tool import BrowserbaseLoadTool
|
from .browserbase_load_tool.browserbase_load_tool import BrowserbaseLoadTool
|
||||||
from .code_docs_search_tool.code_docs_search_tool import CodeDocsSearchTool
|
from .code_docs_search_tool.code_docs_search_tool import CodeDocsSearchTool
|
||||||
|
from .code_interpreter_tool.code_interpreter_tool import CodeInterpreterTool
|
||||||
from .csv_search_tool.csv_search_tool import CSVSearchTool
|
from .csv_search_tool.csv_search_tool import CSVSearchTool
|
||||||
from .directory_search_tool.directory_search_tool import DirectorySearchTool
|
|
||||||
from .directory_read_tool.directory_read_tool import DirectoryReadTool
|
from .directory_read_tool.directory_read_tool import DirectoryReadTool
|
||||||
|
from .directory_search_tool.directory_search_tool import DirectorySearchTool
|
||||||
from .docx_search_tool.docx_search_tool import DOCXSearchTool
|
from .docx_search_tool.docx_search_tool import DOCXSearchTool
|
||||||
from .exa_tools.exa_search_tool import EXASearchTool
|
from .exa_tools.exa_search_tool import EXASearchTool
|
||||||
from .file_read_tool.file_read_tool import FileReadTool
|
from .file_read_tool.file_read_tool import FileReadTool
|
||||||
from .github_search_tool.github_search_tool import GithubSearchTool
|
from .github_search_tool.github_search_tool import GithubSearchTool
|
||||||
from .serper_dev_tool.serper_dev_tool import SerperDevTool
|
|
||||||
from .txt_search_tool.txt_search_tool import TXTSearchTool
|
|
||||||
from .json_search_tool.json_search_tool import JSONSearchTool
|
from .json_search_tool.json_search_tool import JSONSearchTool
|
||||||
|
from .llamaindex_tool.llamaindex_tool import LlamaIndexTool
|
||||||
from .mdx_seach_tool.mdx_search_tool import MDXSearchTool
|
from .mdx_seach_tool.mdx_search_tool import MDXSearchTool
|
||||||
from .pdf_search_tool.pdf_search_tool import PDFSearchTool
|
from .pdf_search_tool.pdf_search_tool import PDFSearchTool
|
||||||
from .pg_seach_tool.pg_search_tool import PGSearchTool
|
from .pg_seach_tool.pg_search_tool import PGSearchTool
|
||||||
from .rag.rag_tool import RagTool
|
from .rag.rag_tool import RagTool
|
||||||
from .scrape_element_from_website.scrape_element_from_website import ScrapeElementFromWebsiteTool
|
from .scrape_element_from_website.scrape_element_from_website import (
|
||||||
|
ScrapeElementFromWebsiteTool,
|
||||||
|
)
|
||||||
from .scrape_website_tool.scrape_website_tool import ScrapeWebsiteTool
|
from .scrape_website_tool.scrape_website_tool import ScrapeWebsiteTool
|
||||||
from .selenium_scraping_tool.selenium_scraping_tool import SeleniumScrapingTool
|
from .selenium_scraping_tool.selenium_scraping_tool import SeleniumScrapingTool
|
||||||
|
from .serper_dev_tool.serper_dev_tool import SerperDevTool
|
||||||
|
from .txt_search_tool.txt_search_tool import TXTSearchTool
|
||||||
from .website_search.website_search_tool import WebsiteSearchTool
|
from .website_search.website_search_tool import WebsiteSearchTool
|
||||||
from .xml_search_tool.xml_search_tool import XMLSearchTool
|
from .xml_search_tool.xml_search_tool import XMLSearchTool
|
||||||
from .youtube_channel_search_tool.youtube_channel_search_tool import YoutubeChannelSearchTool
|
from .youtube_channel_search_tool.youtube_channel_search_tool import (
|
||||||
|
YoutubeChannelSearchTool,
|
||||||
|
)
|
||||||
from .youtube_video_search_tool.youtube_video_search_tool import YoutubeVideoSearchTool
|
from .youtube_video_search_tool.youtube_video_search_tool import YoutubeVideoSearchTool
|
||||||
from .llamaindex_tool.llamaindex_tool import LlamaIndexTool
|
|
||||||
|
|||||||
14
src/crewai_tools/tools/code_interpreter_tool/Dockerfile
Normal file
14
src/crewai_tools/tools/code_interpreter_tool/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Install common utilities
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
software-properties-common
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /workspace
|
||||||
29
src/crewai_tools/tools/code_interpreter_tool/README.md
Normal file
29
src/crewai_tools/tools/code_interpreter_tool/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# CodeInterpreterTool
|
||||||
|
|
||||||
|
## Description
|
||||||
|
This tool is used to give the Agent the ability to run code (Python3) from the code generated by the Agent itself. The code is executed in a sandboxed environment, so it is safe to run any code.
|
||||||
|
|
||||||
|
It is incredible useful since it allows the Agent to generate code, run it in the same environment, get the result and use it to make decisions.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Install the crewai_tools package
|
||||||
|
```shell
|
||||||
|
pip install 'crewai[tools]'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Remember that when using this tool, the code must be generated by the Agent itself. The code must be a Python3 code. And it will take some time for the first time to run because it needs to build the Docker image.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from crewai_tools import CodeInterpreterTool
|
||||||
|
|
||||||
|
Agent(
|
||||||
|
...
|
||||||
|
tools=[CodeInterpreterTool()],
|
||||||
|
)
|
||||||
|
```
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import os
|
||||||
|
from typing import Optional, Type
|
||||||
|
|
||||||
|
import docker
|
||||||
|
from crewai_tools.tools.base_tool import BaseTool
|
||||||
|
from pydantic.v1 import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class FixedCodeInterpreterSchemaSchema(BaseModel):
|
||||||
|
"""Input for CodeInterpreterTool."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CodeInterpreterSchema(FixedCodeInterpreterSchemaSchema):
|
||||||
|
"""Input for CodeInterpreterTool."""
|
||||||
|
|
||||||
|
code: str = Field(
|
||||||
|
...,
|
||||||
|
description="Python3 code used to be interpreted in the Docker container. ALWAYS PRINT the final result and the output of the code",
|
||||||
|
)
|
||||||
|
libraries_used: Optional[str] = Field(
|
||||||
|
None,
|
||||||
|
description="List of libraries used in the code with proper installing names separated by commas. Example: numpy,pandas,beautifulsoup4",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CodeInterpreterTool(BaseTool):
|
||||||
|
name: str = "Code Interpreter"
|
||||||
|
description: str = "Interprets Python code in a Docker container. ALWAYS PRINT the final result and the output of the code"
|
||||||
|
args_schema: Type[BaseModel] = CodeInterpreterSchema
|
||||||
|
code: Optional[str] = None
|
||||||
|
|
||||||
|
def _verify_docker_image(self) -> None:
|
||||||
|
"""
|
||||||
|
Verify if the Docker image is available
|
||||||
|
"""
|
||||||
|
image_tag = "code-interpreter:latest"
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
images = client.images.list()
|
||||||
|
all_tags = [tag for image in images for tag in image.tags]
|
||||||
|
|
||||||
|
if image_tag not in all_tags:
|
||||||
|
client.images.build(
|
||||||
|
path=os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
tag=image_tag,
|
||||||
|
rm=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, code: Optional[str] = None, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
if code is not None:
|
||||||
|
self._verify_docker_image()
|
||||||
|
self.code = code
|
||||||
|
self.description = "Interprets Python code in a Docker container. ALWAYS PRINT the final result and the output of the code"
|
||||||
|
self.args_schema = FixedCodeInterpreterSchemaSchema
|
||||||
|
self._generate_description()
|
||||||
|
|
||||||
|
def _run(self, **kwargs) -> str:
|
||||||
|
code = kwargs.get("code", self.code)
|
||||||
|
libraries_used = kwargs.get("libraries_used", None)
|
||||||
|
return self.run_code_in_docker(code, libraries_used)
|
||||||
|
|
||||||
|
def _install_libraries(
|
||||||
|
self, container: docker.models.containers.Container, libraries: list[str]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Install missing libraries in the Docker container
|
||||||
|
"""
|
||||||
|
for library in libraries:
|
||||||
|
container.exec_run(f"pip install {library}")
|
||||||
|
|
||||||
|
def _init_docker_container(self) -> docker.models.containers.Container:
|
||||||
|
client = docker.from_env()
|
||||||
|
return client.containers.run(
|
||||||
|
"code-interpreter", detach=True, tty=True, working_dir="/workspace"
|
||||||
|
)
|
||||||
|
|
||||||
|
def run_code_in_docker(self, code: str, libraries_used: str) -> str:
|
||||||
|
container = self._init_docker_container()
|
||||||
|
|
||||||
|
if libraries_used:
|
||||||
|
self._install_libraries(container, libraries_used.split(","))
|
||||||
|
|
||||||
|
cmd_to_run = f'python3 -c "{code}"'
|
||||||
|
exec_result = container.exec_run(cmd_to_run)
|
||||||
|
|
||||||
|
container.stop()
|
||||||
|
container.remove()
|
||||||
|
|
||||||
|
if exec_result.exit_code != 0:
|
||||||
|
return f"Something went wrong while running the code: \n{exec_result.output.decode('utf-8')}"
|
||||||
|
return exec_result.output.decode("utf-8")
|
||||||
38
tests/tools/test_code_interpreter_tool.py
Normal file
38
tests/tools/test_code_interpreter_tool.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from crewai_tools.tools.code_interpreter_tool.code_interpreter_tool import (
|
||||||
|
CodeInterpreterTool,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCodeInterpreterTool(unittest.TestCase):
|
||||||
|
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker")
|
||||||
|
def test_run_code_in_docker(self, docker_mock):
|
||||||
|
tool = CodeInterpreterTool()
|
||||||
|
code = "print('Hello, World!')"
|
||||||
|
libraries_used = "numpy,pandas"
|
||||||
|
expected_output = "Hello, World!\n"
|
||||||
|
|
||||||
|
docker_mock.from_env().containers.run().exec_run().exit_code = 0
|
||||||
|
docker_mock.from_env().containers.run().exec_run().output = (
|
||||||
|
expected_output.encode()
|
||||||
|
)
|
||||||
|
result = tool.run_code_in_docker(code, libraries_used)
|
||||||
|
|
||||||
|
self.assertEqual(result, expected_output)
|
||||||
|
|
||||||
|
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker")
|
||||||
|
def test_run_code_in_docker_with_error(self, docker_mock):
|
||||||
|
tool = CodeInterpreterTool()
|
||||||
|
code = "print(1/0)"
|
||||||
|
libraries_used = "numpy,pandas"
|
||||||
|
expected_output = "Something went wrong while running the code: \nZeroDivisionError: division by zero\n"
|
||||||
|
|
||||||
|
docker_mock.from_env().containers.run().exec_run().exit_code = 1
|
||||||
|
docker_mock.from_env().containers.run().exec_run().output = (
|
||||||
|
b"ZeroDivisionError: division by zero\n"
|
||||||
|
)
|
||||||
|
result = tool.run_code_in_docker(code, libraries_used)
|
||||||
|
|
||||||
|
self.assertEqual(result, expected_output)
|
||||||
Reference in New Issue
Block a user