Add custom how-to guides to documentation, additional notebook around how to use dev cli

This commit is contained in:
Charlie Crane
2025-02-26 11:02:00 +00:00
parent 5a7caa33f0
commit 4268e387c4
6 changed files with 696 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
.. only:: comment
© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK
.. _custom_action:
Creating Custom Actions in PrimAITE
***********************************
PrimAITE contains a selection of available actions that can be exercised within a training session, listed within ``actions.py``. `Note`: Agents are only able to perform the actions listed within it's action_map, defined within it's configuration YAML. See :ref:`custom_environment` for more information.
Developing Custom Actions
============================
Actions within PrimAITE follow a default format, as seen below and in ``actions.py``. It's important that they have an identifier when declared, as this is used when creating the training environment.
.. code:: Python
class ExampleActionClass(AbstractAction, identifier="ExampleActions"):
"""Example Action Class"""
def __init__(self, manager: "ActionManager", num_nodes: int, num_folders: int, **kwargs) -> None:
super().__init(manager, num_nodes=num_nodes, num_folders=num_folders, **kwargs)
self.verb: str = "ExampleAction"
Integration with PrimAITE ActionManager
==========================================
Any custom actions should then be added to the `ActionManager` class, and the `act_class_identifiers` dictionary. This will map the action class to the corresponding action type string that would be passed through the PrimAITE `request_system`.
Interaction with the PrimAITE Request Manager
================================================
Where an action would cause a request to be sent through the PrimAITE RequestManager, a `form_request` method is expected to be defined within the Action Class. This should format the action into a format that can be ingested by the `RequestManager`. Examples of this include the `NodeFolderCreateAction`, which sends a formed request to create a folder on a given node (seen below).
.. code:: Python
def form_request(self, node_id: int, folder_name: str) -> RequestFormat:
"""Return the action formatted as a request which can be ingested by the PrimAITE simulation."""
node_name = self.manager.get_node_name_by_idx(node_id)
if node_name is None or folder_name is None:
return ["do_nothing"]
return ["network", "node", node_name, "file_system", "create", "folder", folder_name]
Action Masking
==============
Agents which use the `ProxyAgent` class within PrimAITE are able to use Action Masking. This allows the agent to know if the actions are valid/invalid based on the current environment.
Information on how to ensure this can be applied to your custom action can be found in :ref:`action_masking`

View File

@@ -0,0 +1,26 @@
.. only:: comment
© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK
.. _custom_environment:
Creating Custom Environments for PrimAITE
*****************************************
PrimAITE generates it's training configuration/Environments through ingestion of YAML files. A detailed walkthrough of how to create your own environment can be found within the ``Creating-Custom-Environments`` jupyter notebook.
You configuration file should follow the hierarchy seen below:
.. code:: yaml
io_settings:
...
game:
...
agents:
...
simulation:
...
For detailed information about each configuration item found within the configuration file, see :ref:`Configurable Items`.

View File

@@ -0,0 +1,46 @@
.. only:: comment
© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK
.. _custom_rewards:
Creating Custom Rewards in PrimAITE
***********************************
Rewards within PrimAITE are contained within ``rewards.py``, which details the rewards available for all agents within training sessions, how they are calculated and any other specific information where necessary.
Custom Rewards within PrimAITE should inherit from the ``AbstractReward`` class, found in ``rewards.py``. It's important to include an identifier for any class created within PrimAITE.
.. code:: Python
class ExampleAward(AbstractReward, identifier="ExampleAward"):
"""Example Reward Class """
def calculate(self, state: Dict, last_action_response: "AgentHistoryItem") -> float:
"""Calculate the reward for the current state."""
return 1.0
@classmethod
def from_config(cls, config: dict) -> "AbstractReward":
"""Create a reward function component from a config dictionary."""
return cls()
Custom rewards that have been created should be added to the ``rew_class_identifiers`` dictionary within the ``RewardFunction`` class in ``rewards.py``.
Including Custom Rewards within PrimAITE configuration
======================================================
Custom rewards can then be included within an agents configuration by it's inclusion within the training session configuration YAML.
.. code:: yaml
agents:
- ref: agent_name
reward_function:
reward_components:
- type: DUMMY
weight: 1.0
More detailed information about rewards within PrimAITE can be found within :ref:`Rewards`

View File

@@ -0,0 +1,78 @@
.. only:: comment
© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK
Creating Custom Software for PrimAITE
*************************************
This page aims to provide a how-to guide on how to create your own custom software for use within PrimAITE.
PrimAITE has a base software class which should be inherited from when building custom software. Examples of this can be seen in the ``IOSoftware`` and ``Process`` classes.
It's important that any new software created within PrimAITE has the ``identifier`` attribute defined, for use when generating the environment.
Some default attributes may need to be adjusted to align with the intended application of the custom software.
.. code:: Python
from src.primaite.simulator.system.software import Software
class CustomSoftware(Software, identifier="CustomSoftware"):
"""
An example of Custom Software within PrimAITE.
"""
operating_state: OperatingState
"The current operating state of the Custom software"
def describe_state(self) -> Dict:
"""
Produce a dictionary describing the current state of this object.
:return: Current state of this object and child objects.
:rtype: Dict
"""
state = super().describe_state()
state.update({"operating_state": self.operating_state.value})
Default Install
###############
Software can be set to auto-install onto a Node by adding it to the ``SYSTEM_SOFTWARE`` dictionary for the node. An example can be seen in the ``HostNode`` class, which pre-installs some key software that is expected on Nodes, such as the ``NTPClient`` and ``UserManager``.
Requirements
############
Any custom software will need to provide an implementation of the ``describe_state`` method, and conform to the general Pydantic requirements.
It's a good idea, if possible, to also create a ``.show()`` method, as this can be used for visualising the software's status when developing within PrimAITE.
Interaction with the PrimAITE Request System
############################################
If the software is intended to be used by an agent via a :ref:`custom_action`, then it will likely need an implementation of the ``RequestManager``.
Detailed information about the PrimAITE request system can be seen in :ref:`request_system`. An example implementation, derived from the `Application` class is seen below:
.. code:: Python
def _init_request_manager(self) -> RequestManager:
"""
Initialise the request manager.
More information in user guide and docstring for SimComponent._init_request_manager.
"""
_is_application_running = Application._StateValidator(application=self, state=ApplicationOperatingState.RUNNING)
rm = super()._init_request_manager()
rm.add_request(
"scan",
RequestType(
func=lambda request, context: RequestResponse.from_bool(self.scan()), validator=_is_application_running
),
)
return rm
Further information
###################
For more detailed information about the implementation of software within PrimAITE, see :ref:`software`.

View File

@@ -0,0 +1,8 @@
.. only:: comment
© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK
Utilising the PrimAITE dev-mode cli
***********************************
A guide for utilising `primaite dev-mode` can be found within the ``How-To-Use-Primaite-Dev-Mode`` jupyter notebook, and also :ref:`Developer Tools`.

View File

@@ -0,0 +1,488 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# PrimAITE Developer mode\n",
"\n",
"PrimAITE has built in developer tools.\n",
"\n",
"The dev-mode is designed to help make the development of PrimAITE easier.\n",
"\n",
"`NOTE: For the purposes of the notebook, the commands are preceeded by \"!\". When running the commands, run it without the \"!\".`\n",
"\n",
"To display the available dev-mode options, run the command below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite setup"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode --help"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Save the current PRIMAITE_CONFIG to restore after the notebook runs\n",
"\n",
"from primaite import PRIMAITE_CONFIG\n",
"\n",
"temp_config = PRIMAITE_CONFIG.copy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dev mode options"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### enable\n",
"\n",
"Enables the dev mode for PrimAITE.\n",
"\n",
"This will enable the developer mode for PrimAITE.\n",
"\n",
"By default, when developer mode is enabled, session logs will be generated in the PRIMAITE_ROOT/sessions folder unless configured to be generated in another location."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode enable"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### disable\n",
"\n",
"Disables the dev mode for PrimAITE.\n",
"\n",
"This will disable the developer mode for PrimAITE."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode disable"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### show\n",
"\n",
"Shows if PrimAITE is running in dev mode or production mode.\n",
"\n",
"The command will also show the developer mode configuration."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode show"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### config\n",
"\n",
"Configure the PrimAITE developer mode"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --help"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### path\n",
"\n",
"Set the path where generated session files will be output.\n",
"\n",
"By default, this value will be in PRIMAITE_ROOT/sessions.\n",
"\n",
"To reset the path to default, run:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config path -root\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config path --default"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --sys-log-level or -slevel\n",
"\n",
"Set the system log level.\n",
"\n",
"This will override the system log level in configurations and will make PrimAITE include the set log level and above.\n",
"\n",
"Available options are:\n",
"- `DEBUG`\n",
"- `INFO`\n",
"- `WARNING`\n",
"- `ERROR`\n",
"- `CRITICAL`\n",
"\n",
"Default value is `DEBUG`\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --sys-log-level DEBUG\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -slevel DEBUG"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --agent-log-level or -alevel\n",
"\n",
"Set the agent log level.\n",
"\n",
"This will override the agent log level in configurations and will make PrimAITE include the set log level and above.\n",
"\n",
"Available options are:\n",
"- `DEBUG`\n",
"- `INFO`\n",
"- `WARNING`\n",
"- `ERROR`\n",
"- `CRITICAL`\n",
"\n",
"Default value is `DEBUG`\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --agent-log-level DEBUG\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -alevel DEBUG"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --output-sys-logs or -sys\n",
"\n",
"If enabled, developer mode will output system logs.\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --output-sys-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -sys"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To disable outputting sys logs:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --no-sys-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -nsys"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --output-agent-logs or -agent\n",
"\n",
"If enabled, developer mode will output agent action logs.\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --output-agent-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -agent"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To disable outputting agent action logs:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --no-agent-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -nagent"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --output-pcap-logs or -pcap\n",
"\n",
"If enabled, developer mode will output PCAP logs.\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --output-pcap-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -pcap"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To disable outputting PCAP logs:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --no-pcap-logs\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -npcap"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### --output-to-terminal or -t\n",
"\n",
"If enabled, developer mode will output logs to the terminal.\n",
"\n",
"Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --output-to-terminal\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -t"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To disable terminal outputs:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config --no-terminal\n",
"\n",
"# or\n",
"\n",
"!primaite dev-mode config -nt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Combining commands\n",
"\n",
"It is possible to combine commands to set the configuration.\n",
"\n",
"This saves having to enter multiple commands and allows for a much more efficient setting of PrimAITE developer mode configurations."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Example of setting system log level and enabling the system logging:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config -slevel WARNING -sys"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another example where the system log and agent action log levels are set and enabled and should be printed to terminal:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!primaite dev-mode config -slevel ERROR -sys -alevel ERROR -agent -t"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Restore PRIMAITE_CONFIG\n",
"from primaite.utils.cli.primaite_config_utils import update_primaite_application_config\n",
"\n",
"\n",
"global PRIMAITE_CONFIG\n",
"PRIMAITE_CONFIG[\"developer_mode\"] = temp_config[\"developer_mode\"]\n",
"update_primaite_application_config(config=PRIMAITE_CONFIG)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
}
},
"nbformat": 4,
"nbformat_minor": 2
}