Object.Mailbox (object v0.1.2)
Object-Oriented Reinforcement Learning (OORL) Mailbox implementation based on AAOS Section 4 - Object Interactions.
Implements a comprehensive message-passing protocol with interaction dyads, priority-based message processing, and communication mechanisms for autonomous objects. The mailbox provides reliable, efficient communication between objects in the AAOS system.
Core Features
- Message Passing: Reliable message delivery with acknowledgments
- Interaction Dyads: Bidirectional communication relationships
- Priority Processing: Priority-based message ordering
- Protocol Handlers: Extensible message type handling
- Message History: Bounded history for debugging and analysis
- Routing Tables: Efficient message routing information
- Delivery Confirmations: Acknowledgment tracking system
Message Types
The mailbox supports various message types for different communication patterns:
:state_update
- Object state change notifications:goal_update
- Goal function modifications:belief_update
- World model belief updates:learning_signal
- Learning data and feedback:coordination
- Multi-object coordination protocols:negotiation
- Multi-step negotiation processes:acknowledgment
- Delivery confirmations:heartbeat
- Connection health monitoring
Interaction Dyads
Dyads are bidirectional relationships between objects that enable:
- Enhanced communication efficiency
- Social learning and knowledge transfer
- Coordinated behavior and cooperation
- Trust and reputation building
Performance Characteristics
- Message Throughput: ~1000 messages/second per mailbox
- Latency: <1ms for local message processing
- Memory Usage: ~100 bytes per message + content size
- History Retention: Configurable bounded queue (default: 1000 messages)
- Scalability: Efficient O(1) message insertion and O(log n) priority sorting
Error Handling
- Message validation prevents malformed messages
- Acknowledgment system ensures reliable delivery
- Dead letter handling for undeliverable messages
- Circuit breaker protection against message storms
- Graceful degradation under resource pressure
Example Usage
# Create a new mailbox
mailbox = Object.Mailbox.new("agent_1")
# Send a coordination message
updated_mailbox = Object.Mailbox.send_message(
mailbox, "agent_2", :coordination,
%{action: :form_coalition}, [priority: :high]
)
# Form an interaction dyad
dyad_mailbox = Object.Mailbox.form_dyad(mailbox, "partner_agent", 0.8)
# Process incoming messages
{processed, final_mailbox} = Object.Mailbox.process_inbox(dyad_mailbox)
Summary
Types
Interaction dyad representing a bidirectional relationship between two objects.
Message structure for inter-object communication.
Message type for routing and protocol handling
Message processing priority level
Mailbox structure for an object's communication system.
Functions
Dissolves an interaction dyad.
Forms an interaction dyad between two objects.
Gets all active interaction dyads.
Gets comprehensive mailbox statistics.
Creates a new mailbox for an object.
Processes messages in the inbox based on type and priority.
Receives a message into the inbox.
Registers a protocol handler for specific message types.
Sends a message to another object.
Updates routing table for message delivery.
Types
@type interaction_dyad() :: %{ participants: {String.t(), String.t()}, formation_time: DateTime.t(), interaction_count: non_neg_integer(), compatibility_score: float(), utility_score: float(), active: boolean() }
Interaction dyad representing a bidirectional relationship between two objects.
Fields
participants
- Tuple of the two object IDs in the dyadformation_time
- When the dyad was first establishedinteraction_count
- Number of interactions through this dyadcompatibility_score
- Initial compatibility assessment (0.0-1.0)utility_score
- Calculated utility based on interaction successactive
- Whether the dyad is currently active
Utility Calculation
Utility score is computed as:
utility = min(interaction_count / 100.0, 1.0) * compatibility_score
This rewards both frequent interaction and initial compatibility.
@type message() :: %{ id: String.t(), from: String.t(), to: String.t(), type: message_type(), content: any(), timestamp: DateTime.t(), priority: priority_level(), requires_ack: boolean(), ttl: pos_integer() }
Message structure for inter-object communication.
Fields
id
- Unique message identifier for tracking and deduplicationfrom
- Sender object IDto
- Recipient object IDtype
- Message type atom for routing and handlingcontent
- Message payload (any serializable term)timestamp
- Message creation timestamppriority
- Processing priority levelrequires_ack
- Whether delivery confirmation is requiredttl
- Time-to-live in seconds before message expires
@type message_type() :: :state_update | :goal_update | :belief_update | :learning_signal | :coordination | :negotiation | :acknowledgment | :heartbeat | atom()
Message type for routing and protocol handling
@type priority_level() :: :low | :medium | :high | :critical
Message processing priority level
@type t() :: %Object.Mailbox{ created_at: DateTime.t(), delivery_confirmations: %{required(String.t()) => DateTime.t()}, history_size_limit: pos_integer(), inbox: [message()], interaction_dyads: %{required(String.t()) => interaction_dyad()}, message_history: :queue.queue(message()), object_id: String.t(), outbox: [message()], protocol_handlers: %{required(message_type()) => function()}, routing_table: %{required(String.t()) => any()}, updated_at: DateTime.t() }
Mailbox structure for an object's communication system.
Fields
object_id
- ID of the object this mailbox belongs toinbox
- List of incoming messages awaiting processingoutbox
- List of outgoing messages awaiting deliveryinteraction_dyads
- Map of dyad_id to interaction_dyad structuresmessage_history
- Bounded queue of message history for debuggingrouting_table
- Routing information for efficient message deliveryprotocol_handlers
- Map of message_type to handler functionsdelivery_confirmations
- Map tracking message delivery confirmationscreated_at
- Mailbox creation timestampupdated_at
- Last modification timestamphistory_size_limit
- Maximum number of messages to retain in history
Functions
Dissolves an interaction dyad.
Marks an existing interaction dyad as inactive, effectively ending the enhanced communication relationship while preserving historical interaction data for analysis.
Parameters
mailbox
- Mailbox struct containing the dyadother_object_id
- ID of the other object in the dyad to dissolve
Returns
Updated mailbox with dyad marked as inactive (not removed).
Dissolution vs Removal
Dyads are marked inactive rather than deleted to:
- Preserve interaction history for analysis
- Enable potential reactivation in the future
- Maintain social learning data
- Support reputation and trust calculations
Examples
# Dissolve existing dyad
iex> dissolved = Object.Mailbox.dissolve_dyad(mailbox, "partner_agent")
iex> dyad = dissolved.interaction_dyads["agent_1-partner_agent"]
iex> dyad.active
false
# Dyad data preserved
iex> dyad.interaction_count
25
iex> dyad.formation_time
~D[2024-01-15 10:30:00]
Common Dissolution Triggers
- Poor Performance: Low utility scores over time
- Compatibility Issues: Repeated coordination failures
- Resource Constraints: Too many active dyads
- Task Completion: Project-specific partnerships ending
- Manual Override: Explicit dissolution requests
Effects of Dissolution
Once dissolved, the dyad:
- No longer provides priority message routing
- Stops contributing to social learning
- Removes coordination benefits
- Preserves historical interaction data
- Can be reactivated if needed
Reactivation
Inactive dyads can be reactivated by:
- Calling
form_dyad/3
again with the same partner - Automatic reactivation on successful interactions
- Manual reactivation through administrative tools
Performance
- Dissolution time: ~0.05ms
- Memory preserved: Historical data retained
- No immediate cleanup: Background garbage collection
Forms an interaction dyad between two objects.
Creates a bidirectional interaction relationship between the mailbox owner and another object, enabling enhanced communication, coordination, and social learning capabilities.
Parameters
mailbox
- Mailbox struct to add dyad toother_object_id
- ID of the other object in the dyadcompatibility_score
- Initial compatibility assessment (0.0-1.0, default: 0.5)
Returns
Updated mailbox with new dyad added to interaction_dyads map.
Dyad Structure
Created dyads include:
- Participants: Tuple of both object IDs
- Formation Time: Timestamp of dyad creation
- Interaction Count: Initial count of 0
- Compatibility Score: Initial assessment
- Utility Score: Calculated benefit metric (starts at 0.0)
- Active Status: Set to true for new dyads
Compatibility Guidelines
0.0-0.3
- Low compatibility, limited benefits0.3-0.7
- Moderate compatibility, task-specific benefits0.7-1.0
- High compatibility, excellent collaboration
Examples
# Form high-compatibility dyad
iex> updated = Object.Mailbox.form_dyad(mailbox, "partner_agent", 0.8)
iex> dyads = Object.Mailbox.get_active_dyads(updated)
iex> map_size(dyads)
1
# Default compatibility
iex> mailbox = Object.Mailbox.form_dyad(mailbox, "sensor_1")
iex> dyad = mailbox.interaction_dyads["agent_1-sensor_1"]
iex> dyad.compatibility_score
0.5
Dyad Benefits
Once formed, dyads provide:
Enhanced Communication
- Priority message routing between dyad partners
- Reduced message latency and overhead
- Dedicated communication channels
Social Learning
- Shared experience and knowledge transfer
- Collaborative problem solving
- Behavioral imitation and adaptation
Coordination
- Simplified cooperation protocols
- Joint action planning and execution
- Resource sharing and allocation
Trust Building
- Reputation tracking and assessment
- Reliability and performance monitoring
- Long-term relationship maintenance
Dyad Evolution
Dyads evolve over time through:
- Interaction: Message exchanges update interaction count
- Utility Calculation: Performance-based utility scoring
- Compatibility Adjustment: Adaptation based on outcomes
- Activity Management: Automatic activation/deactivation
Performance
- Formation time: ~0.1ms
- Memory overhead: ~200 bytes per dyad
- Lookup performance: O(1) by dyad ID
- Maximum recommended dyads: 50 per object
@spec get_active_dyads(t()) :: %{required(String.t()) => interaction_dyad()}
Gets all active interaction dyads.
Retrieves all currently active interaction dyads from the mailbox, filtering out inactive dyads and returning only those that provide active communication and coordination benefits.
Parameters
mailbox
- Mailbox struct to query
Returns
Map of dyad_id → interaction_dyad for all active dyads.
Examples
# Get active dyads
iex> active_dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> map_size(active_dyads)
3
# Check specific dyad activity
iex> dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> Map.has_key?(dyads, "agent_1-partner_2")
true
# Examine dyad details
iex> dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> dyad = dyads["agent_1-coordinator_1"]
iex> dyad.utility_score
0.75
iex> dyad.interaction_count
42
Use Cases
Active dyad information is useful for:
Communication Optimization
- Priority routing decisions
- Load balancing across partners
- Connection health monitoring
Social Learning
- Partner selection for knowledge transfer
- Imitation target identification
- Collaborative learning opportunities
Coordination
- Coalition formation decisions
- Task assignment optimization
- Resource sharing partnerships
Performance Analysis
- Dyad effectiveness measurement
- Social network analysis
- Interaction pattern identification
Dyad Activity
Dyads are considered active when:
active
field istrue
- Recent interaction activity (implementation dependent)
- Both participants are still available
- Utility score above minimum threshold
Performance
- Query time: O(n) where n is total dyad count
- Typical response: ~0.1ms for <50 dyads
- Memory usage: New map creation, ~100 bytes overhead
- No side effects: Read-only operation
Filtering Criteria
Only dyads matching these criteria are returned:
dyad.active == true
- Dyad record is not nil
- Both participants are valid object IDs
@spec get_stats(t()) :: %{ total_messages_sent: non_neg_integer(), total_messages_received: non_neg_integer(), pending_inbox: non_neg_integer(), pending_outbox: non_neg_integer(), active_dyads: non_neg_integer(), total_dyads: non_neg_integer(), history_size: non_neg_integer(), history_limit: pos_integer(), uptime: non_neg_integer() }
Gets comprehensive mailbox statistics.
Retrieves detailed statistics about the mailbox's communication patterns, performance metrics, and operational status. Useful for monitoring, debugging, and performance optimization.
Parameters
mailbox
- Mailbox struct to analyze
Returns
Map containing comprehensive statistics:
:total_messages_sent
- Number of messages sent by this mailbox:total_messages_received
- Number of messages received:pending_inbox
- Current unprocessed inbox messages:pending_outbox
- Current unsent outbox messages:active_dyads
- Number of currently active interaction dyads:total_dyads
- Total dyads ever formed (including inactive):history_size
- Current message history queue size:history_limit
- Maximum history size limit:uptime
- Mailbox uptime in seconds
Examples
# Get basic statistics
iex> stats = Object.Mailbox.get_stats(mailbox)
iex> stats.total_messages_sent
127
iex> stats.active_dyads
5
# Calculate performance metrics
iex> stats = Object.Mailbox.get_stats(mailbox)
iex> message_rate = (stats.total_messages_sent + stats.total_messages_received) / stats.uptime
iex> dyad_efficiency = stats.active_dyads / max(1, stats.total_dyads)
Performance Indicators
Message Volume
- High Volume (>100 msg/min): Active hub or coordinator
- Medium Volume (10-100 msg/min): Regular operational object
- Low Volume (<10 msg/min): Peripheral or specialized object
Processing Health
- Pending Inbox: Should be near 0 for healthy processing
- Pending Outbox: Indicates delivery bottlenecks if high
- History Usage:
history_size / history_limit
ratio
Social Connectivity
- Active Dyads: Number of active partnerships
- Dyad Efficiency:
active_dyads / total_dyads
ratio - Interaction Density: Messages per dyad
Monitoring Applications
Performance Monitoring
- Message throughput analysis
- Processing bottleneck identification
- Resource usage tracking
Health Monitoring
- Communication failures detection
- Overload condition identification
- System degradation alerts
Social Analysis
- Interaction pattern analysis
- Network connectivity assessment
- Partner effectiveness evaluation
Debugging
- Message flow tracing
- Delivery failure analysis
- Performance regression investigation
Statistical Calculations
The statistics are computed as follows:
# Message counts from history
sent = Enum.count(history, &(&1.from == object_id))
received = Enum.count(history, &(&1.to == object_id))
# Current queue sizes
pending_in = length(inbox)
pending_out = length(outbox)
# Dyad metrics
active = count_active_dyads(interaction_dyads)
total = map_size(interaction_dyads)
# Temporal metrics
uptime = DateTime.diff(DateTime.utc_now(), created_at, :second)
Performance
- Calculation time: ~0.5-2ms depending on history size
- Memory overhead: Temporary iteration over message history
- No side effects: Read-only operation
- Caching: Statistics can be cached for frequent access
Creates a new mailbox for an object.
Initializes a complete mailbox with all necessary components for inter-object communication, including message queues, protocol handlers, and interaction tracking.
Parameters
object_id
- ID of the object this mailbox belongs toopts
- Optional configuration::history_size_limit
- Maximum message history size (default: 1000)
Returns
New mailbox struct with initialized state and default protocol handlers.
Default Protocol Handlers
The mailbox is created with handlers for common message types:
:state_update
- Process object state updates:goal_update
- Handle goal function changes:belief_update
- Update world model beliefs:learning_signal
- Process learning data:coordination
- Handle coordination protocols:negotiation
- Process negotiation messages:acknowledgment
- Handle delivery confirmations:heartbeat
- Process connectivity checks
Examples
# Create basic mailbox
iex> mailbox = Object.Mailbox.new("agent_1")
iex> mailbox.object_id
"agent_1"
iex> length(mailbox.inbox)
0
# Create with custom history limit
iex> mailbox = Object.Mailbox.new("sensor_1", history_size_limit: 500)
iex> mailbox.history_size_limit
500
Performance
- Creation time: ~0.1ms
- Memory usage: ~1KB base + message storage
- History queue: Efficient FIFO operations
- Protocol handlers: Fast O(1) lookup
Processes messages in the inbox based on type and priority.
Processes all pending messages in the inbox according to priority order, applying appropriate protocol handlers and generating results. This is the core message processing function that drives object communication.
Parameters
mailbox
- Mailbox struct with messages to process
Returns
{processed_messages, updated_mailbox}
where:
processed_messages
- List of{message, result}
tuplesupdated_mailbox
- Mailbox with cleared inbox and updated state
Processing Priority
Messages are processed in strict priority order:
:critical
- System-critical messages (emergency stops, failures):high
- Important coordination and control messages:medium
- Regular operational messages (default):low
- Background tasks and maintenance messages
Within each priority level, messages are processed by timestamp (FIFO).
Protocol Handlers
Each message type has a dedicated handler:
:state_update
→ Update object state:goal_update
→ Modify goal function:belief_update
→ Update world model beliefs:learning_signal
→ Process learning data:coordination
→ Handle coordination protocols:negotiation
→ Process negotiation steps:acknowledgment
→ Handle delivery confirmations:heartbeat
→ Update connection status
Examples
# Process accumulated messages
iex> {processed, updated} = Object.Mailbox.process_inbox(mailbox)
iex> length(processed)
3
iex> length(updated.inbox)
0
# Examine processing results
iex> {[{msg1, result1}, {msg2, result2}], _} = Object.Mailbox.process_inbox(mailbox)
iex> result1
{:coordination_received, %{action: :form_coalition}}
iex> result2
{:state_updated, %{energy: 95}}
Error Handling
- Handler errors are caught and returned as
{:error, reason}
- Unknown message types return
{:error, :no_handler}
- Processing continues despite individual message failures
- Failed messages are logged for debugging
Performance
- Processing rate: ~100-500 messages/second depending on complexity
- Priority sorting: O(n log n) where n is inbox size
- Handler execution: Varies by message type and content
- Memory usage: Temporary overhead during processing
Batch Processing
- All inbox messages processed in single operation
- Atomic inbox clearing prevents message loss
- Efficient sorting reduces processing overhead
- Results collected for analysis and debugging
Receives a message into the inbox.
Validates and accepts an incoming message into the mailbox inbox, updating interaction dyads and handling acknowledgments as needed. Invalid messages are rejected with appropriate error codes.
Parameters
mailbox
- Mailbox struct to receive intomessage
- Message to receive (must include required fields)
Returns
- Updated mailbox with message added to inbox
{:error, reason}
if message validation fails
Message Validation
Required fields for valid messages:
:id
- Unique message identifier:from
- Sender object ID:to
- Recipient object ID (should match mailbox owner):type
- Message type for routing:content
- Message payload:timestamp
- Message creation time
Processing Steps
- Validation: Check message format and required fields
- Inbox Addition: Add to inbox for processing
- History Recording: Add to message history
- Dyad Update: Update interaction dyad with sender
- Acknowledgment: Send ACK if required by message
Examples
# Receive valid coordination message
iex> message = %{
...> id: "msg_123",
...> from: "agent_2",
...> to: "agent_1",
...> type: :coordination,
...> content: %{action: :form_coalition},
...> timestamp: DateTime.utc_now(),
...> priority: :high,
...> requires_ack: true,
...> ttl: 3600
...> }
iex> updated = Object.Mailbox.receive_message(mailbox, message)
iex> length(updated.inbox)
1
# Invalid message format
iex> bad_message = %{invalid: true}
iex> Object.Mailbox.receive_message(mailbox, bad_message)
{:error, :invalid_message_format}
Acknowledgments
When requires_ack: true
:
- Automatic acknowledgment message is sent back to sender
- ACK contains original message ID for correlation
- High priority for timely delivery confirmation
Error Conditions
:invalid_message_format
- Missing required fields:malformed_content
- Content structure invalid:expired_message
- Message TTL exceeded:duplicate_message
- Message ID already processed
Performance
- Validation time: ~0.05ms per message
- Inbox insertion: O(1) operation
- History maintenance: Bounded queue operations
- Dyad updates: O(1) lookup and modification
Registers a protocol handler for specific message types.
Parameters
mailbox
: Mailbox structmessage_type
: Type of message to handlehandler_fn
: Function to handle messages of this type
Returns
Updated mailbox with new handler registered
Sends a message to another object.
Creates and queues a message for delivery to another object, updating the mailbox state and interaction dyad information. Messages are validated and assigned unique IDs for tracking.
Parameters
mailbox
- Mailbox struct to send fromto_object_id
- ID of the recipient objectmessage_type
- Type of message for routing and handlingcontent
- Message content (any serializable term)opts
- Optional message configuration::priority
- Message priority (:low, :medium, :high, :critical):requires_ack
- Whether delivery confirmation is required:ttl
- Time-to-live in seconds (default: 3600)
Returns
Updated mailbox with:
- Message added to outbox
- Message recorded in history
- Interaction dyad updated or created
- Updated timestamp
Message Processing
- Message Creation: Generate unique ID and timestamp
- Validation: Ensure required fields are present
- Outbox Addition: Add to outbox for delivery
- History Recording: Add to bounded message history
- Dyad Update: Update or create interaction dyad
Examples
# Send coordination message
iex> updated = Object.Mailbox.send_message(
...> mailbox, "coordinator_1", :coordination,
...> %{action: :join_coalition},
...> [priority: :high, requires_ack: true]
...> )
iex> length(updated.outbox)
1
# Send learning signal with TTL
iex> Object.Mailbox.send_message(
...> mailbox, "learner_2", :learning_signal,
...> %{reward: 1.0, experience: %{action: :explore}},
...> [ttl: 300]
...> )
%Object.Mailbox{outbox: [%{ttl: 300, ...}], ...}
Interaction Dyads
Sending messages automatically:
- Creates new dyads if they don't exist
- Updates interaction count for existing dyads
- Calculates utility scores based on interaction frequency
- Maintains dyad metadata for social learning
Performance
- Message creation: ~0.1ms
- History bounded at configurable limit
- Dyad updates: O(1) lookup and update
- Memory usage: ~100 bytes + content size per message
Updates routing table for message delivery.
Parameters
mailbox
: Mailbox structobject_id
: Object to update routing forroute_info
: Routing information
Returns
Updated mailbox with new routing information