diff --git a/.gitignore b/.gitignore index 897944e..b9ee3ba 100644 --- a/.gitignore +++ b/.gitignore @@ -13,13 +13,147 @@ outputs # VS Code .vscode +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# .python-version + +# pipenv +#Pipfile.lock + +# UV +#uv.lock + +# poetry +#poetry.lock + +# pdm +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582 +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + # Environments .env .venv env/ venv/ +ENV/ env.bak/ venv.bak/ -# Jupyter Notebook -.ipynb_checkpoints \ No newline at end of file +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +#.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..758e0da --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Base Python image +FROM python:3.12-slim + +# Set working directory +WORKDIR /app + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + gcc \ + g++ \ + zlib1g-dev \ + libjpeg-dev \ + libpng-dev \ + && rm -rf /var/lib/apt/lists/* + +# Copy package files +COPY . /app/ + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Install the package +RUN pip install -e . + +# Command to run when container starts +CMD ["python"] diff --git a/examples/docker_example.py b/examples/docker_example.py new file mode 100644 index 0000000..0db5b53 --- /dev/null +++ b/examples/docker_example.py @@ -0,0 +1,12 @@ +from agents.search import DuckDuckGoSearchTool +from agents.docker_alternative import DockerPythonInterpreter + +container = DockerPythonInterpreter() + +tools = [DuckDuckGoSearchTool] + +output = container.execute("res = web_search(query='whats the capital of Cambodia?'); print(res)", tools=tools) + +print(output) + +container.stop() diff --git a/src/agents/docker_alternative.py b/src/agents/docker_alternative.py new file mode 100644 index 0000000..6fd11e8 --- /dev/null +++ b/src/agents/docker_alternative.py @@ -0,0 +1,66 @@ +import docker +from typing import List, Optional +import warnings + +from agents.tools import Tool + +class DockerPythonInterpreter: + def __init__(self): + self.container = None + try: + self.client = docker.from_env() + self.client.ping() + except docker.errors.DockerException: + raise RuntimeError( + "Could not connect to Docker daemon. Please ensure Docker is installed and running." + ) + + try: + self.container = self.client.containers.run( + "pyrunner:latest", + "tail -f /dev/null", + detach=True, + remove=True, + ) + except docker.errors.DockerException as e: + raise RuntimeError(f"Failed to create Docker container: {e}") + + def stop(self): + """Cleanup: Stop and remove container when object is destroyed""" + if self.container: + try: + self.container.kill() # can consider .stop(), but this is faster + except Exception as e: + warnings.warn(f"Failed to stop Docker container: {e}") + + def execute(self, code: str, tools: Optional[List[Tool]] = None) -> str: + """ + Execute Python code in the container and return stdout and stderr + """ + + tool_instance = tools[0]() + + import_code = f""" +module_path = '{tool_instance.__class__.__module__}' +class_name = '{tool_instance.__class__.__name__}' + +import importlib + +module = importlib.import_module(module_path) +web_search = getattr(module, class_name)() +""" + + full_code = import_code + "\n" + code + + try: + exec_command = self.container.exec_run( + cmd=["python", "-c", full_code], + ) + output = exec_command.output + return output.decode('utf-8') + + except Exception as e: + return f"Error executing code: {str(e)}" + + +__all__ = ["DockerPythonInterpreter"] \ No newline at end of file