Skip to content

Commit 15ecefb

Browse files
authored
Merge pull request #46 from renbytes/dev-sugarscapeSim
Sim - Sugarscape
2 parents 8afbe9e + e07e681 commit 15ecefb

File tree

103 files changed

+6058
-1726
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+6058
-1726
lines changed

.github/workflows/ci.yml

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
# .github/workflows/ci.yml
2-
32
name: Continuous Integration
4-
53
on:
64
push:
75
branches: [main]
86
pull_request:
97
branches: [main]
10-
118
jobs:
129
ci:
1310
# The job name will be dynamically set based on the matrix task name
@@ -28,7 +25,6 @@ jobs:
2825
run_command: poetry run pytest --cov=agent_core --cov=agent_engine --cov-report=term-missing --cov-fail-under=80
2926
# Add a flag to indicate that this specific task needs graphviz
3027
needs_graphviz: true
31-
3228
steps:
3329
- name: "Checkout code"
3430
uses: actions/checkout@v4
@@ -59,12 +55,30 @@ jobs:
5955
uses: actions/cache@v4
6056
with:
6157
path: .venv
62-
key: poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
58+
# Include pyproject.toml in cache key to invalidate when dependencies change
59+
key: poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock', '**/pyproject.toml') }}-v2
6360
restore-keys: |
61+
poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock', '**/pyproject.toml') }}-v2
6462
poetry-${{ runner.os }}-
6563
66-
- name: "Install dependencies"
67-
run: poetry install
64+
- name: "Clean corrupted packages and install dependencies"
65+
run: |
66+
# Clean any corrupted mlflow packages that might exist
67+
if [ -d ".venv" ]; then
68+
poetry run pip uninstall mlflow mlflow-skinny mlflow-tracing -y || true
69+
fi
70+
71+
# Install dependencies with retry logic
72+
poetry install --no-cache || {
73+
echo "Installation failed, cleaning environment and retrying..."
74+
poetry env remove --all || true
75+
poetry install --no-cache
76+
}
77+
78+
- name: "Verify installation"
79+
run: |
80+
poetry run python -c "import sys; print('Python version:', sys.version)"
81+
poetry run python -c "import pygraphviz; print('pygraphviz version:', pygraphviz.__version__)" || echo "pygraphviz not needed for this job"
6882
6983
- name: "Run Task"
7084
run: ${{ matrix.task.run_command }}

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.6.0
3+
rev: v6.0.0
44
hooks:
55
- id: trailing-whitespace
66
- id: end-of-file-fixer
77
- id: check-yaml
88
- id: check-added-large-files
99

1010
- repo: https://github.com/astral-sh/ruff-pre-commit
11-
rev: v0.4.4
11+
rev: v0.12.12
1212
hooks:
1313
- id: ruff
1414
args: [--fix, --exit-non-zero-on-fix]
1515
- id: ruff-format
1616

1717
- repo: https://github.com/pre-commit/mirrors-mypy
18-
rev: v1.10.0
18+
rev: v1.17.1
1919
hooks:
2020
- id: mypy
2121
additional_dependencies: [

Dockerfile

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,38 @@
1-
# Use an official Python base image
2-
FROM python:3.11.9-slim
1+
# Build stage
2+
FROM python:3.11.9-slim as builder
33

4-
# Configure environment variables for Python and Poetry
5-
ENV PYTHONDONTWRITEBYTECODE=1
6-
ENV PYTHONUNBUFFERED=1
74
ENV POETRY_HOME="/opt/poetry"
85
ENV POETRY_NO_INTERACTION=1
96
ENV POETRY_VIRTUALENVS_CREATE=false
10-
11-
# Add Poetry's bin directory to the system PATH.
127
ENV PATH="$POETRY_HOME/bin:$PATH"
138

14-
# Set the working directory
15-
WORKDIR /app
16-
17-
# Install system dependencies and Poetry itself
189
RUN apt-get update \
19-
&& apt-get install -y --no-install-recommends curl gifsicle \
20-
&& curl -sSL https://install.python-poetry.org | python - \
21-
&& apt-get remove -y curl \
22-
&& apt-get autoremove -y \
23-
&& rm -rf /var/lib/apt/lists/*
10+
&& apt-get install -y --no-install-recommends \
11+
build-essential \
12+
cmake \
13+
git \
14+
graphviz-dev \
15+
libgraphviz-dev \
16+
pkg-config \
17+
curl \
18+
&& curl -sSL https://install.python-poetry.org | python -
2419

25-
# Copy ALL project files before installing dependencies.
26-
# This ensures Poetry can find the local path dependencies (agent-core, etc.).
20+
WORKDIR /app
2721
COPY . .
22+
RUN poetry install --without dev
23+
24+
# Runtime stage
25+
FROM python:3.11.9-slim
2826

29-
# Install build dependencies, install Python packages, then remove build dependencies.
30-
# This ensures packages that need compilation (like ecos, osqp) can be built,
31-
# while keeping the final image size smaller.
3227
RUN apt-get update \
33-
&& apt-get install -y --no-install-recommends build-essential cmake git graphviz libgraphviz-dev \
34-
&& poetry install --without dev \
35-
&& apt-get purge -y --auto-remove build-essential cmake git graphviz libgraphviz-dev \
28+
&& apt-get install -y --no-install-recommends \
29+
graphviz \
30+
gifsicle \
3631
&& rm -rf /var/lib/apt/lists/*
3732

38-
# The default command to run when the container starts
33+
WORKDIR /app
34+
COPY --from=builder /usr/local/lib/python3.11/site-packages/ /usr/local/lib/python3.11/site-packages/
35+
COPY --from=builder /usr/local/bin/ /usr/local/bin/
36+
COPY --from=builder /app .
37+
3938
CMD ["tail", "-f", "/dev/null"]

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,15 @@ WORKERS ?= 4
2626

2727
# --- Core Docker Commands ---
2828

29-
## up: Build images and start all services in the background.
29+
## up: Build images, start services, and initialize the database.
3030
up:
3131
@echo "🚀 Building images and starting all services..."
3232
@docker compose up -d --build
33+
@echo "⏳ Waiting 10 seconds for services to stabilize..."
34+
@sleep 10
35+
@echo "🔑 Initializing database tables..."
36+
@make init-db
37+
@echo "✅ All services are up and the database is initialized."
3338

3439
## down: Stop and remove all containers, networks, and volumes.
3540
down:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,4 @@ docker compose up -d
7474
7575
```bash
7676
make down
77-
```
77+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# src/agent_core/agents/action_cost_provider_interface.py
2+
from abc import ABC, abstractmethod
3+
from typing import TYPE_CHECKING
4+
5+
if TYPE_CHECKING:
6+
from agent_core.core.ecs.abstractions import SimulationState
7+
8+
9+
class ActionCostProviderInterface(ABC):
10+
"""
11+
Abstract Base Class for an action cost provider.
12+
13+
The concrete implementation of this interface will live in the final
14+
simulation application. It is responsible for applying the cost of
15+
an action to the appropriate agent component (e.g., TimeBudgetComponent,
16+
EnergyComponent, etc.).
17+
"""
18+
19+
@abstractmethod
20+
def apply_action_cost(
21+
self,
22+
entity_id: str,
23+
cost: float,
24+
simulation_state: "SimulationState",
25+
) -> None:
26+
"""
27+
Deducts the cost of an action from the relevant agent component.
28+
29+
Args:
30+
entity_id: The ID of the agent performing the action.
31+
cost: The calculated cost of the action.
32+
simulation_state: The current state of the simulation.
33+
"""
34+
raise NotImplementedError

agent-core/src/agent_core/agents/actions/action_interface.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from abc import ABC, abstractmethod
99
from typing import TYPE_CHECKING, Any, Dict, List
10+
from .action_outcome import ActionOutcome
1011

1112
if TYPE_CHECKING:
1213
from agent_core.core.ecs.abstractions import SimulationState
@@ -52,7 +53,7 @@ def execute(
5253
simulation_state: "SimulationState",
5354
params: Dict[str, Any],
5455
current_tick: int,
55-
) -> Dict[str, Any]:
56+
) -> ActionOutcome:
5657
"""
5758
Executes the action's logic and modifies the simulation state.
5859
"""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# agent-core/src/agent_core/agents/actions/action_outcome.py
2+
from dataclasses import dataclass, field
3+
from typing import Any, Dict
4+
5+
6+
@dataclass
7+
class ActionOutcome:
8+
"""A data structure to hold the results of an action's execution."""
9+
10+
success: bool
11+
message: str
12+
base_reward: float
13+
details: Dict[str, Any] = field(default_factory=dict)
14+
reward: float = field(init=False)
15+
16+
def __post_init__(self):
17+
# The final reward can be modified by other systems,
18+
# but it starts as the base reward.
19+
self.reward = self.base_reward

agent-core/src/agent_core/agents/actions/action_registry.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# src/agent_core/agents/actions/action_registry.py
22
"""
33
Defines the ActionRegistry, a singleton object that discovers and manages
4-
all available actions in the simulation.
4+
all available actions in the simulation. This version is designed to be
5+
"override-friendly" for testing purposes.
56
"""
67

78
import importlib
@@ -13,23 +14,27 @@
1314
class ActionRegistry:
1415
"""
1516
A registry for discovering, storing, and retrieving action classes.
17+
18+
In its default mode (`strict=False`), it allows new actions to override
19+
existing actions with the same `action_id`. This is useful in testing
20+
environments where multiple simulations define their own version of a
21+
common action (e.g., 'move').
22+
23+
In `strict=True` mode, it will raise a ValueError if a duplicate
24+
action_id is registered, which is useful for production to prevent
25+
accidental naming collisions.
1626
"""
1727

18-
def __init__(self) -> None:
28+
def __init__(self, strict: bool = False) -> None:
1929
self._actions: Dict[str, Type[ActionInterface]] = {}
20-
print("ActionRegistry initialized.")
30+
self.strict = strict
31+
print(
32+
f"ActionRegistry initialized in {'strict' if self.strict else 'override'} mode."
33+
)
2134

2235
def load_actions_from_paths(self, module_paths: List[str]) -> None:
2336
"""
2437
Dynamically imports Python modules from a list of string paths.
25-
26-
This is the core of the plugin system. Importing a module that
27-
contains an @action_registry.register decorator will cause that
28-
action to be registered.
29-
30-
Args:
31-
module_paths: A list of module paths, e.g.,
32-
["simulations.soul_sim.actions.move_action"].
3338
"""
3439
print(f"Dynamically loading actions from: {module_paths}")
3540
for path in module_paths:
@@ -64,9 +69,11 @@ def register(self, action_class: Type[ActionInterface]) -> Type[ActionInterface]
6469
f"Action class {action_class.__name__} has an invalid 'action_id' property."
6570
)
6671

67-
if action_id in self._actions:
72+
# Only raise an error if the registry is in strict mode.
73+
if action_id in self._actions and self.strict:
6874
raise ValueError(f"Action with ID '{action_id}' is already registered.")
6975

76+
# Silently override the existing action if not in strict mode.
7077
self._actions[action_id] = action_class
7178
print(f"Action '{action_name}' registered with ID '{action_id}'.")
7279
return action_class
@@ -88,6 +95,5 @@ def action_ids(self) -> List[str]:
8895
return sorted(self._actions.keys())
8996

9097

91-
# Create a global singleton instance of the registry.
92-
# All other parts of the application will import this instance.
93-
action_registry = ActionRegistry()
98+
# Create a global singleton instance of the registry in non-strict mode.
99+
action_registry = ActionRegistry(strict=False)

agent-core/src/agent_core/agents/actions/base_action.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
from abc import abstractmethod
1010
from enum import Enum
11-
from typing import Any, Dict, List, Optional
11+
from typing import Any, Dict, List
1212

1313
from agent_core.agents.actions.action_interface import ActionInterface
14+
from agent_core.agents.actions.action_outcome import ActionOutcome
1415

1516

1617
class Intent(Enum):
@@ -21,23 +22,6 @@ class Intent(Enum):
2122
COMPETE = "COMPETE"
2223

2324

24-
class ActionOutcome:
25-
"""Standardized structure for the result of executing an action."""
26-
27-
def __init__(
28-
self,
29-
success: bool,
30-
message: str,
31-
base_reward: float,
32-
details: Optional[Dict[str, Any]] = None,
33-
):
34-
self.success = success
35-
self.message = message
36-
self.base_reward = base_reward
37-
self.details = details if details is not None else {}
38-
self.reward: float = base_reward
39-
40-
4125
class Action(ActionInterface):
4226
"""
4327
A concrete base class for all actions that implements the ActionInterface.
@@ -71,7 +55,7 @@ def execute(
7155
simulation_state: Any,
7256
params: Dict[str, Any],
7357
current_tick: int,
74-
) -> Dict[str, Any]:
58+
) -> ActionOutcome:
7559
raise NotImplementedError
7660

7761
@abstractmethod

0 commit comments

Comments
 (0)