diff --git a/ROADMAP.md b/ROADMAP.md index d876916..d9cd7b6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -17,3 +17,10 @@ - [x] **Linting Integration**: Automatically run and fix linting issues. - [x] **Multi-Model Support**: Easily switch between different LLM providers. - [x] **CI/CD Integration**: Github Actions to run AutoMetabuilder on schedule or trigger. + +## Phase 4: Optimization & Scalability +- [/] **Dockerization**: Provide a Dockerfile and docker-compose for easy environment setup. Added `run_docker_task` tool. +- [ ] **Extended Toolset**: Add tools for dependency management (poetry) and file manipulation (read/write/edit). +- [ ] **Self-Improvement**: Allow the bot to suggest and apply changes to its own `prompt.yml` or `tools.json`. +- [ ] **Robust Error Handling**: Implement exponential backoff for API calls and better error recovery. +- [ ] **Monitoring & Logging**: Structured logging and status reporting for long-running tasks. diff --git a/src/autometabuilder/docker_utils.py b/src/autometabuilder/docker_utils.py new file mode 100644 index 0000000..4881af6 --- /dev/null +++ b/src/autometabuilder/docker_utils.py @@ -0,0 +1,34 @@ +import subprocess +import os + +def run_command_in_docker(image: str, command: str, volumes: dict = None, workdir: str = None): + """ + Run a command inside a Docker container. + + :param image: Docker image to use. + :param command: Command to execute. + :param volumes: Dictionary of volume mappings {host_path: container_path}. + :param workdir: Working directory inside the container. + :return: Standard output of the command. + """ + docker_command = ["docker", "run", "--rm"] + + if volumes: + for host_path, container_path in volumes.items(): + docker_command.extend(["-v", f"{os.path.abspath(host_path)}:{container_path}"]) + + if workdir: + docker_command.extend(["-w", workdir]) + + docker_command.append(image) + docker_command.extend(["sh", "-c", command]) + + print(f"Executing in Docker ({image}): {command}") + result = subprocess.run(docker_command, capture_output=True, text=True, check=False) + + output = result.stdout + if result.stderr: + output += "\n" + result.stderr + + print(output) + return output diff --git a/src/autometabuilder/main.py b/src/autometabuilder/main.py index 7c9df3b..f3eb599 100644 --- a/src/autometabuilder/main.py +++ b/src/autometabuilder/main.py @@ -10,6 +10,7 @@ from dotenv import load_dotenv from openai import OpenAI from . import load_messages from .github_integration import GitHubIntegration, get_repo_name_from_env +from .docker_utils import run_command_in_docker load_dotenv() @@ -99,6 +100,15 @@ def run_lint(path: str = "src"): return result.stdout +def run_docker_task(image: str, command: str, workdir: str = "/workspace"): + """ + Run a task inside a Docker container. + Volumes are automatically mapped from current directory to /workspace. + """ + volumes = {os.getcwd(): "/workspace"} + return run_command_in_docker(image, command, volumes=volumes, workdir=workdir) + + def handle_tool_calls(resp_msg, gh: GitHubIntegration, msgs: dict, dry_run: bool = False, yolo: bool = False) -> list: """Process tool calls from the AI response and return results for the assistant.""" if not resp_msg.tool_calls: @@ -113,6 +123,7 @@ def handle_tool_calls(resp_msg, gh: GitHubIntegration, msgs: dict, dry_run: bool "list_files": list_files, "run_tests": run_tests, "run_lint": run_lint, + "run_docker_task": run_docker_task, } # Tools that modify state and should be skipped in dry-run diff --git a/src/autometabuilder/tools.json b/src/autometabuilder/tools.json index 7f5c4f2..c082460 100644 --- a/src/autometabuilder/tools.json +++ b/src/autometabuilder/tools.json @@ -145,5 +145,34 @@ } } } + }, + { + "type": "function", + "function": { + "name": "run_docker_task", + "description": "Run a task inside a Docker container. Useful for git clone, TypeScript, or Python tasks in a isolated environment.", + "parameters": { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "The Docker image to use (e.g., 'python:3.9', 'node:lts', 'alpine/git')" + }, + "command": { + "type": "string", + "description": "The command to run inside the container" + }, + "workdir": { + "type": "string", + "description": "The working directory inside the container", + "default": "/workspace" + } + }, + "required": [ + "image", + "command" + ] + } + } } ]