Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ Thumbs.db
*.swo
*~

# Pyh
__pycache__/
*.pyc
*.pyo
*.pyd
*.pyw
*.pyz
.gradio/
.venv/

# Terraform/OpenTofu files
*.tfstate
*.tfstate.*
Expand All @@ -59,3 +69,6 @@ local.pkr.hcl
# Script logs and temporary files
scripts/*.log
scripts/temp/

# Docker volumes
volumes/
45 changes: 45 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
PACKER_DIR := infrastructure/ai-inference/packer
PACKER_CONFIG := $(PACKER_DIR)
PACKER_LOG_FILE := $(PACKER_DIR)/packer-build.log
TEMPORAL_DIR := infrastructure/temporal

# Default values
AWS_REGION ?= us-east-1
Expand All @@ -28,6 +29,9 @@ help: ## Show this help message
@echo "$(YELLOW)Agent Commands:$(NC)"
@awk 'BEGIN {FS = ":.*?## "} /^agent-.*:.*?## / {printf " $(GREEN)%-25s$(NC) %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
@echo ""
@echo "$(YELLOW)Temporal Commands:$(NC)"
@awk 'BEGIN {FS = ":.*?## "} /^temporal-.*:.*?## / {printf " $(GREEN)%-25s$(NC) %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
@echo ""
@echo "$(YELLOW)Infrastructure/OpenTofu Commands:$(NC)"
@awk 'BEGIN {FS = ":.*?## "} /^tofu-.*:.*?## / {printf " $(GREEN)%-25s$(NC) %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
@echo ""
Expand Down Expand Up @@ -237,6 +241,47 @@ agent-oss-check: ## Check OSS agent environment and dependencies
@echo "$(BLUE)Environment variables:$(NC)"
@echo " API_ENDPOINT: ${API_ENDPOINT:-Not set}"

# Temporal Docker Compose commands
.PHONY: temporal-up
temporal-up: ## Start Temporal Docker Compose stack
@echo "$(BLUE)Starting Temporal Docker Compose stack...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env up -d
@echo "$(GREEN)Temporal stack started! Check http://localhost:8080 for Web UI$(NC)"

.PHONY: temporal-down
temporal-down: ## Stop and remove Temporal Docker Compose stack
@echo "$(BLUE)Stopping and removing Temporal Docker Compose stack...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env down
@echo "$(GREEN)Temporal stack stopped and removed!$(NC)"

.PHONY: temporal-stop
temporal-stop: ## Stop Temporal Docker Compose stack (without removing)
@echo "$(BLUE)Stopping Temporal Docker Compose stack...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env stop
@echo "$(GREEN)Temporal stack stopped!$(NC)"

.PHONY: temporal-start
temporal-start: ## Start existing Temporal Docker Compose stack
@echo "$(BLUE)Starting Temporal Docker Compose stack...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env start
@echo "$(GREEN)Temporal stack started!$(NC)"

.PHONY: temporal-restart
temporal-restart: ## Restart Temporal Docker Compose stack
@echo "$(BLUE)Restarting Temporal Docker Compose stack...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env restart
@echo "$(GREEN)Temporal stack restarted!$(NC)"

.PHONY: temporal-logs
temporal-logs: ## Show logs from Temporal Docker Compose stack
@echo "$(BLUE)Showing Temporal Docker Compose logs...$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env logs -f

.PHONY: temporal-status
temporal-status: ## Show status of Temporal Docker Compose stack
@echo "$(BLUE)Temporal Docker Compose status:$(NC)"
@cd $(TEMPORAL_DIR) && docker compose --env-file config.env ps

.PHONY: ami-clean
ami-clean: ## Clean up build artifacts and logs
@echo "$(BLUE)Cleaning up build artifacts...$(NC)"
Expand Down
105 changes: 105 additions & 0 deletions agents/tools/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import inspect
from datetime import datetime
from typing import Callable


class Tool:
"""
A class representing a tool that can be used by the agent.

Attributes:
name (str): The name of the tool.
description (str): A description of the tool.
func (Callable): The function to call when the tool is used.
args (list): The arguments to pass to the function.
outputs (str): The return type(s) of the function.
"""

def __init__(
self,
name: str,
description: str,
func: Callable,
args: list,
outputs: str,
):
self.name = name
self.description = description
self.func = func
self.args = args
self.outputs = outputs

def to_string(self):
"""
Return a string representation of the tool,
including its name, description, arguments, and outputs.
"""
args_str = ", ".join([
f"{arg_name}: {arg_type}" for arg_name, arg_type in self.args
])

return (
f"Tool Name: {self.name},"
f" Description: {self.description},"
f" Arguments: {args_str},"
f" Outputs: {self.outputs}"
)

def __call__(self, *args, **kwargs):
"""
Call the underlying function with the given arguments.
"""
return self.func(*args, **kwargs)


def tool(func: Callable):
"""
A decorator to convert a function into a tool.
"""
# Get the function signature
signature = inspect.signature(func)
args = []
for param in signature.parameters.values():
annotation_name = (
param.annotation.__name__
if hasattr(param.annotation, "__name__")
else str(param.annotation)
)
args.append((param.name, annotation_name))

# Determine the return annotation
return_annotation = signature.return_annotation
if return_annotation is inspect._empty:
outputs = "No return annotation"
else:
outputs = (
return_annotation.__name__
if hasattr(return_annotation, "__name__")
else str(return_annotation)
)

# Use the function's docstring as the description (default if None)
description = func.__doc__ or "No description provided."

# The function name becomes the Tool name
name = func.__name__

# Return a new Tool instance
return Tool(
name=name,
description=description,
func=func,
args=args,
outputs=outputs,
)

# Example tool
@tool
def get_current_time() -> str:
"""
Get the current time.
"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

if __name__ == "__main__":
print(get_current_time.to_string())
167 changes: 167 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Docker Containers

This directory contains generic, reusable Docker containers for the AI Engineering project.

## Available Containers

### [Chromium](./chromium/)

A lightweight, Alpine-based Chromium container for web automation and scraping.

- **Base Image**: Alpine Linux 3.19
- **Size**: ~150MB
- **Use Cases**: Web scraping, automated testing, PDF generation, headless browsing
- **Features**: Headless mode, GUI mode with VNC, Python automation tools

**Quick Start:**
```bash
# Build the image
docker build -t ai-engineering/chromium docker/chromium/

# Run headless Chromium
docker run -p 9222:9222 ai-engineering/chromium
```

## Design Principles

### Generic Containers
These containers are designed to be:
- **Reusable** across different projects and agents
- **Lightweight** using Alpine Linux where possible
- **Secure** running as non-root users
- **Well-documented** with comprehensive READMEs
- **Flexible** with configurable entrypoints

### Security Best Practices
- Non-root user execution
- Minimal package installation
- Read-only filesystem support
- Health checks for monitoring
- Secure defaults

### Container Organization

```
docker/
├── README.md # This file
└── chromium/ # Generic Chromium browser
├── Dockerfile
├── .dockerignore
├── entrypoint.sh
└── README.md
```

## Future Containers

Planned additions:
- **PostgreSQL** - Database container for development
- **Redis** - Caching and message broker
- **Selenium Grid** - Distributed browser testing
- **Playwright** - Modern web automation
- **Node.js** - JavaScript runtime environment

## Usage Patterns

### With Docker Compose

```yaml
version: '3.8'
services:
chromium:
build: ./docker/chromium
ports:
- "9222:9222"
volumes:
- ./scripts:/home/chromium/scripts
```

### In Agents

```python
# Python agent using Chromium container
import docker

client = docker.from_env()
container = client.containers.run(
'ai-engineering/chromium',
ports={'9222/tcp': 9222},
detach=True
)
```

### For Development

```bash
# Interactive development
docker run -it ai-engineering/chromium shell

# Mount local code
docker run -v $(pwd):/workspace ai-engineering/chromium python /workspace/script.py
```

## Building All Containers

```bash
# Build all containers from project root
make docker-build-all

# Or individually
docker build -t ai-engineering/chromium docker/chromium/
```

## Contributing

When adding new containers:

1. **Create directory structure**:
```
docker/<container-name>/
├── Dockerfile
├── .dockerignore
├── entrypoint.sh (if needed)
└── README.md
```

2. **Follow naming conventions**:
- Directory: lowercase with hyphens
- Image tag: `ai-engineering/<name>`
- User: match the service name

3. **Security requirements**:
- Use Alpine Linux when possible
- Run as non-root user
- Include health checks
- Minimize installed packages

4. **Documentation**:
- Comprehensive README.md
- Usage examples
- Environment variables table
- Common use cases

5. **Testing**:
- Test basic functionality
- Test security (non-root execution)
- Test health checks
- Verify minimal size

## Integration

These containers integrate with:
- **Agents** (`/agents/`) - Used by Python agents for browser automation
- **Infrastructure** (`/infrastructure/`) - Referenced in docker-compose files
- **CI/CD** (`.github/workflows/`) - Built and tested in pipelines

## Makefile Integration

Add container commands to the main Makefile:

```makefile
# Docker commands
.PHONY: docker-build-chromium
docker-build-chromium: ## Build Chromium container
docker build -t ai-engineering/chromium docker/chromium/

.PHONY: docker-build-all
docker-build-all: docker-build-chromium ## Build all containers
```
Loading