From 4c4ead3e02c93a21ee116be9c30b73109a28d213 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 09:49:51 +0200 Subject: [PATCH 01/12] run initialization in before starting the game task --- AIDojoCoordinator/coordinator.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/AIDojoCoordinator/coordinator.py b/AIDojoCoordinator/coordinator.py index 504059c3..9c9d5156 100644 --- a/AIDojoCoordinator/coordinator.py +++ b/AIDojoCoordinator/coordinator.py @@ -383,6 +383,9 @@ async def start_tasks(self): self.logger.info(f"Rewards set to:{self._rewards}") self._min_required_players = self.task_config.get_required_num_players() self.logger.info(f"Min player requirement set to:{self._min_required_players}") + # run self initialization + self._initialize() + # start server for agent communication self._spawn_task(self.start_tcp_server) @@ -804,6 +807,12 @@ async def step(self, agent_id:tuple, agent_state:GameState, action:Action): async def reset(self): return NotImplemented + def _initialize(self): + """ + Initialize the game state and other necessary components. This is called at the start of the game after the configuration is loaded. + """ + return NotImplemented + def goal_check(self, agent_addr:tuple)->bool: """ Check if the goal conditons were satisfied in a given game state From be5ea68296af8c0c20e68a203c796a5436307784 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 09:51:02 +0200 Subject: [PATCH 02/12] Initialization is called BEFORE start of the game --- AIDojoCoordinator/worlds/NSEGameCoordinator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/AIDojoCoordinator/worlds/NSEGameCoordinator.py b/AIDojoCoordinator/worlds/NSEGameCoordinator.py index d95aa40f..954ad0b5 100644 --- a/AIDojoCoordinator/worlds/NSEGameCoordinator.py +++ b/AIDojoCoordinator/worlds/NSEGameCoordinator.py @@ -889,8 +889,6 @@ def update_log_file(self, known_data:set, action, target_host:IP): self._data[hostaname].add(Data(owner="system", id="logfile", type="log", size=len(new_content) , content= new_content)) async def register_agent(self, agent_id, agent_role, agent_initial_view)->GameState: - if len(self._networks) == 0: - self._initialize() game_state = self._create_state_from_view(agent_initial_view) return game_state From 1aa6f2ae6c75a17d578624106161de94d42970c5 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 10:37:57 +0200 Subject: [PATCH 03/12] Experimental env with action mapping --- .../worlds/ExperimentalNSGCoordinator.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py diff --git a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py new file mode 100644 index 00000000..50e48615 --- /dev/null +++ b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py @@ -0,0 +1,105 @@ +import itertools +from AIDojoCoordinator.utils.utils import get_logging_level +from AIDojoCoordinator.game_components import GameState, Action, ActionType, Service,IP +from AIDojoCoordinator.worlds.NSEGameCoordinator import NSGCoordinator + + + + +class ExperimentalNSGCoordinator(NSGCoordinator): + + def __init__(self, game_host, game_port, task_config, allowed_roles=..., seed=42): + super().__init__(game_host, game_port, task_config, allowed_roles, seed) + self._action_mapping = None + + def _initialize(self): + # First do the parent initialization + super()._initialize() + # All components are initialized, now we can set the action mapping + self._create_action_mapping() + + def _create_action_mapping(self)-> dict: + """ + Create the action mapping for the game. + This method should be overridden in subclasses to provide specific action mappings. + """ + actions = {} + all_ips = [self._ip_mapping[ip] for ip in self._ip_to_hostname.values()] + all_networks = self._networks.keys() + all_data = set() + ip_with_services = {} + for ip in all_ips: + if ip in self._ip_to_hostname: + hostname = self._ip_to_hostname[ip] + if hostname in self._services: + ip_with_services[ip] = self._services[hostname] + + # Collect all data from all hosts + for data in self._data.values(): + all_data.update(data) + host_combinations = itertools.product(all_ips, all_ips) + + + # Network Scans + for source_host, target_network in itertools.product(all_ips, all_networks): + actions[len(actions)] = Action( + ActionType.ScanNetwork, + parameters={ + "source_host": source_host, + "target_network": target_network + } + ) + # Service Scans + for source_host, target_host in host_combinations: + actions[len(actions)] = Action( + ActionType.ScanServices, + parameters={ + "source_host": source_host, + "target_host": target_host + } + ) + # Service Exploits + for source_host, target_host in itertools.product(all_ips, ip_with_services.keys()): + for service in ip_with_services[target_host]: + actions[len(actions)] = Action( + ActionType.ExploitService, + parameters={ + "source_host": source_host, + "target_host": target_host, + "service": service + } + ) + # Data Scans + for source_host, target_host in host_combinations: + actions[len(actions)] = Action( + ActionType.FindData, + parameters={ + "source_host": source_host, + "target_host": target_host + } + ) + # Data transfers + for (source_host, target_host), datum in itertools.product(host_combinations, all_data): + actions[len(actions)] = Action( + ActionType.ExfiltrateData, + parameters={ + "source_host": source_host, + "target_host": target_host, + "data": datum + } + ) + + # Blocks + for (source_host, target_host), blocked_ip in itertools.product(host_combinations, all_ips): + actions[len(actions)] = Action( + ActionType.BlockHost, + parameters={ + "source_host": source_host, + "target_host": target_host, + "blocked_ip": blocked_ip + } + ) + self.logger.info(f"Created action mapping with {len(actions)} actions.") + for action_id, action in actions.items(): + self.logger.warning(f"Action {action_id}: {action.type} with parameters {action.parameters}") + self._action_mapping = actions \ No newline at end of file From 55687bf64773bba6db12ad0bb98bc1cd046cb10c Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 10:56:12 +0200 Subject: [PATCH 04/12] Add option to exclude blocks --- .../worlds/ExperimentalNSGCoordinator.py | 108 +++++++++++++++--- 1 file changed, 92 insertions(+), 16 deletions(-) diff --git a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py index 50e48615..03cfdd2d 100644 --- a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py +++ b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py @@ -1,4 +1,8 @@ import itertools +import argparse +import logging +import os +from pathlib import Path from AIDojoCoordinator.utils.utils import get_logging_level from AIDojoCoordinator.game_components import GameState, Action, ActionType, Service,IP from AIDojoCoordinator.worlds.NSEGameCoordinator import NSGCoordinator @@ -8,14 +12,16 @@ class ExperimentalNSGCoordinator(NSGCoordinator): - def __init__(self, game_host, game_port, task_config, allowed_roles=..., seed=42): + def __init__(self, game_host, game_port, task_config, allowed_roles=["Attacker", "Defender", "Benign"], seed=42, include_block_action=True): super().__init__(game_host, game_port, task_config, allowed_roles, seed) self._action_mapping = None + self._include_block_action = include_block_action def _initialize(self): # First do the parent initialization super()._initialize() # All components are initialized, now we can set the action mapping + self.logger.debug("Creating action mapping for the game.") self._create_action_mapping() def _create_action_mapping(self)-> dict: @@ -24,7 +30,8 @@ def _create_action_mapping(self)-> dict: This method should be overridden in subclasses to provide specific action mappings. """ actions = {} - all_ips = [self._ip_mapping[ip] for ip in self._ip_to_hostname.values()] + all_ips = [self._ip_mapping[ip] for ip in self._ip_to_hostname.keys()] + print(all_ips) all_networks = self._networks.keys() all_data = set() ip_with_services = {} @@ -52,7 +59,7 @@ def _create_action_mapping(self)-> dict: # Service Scans for source_host, target_host in host_combinations: actions[len(actions)] = Action( - ActionType.ScanServices, + ActionType.FindServices, parameters={ "source_host": source_host, "target_host": target_host @@ -70,7 +77,7 @@ def _create_action_mapping(self)-> dict: } ) # Data Scans - for source_host, target_host in host_combinations: + for source_host, target_host in itertools.product(all_ips, all_ips): actions[len(actions)] = Action( ActionType.FindData, parameters={ @@ -79,7 +86,7 @@ def _create_action_mapping(self)-> dict: } ) # Data transfers - for (source_host, target_host), datum in itertools.product(host_combinations, all_data): + for (source_host, target_host), datum in itertools.product(itertools.product(all_ips, all_ips), all_data): actions[len(actions)] = Action( ActionType.ExfiltrateData, parameters={ @@ -90,16 +97,85 @@ def _create_action_mapping(self)-> dict: ) # Blocks - for (source_host, target_host), blocked_ip in itertools.product(host_combinations, all_ips): - actions[len(actions)] = Action( - ActionType.BlockHost, - parameters={ - "source_host": source_host, - "target_host": target_host, - "blocked_ip": blocked_ip - } - ) + if self._include_block_action: + for (source_host, target_host), blocked_ip in itertools.product(itertools.product(all_ips, all_ips), all_ips): + actions[len(actions)] = Action( + ActionType.BlockIP, + parameters={ + "source_host": source_host, + "target_host": target_host, + "blocked_ip": blocked_ip + } + ) self.logger.info(f"Created action mapping with {len(actions)} actions.") for action_id, action in actions.items(): - self.logger.warning(f"Action {action_id}: {action.type} with parameters {action.parameters}") - self._action_mapping = actions \ No newline at end of file + self.logger.debug(f"Action {action_id}: {action.type} with parameters {action.parameters}") + self._action_mapping = actions + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="NetSecGame Coordinator Server Author: Ondrej Lukas ondrej.lukas@aic.fel.cvut.cz", + usage="%(prog)s [options]", + ) + + parser.add_argument( + "-l", + "--debug_level", + help="Define the debug level for the logs. DEBUG, INFO, WARNING, ERROR, CRITICAL", + action="store", + required=False, + type=str, + default="INFO", + ) + + parser.add_argument( + "-gh", + "--game_host", + help="host where to run the game server", + action="store", + required=False, + type=str, + default="127.0.0.1", + ) + + parser.add_argument( + "-gp", + "--game_port", + help="Port where to run the game server", + action="store", + required=False, + type=int, + default="9000", + ) + + parser.add_argument( + "-c", + "--task_config", + help="File with the task configuration", + action="store", + required=True, + type=str, + default="netsecenv_conf.yaml", + ) + + args = parser.parse_args() + print(args) + # Set the logging + log_filename = Path("logs/EXPERIMENTAL_NSG_coordinator.log") + if not log_filename.parent.exists(): + os.makedirs(log_filename.parent) + + # Convert the logging level in the args to the level to use + pass_level = get_logging_level(args.debug_level) + + logging.basicConfig( + filename=log_filename, + filemode="w", + format="%(asctime)s %(name)s %(levelname)s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + level=pass_level, + ) + + game_server = ExperimentalNSGCoordinator(args.game_host, args.game_port, args.task_config) + # Run it! + game_server.run() \ No newline at end of file From e16229ccfda1124456cef8c695a6d1c5ce54ac09 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 11:00:58 +0200 Subject: [PATCH 05/12] Cleanup of code --- AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py index 03cfdd2d..62774fa1 100644 --- a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py +++ b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py @@ -31,7 +31,6 @@ def _create_action_mapping(self)-> dict: """ actions = {} all_ips = [self._ip_mapping[ip] for ip in self._ip_to_hostname.keys()] - print(all_ips) all_networks = self._networks.keys() all_data = set() ip_with_services = {} @@ -44,8 +43,6 @@ def _create_action_mapping(self)-> dict: # Collect all data from all hosts for data in self._data.values(): all_data.update(data) - host_combinations = itertools.product(all_ips, all_ips) - # Network Scans for source_host, target_network in itertools.product(all_ips, all_networks): @@ -57,7 +54,7 @@ def _create_action_mapping(self)-> dict: } ) # Service Scans - for source_host, target_host in host_combinations: + for source_host, target_host in itertools.product(all_ips, all_ips): actions[len(actions)] = Action( ActionType.FindServices, parameters={ @@ -95,7 +92,6 @@ def _create_action_mapping(self)-> dict: "data": datum } ) - # Blocks if self._include_block_action: for (source_host, target_host), blocked_ip in itertools.product(itertools.product(all_ips, all_ips), all_ips): From 66fb803d0d3b2ef74f612f1082931406073b0916 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 14:40:03 +0200 Subject: [PATCH 06/12] Change into list --- .../worlds/ExperimentalNSGCoordinator.py | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py index 62774fa1..6cbba2b7 100644 --- a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py +++ b/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py @@ -2,17 +2,21 @@ import argparse import logging import os +import json from pathlib import Path from AIDojoCoordinator.utils.utils import get_logging_level -from AIDojoCoordinator.game_components import GameState, Action, ActionType, Service,IP +from AIDojoCoordinator.game_components import Action, ActionType from AIDojoCoordinator.worlds.NSEGameCoordinator import NSGCoordinator -class ExperimentalNSGCoordinator(NSGCoordinator): - - def __init__(self, game_host, game_port, task_config, allowed_roles=["Attacker", "Defender", "Benign"], seed=42, include_block_action=True): +class WhiteBoxNSGCoordinator(NSGCoordinator): + """ + WhiteBoxNSGCoordinator is an extension for the NetSecGame environment + that provides list of all possible actions to each agent that registers in the game. + """ + def __init__(self, game_host, game_port, task_config, allowed_roles=["Attacker", "Defender", "Benign"], seed=42, include_block_action=False): super().__init__(game_host, game_port, task_config, allowed_roles, seed) self._action_mapping = None self._include_block_action = include_block_action @@ -23,13 +27,16 @@ def _initialize(self): # All components are initialized, now we can set the action mapping self.logger.debug("Creating action mapping for the game.") self._create_action_mapping() + self._generate_all_actions = { + "all_actions": json.dumps([v.as_dict for v in self._action_mapping.values()]), + } + - def _create_action_mapping(self)-> dict: + def _generate_all_actions(self)-> list: """ - Create the action mapping for the game. - This method should be overridden in subclasses to provide specific action mappings. + Generate a list of all possible actions for the game. """ - actions = {} + actions = [] all_ips = [self._ip_mapping[ip] for ip in self._ip_to_hostname.keys()] all_networks = self._networks.keys() all_data = set() @@ -46,66 +53,67 @@ def _create_action_mapping(self)-> dict: # Network Scans for source_host, target_network in itertools.product(all_ips, all_networks): - actions[len(actions)] = Action( - ActionType.ScanNetwork, - parameters={ - "source_host": source_host, - "target_network": target_network - } - ) + actions.append(Action( + ActionType.ScanNetwork, + parameters={ + "source_host": source_host, + "target_network": target_network + } + )) + # Service Scans for source_host, target_host in itertools.product(all_ips, all_ips): - actions[len(actions)] = Action( + actions.append(Action( ActionType.FindServices, parameters={ "source_host": source_host, "target_host": target_host } - ) + )) # Service Exploits for source_host, target_host in itertools.product(all_ips, ip_with_services.keys()): for service in ip_with_services[target_host]: - actions[len(actions)] = Action( + actions.append(Action( ActionType.ExploitService, parameters={ "source_host": source_host, "target_host": target_host, "service": service } - ) + )) # Data Scans for source_host, target_host in itertools.product(all_ips, all_ips): - actions[len(actions)] = Action( + actions.append(Action( ActionType.FindData, parameters={ "source_host": source_host, "target_host": target_host } - ) + )) # Data transfers for (source_host, target_host), datum in itertools.product(itertools.product(all_ips, all_ips), all_data): - actions[len(actions)] = Action( + actions.append(Action( ActionType.ExfiltrateData, parameters={ "source_host": source_host, "target_host": target_host, "data": datum } - ) + )) # Blocks if self._include_block_action: for (source_host, target_host), blocked_ip in itertools.product(itertools.product(all_ips, all_ips), all_ips): - actions[len(actions)] = Action( + actions.append(Action( ActionType.BlockIP, parameters={ "source_host": source_host, "target_host": target_host, "blocked_ip": blocked_ip } - ) + )) self.logger.info(f"Created action mapping with {len(actions)} actions.") - for action_id, action in actions.items(): - self.logger.debug(f"Action {action_id}: {action.type} with parameters {action.parameters}") + for action in actions: + self.logger.debug(action) self._action_mapping = actions if __name__ == "__main__": @@ -172,6 +180,6 @@ def _create_action_mapping(self)-> dict: level=pass_level, ) - game_server = ExperimentalNSGCoordinator(args.game_host, args.game_port, args.task_config) + game_server = WhiteBoxNSGCoordinator(args.game_host, args.game_port, args.task_config) # Run it! game_server.run() \ No newline at end of file From 09ce4a0bb476dc3cae0f800f76945181f87a48ec Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 14:41:16 +0200 Subject: [PATCH 07/12] Add registration info if the subclass provides it --- AIDojoCoordinator/coordinator.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/AIDojoCoordinator/coordinator.py b/AIDojoCoordinator/coordinator.py index 9c9d5156..36fbcab9 100644 --- a/AIDojoCoordinator/coordinator.py +++ b/AIDojoCoordinator/coordinator.py @@ -255,6 +255,7 @@ async def _fetch_initialization_objects(self): self.logger.error(f"Error fetching initialization objects: {e}") # Temporary fix self.task_config = ConfigParser(self._task_config_file) + def _load_initialization_objects(self)->None: """ Loads task configuration from a local file. @@ -487,6 +488,9 @@ async def _process_join_game_action(self, agent_addr: tuple, action: Action)->No "configuration_hash": self._CONFIG_FILE_HASH }, } + if self._registration_info: + for key, value in self._registration_info.items(): + output_message_dict["message"][key] = value await self._agent_response_queues[agent_addr].put(self.convert_msg_dict_to_json(output_message_dict)) else: self.logger.info( @@ -753,6 +757,7 @@ def _initialize_new_player(self, agent_addr:tuple, agent_current_state:GameState self._agent_status[agent_addr] = AgentStatus.Playing self._agent_trajectories[agent_addr] = self._reset_trajectory(agent_addr) self.logger.info(f"\tAgent {agent_name} ({agent_addr}), registred as {agent_role}") + # create initial observation return Observation(self._agent_states[agent_addr], 0, False, {}) async def register_agent(self, agent_id:tuple, agent_role:str, agent_initial_view:dict)->GameState: From 65302b3760ec55ff1cb0091efe966c6c2cd838ec Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 14:42:58 +0200 Subject: [PATCH 08/12] rename file --- .../{ExperimentalNSGCoordinator.py => WhiteBoxNSGCoordinator.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AIDojoCoordinator/worlds/{ExperimentalNSGCoordinator.py => WhiteBoxNSGCoordinator.py} (100%) diff --git a/AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py b/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py similarity index 100% rename from AIDojoCoordinator/worlds/ExperimentalNSGCoordinator.py rename to AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py From 8350c2620e9d35583622ae28551f1f88e316976c Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 14:52:51 +0200 Subject: [PATCH 09/12] rename variables --- AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py b/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py index 6cbba2b7..7d46accb 100644 --- a/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py +++ b/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py @@ -18,7 +18,7 @@ class WhiteBoxNSGCoordinator(NSGCoordinator): """ def __init__(self, game_host, game_port, task_config, allowed_roles=["Attacker", "Defender", "Benign"], seed=42, include_block_action=False): super().__init__(game_host, game_port, task_config, allowed_roles, seed) - self._action_mapping = None + self._all_actions = None self._include_block_action = include_block_action def _initialize(self): @@ -26,9 +26,9 @@ def _initialize(self): super()._initialize() # All components are initialized, now we can set the action mapping self.logger.debug("Creating action mapping for the game.") - self._create_action_mapping() - self._generate_all_actions = { - "all_actions": json.dumps([v.as_dict for v in self._action_mapping.values()]), + self._generate_all_actions() + self._registration_info = { + "all_actions": json.dumps([v.as_dict for v in self._all_actions]), } @@ -114,7 +114,7 @@ def _generate_all_actions(self)-> list: self.logger.info(f"Created action mapping with {len(actions)} actions.") for action in actions: self.logger.debug(action) - self._action_mapping = actions + self._all_actions = actions if __name__ == "__main__": parser = argparse.ArgumentParser( @@ -165,7 +165,7 @@ def _generate_all_actions(self)-> list: args = parser.parse_args() print(args) # Set the logging - log_filename = Path("logs/EXPERIMENTAL_NSG_coordinator.log") + log_filename = Path("logs/WhiteBox_NSG_coordinator.log") if not log_filename.parent.exists(): os.makedirs(log_filename.parent) From bb4582614cab1d1951860e91c81b87176f969754 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 15:16:01 +0200 Subject: [PATCH 10/12] Fix parameter name --- AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py b/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py index 7d46accb..852c1134 100644 --- a/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py +++ b/AIDojoCoordinator/worlds/WhiteBoxNSGCoordinator.py @@ -78,7 +78,7 @@ def _generate_all_actions(self)-> list: parameters={ "source_host": source_host, "target_host": target_host, - "service": service + "target_service": service } )) # Data Scans From d96d7fa5ec5a5f07f19de46ff27d921eaa7a836f Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 16:51:11 +0200 Subject: [PATCH 11/12] add correct python version --- .github/workflows/python-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml index 7d478401..aec9a14e 100644 --- a/.github/workflows/python-checks.yml +++ b/.github/workflows/python-checks.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.12"] + python-version: ["3.12.10"] steps: - uses: actions/checkout@v3 From 57322401bfa8b8dddba2dbd79eca9b6315cf9728 Mon Sep 17 00:00:00 2001 From: Ondrej Lukas Date: Thu, 17 Jul 2025 17:00:29 +0200 Subject: [PATCH 12/12] add check for the presence of the attribute --- AIDojoCoordinator/coordinator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AIDojoCoordinator/coordinator.py b/AIDojoCoordinator/coordinator.py index 36fbcab9..96fbd3f4 100644 --- a/AIDojoCoordinator/coordinator.py +++ b/AIDojoCoordinator/coordinator.py @@ -488,7 +488,7 @@ async def _process_join_game_action(self, agent_addr: tuple, action: Action)->No "configuration_hash": self._CONFIG_FILE_HASH }, } - if self._registration_info: + if hasattr(self, "_registration_info"): for key, value in self._registration_info.items(): output_message_dict["message"][key] = value await self._agent_response_queues[agent_addr].put(self.convert_msg_dict_to_json(output_message_dict))