mhagenta 1.0.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.
Files changed (60) hide show
  1. mhagenta-1.0.0/LICENSE +21 -0
  2. mhagenta-1.0.0/PKG-INFO +118 -0
  3. mhagenta-1.0.0/README.md +101 -0
  4. mhagenta-1.0.0/mhagenta/__init__.py +6 -0
  5. mhagenta-1.0.0/mhagenta/bases/__init__.py +10 -0
  6. mhagenta-1.0.0/mhagenta/containers/CONTAINER_VERSION.py +1 -0
  7. mhagenta-1.0.0/mhagenta/containers/__init__.py +10 -0
  8. mhagenta-1.0.0/mhagenta/containers/mha-base/Dockerfile +10 -0
  9. mhagenta-1.0.0/mhagenta/containers/mha-main/Dockerfile +14 -0
  10. mhagenta-1.0.0/mhagenta/containers/mha-rabbitmq/Dockerfile +5 -0
  11. mhagenta-1.0.0/mhagenta/containers/mha-rabbitmq/rabbitmq-install.sh +44 -0
  12. mhagenta-1.0.0/mhagenta/core/__init__.py +6 -0
  13. mhagenta-1.0.0/mhagenta/core/agent_launcher.py +27 -0
  14. mhagenta-1.0.0/mhagenta/core/connection/__init__.py +7 -0
  15. mhagenta-1.0.0/mhagenta/core/connection/connector.py +96 -0
  16. mhagenta-1.0.0/mhagenta/core/connection/module_messenger.py +93 -0
  17. mhagenta-1.0.0/mhagenta/core/connection/rabbitmq_connector.py +576 -0
  18. mhagenta-1.0.0/mhagenta/core/connection/root_messenger.py +75 -0
  19. mhagenta-1.0.0/mhagenta/core/module_launcher.py +50 -0
  20. mhagenta-1.0.0/mhagenta/core/orchestrator.py +475 -0
  21. mhagenta-1.0.0/mhagenta/core/processes/__init__.py +6 -0
  22. mhagenta-1.0.0/mhagenta/core/processes/mha_module.py +426 -0
  23. mhagenta-1.0.0/mhagenta/core/processes/mha_root.py +328 -0
  24. mhagenta-1.0.0/mhagenta/core/processes/process.py +366 -0
  25. mhagenta-1.0.0/mhagenta/defaults/__init__.py +0 -0
  26. mhagenta-1.0.0/mhagenta/defaults/actuators/__init__.py +0 -0
  27. mhagenta-1.0.0/mhagenta/defaults/actuators/actuators.py +0 -0
  28. mhagenta-1.0.0/mhagenta/defaults/agents/__init__.py +0 -0
  29. mhagenta-1.0.0/mhagenta/defaults/agents/agents.py +0 -0
  30. mhagenta-1.0.0/mhagenta/defaults/perceptors/__init__.py +0 -0
  31. mhagenta-1.0.0/mhagenta/defaults/perceptors/perceptors.py +0 -0
  32. mhagenta-1.0.0/mhagenta/modules/__init__.py +8 -0
  33. mhagenta-1.0.0/mhagenta/modules/actuation/__init__.py +4 -0
  34. mhagenta-1.0.0/mhagenta/modules/actuation/actuator.py +84 -0
  35. mhagenta-1.0.0/mhagenta/modules/high_level/__init__.py +8 -0
  36. mhagenta-1.0.0/mhagenta/modules/high_level/goals/__init__.py +4 -0
  37. mhagenta-1.0.0/mhagenta/modules/high_level/goals/goal_graph.py +112 -0
  38. mhagenta-1.0.0/mhagenta/modules/high_level/high_level_reasoner.py +164 -0
  39. mhagenta-1.0.0/mhagenta/modules/high_level/knowledge/__init__.py +4 -0
  40. mhagenta-1.0.0/mhagenta/modules/high_level/knowledge/knowledge.py +129 -0
  41. mhagenta-1.0.0/mhagenta/modules/low_level/__init__.py +5 -0
  42. mhagenta-1.0.0/mhagenta/modules/low_level/learning/__init__.py +4 -0
  43. mhagenta-1.0.0/mhagenta/modules/low_level/learning/learner.py +160 -0
  44. mhagenta-1.0.0/mhagenta/modules/low_level/low_level_reasoner.py +279 -0
  45. mhagenta-1.0.0/mhagenta/modules/memory/__init__.py +4 -0
  46. mhagenta-1.0.0/mhagenta/modules/memory/memory.py +175 -0
  47. mhagenta-1.0.0/mhagenta/modules/perception/__init__.py +4 -0
  48. mhagenta-1.0.0/mhagenta/modules/perception/perceptor.py +85 -0
  49. mhagenta-1.0.0/mhagenta/outboxes/__init__.py +9 -0
  50. mhagenta-1.0.0/mhagenta/scripts/README.md +0 -0
  51. mhagenta-1.0.0/mhagenta/scripts/pyproject.toml +21 -0
  52. mhagenta-1.0.0/mhagenta/scripts/start.sh +8 -0
  53. mhagenta-1.0.0/mhagenta/states/__init__.py +9 -0
  54. mhagenta-1.0.0/mhagenta/utils/__init__.py +8 -0
  55. mhagenta-1.0.0/mhagenta/utils/common/__init__.py +9 -0
  56. mhagenta-1.0.0/mhagenta/utils/common/classes.py +581 -0
  57. mhagenta-1.0.0/mhagenta/utils/common/logging.py +43 -0
  58. mhagenta-1.0.0/mhagenta/utils/common/typing/__init__.py +4 -0
  59. mhagenta-1.0.0/mhagenta/utils/common/typing/typing.py +11 -0
  60. mhagenta-1.0.0/pyproject.toml +19 -0
mhagenta-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dmitry Gnatyshak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the Software), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, andor sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.1
2
+ Name: mhagenta
3
+ Version: 1.0.0
4
+ Summary: MHAgentA allows you to design and run simulations with containerized Modular Hybrid Agents.
5
+ Author: Dmitry Gnatyshak
6
+ Author-email: dmitry.gnatyshak@gmail.com
7
+ Requires-Python: >=3.12,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Requires-Dist: dill (>=0.3.9,<0.4.0)
11
+ Requires-Dist: docker (>=7.1.0,<8.0.0)
12
+ Requires-Dist: pika (>=1.3.2,<2.0.0)
13
+ Requires-Dist: pika-stubs (>=0.1.3,<0.2.0)
14
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ # MHAgentA
18
+
19
+ **MHAgentA** (Modular Hybrid Agent Architecture) is a framework for developing container-based agents with complex
20
+ behaviors. Each agent is composed of a number of semi-autonomous modules, each focusing on their own tasks (either
21
+ reactively or proactively) and communicating with each other.
22
+
23
+ The framework handles all the internal workings of each agent automatically, but leaves the exact implementations of
24
+ their modules' behaviors up to the user. This way agents of varying levels of complexity can be developed, from simple
25
+ reactive ones, to sophisticated deliberative (or hybrid) ones.
26
+
27
+ ## Installation
28
+
29
+ You can install MHAgentA with pip:
30
+
31
+ ```
32
+ pip install mhagenta
33
+ ```
34
+
35
+ ## High-level Model
36
+
37
+ Full high-level model of MHAgentA modules and their interaction scheme is shown below. An agent can have instances of
38
+ each of them (including multiple instances of modules of the same type) or just a subset. Note that it is not necessary
39
+ to use all of these module types, as simplest behaviors can be implemented even with just one of them.
40
+
41
+ ![](https://raw.githubusercontent.com/Auron-tliar/mhagenta/957ec749d5fba20b21b2ab1e353481aed530ea58/docs/images/MHAgentA_modules.png)
42
+
43
+ There are 8 types of agent modules, defined by their role in the agent's internal communication scheme. Each module is
44
+ run as a separate process communicating with others as necessary. Each of them can be purely reactive, or can have
45
+ additional periodic internal computations. Essentially, one can view each agent's module as an agent in itw own right,
46
+ and a MHAgentA agent – as a closed multi-agent system with strongly defined communication scheme.
47
+
48
+ 1. **Low-level reasoner**. Handles basic fast decision-making and reactions to the environment.
49
+ 2. **Perceptor**. Observes the agent's environment.
50
+ 3. **Actuator**. Acts upon the environment.
51
+ 4. **Knowledge model**. Contains and processes inherent and acquired knowledge of the agent and/or the world model.
52
+ 5. **High-level reasoner**. Strategizes and comes up with long-term plans.
53
+ 6. **Goal graph**. Represents agent's plan structure and handles its updates.
54
+ 7. **Memory**. Stores, processes, and allows access to old observation data and outdated knowledge.
55
+ 8. **Learner**. Handles (machine learning-based) training of models used by the reasoners.
56
+
57
+ When interacting with each other, each model can either request or send some data as per the communication scheme above.
58
+
59
+ ## Agent Implementation and Execution
60
+
61
+ To define an agent, you need to define all the relevant modules. You can to that by extending base classes for them from
62
+ the `mhagenta.bases` submodule. Each of these base classes have the nameplate functions related to the module's periodic
63
+ step function, utility functions, and reaction to messages from other modules functions. Override their default (empty)
64
+ implementations to define a desired behavior.
65
+
66
+ The functions to override are:
67
+
68
+ - `step(state: State) -> State` – is called in set intervals as defined by the `step_frequency` parameter when defining
69
+ an agent;
70
+ - `on_init(**kwargs) -> None` – is called when the module has finished initializing, before the agent execution start
71
+ is scheduled. Use it if your custom module needs additional setup steps;
72
+ - `on_first(state: State) -> State` – is called right after the execution start before the first call to the `step`
73
+ function
74
+ - `on_last(state: State) -> State` – is called when the stop signal is received (or when the timeout is reached), right
75
+ before the module execution stops;
76
+ - `on_<message_type>(state: State, ...) -> State` – is called when a message of a predefined type received.
77
+
78
+ Each module base has its own set of nameplates for the message reactions that you can override. Additionally, when
79
+ overriding these functions, it is recommended to use module specific State classes from `mhagenta.states` as type hints,
80
+ such as `PerceptorState` or `LLState` instead of the default one, as they will provide hints for outboxes (see below).
81
+
82
+ All the functions executed during the agent execution have State as their first argument. States contain user defined
83
+ fields (you can specify them during the module initialization), additional useful information, such as module ID, time
84
+ since the start of the agent execution, a directory of all other module IDs organized by module types, and the `outbox`
85
+ object. The latter is used to send out messages to other modules. If module-specific state class is used, it will
86
+ display the functions for sending all the established message types to their designated recipients. For instance, to
87
+ send a set of beliefs from a low-level reasoner to a knowledge module, you type:
88
+
89
+ ```python
90
+ state.outbox.send_beliefs(knowledge_id='knowledge_module_id', beliefs=[...], ...)
91
+ ```
92
+
93
+ Be sure to return the modified state at the end of the function, and all the outgoing messages will get processed
94
+ automatically.
95
+
96
+ After defining all the necessary module classes, you need an `Orchestrator` (available from module's root) object to
97
+ handle the creation and execution of the agents. When creating an orchestrator you can also redefine default values of
98
+ agent parameters if different agents share them a lot. Full signature of the Orchestrator's init function is
99
+ provided in the MHAgentA's API documentation.
100
+
101
+ Use `Orchestrator.add_agent(...)` then to compose an agent. To it you need to pass on instances of the relevant modules
102
+ and various parameters, such as agent's ID, frequencies of periodic functions, number of copies of this agent to run,
103
+ whether to resume execution with previously saved stated, etc.
104
+
105
+ Note that when instantiating modules, you need to define a unique ID for the module, any keyword arguments for its
106
+ `on_init` function (if any), and initial fields and their value for the module's state as a `dict[str, Any]`. These
107
+ fields will be added to the state's field dictionary and so that they can be accessed at the runtime with
108
+ `state.field_name`.
109
+
110
+ When done, you can run all the agents togather with
111
+
112
+ ```python
113
+ orchestrator.run()
114
+ ```
115
+
116
+ The internal run function is asynchronous. If you want to hande its execution yourself (i.e. add it to another task
117
+ group), you can use `orchestrator.arun()` instead.
118
+
@@ -0,0 +1,101 @@
1
+ # MHAgentA
2
+
3
+ **MHAgentA** (Modular Hybrid Agent Architecture) is a framework for developing container-based agents with complex
4
+ behaviors. Each agent is composed of a number of semi-autonomous modules, each focusing on their own tasks (either
5
+ reactively or proactively) and communicating with each other.
6
+
7
+ The framework handles all the internal workings of each agent automatically, but leaves the exact implementations of
8
+ their modules' behaviors up to the user. This way agents of varying levels of complexity can be developed, from simple
9
+ reactive ones, to sophisticated deliberative (or hybrid) ones.
10
+
11
+ ## Installation
12
+
13
+ You can install MHAgentA with pip:
14
+
15
+ ```
16
+ pip install mhagenta
17
+ ```
18
+
19
+ ## High-level Model
20
+
21
+ Full high-level model of MHAgentA modules and their interaction scheme is shown below. An agent can have instances of
22
+ each of them (including multiple instances of modules of the same type) or just a subset. Note that it is not necessary
23
+ to use all of these module types, as simplest behaviors can be implemented even with just one of them.
24
+
25
+ ![](https://raw.githubusercontent.com/Auron-tliar/mhagenta/957ec749d5fba20b21b2ab1e353481aed530ea58/docs/images/MHAgentA_modules.png)
26
+
27
+ There are 8 types of agent modules, defined by their role in the agent's internal communication scheme. Each module is
28
+ run as a separate process communicating with others as necessary. Each of them can be purely reactive, or can have
29
+ additional periodic internal computations. Essentially, one can view each agent's module as an agent in itw own right,
30
+ and a MHAgentA agent – as a closed multi-agent system with strongly defined communication scheme.
31
+
32
+ 1. **Low-level reasoner**. Handles basic fast decision-making and reactions to the environment.
33
+ 2. **Perceptor**. Observes the agent's environment.
34
+ 3. **Actuator**. Acts upon the environment.
35
+ 4. **Knowledge model**. Contains and processes inherent and acquired knowledge of the agent and/or the world model.
36
+ 5. **High-level reasoner**. Strategizes and comes up with long-term plans.
37
+ 6. **Goal graph**. Represents agent's plan structure and handles its updates.
38
+ 7. **Memory**. Stores, processes, and allows access to old observation data and outdated knowledge.
39
+ 8. **Learner**. Handles (machine learning-based) training of models used by the reasoners.
40
+
41
+ When interacting with each other, each model can either request or send some data as per the communication scheme above.
42
+
43
+ ## Agent Implementation and Execution
44
+
45
+ To define an agent, you need to define all the relevant modules. You can to that by extending base classes for them from
46
+ the `mhagenta.bases` submodule. Each of these base classes have the nameplate functions related to the module's periodic
47
+ step function, utility functions, and reaction to messages from other modules functions. Override their default (empty)
48
+ implementations to define a desired behavior.
49
+
50
+ The functions to override are:
51
+
52
+ - `step(state: State) -> State` – is called in set intervals as defined by the `step_frequency` parameter when defining
53
+ an agent;
54
+ - `on_init(**kwargs) -> None` – is called when the module has finished initializing, before the agent execution start
55
+ is scheduled. Use it if your custom module needs additional setup steps;
56
+ - `on_first(state: State) -> State` – is called right after the execution start before the first call to the `step`
57
+ function
58
+ - `on_last(state: State) -> State` – is called when the stop signal is received (or when the timeout is reached), right
59
+ before the module execution stops;
60
+ - `on_<message_type>(state: State, ...) -> State` – is called when a message of a predefined type received.
61
+
62
+ Each module base has its own set of nameplates for the message reactions that you can override. Additionally, when
63
+ overriding these functions, it is recommended to use module specific State classes from `mhagenta.states` as type hints,
64
+ such as `PerceptorState` or `LLState` instead of the default one, as they will provide hints for outboxes (see below).
65
+
66
+ All the functions executed during the agent execution have State as their first argument. States contain user defined
67
+ fields (you can specify them during the module initialization), additional useful information, such as module ID, time
68
+ since the start of the agent execution, a directory of all other module IDs organized by module types, and the `outbox`
69
+ object. The latter is used to send out messages to other modules. If module-specific state class is used, it will
70
+ display the functions for sending all the established message types to their designated recipients. For instance, to
71
+ send a set of beliefs from a low-level reasoner to a knowledge module, you type:
72
+
73
+ ```python
74
+ state.outbox.send_beliefs(knowledge_id='knowledge_module_id', beliefs=[...], ...)
75
+ ```
76
+
77
+ Be sure to return the modified state at the end of the function, and all the outgoing messages will get processed
78
+ automatically.
79
+
80
+ After defining all the necessary module classes, you need an `Orchestrator` (available from module's root) object to
81
+ handle the creation and execution of the agents. When creating an orchestrator you can also redefine default values of
82
+ agent parameters if different agents share them a lot. Full signature of the Orchestrator's init function is
83
+ provided in the MHAgentA's API documentation.
84
+
85
+ Use `Orchestrator.add_agent(...)` then to compose an agent. To it you need to pass on instances of the relevant modules
86
+ and various parameters, such as agent's ID, frequencies of periodic functions, number of copies of this agent to run,
87
+ whether to resume execution with previously saved stated, etc.
88
+
89
+ Note that when instantiating modules, you need to define a unique ID for the module, any keyword arguments for its
90
+ `on_init` function (if any), and initial fields and their value for the module's state as a `dict[str, Any]`. These
91
+ fields will be added to the state's field dictionary and so that they can be accessed at the runtime with
92
+ `state.field_name`.
93
+
94
+ When done, you can run all the agents togather with
95
+
96
+ ```python
97
+ orchestrator.run()
98
+ ```
99
+
100
+ The internal run function is asynchronous. If you want to hande its execution yourself (i.e. add it to another task
101
+ group), you can use `orchestrator.arun()` instead.
@@ -0,0 +1,6 @@
1
+ from mhagenta.core import Orchestrator
2
+ from mhagenta.utils import State, Directory, Observation, Goal, Belief, ActionStatus
3
+ from mhagenta import bases, outboxes
4
+
5
+
6
+ __all__ = ['Orchestrator', 'State', 'Directory', 'Observation', 'Goal', 'Belief', 'ActionStatus', 'bases', 'outboxes']
@@ -0,0 +1,10 @@
1
+ from mhagenta.core.processes import ModuleBase
2
+ from mhagenta.modules.perception import PerceptorBase
3
+ from mhagenta.modules.actuation import ActuatorBase
4
+ from mhagenta.modules.low_level import LLReasonerBase, LearnerBase
5
+ from mhagenta.modules.high_level import KnowledgeBase, HLReasonerBase, GoalGraphBase
6
+ from mhagenta.modules.memory import MemoryBase
7
+
8
+
9
+ __all__ = ['ModuleBase', 'PerceptorBase', 'ActuatorBase', 'LLReasonerBase', 'LearnerBase', 'KnowledgeBase', 'HLReasonerBase',
10
+ 'GoalGraphBase', 'MemoryBase']
@@ -0,0 +1 @@
1
+ CONTAINER_VERSION = '1.0.0'
@@ -0,0 +1,10 @@
1
+ from pathlib import Path
2
+ from .CONTAINER_VERSION import CONTAINER_VERSION
3
+
4
+
5
+ RABBIT_IMG_PATH = str((Path(__file__).parent / 'mha-rabbitmq/').absolute())
6
+ BASE_IMG_PATH = str((Path(__file__).parent / 'mha-base/').absolute())
7
+ AGENT_IMG_PATH = str((Path(__file__).parent / 'mha-main/').absolute())
8
+
9
+
10
+ __all__ = ['RABBIT_IMG_PATH', 'BASE_IMG_PATH', 'AGENT_IMG_PATH', 'CONTAINER_VERSION']
@@ -0,0 +1,10 @@
1
+ ARG SRC_IMAGE="mha-rabbitmq"
2
+ ARG SRC_VERSION="latest"
3
+ FROM ${SRC_IMAGE}:${SRC_VERSION}
4
+ LABEL authors="Dmitry Gnatyshak"
5
+
6
+ COPY ./mhagenta/ /src/mhagenta/
7
+ COPY ./mhagenta/scripts/pyproject.toml /src/pyproject.toml
8
+ COPY ./mhagenta/scripts/README.md /src/README.md
9
+
10
+ RUN python -m pip install -e /src/
@@ -0,0 +1,14 @@
1
+ ARG SRC_IMAGE="mha-base"
2
+ ARG SRC_VERSION="latest"
3
+ FROM ${SRC_IMAGE}:${SRC_VERSION}
4
+ LABEL authors="Dmitry Gnatyshak"
5
+
6
+ COPY src /agent/
7
+ RUN mkdir -p /out/logs
8
+ RUN mkdir -p /out/save
9
+
10
+ RUN rabbitmq-server -detached
11
+ RUN sleep 5
12
+
13
+ ENTRYPOINT ["sh"]
14
+ CMD ["/agent/start.sh"]
@@ -0,0 +1,5 @@
1
+ FROM python:3.12
2
+ LABEL authors="Dmitry Gnatyshak"
3
+
4
+ COPY rabbitmq-install.sh /src/
5
+ RUN sh /src/rabbitmq-install.sh
@@ -0,0 +1,44 @@
1
+ #!/bin/sh
2
+
3
+ apt-get install curl gnupg apt-transport-https -y
4
+
5
+ ## Team RabbitMQ's main signing key
6
+ curl -1sLf "https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA" | gpg --dearmor | tee /usr/share/keyrings/com.rabbitmq.team.gpg > /dev/null
7
+ ## Community mirror of Cloudsmith: modern Erlang repository
8
+ curl -1sLf https://github.com/rabbitmq/signing-keys/releases/download/3.0/cloudsmith.rabbitmq-erlang.E495BB49CC4BBE5B.key | gpg --dearmor | tee /usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg > /dev/null
9
+ ## Community mirror of Cloudsmith: RabbitMQ repository
10
+ curl -1sLf https://github.com/rabbitmq/signing-keys/releases/download/3.0/cloudsmith.rabbitmq-server.9F4587F226208342.key | gpg --dearmor | tee /usr/share/keyrings/rabbitmq.9F4587F226208342.gpg > /dev/null
11
+
12
+ ## Add apt repositories maintained by Team RabbitMQ
13
+ tee /etc/apt/sources.list.d/rabbitmq.list <<EOF
14
+ ## Provides modern Erlang/OTP releases
15
+ ##
16
+ deb [signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa1.novemberain.com/rabbitmq/rabbitmq-erlang/deb/ubuntu jammy main
17
+ deb-src [signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa1.novemberain.com/rabbitmq/rabbitmq-erlang/deb/ubuntu jammy main
18
+
19
+ # another mirror for redundancy
20
+ deb [signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa2.novemberain.com/rabbitmq/rabbitmq-erlang/deb/ubuntu jammy main
21
+ deb-src [signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa2.novemberain.com/rabbitmq/rabbitmq-erlang/deb/ubuntu jammy main
22
+
23
+ ## Provides RabbitMQ
24
+ ##
25
+ deb [signed-by=/usr/share/keyrings/rabbitmq.9F4587F226208342.gpg] https://ppa1.novemberain.com/rabbitmq/rabbitmq-server/deb/ubuntu jammy main
26
+ deb-src [signed-by=/usr/share/keyrings/rabbitmq.9F4587F226208342.gpg] https://ppa1.novemberain.com/rabbitmq/rabbitmq-server/deb/ubuntu jammy main
27
+
28
+ # another mirror for redundancy
29
+ deb [signed-by=/usr/share/keyrings/rabbitmq.9F4587F226208342.gpg] https://ppa2.novemberain.com/rabbitmq/rabbitmq-server/deb/ubuntu jammy main
30
+ deb-src [signed-by=/usr/share/keyrings/rabbitmq.9F4587F226208342.gpg] https://ppa2.novemberain.com/rabbitmq/rabbitmq-server/deb/ubuntu jammy main
31
+ EOF
32
+
33
+ ## Update package indices
34
+ apt-get update -y
35
+
36
+ ## Install Erlang packages
37
+ apt-get install -y erlang-base \
38
+ erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets \
39
+ erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key \
40
+ erlang-runtime-tools erlang-snmp erlang-ssl \
41
+ erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl
42
+
43
+ ## Install rabbitmq-server and its dependencies
44
+ apt-get install rabbitmq-server -y --fix-missing
@@ -0,0 +1,6 @@
1
+ from .processes import MHAProcess
2
+ from .orchestrator import Orchestrator
3
+ from .connection import Connector, RabbitMQConnector
4
+
5
+
6
+ __all__ = ['Connector', 'RabbitMQConnector', 'MHAProcess', 'Orchestrator']
@@ -0,0 +1,27 @@
1
+ import os
2
+ import dill
3
+ import asyncio
4
+ from typing import Any
5
+
6
+ from mhagenta.core.processes import MHARoot
7
+ from mhagenta.modules import *
8
+ from mhagenta.states import *
9
+ from mhagenta.utils import ModuleTypes, Observation, ActionStatus
10
+ from mhagenta.core.processes import run_agent_module, MHAModule, ModuleBase, GlobalParams
11
+ from mhagenta.bases import *
12
+
13
+
14
+ async def main():
15
+ with open('/agent/agent_params', 'rb') as f:
16
+ params: dict[str, Any] = dill.load(f)
17
+
18
+ id_override = os.environ.get('AGENT_ID')
19
+ if id_override is not None and id_override != '':
20
+ params['agent_id'] = os.environ.get('AGENT_ID')
21
+ agent = MHARoot(**params)
22
+ await agent.initialize()
23
+ await agent.start()
24
+
25
+
26
+ if __name__ == '__main__':
27
+ asyncio.run(main())
@@ -0,0 +1,7 @@
1
+ from .connector import Connector
2
+ from .module_messenger import ModuleMessenger
3
+ from .root_messenger import RootMessenger
4
+ from .rabbitmq_connector import RabbitMQConnector
5
+
6
+
7
+ __all__ = ['Connector', 'ModuleMessenger', 'RootMessenger', 'RabbitMQConnector']
@@ -0,0 +1,96 @@
1
+ import logging
2
+ from abc import ABC, abstractmethod
3
+ from typing import Callable, Iterable
4
+
5
+ import dill
6
+
7
+ from mhagenta.utils.common import Message, MHABase, StatusReport, AgentCmd, ModuleTypes, AgentTime, LoggerExtras, \
8
+ DEFAULT_LOG_FORMAT
9
+ from mhagenta.utils.common.typing import MsgProcessorCallback
10
+
11
+
12
+ class Connector(MHABase, ABC):
13
+ def __init__(self,
14
+ agent_id: str,
15
+ sender_id: str,
16
+ agent_time: AgentTime,
17
+ log_tags: list[str] = '',
18
+ log_level: int | str = logging.DEBUG,
19
+ log_format: str = DEFAULT_LOG_FORMAT,
20
+ *args, **kwargs
21
+ ) -> None:
22
+ super().__init__(
23
+ agent_id=agent_id,
24
+ log_tags=log_tags,
25
+ log_level=log_level,
26
+ log_format=log_format
27
+ )
28
+ self._id = self.__class__.__name__
29
+ self._sender_id = sender_id
30
+ self._time = agent_time
31
+
32
+ @abstractmethod
33
+ async def start(self) -> None:
34
+ pass
35
+
36
+ @abstractmethod
37
+ async def stop(self) -> None:
38
+ pass
39
+
40
+ @abstractmethod
41
+ async def subscribe_to_in_channel(self, sender: str, channel: str, callback: MsgProcessorCallback, **kwargs) -> None:
42
+ pass
43
+
44
+ @abstractmethod
45
+ async def register_out_channel(self, recipient: str, channel: str, **kwargs) -> None:
46
+ pass
47
+
48
+ @abstractmethod
49
+ async def subscribe_to_cmds(self, callback: Callable[[AgentCmd], None], **kwargs) -> None:
50
+ pass
51
+
52
+ @abstractmethod
53
+ async def register_cmd_out_channel(self, **kwargs) -> None:
54
+ pass
55
+
56
+ @abstractmethod
57
+ async def subscribe_to_statuses(self, callback: Callable[[StatusReport], None], **kwargs) -> None:
58
+ pass
59
+
60
+ @abstractmethod
61
+ async def register_status_out_channel(self, **kwargs) -> None:
62
+ pass
63
+
64
+ @abstractmethod
65
+ def send(self, recipient: str, channel: str, msg: Message, **kwargs) -> None:
66
+ pass
67
+
68
+ @abstractmethod
69
+ def cmd(self,
70
+ cmd: AgentCmd,
71
+ module_types: str | Iterable[str] = ModuleTypes.ALL,
72
+ module_ids: str | Iterable[str] = ModuleTypes.ALL,
73
+ **kwargs) -> None:
74
+ pass
75
+
76
+ @abstractmethod
77
+ def status(self, status: StatusReport, **kwargs) -> None:
78
+ pass
79
+
80
+ @staticmethod
81
+ def encode_msg(msg: Message) -> bytes:
82
+ return dill.dumps(msg)
83
+
84
+ @staticmethod
85
+ def decode_msg(msg: bytes) -> Message:
86
+ return dill.loads(msg)
87
+
88
+ @property
89
+ def _logger_extras(self) -> LoggerExtras | None:
90
+ return LoggerExtras(
91
+ agent_time=self._time.agent,
92
+ mod_time=self._time.module,
93
+ exec_time=str(self._time.exec) if self._time.exec is not None else '-',
94
+ tags=self.log_tag_str
95
+ )
96
+
@@ -0,0 +1,93 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Iterable, Callable
4
+
5
+ from mhagenta.utils.common import MHABase, AgentTime, DEFAULT_LOG_FORMAT
6
+ from mhagenta.utils.common.typing import MsgProcessorCallback, Sender, Recipient, Channel
7
+ from mhagenta.core.connection.connector import Connector
8
+ from mhagenta.utils import Message, AgentCmd, StatusReport, LoggerExtras
9
+
10
+
11
+ class ModuleMessenger(MHABase):
12
+ def __init__(self,
13
+ connector_cls: type[Connector],
14
+ agent_id: str,
15
+ module_type: str,
16
+ module_id: str,
17
+ agent_time: AgentTime,
18
+ out_id_channels: Iterable[tuple[Recipient, Channel]],
19
+ in_id_channel_callbacks: Iterable[tuple[Sender, Channel, MsgProcessorCallback]],
20
+ agent_cmd_callback: Callable[[AgentCmd], None],
21
+ log_tags: list[str],
22
+ log_level: int | str = logging.DEBUG,
23
+ log_format: str = DEFAULT_LOG_FORMAT,
24
+ **kwargs
25
+ ) -> None:
26
+ super().__init__(
27
+ agent_id=agent_id,
28
+ log_tags=log_tags,
29
+ log_level=log_level,
30
+ log_format=log_format
31
+ )
32
+
33
+ self._time = agent_time
34
+ self._out_id_channels = out_id_channels
35
+ self._in_id_channel_callbacks = in_id_channel_callbacks
36
+ self._agent_cmd_callback = agent_cmd_callback
37
+
38
+ self._connector = connector_cls(
39
+ agent_id=agent_id,
40
+ sender_id=f'{module_type}.{module_id}',
41
+ agent_time=self._time,
42
+ log_tags=self._log_tags,
43
+ log_level=log_level,
44
+ log_format=log_format,
45
+ **kwargs
46
+ )
47
+
48
+ async def initialize(self) -> None:
49
+ await self._connector.initialize()
50
+
51
+ async with asyncio.TaskGroup() as tg:
52
+ tasks: list[asyncio.Task] = list()
53
+ for recipient, channel in self._out_id_channels:
54
+ tasks.append(tg.create_task(self._connector.register_out_channel(recipient=recipient, channel=channel)))
55
+ for sender, channel, callback in self._in_id_channel_callbacks:
56
+ tasks.append(tg.create_task(self._connector.subscribe_to_in_channel(sender=sender, channel=channel, callback=self._msg_callback_generator(callback))))
57
+ tasks.append(tg.create_task(self._connector.subscribe_to_cmds(self._cmd_callback_generator(self._agent_cmd_callback))))
58
+ tasks.append(tg.create_task(self._connector.register_status_out_channel()))
59
+
60
+ async def start(self) -> None:
61
+ await self._connector.start()
62
+
63
+ async def stop(self) -> None:
64
+ await self._connector.stop()
65
+
66
+ def send(self, recipient: str, channel: str, msg: Message) -> None:
67
+ self._connector.send(recipient, channel, msg)
68
+ self.debug(f'Sent {msg}')
69
+
70
+ def _msg_callback_generator(self, msg_callback: Callable[[Sender, Channel, Message], None]) -> Callable[[Sender, Channel, Message], None]:
71
+ def callback(sender: str, channel: str, msg: Message) -> None:
72
+ self.debug(f'Received {msg}')
73
+ msg_callback(sender, channel, msg)
74
+ return callback
75
+
76
+ def report_status(self, status: StatusReport) -> None:
77
+ self._connector.status(status)
78
+ self.debug(f'Reported {status}')
79
+
80
+ def _cmd_callback_generator(self, cmd_callback: Callable[[AgentCmd], None]) -> Callable[[AgentCmd], None]:
81
+ def callback(cmd: AgentCmd) -> None:
82
+ self.debug(f'Received {cmd}')
83
+ cmd_callback(cmd)
84
+ return callback
85
+
86
+ @property
87
+ def _logger_extras(self) -> LoggerExtras | None:
88
+ return LoggerExtras(
89
+ agent_time=self._time.agent,
90
+ mod_time=self._time.module,
91
+ exec_time=str(self._time.exec) if self._time.exec is not None else '-',
92
+ tags=self.log_tag_str
93
+ )