mirror of
https://github.com/aljazceru/Auto-GPT.git
synced 2026-01-31 11:54:30 +01:00
feat: Re-use Docker container for code execution
- Create a unique container name based on agent ID - Check if the container with the name exists, otherwise create a new container - If the container is not running, start it; otherwise, restart it - Execute the code in the container - Return the output of the code execution This change enables reusing the same container for consecutive code execution commands, allowing for iterative changes to the execution environment. Note: This change also includes handling the case where the Docker image is not found locally by pulling it from Docker Hub. The image used in this case is "python:3-alpine".
This commit is contained in:
@@ -10,7 +10,7 @@ from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import docker
|
||||
from docker.errors import DockerException, ImageNotFound
|
||||
from docker.errors import DockerException, ImageNotFound, NotFound
|
||||
from docker.models.containers import Container as DockerContainer
|
||||
|
||||
from autogpt.agents.agent import Agent
|
||||
@@ -135,58 +135,75 @@ def execute_python_file(
|
||||
|
||||
logger.debug("AutoGPT is not running in a Docker container")
|
||||
try:
|
||||
assert agent.state.agent_id, "Need Agent ID to attach Docker container"
|
||||
|
||||
client = docker.from_env()
|
||||
# You can replace this with the desired Python image/version
|
||||
# You can find available Python images on Docker Hub:
|
||||
# https://hub.docker.com/_/python
|
||||
image_name = "python:3-alpine"
|
||||
container_is_fresh = False
|
||||
container_name = f"{agent.state.agent_id}_sandbox"
|
||||
try:
|
||||
client.images.get(image_name)
|
||||
logger.debug(f"Image '{image_name}' found locally")
|
||||
except ImageNotFound:
|
||||
logger.info(
|
||||
f"Image '{image_name}' not found locally, pulling from Docker Hub..."
|
||||
)
|
||||
# Use the low-level API to stream the pull response
|
||||
low_level_client = docker.APIClient()
|
||||
for line in low_level_client.pull(image_name, stream=True, decode=True):
|
||||
# Print the status and progress, if available
|
||||
status = line.get("status")
|
||||
progress = line.get("progress")
|
||||
if status and progress:
|
||||
logger.info(f"{status}: {progress}")
|
||||
elif status:
|
||||
logger.info(status)
|
||||
container: DockerContainer = client.containers.get(container_name) # type: ignore
|
||||
except NotFound:
|
||||
try:
|
||||
client.images.get(image_name)
|
||||
logger.debug(f"Image '{image_name}' found locally")
|
||||
except ImageNotFound:
|
||||
logger.info(
|
||||
f"Image '{image_name}' not found locally, pulling from Docker Hub..."
|
||||
)
|
||||
# Use the low-level API to stream the pull response
|
||||
low_level_client = docker.APIClient()
|
||||
for line in low_level_client.pull(image_name, stream=True, decode=True):
|
||||
# Print the status and progress, if available
|
||||
status = line.get("status")
|
||||
progress = line.get("progress")
|
||||
if status and progress:
|
||||
logger.info(f"{status}: {progress}")
|
||||
elif status:
|
||||
logger.info(status)
|
||||
|
||||
logger.debug(f"Running {file_path} in a {image_name} container...")
|
||||
container: DockerContainer = client.containers.run(
|
||||
image_name,
|
||||
logger.debug(f"Creating new {image_name} container...")
|
||||
container: DockerContainer = client.containers.run(
|
||||
image_name,
|
||||
["sleep", "60"], # Max 60 seconds to prevent permanent hangs
|
||||
volumes={
|
||||
str(agent.workspace.root): {
|
||||
"bind": "/workspace",
|
||||
"mode": "rw",
|
||||
}
|
||||
},
|
||||
working_dir="/workspace",
|
||||
stderr=True,
|
||||
stdout=True,
|
||||
detach=True,
|
||||
name=container_name,
|
||||
) # type: ignore
|
||||
container_is_fresh = True
|
||||
|
||||
if not container.status == "running":
|
||||
container.start()
|
||||
elif not container_is_fresh:
|
||||
container.restart()
|
||||
|
||||
logger.debug(f"Running {file_path} in container {container.name}...")
|
||||
exec_result = container.exec_run(
|
||||
[
|
||||
"python",
|
||||
"-B",
|
||||
file_path.relative_to(agent.workspace.root).as_posix(),
|
||||
]
|
||||
+ args,
|
||||
volumes={
|
||||
str(agent.workspace.root): {
|
||||
"bind": "/workspace",
|
||||
"mode": "rw",
|
||||
}
|
||||
},
|
||||
working_dir="/workspace",
|
||||
stderr=True,
|
||||
stdout=True,
|
||||
detach=True,
|
||||
) # type: ignore
|
||||
)
|
||||
|
||||
container.wait()
|
||||
logs = container.logs().decode("utf-8")
|
||||
container.remove()
|
||||
if exec_result.exit_code != 0:
|
||||
raise CodeExecutionError(exec_result.output.decode("utf-8"))
|
||||
|
||||
# print(f"Execution complete. Output: {output}")
|
||||
# print(f"Logs: {logs}")
|
||||
|
||||
return logs
|
||||
return exec_result.output.decode("utf-8")
|
||||
|
||||
except DockerException as e:
|
||||
logger.warn(
|
||||
|
||||
Reference in New Issue
Block a user