The Environment system is the core orchestration layer of Meta Agents Research Environments simulations. It manages the simulation lifecycle, event scheduling, time management, and coordination between apps and agents. This section documents the Environment API and configuration options.
fromare.simulation.environmentimportEnvironment,EnvironmentConfig# Create configurationconfig=EnvironmentConfig(start_time=0,# Start at time 0 (or use datetime.now().timestamp())duration=60,# Run for 60 secondstime_increment_in_seconds=1# 1-second time steps)# Create and start environmentenv=Environment(config=config)env.start()# Non-blocking, starts event loop in separate thread
importdatetimefromare.simulation.environmentimportEnvironmentConfigconfig=EnvironmentConfig(# Absolute start time - use datetime for specific timesstart_time=datetime.datetime(2024,1,1,9,0,0).timestamp(),# Duration in seconds (None = run until main thread exits)duration=3600,# 1 hour simulation# Time increment between tickstime_increment_in_seconds=0.5,)
env=Environment(config=config)# Start the simulation (non-blocking)env.start()# Simulation is now running in background# Main thread can continue with other worktime.sleep(10)# Pause simulationenv.pause()# Do other work while pausedtime.sleep(5)# Resume simulationenv.resume()# Keep main thread alive until simulation completestime.sleep(config.duration)
fromare.simulation.apps.email_clientimportEmailClientAppfromare.simulation.apps.calendarimportCalendarApp# Create appsemail_app=EmailClientApp()calendar_app=CalendarApp()# Register apps with environmentenv.register_apps([email_app,calendar_app])# Get registered app by nameemail_app=env.get_app("EmailClientApp")
# Get current simulation timecurrent_time=env.time_manager.time()# Check if simulation is runningifenv.is_running():print("Simulation is active")# Get simulation durationtotal_duration=env.config.duration# Get elapsed timeelapsed=env.time_manager.time_passed()
The Environment runs its event loop in a separate background thread, allowing the main thread to continue other work:
env=Environment(config)env.start()# Returns immediately, event loop runs in background# You can do other stuff while event loop is running e.g. do nothing for a few secondstime.sleep(2)# You can also pause the simulation here for 3 secondsenv.pause()time.sleep(3)# And then resume itenv.resume()
For practical examples, see the environment tutorial Tutorials.
This class encapsulates all the parameters required to configure the behavior of the Environment,
including time management, event loop behavior, and other settings.
Attributes:
- start_time (float or None): The start time of the environment in seconds. Defaults to 0 if None.
- duration (float or None): The duration of the environment in seconds. If None, the environment runs indefinitely.
- time_increment_in_seconds (int): The time increment between each tick in seconds. Must be >= 1.
- oracle_mode (bool): Indicates if the environment is in oracle mode. In oracle mode, OracleEvents are processed.
- dump_dir (str): Directory to dump environment states. Used only in oracle mode.
- exit_when_no_events (bool): Determines whether to exit the event loop when there are no more events to process.
If True, the event loop exits once all events are processed, even if the duration hasn’t been reached.
This should be set to True only when running without an agent, as it prevents the event loop from blocking indefinitely in env.join().
When running with an agent, it should typically be False to allow the agent to generate more events over time.
- queue_based_loop (bool): Controls whether the event loop is based on the queue or time:
Queue-based event loop: The event queue is polled for events to process, and time jumps to the next event time.
Time-based event loop: Time is incremented in fixed increments, and events are processed when they are ready.
- wait_for_user_input_timeout (float): Timeout for waiting for user input in seconds.
- verbose (bool): Indicates whether to print event loop ticks to the terminal.
The Environment class is responsible for managing the simulation environment.
It handles the event loop, time management, app registration, and event processing.
The environment can run with or without an agent, and its behavior can be configured
through the EnvironmentConfig class.
The event loop can operate in two modes:
1. Time-based: Time is incremented in fixed intervals, and events are processed when ready
2. Queue-based: The event queue is polled for events, and time jumps to the next event time
The event loop will exit when one of the following conditions is met:
- The duration is reached (if specified)
- The stop event is set (via the stop() method)
- There are no more events to process (if exit_when_no_events is True)
This last condition should only be enabled when running without an agent, as it prevents
the event loop from blocking indefinitely when there are no more events to process.
When running with an agent, exit_when_no_events should typically be False to allow
the agent to generate more events over time.
This method blocks until the event loop thread completes. The event loop
will complete when one of the following conditions is met:
1. The duration is reached (if specified)
2. The stop event is set (via the stop() method)
3. There are no more events to process (if exit_when_no_events is True)
When running without an agent, it’s important to set exit_when_no_events=True
in the EnvironmentConfig to ensure this method doesn’t block indefinitely.
When running with an agent, exit_when_no_events should typically be False to
allow the agent to generate more events over time.
Resolve arg placeholders in the event.
If an event has an arg that is a placeholder for a past event, we replace it with the return value of that event.
Registers apps to the environment, this function initializes the add event to an app.
It is also used to verify if a condition is possible to be met in the triggers
The Environment class is responsible for managing the simulation environment.
It handles the event loop, time management, app registration, and event processing.
The environment can run with or without an agent, and its behavior can be configured
through the EnvironmentConfig class.
The event loop can operate in two modes:
1. Time-based: Time is incremented in fixed intervals, and events are processed when ready
2. Queue-based: The event queue is polled for events, and time jumps to the next event time
The event loop will exit when one of the following conditions is met:
- The duration is reached (if specified)
- The stop event is set (via the stop() method)
- There are no more events to process (if exit_when_no_events is True)
This last condition should only be enabled when running without an agent, as it prevents
the event loop from blocking indefinitely when there are no more events to process.
When running with an agent, exit_when_no_events should typically be False to allow
the agent to generate more events over time.
This method blocks until the event loop thread completes. The event loop
will complete when one of the following conditions is met:
1. The duration is reached (if specified)
2. The stop event is set (via the stop() method)
3. There are no more events to process (if exit_when_no_events is True)
When running without an agent, it’s important to set exit_when_no_events=True
in the EnvironmentConfig to ensure this method doesn’t block indefinitely.
When running with an agent, exit_when_no_events should typically be False to
allow the agent to generate more events over time.
Resolve arg placeholders in the event.
If an event has an arg that is a placeholder for a past event, we replace it with the return value of that event.
Registers apps to the environment, this function initializes the add event to an app.
It is also used to verify if a condition is possible to be met in the triggers
This class encapsulates all the parameters required to configure the behavior of the Environment,
including time management, event loop behavior, and other settings.
Attributes:
- start_time (float or None): The start time of the environment in seconds. Defaults to 0 if None.
- duration (float or None): The duration of the environment in seconds. If None, the environment runs indefinitely.
- time_increment_in_seconds (int): The time increment between each tick in seconds. Must be >= 1.
- oracle_mode (bool): Indicates if the environment is in oracle mode. In oracle mode, OracleEvents are processed.
- dump_dir (str): Directory to dump environment states. Used only in oracle mode.
- exit_when_no_events (bool): Determines whether to exit the event loop when there are no more events to process.
If True, the event loop exits once all events are processed, even if the duration hasn’t been reached.
This should be set to True only when running without an agent, as it prevents the event loop from blocking indefinitely in env.join().
When running with an agent, it should typically be False to allow the agent to generate more events over time.
- queue_based_loop (bool): Controls whether the event loop is based on the queue or time:
Queue-based event loop: The event queue is polled for events to process, and time jumps to the next event time.
Time-based event loop: Time is incremented in fixed increments, and events are processed when they are ready.
- wait_for_user_input_timeout (float): Timeout for waiting for user input in seconds.
- verbose (bool): Indicates whether to print event loop ticks to the terminal.
This method blocks until the event loop thread completes. The event loop
will complete when one of the following conditions is met:
1. The duration is reached (if specified)
2. The stop event is set (via the stop() method)
3. There are no more events to process (if exit_when_no_events is True)
When running without an agent, it’s important to set exit_when_no_events=True
in the EnvironmentConfig to ensure this method doesn’t block indefinitely.
When running with an agent, exit_when_no_events should typically be False to
allow the agent to generate more events over time.
Registers apps to the environment, this function initializes the add event to an app.
It is also used to verify if a condition is possible to be met in the triggers
The EventLog is a crucial component of the Environment that stores all completed events during simulation execution. It maintains a chronological record of all events that have occurred, enabling monitoring, analysis, and debugging of simulation behavior.
The event log is automatically populated as events are processed by the environment. Each time an event completes (successfully or with errors), it’s added to the event log as a CompletedEvent instance.
The state of the environment.
- SETUP: the environment is being setup, this is the initial state of the environment
- RUNNING: the environment event loop is running and events are being registered and logged
- PAUSED: the environment is paused, and no events are being registered or logged, but can be restarted
- STOPPED: the environment is completely stopped
Hint associated with an event
- hint_type: Type of the hint, depends on the linked event
- content: Content of the hint
- associated_event_id: The id of the event that this hint is associated with
Type of the events, depends on who initiated them.
- AGENT: events initiated by the agent
- ENV: events initiated by the environment, or the scenario designer.
- USER: events initiated by the user or user proxy (unused for now).
- CONDITION: events that check a condition and trigger other events.
- VALIDATION: events that validate the state of the environment.
- STOP: events that stop the simulation.
Action associated with an event, this is a function that will be called when the event is executed.
function: Function to be called when the action is executed, it can be a class method, or a regular function
args: Dict mapping the argument names to values to call the function with at execution time
app: The actual App instance to use for the action execution
If not specified at creation time, it can be deducted from the function instance
e.g. if function=email_app.add_emails then we can deduct that app=email_app
action_id: The unique id of the action, this is used to identify the event in the logs.
This is created automatically and does NOT need to be handled by the user
tool_metadata: Optional metadata from the AppTool that this action is associated with
Metadata for a completed event, which includes details such as the return value, exception, etc.
This is particularly useful for completed events from App API calls, where:
- return_value: the return value of the API call.
- exception: any exception that occurred during the API call.
- exception_stack_trace: the stack trace of the exception, if any.
- completed: a flag indicating whether the execution was completed.
This function is used to add dependencies to the event.
If e1 depends on e2 and e3, then e1 will only be executed after e2 and e3 are executed.
If a delay is specified, then the event will be executed after the delay after the dependencies are executed.
This function is used to add successors to the event.
If e1 is followed by e2 and e3, then e2 and e3 will only be executed after
e1 is executed.
If a delay is specified, then the event will be executed after the delay after the dependencies are executed.
This function is used to check if the event is ready to be scheduled i.e. put into the event_queue.
An event is ready to be executed if all its dependencies are executed.
When an event has its event_time set, it means it is ready to be scheduled.
Executes the action corresponding to the events and returns the completed event with its metadata.
Here by default we make sure we only have ONE event, and that whatever happens inside the action is not registered as individual events.
This is to guarantee 2 things:
1/ Events are transactional, either the whole Event (and associated action) happened or not
2/ Not duplicating what is registered in the event log (otherwise we will register BOTH the current event, and every underlying one)
This function is used to add dependencies to the event.
If e1 depends on e2 and e3, then e1 will only be executed after e2 and e3 are executed.
If a delay is specified, then the event will be executed after the delay after the dependencies are executed.
Create a completed oracle event from a completed event and an oracle event.
The absolute event time is taken from the oracle event, and the event time comparator is taken from the oracle event if it exists.
Represents the result of a validation event.
- success: whether the validation was successful or not.
- message: a message describing the result of the validation.
- failed_milestones: the list of milestones that failed during the validation.
- triggered_minefields: the list of minefields that were triggered during the validation.
This function is used to add dependencies to the event.
If e1 depends on e2 and e3, then e1 will only be executed after e2 and e3 are executed.
If a delay is specified, then the event will be executed after the delay after the dependencies are executed.
This function is used to add dependencies to the event.
If e1 depends on e2 and e3, then e1 will only be executed after e2 and e3 are executed.
If a delay is specified, then the event will be executed after the delay after the dependencies are executed.
Capture mode makes sure a function call is not executed but only a “fictitious” CompletedEvent is returned.
This is useful for debugging and testing, as well as easily creating CompletedEvent instances for validation
This decorator is used to wrap API calls, so that we can register the event and add the appropriate CompletedEvent instance.
The CompletedEvent instance is only added to the Event Log if the App is already registered in the environment.
This decorator is also used to capture fictitious CompletedEvent instances when capture mode is active.
Capture mode allows to easily simulate and create CompletedEvent instances without actually executing the API call.
This is useful for debugging and testing, as well as defining validation trajectories.
This decorator is used to wrap API calls, so that we can register the event and add the appropriate CompletedEvent instance.
The CompletedEvent instance is only added to the Event Log if the App is already registered in the environment.
This decorator is also used to capture fictitious CompletedEvent instances when capture mode is active.
Capture mode allows to easily simulate and create CompletedEvent instances without actually executing the API call.
This is useful for debugging and testing, as well as defining validation trajectories.
are.simulation.types.disable_events()
Context manager to disable event firing, and just return the result of the function call as if decorator is not applied.
fromare.simulation.time_managerimportTimeManager# Create time managertime_manager=TimeManager()# Get current simulation timecurrent_time=time_manager.time()# Get time passed since startelapsed=time_manager.time_passed()# Pause simulation timetime_manager.pause()# Resume simulation timetime_manager.resume()# Reset time with custom start timeimporttimetime_manager.reset(time.time())# Add time offset (e.g., for time jumps)time_manager.add_offset(3600)# Jump forward 1 hour
Defines the verbosity levels for the notification system.
LOW: Only user messages and system notifications (due reminders, wait for notification timeouts) are notified.
MEDIUM: ENV events that are possible consequences of Agent actions are notified.
HIGH: Most ENV events are notified, even independent ENV events that are not caused by Agent actions.
Handle timeout notifications after all events in the current tick have been processed.
This ensures that timeout notifications are only triggered if no other notifications
were generated during the same tick.
Handle timeout notifications after all events in the current tick have been processed.
This ensures that timeout notifications are only triggered if no other notifications
were generated during the same tick.
Defines the verbosity levels for the notification system.
LOW: Only user messages and system notifications (due reminders, wait for notification timeouts) are notified.
MEDIUM: ENV events that are possible consequences of Agent actions are notified.
HIGH: Most ENV events are notified, even independent ENV events that are not caused by Agent actions.
fromare.simulation.notification_systemimport(BaseNotificationSystem,VerboseNotificationSystem,VerbosityLevel,NotificationSystemConfig)# Create a notification system with specific verbositynotification_system=VerboseNotificationSystem(verbosity_level=VerbosityLevel.MEDIUM)# Or create with custom configurationconfig=NotificationSystemConfig(notified_tools={"EmailClientApp":["send_email","receive_email"],"CalendarApp":["add_event","delete_event"]})base_notification_system=BaseNotificationSystem(config)# Initialize with time managerfromare.simulation.time_managerimportTimeManagertime_manager=TimeManager()notification_system.initialize(time_manager)# Handle eventsnotification_system.handle_event(some_event)# Get messages for current timecurrent_time=datetime.now(timezone.utc)messages=notification_system.message_queue.get_by_timestamp(current_time)