1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! The agent module contains the core agent abstraction for the Arbiter Engine.
use std::{fmt::Debug, sync::Arc};
use arbiter_core::middleware::ArbiterMiddleware;
use serde::{de::DeserializeOwned, Serialize};
use super::*;
use crate::{
machine::{Behavior, Engine, StateMachine},
messager::Messager,
};
/// An agent is an entity capable of processing events and producing actions.
/// These are the core actors in simulations or in onchain systems.
/// Agents can be connected of other agents either as a dependent, or a
/// dependency.
///
/// # How it works
/// When the [`World`] that owns the [`Agent`] is ran, it has each [`Agent`] run
/// each of its [`Behavior`]s `startup()` methods. The [`Behavior`]s themselves
/// will return a stream of events that then let the [`Behavior`] move into the
/// `State::Processing` stage.
#[derive(Debug)]
pub struct Agent {
/// Identifier for this agent.
/// Used for routing messages.
pub id: String,
/// The messager the agent uses to send and receive messages from other
/// agents.
pub messager: Messager,
/// The client the agent uses to interact with the blockchain.
pub client: Arc<ArbiterMiddleware>,
/// The engines/behaviors that the agent uses to sync, startup, and process
/// events.
pub(crate) behavior_engines: Vec<Box<dyn StateMachine>>,
}
impl Agent {
/// Creates a new [`AgentBuilder`] instance with a specified identifier.
///
/// This method initializes an [`AgentBuilder`] with the provided `id` and
/// sets the `behavior_engines` field to `None`. The returned
/// [`AgentBuilder`] can be further configured using its methods before
/// finalizing the creation of an [`Agent`].
///
/// # Arguments
///
/// * `id` - A string slice that holds the identifier for the agent being
/// built.
///
/// # Returns
///
/// Returns an [`AgentBuilder`] instance that can be used to configure and
/// build an [`Agent`].
pub fn builder(id: &str) -> AgentBuilder {
AgentBuilder {
id: id.to_owned(),
behavior_engines: None,
}
}
}
/// [`AgentBuilder`] represents the intermediate state of agent creation before
/// it is converted into a full on [`Agent`]
pub struct AgentBuilder {
/// Identifier for this agent.
/// Used for routing messages.
pub id: String,
/// The engines/behaviors that the agent uses to sync, startup, and process
/// events.
behavior_engines: Option<Vec<Box<dyn StateMachine>>>,
}
impl AgentBuilder {
/// Appends a behavior onto an [`AgentBuilder`]. Behaviors are initialized
/// when the agent builder is added to the [`crate::world::World`]
pub fn with_behavior<E: DeserializeOwned + Serialize + Send + Sync + Debug + 'static>(
mut self,
behavior: impl Behavior<E> + Serialize + DeserializeOwned + 'static,
) -> Self {
let engine = Engine::new(behavior);
if let Some(engines) = &mut self.behavior_engines {
engines.push(Box::new(engine));
} else {
self.behavior_engines = Some(vec![Box::new(engine)]);
};
self
}
/// Adds a state machine engine to the agent builder.
///
/// This method allows for the addition of a custom state machine engine to
/// the agent's behavior engines. If the agent builder already has some
/// engines, the new engine is appended to the list. If no engines are
/// present, a new list is created with the provided engine as its first
/// element.
///
/// # Parameters
///
/// - `engine`: The state machine engine to be added to the agent builder.
/// This engine must
/// implement the `StateMachine` trait and is expected to be provided as a
/// boxed trait object to allow for dynamic dispatch.
///
/// # Returns
///
/// Returns the `AgentBuilder` instance to allow for method chaining.
pub(crate) fn with_engine(mut self, engine: Box<dyn StateMachine>) -> Self {
if let Some(engines) = &mut self.behavior_engines {
engines.push(engine);
} else {
self.behavior_engines = Some(vec![engine]);
};
self
}
/// Constructs and returns a new [`Agent`] instance using the provided
/// `client` and `messager`.
///
/// This method finalizes the building process of an [`Agent`] by taking
/// ownership of the builder, and attempting to construct an `Agent`
/// with the accumulated configurations and the provided `client` and
/// `messager`. The `client` is an [`Arc<RevmMiddleware>`] that represents
/// the connection to the blockchain or environment, and `messager` is a
/// communication layer for the agent.
///
/// # Parameters
///
/// - `client`: A shared [`Arc<RevmMiddleware>`] instance that provides the
/// agent with access to the blockchain or environment.
/// - `messager`: A [`Messager`] instance for the agent to communicate with
/// other agents or systems.
///
/// # Returns
///
/// Returns a `Result` that, on success, contains the newly created
/// [`Agent`] instance. On failure, it returns an
/// [`AgentBuildError::MissingBehaviorEngines`] error indicating that the
/// agent was attempted to be built without any behavior engines
/// configured.
///
/// # Examples
///
/// ```ignore
/// let agent_builder = AgentBuilder::new("agent_id");
/// let client = Arc::new(RevmMiddleware::new(...));
/// let messager = Messager::new(...);
/// let agent = agent_builder.build(client, messager).expect("Failed to build agent");
/// ```
pub fn build(
self,
client: Arc<ArbiterMiddleware>,
messager: Messager,
) -> Result<Agent, ArbiterEngineError> {
match self.behavior_engines {
Some(engines) => Ok(Agent {
id: self.id,
messager,
client,
behavior_engines: engines,
}),
None => Err(ArbiterEngineError::AgentBuildError(
"Missing behavior engines".to_owned(),
)),
}
}
}