State Management¶
State in RobotLab tracks all data and history for a conversation or workflow.
State Structure¶
The State class holds:
state = RobotLab.create_state(
message: "Hello!", # Current user message
data: { user_id: "123" } # Custom workflow data
)
state.data # StateProxy - custom key-value data
state.results # Array<RobotResult> - execution history
state.messages # Array<Message> - formatted conversation
state.thread_id # String - optional persistence ID
state.memory # Memory - shared key-value store
Creating State¶
Basic Creation¶
With Custom Data¶
state = RobotLab.create_state(
message: "Process my order",
data: {
user_id: "user_123",
order_id: "ord_456",
priority: "high"
}
)
From Existing Results¶
state = RobotLab.create_state(
message: "Continue our conversation",
results: previous_results,
thread_id: "thread_abc"
)
StateProxy¶
The data attribute is a StateProxy that provides convenient access:
state.data[:user_id] # Hash-style access
state.data[:user_id] = "456" # Assignment
state.data.user_id # Method-style access
state.data.user_id = "456" # Method-style assignment
state.data.key?(:user_id) # Check existence
state.data.keys # Get all keys
state.data.to_h # Convert to plain hash
Change Tracking¶
StateProxy can track changes:
state = State.new(
data: { count: 0 },
on_change: ->(key, old_val, new_val) {
puts "#{key}: #{old_val} -> #{new_val}"
}
)
state.data[:count] = 1 # Prints: "count: 0 -> 1"
Memory¶
Memory provides a shared key-value store across robots:
# Store values
state.memory.remember("user_name", "Alice")
state.memory.remember("preferences", { theme: "dark" })
# Retrieve values
name = state.memory.recall("user_name") # => "Alice"
# Check existence
state.memory.exists?("user_name") # => true
# Remove values
state.memory.forget("user_name")
# List all
state.memory.all # => { "user_name" => "Alice", ... }
Scoped Memory¶
Organize memory with namespaces:
# Create scoped view
user_memory = state.memory.scoped("user:123")
user_memory.remember("last_login", Time.now)
# Access scoped data
user_memory.recall("last_login")
# Full key is "user:123:last_login"
state.memory.recall("user:123:last_login")
Shared Memory¶
Use the SHARED namespace for cross-robot data:
# In first robot
state.memory.remember("SHARED:context", important_data)
# In second robot (same or different network run)
data = state.memory.recall("SHARED:context")
Memory Operations¶
# Search by pattern
matches = state.memory.search("user:*")
# Get statistics
state.memory.stats
# => { total_keys: 15, namespaces: ["user", "session"] }
# Clear namespace
state.memory.scoped("temp").clear
# Clear everything
state.memory.clear_all
Results¶
Results track the history of robot executions:
# Append a result
state.append_result(robot_result)
# Get all results
state.results
# Get results from index
state.results_from(5) # Results starting at index 5
# Format for LLM conversation
state.format_history
Result History¶
Each RobotResult contains:
result.robot_name # Which robot produced this
result.output # Array<Message> - response content
result.tool_calls # Array<ToolMessage> - tools called
result.stop_reason # "stop", "tool", etc.
result.created_at # When it was created
Messages¶
The messages method formats state for LLM consumption:
messages = state.messages
# Returns Array<Message> with:
# - System message (if present)
# - Alternating user/assistant messages
# - Tool calls and results
Thread ID¶
For persistent conversations:
# Set thread ID
state.thread_id = "thread_123"
# Or via UserMessage
message = UserMessage.new(
"Continue",
thread_id: "thread_123"
)
state = RobotLab.create_state(message: message)
State Cloning¶
Create independent copies:
original = RobotLab.create_state(data: { count: 1 })
clone = original.clone
clone.data[:count] = 2
original.data[:count] # Still 1
Serialization¶
Convert state to/from hash:
Hash Structure¶
{
data: { ... },
results: [
{
robot_name: "assistant",
output: [...],
tool_calls: [...],
stop_reason: "stop"
}
],
thread_id: "thread_123"
}
UserMessage¶
Enhanced message with metadata:
message = UserMessage.new(
"What's the status of my order?",
thread_id: "thread_123",
system_prompt: "Respond in Spanish", # Augment system prompt
metadata: {
user_id: "user_456",
source: "web_chat"
}
)
state = RobotLab.create_state(message: message)
UserMessage Properties¶
| Property | Description |
|---|---|
content |
The message text |
thread_id |
Conversation thread ID |
system_prompt |
Additional system instructions |
metadata |
Custom key-value data |
id |
Unique message identifier |
created_at |
Timestamp |
Best Practices¶
1. Use Memory for Cross-Robot Data¶
# Don't pass data through routing
router = ->(args) {
# Bad: parsing previous output for data
}
# Do: use memory
state.memory.remember("classification", "billing")
# Later robot reads it directly
2. Scope Memory Appropriately¶
# Session data
session = state.memory.scoped("session:#{session_id}")
# User preferences
user = state.memory.scoped("user:#{user_id}")
# Temporary working data
temp = state.memory.scoped("temp")
3. Keep Data Minimal¶
# Don't store large objects
state.data[:huge_response] = api_response # Bad
# Store references instead
state.data[:response_id] = response.id # Good
Next Steps¶
- Memory System - Advanced memory patterns
- History Guide - Persisting state
- Message Flow - How messages are processed