Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/salesforce/ai-economist/llms.txt

Use this file to discover all available pages before exploring further.

BaseComponent is the abstract base class for all component implementations in Foundation. Components add dynamics, action spaces, observations, and logging behavior to environments. Every component must subclass BaseComponent and implement its four abstract methods.

Class attributes

name
str
required
Unique string identifier for the component class. Must be non-empty. This name is used to register the component in the component registry.
name = "Gather"
component_type
str | None
Optional shorthand category label (e.g. "Trading", "Building"). Does not need to be unique. Used by BaseEnvironment.get_component and log finalization. Defaults to None.
component_type = "Trading"
agent_subclasses
list | tuple
required
The agent subclass names (strings) that this component applies to. Must be a non-empty list or tuple. If multiple subclasses are listed, none may be a subclass of another.
agent_subclasses = ["BasicMobileAgent"]
required_entities
list | tuple
required
Names of the non-agent game entities (resources or landmarks) that must be in play for this component to function. May be empty.
required_entities = ["Wood", "Stone"]

Constructor

BaseComponent(world, episode_length, inventory_scale=1)
world
World
required
The World instance for the environment this component is part of. Exposes world.maps, world.agents, and world.planner.
episode_length
int
required
Total number of timesteps per episode. Must be a positive integer.
inventory_scale
float
default:"1"
Scaling factor applied to inventory quantities when generating observations. Accessible via self.inv_scale. All component instances in an environment share the same value, set by the environment during construction.
During construction, the following attributes are set from the world argument:
  • self.n_agents — number of mobile agents
  • self.resources — list of resource names
  • self.landmarks — list of landmark names
  • self.timescale — number of env steps per component step (default 1)

Properties

world
World
The world object for the environment this component belongs to. Exposes world.maps, world.agents, and world.planner.
episode_length
int
Episode length of the environment this component is part of.
inv_scale
float
The inventory scaling value used when generating observations. Set by the environment at construction time.
shorthand
str
Returns component_type if defined, otherwise falls back to name.

Abstract methods

These four methods must be implemented in every concrete BaseComponent subclass.

get_n_actions

def get_n_actions(self, agent_cls_name: str) -> None | int | list
Returns the number of actions this component adds for agents of type agent_cls_name. Called by the environment during agent action-space construction.
agent_cls_name
str
required
Name of the agent class being queried, e.g. "BasicMobileAgent" or "BasicPlanner".
Return value:
action_space
None | int | list
  • None — this component adds no actions for this agent type.
  • int — a single action subspace with this many (non-NO-OP) actions.
  • list of (str, int) tuples — multiple named action subspaces: [("Tax_0", 10), ("Tax_1", 10), ...].
# Single action space: Move component adds 4 actions for mobile agents
def get_n_actions(self, agent_cls_name):
    if agent_cls_name == "BasicMobileAgent":
        return 4  # up, down, left, right
    return None

# Multiple action spaces: Planner sets per-agent tax levels
def get_n_actions(self, agent_cls_name):
    if agent_cls_name == "BasicPlanner":
        return [("Tax_{}".format(i), 10) for i in range(self.n_agents)]
    return None

get_additional_state_fields

def get_additional_state_fields(self, agent_cls_name: str) -> dict
Returns a dictionary of extra state fields and their reset values that this component manages for agents of type agent_cls_name. The returned fields are merged into agent.state and reset to their specified values when the environment resets.
agent_cls_name
str
required
Name of the agent class being queried.
extra_state_dict
dict
A {"state_field": reset_value} dictionary. Return an empty dict {} if no extra state is needed for this agent type.
def get_additional_state_fields(self, agent_cls_name):
    if agent_cls_name == "BasicMobileAgent":
        return {"build_payment": 0, "build_resources": 0}
    return {}

component_step

def component_step(self)
Executes the actions for all relevant agents. This is where the core logic of the component is implemented — reading each agent’s action buffer, modifying world/agent state, and updating any internal component state. This method is called by the environment each timestep. It should not return anything.
def component_step(self):
    world = self.world
    for agent in world.agents:
        action = agent.get_component_action(self.name)
        if action == 0:  # NO-OP
            continue
        # Apply the action to world/agent state
        ...

generate_observations

def generate_observations(self) -> dict
Produces observations from this component given the current world, agent, and component state.
obs
dict
A {agent.idx: agent_obs_dict} dictionary. Only agents that receive observations from this component need entries. The structure must remain consistent across calls within an episode.
def generate_observations(self):
    obs = {}
    world = self.world
    for agent in world.agents:
        obs[agent.idx] = {
            "inventory": np.array(list(agent.inventory.values())) / self.inv_scale,
        }
    return obs

Optional override methods

These methods have default implementations and can be overridden for custom behavior.

generate_masks

def generate_masks(self, completions: int = 0) -> dict
Creates action masks indicating which actions are valid (1) or invalid (0). NO-OP actions are always valid and should not be included in the mask. The default implementation returns all-ones masks (all actions enabled).
completions
int
default:"0"
Number of completed episodes. Intended for curriculum learning, where actions may be masked or unmasked over training.
masks
dict
A {agent.idx: mask} dictionary.
  • For a single action subspace: mask is a binary NumPy array of length n_actions.
  • For multiple action subspaces: mask is a {"action_set_name": binary_array} dict.
# Prevent agent 0 from moving left (action index 3)
def generate_masks(self, completions=0):
    masks = super().generate_masks(completions)
    masks[0] = np.array([1, 1, 0, 1])  # up, down, left, right
    return masks

additional_reset_steps

def additional_reset_steps(self)
Called by reset() after state fields have been re-initialized. Use this to reset any internal component state (e.g., counters, trackers). Should not return anything.
def additional_reset_steps(self):
    self._build_counts = {agent.idx: 0 for agent in self.world.agents}

get_metrics

def get_metrics(self) -> dict | None
Returns a dictionary of scalar metrics describing the episode through the lens of this component. The environment merges these with scenario metrics into a single metrics report.
metrics
dict | None
A {"metric_key": scalar_value} dictionary, or None to exclude this component from the metrics report.
def get_metrics(self):
    return {
        "total_builds": sum(self._build_counts.values()),
        "avg_builds_per_agent": np.mean(list(self._build_counts.values())),
    }

get_dense_log

def get_dense_log(self) -> tuple | list | dict | None
Returns a dense log of episode data from this component’s perspective. Return None (default) if this component does not produce a dense log.

Reset lifecycle

The reset() method (not intended for override) coordinates component reset:
  1. For each agent (mobile agents + planner), calls get_additional_state_fields(agent.name) and merges the result into agent.state.
  2. Calls additional_reset_steps() for component-internal cleanup.

Registration

Components are registered using the component_registry decorator defined at the bottom of base_component.py:
from ai_economist.foundation.base.base_component import BaseComponent, component_registry

@component_registry.add
class MyComponent(BaseComponent):
    name = "MyComponent"
    component_type = "Custom"
    agent_subclasses = ["BasicMobileAgent"]
    required_entities = []

    def get_n_actions(self, agent_cls_name):
        if agent_cls_name == "BasicMobileAgent":
            return 3
        return None

    def get_additional_state_fields(self, agent_cls_name):
        return {}

    def component_step(self):
        pass

    def generate_observations(self):
        return {}
A component registered with @component_registry.add is only visible in foundation.components if the file defining it is imported in ai_economist/foundation/components/__init__.py.
The registry is also accessible as foundation.components from the top-level package:
import ai_economist.foundation as foundation

MyComponentClass = foundation.components.get("MyComponent")

Build docs developers (and LLMs) love