mango-agents 2.1.2__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mango_agents-2.2.0/PKG-INFO +392 -0
- mango_agents-2.2.0/mango/__init__.py +102 -0
- mango_agents-2.2.0/mango/agent/core.py +941 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/agent/role.py +183 -15
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/core.py +9 -7
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/external_coupling.py +8 -7
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/factory.py +16 -1
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/mp.py +16 -5
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/mqtt.py +47 -28
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/tcp.py +15 -23
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/express/api.py +217 -0
- mango_agents-2.2.0/mango/express/topology.py +672 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/codecs.py +46 -12
- mango_agents-2.2.0/mango/simulation/__init__.py +45 -0
- mango_agents-2.2.0/mango/simulation/communication.py +229 -0
- mango_agents-2.2.0/mango/simulation/environment.py +342 -0
- mango_agents-2.2.0/mango/simulation/visualization.py +347 -0
- mango_agents-2.2.0/mango/simulation/world.py +724 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/distributed_clock.py +2 -2
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/scheduling.py +41 -6
- mango_agents-2.2.0/mango_agents.egg-info/PKG-INFO +392 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango_agents.egg-info/SOURCES.txt +5 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango_agents.egg-info/requires.txt +1 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/pyproject.toml +3 -2
- mango_agents-2.2.0/readme.md +335 -0
- mango_agents-2.1.2/PKG-INFO +0 -230
- mango_agents-2.1.2/mango/__init__.py +0 -32
- mango_agents-2.1.2/mango/agent/core.py +0 -499
- mango_agents-2.1.2/mango/express/topology.py +0 -139
- mango_agents-2.1.2/mango_agents.egg-info/PKG-INFO +0 -230
- mango_agents-2.1.2/readme.md +0 -175
- {mango_agents-2.1.2 → mango_agents-2.2.0}/LICENSE +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/agent/__init__.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/__init__.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/container/protocol.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/express/__init__.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/__init__.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/acl_message_pb2.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/fast_json.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/mango_message_pb2.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/message.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/messages/other_proto_msgs_pb2.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/__init__.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/clock.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/multiprocessing.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango/util/termination_detection.py +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango_agents.egg-info/dependency_links.txt +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/mango_agents.egg-info/top_level.txt +0 -0
- {mango_agents-2.1.2 → mango_agents-2.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mango-agents
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Modular Python Agent Framework
|
|
5
|
+
Author-email: mango Team <mango@offis.de>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2020 OFFIS e.V.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://mango-agents.readthedocs.io
|
|
29
|
+
Project-URL: Repository, https://github.com/OFFIS-DAI/mango
|
|
30
|
+
Project-URL: Issues, https://github.com/OFFIS-DAI/mango/issues
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Programming Language :: Python
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
39
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
40
|
+
Requires-Python: >=3.10
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Requires-Dist: paho-mqtt>=2.1.0
|
|
44
|
+
Requires-Dist: python-dateutil>=2.9.0
|
|
45
|
+
Requires-Dist: dill>=0.3.8
|
|
46
|
+
Requires-Dist: protobuf==5.27.2
|
|
47
|
+
Requires-Dist: networkx>=3.4.1
|
|
48
|
+
Provides-Extra: fastjson
|
|
49
|
+
Requires-Dist: msgspec>=0.18.6; extra == "fastjson"
|
|
50
|
+
Provides-Extra: test
|
|
51
|
+
Requires-Dist: pytest; extra == "test"
|
|
52
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
53
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
54
|
+
Requires-Dist: pre-commit; extra == "test"
|
|
55
|
+
Requires-Dist: matplotlib; extra == "test"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<img src="docs/source/_static/Logo_mango_ohne_sub.svg#gh-light-mode-only" alt="mango" width="320"/>
|
|
60
|
+
<img src="docs/source/_static/Logo_mango_ohne_sub_white.svg#gh-dark-mode-only" alt="mango" width="320"/>
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
<p align="center">
|
|
64
|
+
<b>Modular Python Agent Framework</b><br>
|
|
65
|
+
<i>A lightweight, asyncio-based library for research and development of multi-agent systems</i>
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<a href="https://pypi.org/project/mango-agents/">PyPI</a> |
|
|
70
|
+
<a href="https://mango-agents.readthedocs.io">Read the Docs</a> |
|
|
71
|
+
<a href="https://github.com/OFFIS-DAI/mango">GitHub</a> |
|
|
72
|
+
<a href="mailto:mango@offis.de">mango@offis.de</a>
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<p align="center">
|
|
76
|
+
<a href="https://pypi.org/project/mango-agents/"><img src="https://img.shields.io/pypi/v/mango-agents?color=blue&label=PyPI" alt="PyPI"></a>
|
|
77
|
+
<a href="https://mango-agents.readthedocs.io"><img src="https://img.shields.io/badge/docs-readthedocs-informational" alt="Docs"></a>
|
|
78
|
+
<a href="https://github.com/OFFIS-DAI/mango/actions/workflows/test-mango.yml"><img src="https://github.com/OFFIS-DAI/mango/actions/workflows/test-mango.yml/badge.svg" alt="Tests"></a>
|
|
79
|
+
<a href="https://codecov.io/gh/OFFIS-DAI/mango"><img src="https://codecov.io/gh/OFFIS-DAI/mango/graph/badge.svg?token=6KVKBICGYG" alt="Coverage"></a>
|
|
80
|
+
<a href="https://github.com/OFFIS-DAI/mango/blob/development/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
81
|
+
<img src="https://img.shields.io/badge/lifecycle-maturing-blue.svg" alt="Lifecycle">
|
|
82
|
+
</p>
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
**mango** (**m**odul**a**r pytho**n** a**g**ent framew**o**rk) is a Python library for building and simulating multi-agent systems (MAS) on top of `asyncio`. It targets researchers and engineers who need reproducible agent behaviour, structured agent architectures, and controlled experimental conditions — without sacrificing usability for prototyping.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Features
|
|
91
|
+
|
|
92
|
+
| Feature | Description |
|
|
93
|
+
|---|---|
|
|
94
|
+
| **Container-based messaging** | Agents are co-located in containers that short-circuit local message exchange without serialisation overhead |
|
|
95
|
+
| **FIPA ACL message format** | Standardised message envelope with built-in JSON and protobuf codecs |
|
|
96
|
+
| **Role decomposition** | Complex agent behaviour is factored into loosely coupled, independently testable role objects |
|
|
97
|
+
| **TCP and MQTT transport** | Agents communicate across process and machine boundaries via TCP sockets or an external MQTT broker |
|
|
98
|
+
| **Discrete-event simulation** | A clock-driven `SimulationWorld` provides deterministic, reproducible runs with configurable communication delay and loss, spatial environments, and structured data recording |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Installation
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install mango-agents
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Requires Python 3.10 or later.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Quick Start
|
|
113
|
+
|
|
114
|
+
Every agent is a subclass of `Agent`. Incoming messages are handled by `handle_message`; two lifecycle hooks signal when the agent has joined its container (`on_register`) and when all containers are active and external messaging is safe (`on_ready`).
|
|
115
|
+
|
|
116
|
+
The following complete script creates two agents, starts them in a shared container, sends a message from one to the other, and shuts everything down cleanly:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import asyncio
|
|
120
|
+
from mango import Agent, create_tcp_container, activate
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class ReportingAgent(Agent):
|
|
124
|
+
def on_ready(self):
|
|
125
|
+
print(f"{self.aid}: ready")
|
|
126
|
+
|
|
127
|
+
def handle_message(self, content, meta):
|
|
128
|
+
print(f"{self.aid}: received '{content}' from {meta['sender_id']}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def main():
|
|
132
|
+
# All agents on a single host share one container.
|
|
133
|
+
container = create_tcp_container(addr=("127.0.0.1", 5555))
|
|
134
|
+
|
|
135
|
+
sender = container.register(ReportingAgent())
|
|
136
|
+
receiver = container.register(ReportingAgent())
|
|
137
|
+
|
|
138
|
+
async with activate(container):
|
|
139
|
+
await sender.send_message("Hello!", receiver.addr)
|
|
140
|
+
await asyncio.sleep(0.1) # allow time for message processing
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
asyncio.run(main())
|
|
144
|
+
# agent0: ready
|
|
145
|
+
# agent1: ready
|
|
146
|
+
# agent1: received 'Hello!' from agent0
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`activate` starts the container and guarantees a clean shutdown when its block exits, even if an exception is raised.
|
|
150
|
+
|
|
151
|
+
### Proactive behavior
|
|
152
|
+
|
|
153
|
+
Agents are not limited to reacting to messages. `schedule_periodic_task` registers a coroutine that is called repeatedly at a fixed interval — useful for polling, broadcasting, or any time-driven behavior:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
class SensorAgent(Agent):
|
|
157
|
+
def __init__(self, target_addr):
|
|
158
|
+
super().__init__()
|
|
159
|
+
self.target_addr = target_addr
|
|
160
|
+
|
|
161
|
+
def on_ready(self):
|
|
162
|
+
# Broadcast a reading every 5 seconds of (simulated) time.
|
|
163
|
+
self.schedule_periodic_task(self._broadcast, delay=5.0)
|
|
164
|
+
|
|
165
|
+
async def _broadcast(self):
|
|
166
|
+
await self.send_message("temperature: 22.5 C", self.target_addr)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Simulation
|
|
172
|
+
|
|
173
|
+
Running agents over a real network introduces timing variability that makes experiments hard to reproduce. The `SimulationWorld` eliminates this by replacing the network layer with a controlled, clock-driven execution model: time advances only when the simulation explicitly steps forward, there are no open sockets, and every run with the same inputs produces the same outputs.
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
┌──────────────────────────────────────────────────────┐
|
|
177
|
+
│ SimulationWorld │
|
|
178
|
+
│ │
|
|
179
|
+
│ ExternalClock ──► step_simulation() │
|
|
180
|
+
│ | │
|
|
181
|
+
│ ┌─────────────────┼─────────────────┐ │
|
|
182
|
+
│ v v v │
|
|
183
|
+
│ on_step() message delivery environment │
|
|
184
|
+
│ (all agents) (delay / loss model) .step() │
|
|
185
|
+
└──────────────────────────────────────────────────────┘
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Each step proceeds in order:
|
|
189
|
+
|
|
190
|
+
1. All agents receive `on_step(env, clock, step_size_s)`.
|
|
191
|
+
2. The environment behavior advances by `step_size_s`.
|
|
192
|
+
3. The internal clock is set to the new time, waking any sleeping scheduled tasks.
|
|
193
|
+
4. A convergence loop delivers all messages whose simulated arrival time has passed and waits for agents to settle between deliveries.
|
|
194
|
+
|
|
195
|
+
### Complete example
|
|
196
|
+
|
|
197
|
+
The following self-contained script runs two agents for 60 simulated seconds and records how many readings the monitor received over time:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import asyncio
|
|
201
|
+
from mango import Agent, create_world, discrete_step_until, record_agent
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class SensorAgent(Agent):
|
|
205
|
+
"""Periodically sends a reading to a monitor agent."""
|
|
206
|
+
|
|
207
|
+
def __init__(self):
|
|
208
|
+
super().__init__()
|
|
209
|
+
self.monitor_addr = None # set after both agents are registered
|
|
210
|
+
self.readings_sent = 0
|
|
211
|
+
|
|
212
|
+
def on_ready(self):
|
|
213
|
+
self.schedule_periodic_task(self._broadcast, delay=10.0)
|
|
214
|
+
|
|
215
|
+
async def _broadcast(self):
|
|
216
|
+
await self.send_message(f"reading {self.readings_sent}", self.monitor_addr)
|
|
217
|
+
self.readings_sent += 1
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class MonitorAgent(Agent):
|
|
221
|
+
"""Counts incoming readings."""
|
|
222
|
+
|
|
223
|
+
def __init__(self):
|
|
224
|
+
super().__init__()
|
|
225
|
+
self.received = 0
|
|
226
|
+
|
|
227
|
+
def handle_message(self, content, meta):
|
|
228
|
+
self.received += 1
|
|
229
|
+
print(f" t={self.current_timestamp:.0f} s monitor received: {content}")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
async def run():
|
|
233
|
+
world = create_world(start_time=0.0)
|
|
234
|
+
sensor = world.register(SensorAgent())
|
|
235
|
+
monitor = world.register(MonitorAgent())
|
|
236
|
+
|
|
237
|
+
# Set the target address before on_ready is called (inside async with).
|
|
238
|
+
sensor.monitor_addr = monitor.addr
|
|
239
|
+
|
|
240
|
+
# Record the running count of received messages after every step.
|
|
241
|
+
record_agent(world, "received", lambda a: a.received,
|
|
242
|
+
filter_fn=lambda a: isinstance(a, MonitorAgent))
|
|
243
|
+
|
|
244
|
+
async with world:
|
|
245
|
+
await discrete_step_until(world, max_advance_time_s=60.0)
|
|
246
|
+
|
|
247
|
+
print(f"\nTotal readings received: {monitor.received}")
|
|
248
|
+
# world.data_agent_collections["received"].timeseries holds the full time series.
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
asyncio.run(run())
|
|
252
|
+
# t=10 s monitor received: reading 0
|
|
253
|
+
# t=20 s monitor received: reading 1
|
|
254
|
+
# t=30 s monitor received: reading 2
|
|
255
|
+
# t=40 s monitor received: reading 3
|
|
256
|
+
# t=50 s monitor received: reading 4
|
|
257
|
+
# t=60 s monitor received: reading 5
|
|
258
|
+
#
|
|
259
|
+
# Total readings received: 6
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`discrete_step_until` automatically determines each step size as the time until the next scheduled event — a message arrival or a task wakeup — and stops when no further events remain within the time budget.
|
|
263
|
+
|
|
264
|
+
For experiment designs that require fixed, uniform time increments, call `step_simulation` directly:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from mango import step_simulation
|
|
268
|
+
|
|
269
|
+
async with world:
|
|
270
|
+
for _ in range(10):
|
|
271
|
+
result = await step_simulation(world, step_size_s=1.0)
|
|
272
|
+
print(f"t = {world.clock.time:.1f} s messages delivered: {result.messages_delivered}")
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Communication modelling
|
|
276
|
+
|
|
277
|
+
By default, messages are delivered instantly with no loss. `SimpleCommunicationSimulation` adds configurable one-way latency and independent packet loss, with optional per-link overrides:
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from mango import create_world, SimpleCommunicationSimulation
|
|
281
|
+
|
|
282
|
+
world = create_world(
|
|
283
|
+
start_time=0.0,
|
|
284
|
+
communication_sim=SimpleCommunicationSimulation(
|
|
285
|
+
default_delay_s=0.1, # baseline one-way latency
|
|
286
|
+
loss_percent=0.01, # independent loss probability per message
|
|
287
|
+
delay_s_directed_edge_dict={
|
|
288
|
+
("agent0", "agent1"): 0.5, # directed per-link override
|
|
289
|
+
},
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
For delays drawn from a distribution at runtime, use `DelayProviderCommunicationSimulation`:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
import random
|
|
298
|
+
from mango import create_world, DelayProviderCommunicationSimulation
|
|
299
|
+
|
|
300
|
+
world = create_world(
|
|
301
|
+
start_time=0.0,
|
|
302
|
+
communication_sim=DelayProviderCommunicationSimulation(
|
|
303
|
+
default_delay_s_provider=lambda: random.gauss(0.1, 0.01),
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Custom communication models are implemented by subclassing `CommunicationSimulation` and overriding `calculate_communication`.
|
|
309
|
+
|
|
310
|
+
### Spatial environments
|
|
311
|
+
|
|
312
|
+
Agents can be embedded in a continuous 2-D area. Agents without a pre-assigned position are placed uniformly at random within the area during initialisation, and their positions can be recorded as a time series:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from mango import (
|
|
316
|
+
create_world, Area2D, DefaultEnvironment,
|
|
317
|
+
record_position, position_history, discrete_step_until,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
env = DefaultEnvironment(space=Area2D(width=100.0, height=100.0))
|
|
321
|
+
world = create_world(start_time=0.0, environment=env)
|
|
322
|
+
|
|
323
|
+
# ... register agents ...
|
|
324
|
+
record_position(world)
|
|
325
|
+
|
|
326
|
+
async with world:
|
|
327
|
+
await discrete_step_until(world, max_advance_time_s=10.0)
|
|
328
|
+
|
|
329
|
+
history = position_history(world)
|
|
330
|
+
# history.timeseries["agent0"] -> [Position2D(x=..., y=...), ...]
|
|
331
|
+
# history.time -> [0.0, 5.0, 10.0, ...]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Custom spaces and environment behaviors are implemented by subclassing `Space` and `Behavior` respectively.
|
|
335
|
+
|
|
336
|
+
### Data recording
|
|
337
|
+
|
|
338
|
+
`record_world` and `record_agent` capture any scalar as a time series after every simulation step, without modifying agent code. Results are plain Python lists compatible with numpy, pandas, and matplotlib:
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
from mango import create_world, record_world, record_agent
|
|
342
|
+
|
|
343
|
+
world = create_world(start_time=0.0)
|
|
344
|
+
# ... register agents ...
|
|
345
|
+
|
|
346
|
+
# World-level: total messages delivered so far
|
|
347
|
+
record_world(world, "msg_count", lambda: len(world.recorded_messages))
|
|
348
|
+
|
|
349
|
+
# Per-agent: an arbitrary attribute
|
|
350
|
+
record_agent(world, "energy_kwh", lambda a: a.energy_kwh)
|
|
351
|
+
|
|
352
|
+
async with world:
|
|
353
|
+
await discrete_step_until(world, max_advance_time_s=3600.0)
|
|
354
|
+
|
|
355
|
+
world.data_collections["msg_count"].timeseries # list of scalars
|
|
356
|
+
world.data_agent_collections["energy_kwh"].timeseries # dict[aid -> list]
|
|
357
|
+
world.data_agent_collections["energy_kwh"].time # shared time axis
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
All exchanged messages are also logged automatically in `world.recorded_messages` as `MessageTransaction` objects, each carrying sender, receiver, send time, and arrival time.
|
|
361
|
+
|
|
362
|
+
### Agent hooks for simulation
|
|
363
|
+
|
|
364
|
+
Any `Agent` or `Role` subclass may override these methods to participate in the simulation lifecycle:
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
class MyAgent(Agent):
|
|
368
|
+
def on_step(self, env, clock, step_size_s: float):
|
|
369
|
+
"""Invoked at the start of every simulation step, before the clock advances."""
|
|
370
|
+
|
|
371
|
+
def on_global_event(self, event):
|
|
372
|
+
"""Invoked when the environment broadcasts an event to all agents."""
|
|
373
|
+
|
|
374
|
+
def on_agent_event(self, event):
|
|
375
|
+
"""Invoked when the environment targets this specific agent."""
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Documentation & Support
|
|
381
|
+
|
|
382
|
+
| Resource | Link |
|
|
383
|
+
|---|---|
|
|
384
|
+
| Full documentation | [mango-agents.readthedocs.io](https://mango-agents.readthedocs.io) |
|
|
385
|
+
| Issue tracker | [github.com/OFFIS-DAI/mango/issues](https://github.com/OFFIS-DAI/mango/issues) |
|
|
386
|
+
| Contact | [mango@offis.de](mailto:mango@offis.de) |
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
Distributed under the [MIT License](https://github.com/OFFIS-DAI/mango/blob/development/LICENSE).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from .messages.message import create_acl, Performatives
|
|
2
|
+
from .agent.core import (
|
|
3
|
+
Agent,
|
|
4
|
+
AgentAddress,
|
|
5
|
+
AgentDescription,
|
|
6
|
+
ForwardingRule,
|
|
7
|
+
State,
|
|
8
|
+
TopologyNeighbor,
|
|
9
|
+
TopologyService,
|
|
10
|
+
)
|
|
11
|
+
from .agent.role import (
|
|
12
|
+
MessagePreprocessor,
|
|
13
|
+
Role,
|
|
14
|
+
RoleAgent,
|
|
15
|
+
RoleContext,
|
|
16
|
+
WaitingMessagePreprocessor,
|
|
17
|
+
)
|
|
18
|
+
from .container.factory import (
|
|
19
|
+
create_tcp as create_tcp_container,
|
|
20
|
+
create_mqtt as create_mqtt_container,
|
|
21
|
+
create_external_coupling as create_ec_container,
|
|
22
|
+
)
|
|
23
|
+
from .express.api import (
|
|
24
|
+
activate,
|
|
25
|
+
run_with_mqtt,
|
|
26
|
+
run_with_tcp,
|
|
27
|
+
run_with_simulation,
|
|
28
|
+
behavior_in,
|
|
29
|
+
agent_composed_of,
|
|
30
|
+
PrintingAgent,
|
|
31
|
+
sender_addr,
|
|
32
|
+
addr,
|
|
33
|
+
)
|
|
34
|
+
from .util.distributed_clock import DistributedClockAgent, DistributedClockManager
|
|
35
|
+
from .util.clock import ExternalClock, AsyncioClock
|
|
36
|
+
from .messages.codecs import (
|
|
37
|
+
json_serializable,
|
|
38
|
+
JSON,
|
|
39
|
+
PROTOBUF,
|
|
40
|
+
SerializationError,
|
|
41
|
+
)
|
|
42
|
+
from .express.topology import (
|
|
43
|
+
AgentNode,
|
|
44
|
+
Topology,
|
|
45
|
+
assign_agents,
|
|
46
|
+
auto_assign,
|
|
47
|
+
broadcast_to_neighbors,
|
|
48
|
+
complete_topology,
|
|
49
|
+
connect_topologies,
|
|
50
|
+
create_topology,
|
|
51
|
+
custom_topology,
|
|
52
|
+
cycle_topology,
|
|
53
|
+
graph_topology,
|
|
54
|
+
mark_as_connector,
|
|
55
|
+
modify_topology,
|
|
56
|
+
per_node,
|
|
57
|
+
star_topology,
|
|
58
|
+
topology_characteristic,
|
|
59
|
+
topology_connection_types,
|
|
60
|
+
topology_connectors,
|
|
61
|
+
topology_neighbors,
|
|
62
|
+
topology_node_id,
|
|
63
|
+
topology_to_aid_graph,
|
|
64
|
+
)
|
|
65
|
+
from .simulation import (
|
|
66
|
+
CommunicationSimulation,
|
|
67
|
+
CommunicationSimulationResult,
|
|
68
|
+
DelayProviderCommunicationSimulation,
|
|
69
|
+
MessagePackage,
|
|
70
|
+
PackageResult,
|
|
71
|
+
SimpleCommunicationSimulation,
|
|
72
|
+
create_distribution_based_com_sim,
|
|
73
|
+
Area2D,
|
|
74
|
+
Behavior,
|
|
75
|
+
DefaultEnvironment,
|
|
76
|
+
Environment,
|
|
77
|
+
NoSpace,
|
|
78
|
+
Position,
|
|
79
|
+
Position2D,
|
|
80
|
+
Space,
|
|
81
|
+
distance,
|
|
82
|
+
AgentsRecording,
|
|
83
|
+
DISCRETE_EVENT,
|
|
84
|
+
MessageTransaction,
|
|
85
|
+
SimulationResult,
|
|
86
|
+
SimulationWorld,
|
|
87
|
+
WorldRecording,
|
|
88
|
+
collect_agent_data,
|
|
89
|
+
collect_data,
|
|
90
|
+
create_world,
|
|
91
|
+
discrete_step_until,
|
|
92
|
+
position_history,
|
|
93
|
+
record_agent,
|
|
94
|
+
record_agent_having,
|
|
95
|
+
record_position,
|
|
96
|
+
record_world,
|
|
97
|
+
step_simulation,
|
|
98
|
+
plot_agents,
|
|
99
|
+
plot_recordings,
|
|
100
|
+
plot_world,
|
|
101
|
+
show_communication_data,
|
|
102
|
+
)
|