Overview
The Rust implementation of elizaOS provides native performance and WebAssembly compilation support. It maintains 100% type compatibility with the TypeScript implementation through Protocol Buffers and offers both synchronous and asynchronous APIs. Crate:elizaos
Version: 2.0.0
Rust Edition: 2021
MSRV: 1.70+
Key Features
- Native performance with zero-cost abstractions
- WebAssembly compilation for browser and Node.js
- Synchronous runtime for ICP and embedded systems
- Full type compatibility with TypeScript
- Character loading and validation
- Plugin system with dependency resolution
- Memory-safe with Rust’s ownership model
Installation
Add elizaOS to yourCargo.toml:
[dependencies]
elizaos = "2.0"
# For async support (native)
tokio = { version = "1.0", features = ["full"] }
# For serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Feature Flags
[dependencies]
elizaos = { version = "2.0", features = ["native"] }
# Available features:
# - native: Native runtime with tokio (default)
# - wasm: WebAssembly support
# - bootstrap: Bootstrap plugin functionality
# - sync: Synchronous runtime for ICP/embedded
# - icp: Internet Computer Protocol support
Quick Start
Basic Agent Setup
use elizaos::{
AgentRuntime, Character, RuntimeOptions,
parse_character, Result,
};
#[tokio::main]
async fn main() -> Result<()> {
// Define character
let character_json = r#"{
"name": "Eliza",
"username": "eliza",
"bio": "A helpful AI assistant built with elizaOS",
"system": "You are a helpful and concise AI assistant."
}"#;
let character = parse_character(character_json)?;
// Create runtime
let runtime = AgentRuntime::new(RuntimeOptions {
character: Some(character),
log_level: LogLevel::Info,
..Default::default()
}).await?;
// Initialize
runtime.initialize().await?;
println!("✓ Agent initialized: {}", runtime.character.name);
Ok(())
}
Processing Messages
use elizaos::{
AgentRuntime, Memory, Content, ChannelType,
UUID, uuid::Uuid,
};
#[tokio::main]
async fn main() -> Result<()> {
let runtime = create_runtime().await?;
let user_id = Uuid::new_v4();
let room_id = Uuid::new_v4();
// Create message
let message = Memory {
id: Some(Uuid::new_v4().to_string()),
entity_id: user_id.to_string(),
room_id: room_id.to_string(),
content: Content {
text: Some("Hello, Eliza!".to_string()),
source: Some("cli".to_string()),
channel_type: Some(ChannelType::Dm as i32),
..Default::default()
},
created_at: Some(now_timestamp()),
..Default::default()
};
// Process message
let result = runtime.handle_message(message).await?;
println!("Response: {}", result.content.text.unwrap_or_default());
Ok(())
}
Core Concepts
Actions
Define custom actions:use elizaos::{
ActionDefinition, ActionHandler, ActionResult,
IAgentRuntime, Memory, State,
};
use async_trait::async_trait;
use std::sync::Arc;
pub struct WeatherAction;
#[async_trait]
impl ActionHandler for WeatherAction {
async fn validate(
&self,
_runtime: Arc<dyn IAgentRuntime>,
message: &Memory,
_state: Option<&State>,
) -> Result<bool> {
// Check if message mentions weather
let text = message.content.text.as_ref()
.map(|s| s.to_lowercase())
.unwrap_or_default();
Ok(text.contains("weather") ||
text.contains("temperature") ||
text.contains("forecast"))
}
async fn handler(
&self,
_runtime: Arc<dyn IAgentRuntime>,
message: &Memory,
_state: Option<&State>,
_options: Option<&HandlerOptions>,
) -> Result<ActionResult> {
let location = extract_location(&message.content.text);
let weather = fetch_weather(&location).await?;
Ok(ActionResult {
success: true,
content: Some(Content {
text: Some(format!(
"The weather in {} is {}°F and {}",
location, weather.temp, weather.condition
)),
..Default::default()
}),
..Default::default()
})
}
}
// Create action definition
pub fn weather_action() -> ActionDefinition {
ActionDefinition {
name: "GET_WEATHER".to_string(),
description: "Get current weather for a location".to_string(),
handler: Arc::new(WeatherAction),
..Default::default()
}
}
Providers
Supply contextual information:use elizaos::{
ProviderDefinition, ProviderHandler,
IAgentRuntime, Memory, State,
};
use async_trait::async_trait;
use chrono::Utc;
use serde_json::{json, Value};
use std::sync::Arc;
pub struct TimeProvider;
#[async_trait]
impl ProviderHandler for TimeProvider {
async fn get(
&self,
_runtime: Arc<dyn IAgentRuntime>,
_message: &Memory,
_state: Option<&State>,
) -> Result<Value> {
let now = Utc::now();
Ok(json!({
"currentTime": now.to_rfc3339(),
"timeZone": "UTC",
"timestamp": now.timestamp_millis()
}))
}
}
// Create provider definition
pub fn time_provider() -> ProviderDefinition {
ProviderDefinition {
name: "CURRENT_TIME".to_string(),
handler: Arc::new(TimeProvider),
}
}
Evaluators
Analyze conversations:use elizaos::{
EvaluatorDefinition, EvaluatorHandler, PreEvaluatorResult,
IAgentRuntime, Memory, State, EvaluatorPhase,
};
use async_trait::async_trait;
use std::sync::Arc;
pub struct SentimentEvaluator;
#[async_trait]
impl EvaluatorHandler for SentimentEvaluator {
async fn handler(
&self,
_runtime: Arc<dyn IAgentRuntime>,
message: &Memory,
_state: Option<&State>,
) -> Result<PreEvaluatorResult> {
let sentiment = analyze_sentiment(
message.content.text.as_deref().unwrap_or("")
);
Ok(PreEvaluatorResult {
success: true,
data: Some(json!({
"sentiment": sentiment
})),
..Default::default()
})
}
}
// Create evaluator definition
pub fn sentiment_evaluator() -> EvaluatorDefinition {
EvaluatorDefinition {
name: "SENTIMENT_ANALYZER".to_string(),
description: "Analyzes sentiment of user messages".to_string(),
phase: EvaluatorPhase::AfterResponse,
handler: Arc::new(SentimentEvaluator),
..Default::default()
}
}
Creating Plugins
use elizaos::{Plugin, PluginDefinition};
pub fn my_plugin() -> Plugin {
Plugin {
definition: PluginDefinition {
name: "my-custom-plugin".to_string(),
description: "A custom plugin for elizaOS".to_string(),
..Default::default()
},
actions: vec![weather_action()],
providers: vec![time_provider()],
evaluators: vec![sentiment_evaluator()],
services: vec![],
}
}
// Use in runtime
let runtime = AgentRuntime::new(RuntimeOptions {
character: Some(character),
plugins: vec![my_plugin()],
..Default::default()
}).await?;
WebAssembly Support
Building for WASM
# Install wasm-pack
cargo install wasm-pack
# Build for web browsers
wasm-pack build --target web --features wasm
# Build for Node.js
wasm-pack build --target nodejs --features wasm
Using in JavaScript
import init, {
WasmAgentRuntime,
parse_character,
validate_character,
} from "./pkg/elizaos.js";
// Initialize WASM module
await init();
// Create character
const characterJson = JSON.stringify({
name: "Eliza",
bio: "AI assistant",
system: "You are helpful.",
});
// Validate character
const validation = validate_character(characterJson);
if (!validation.valid) {
console.error("Invalid character:", validation.errors);
return;
}
// Create runtime
const runtime = await new WasmAgentRuntime(characterJson);
await runtime.initialize();
console.log(`Agent ID: ${runtime.agent_id}`);
console.log(`Character: ${runtime.character_name}`);
// Process message
const result = await runtime.handle_message_json(JSON.stringify({
entity_id: "user-123",
room_id: "room-456",
content: {
text: "Hello!",
source: "web",
},
}));
console.log("Response:", JSON.parse(result));
Configuration
Runtime Options
use elizaos::{AgentRuntime, RuntimeOptions, LogLevel};
let runtime = AgentRuntime::new(RuntimeOptions {
character: Some(character),
// Logging
log_level: LogLevel::Debug,
// Conversation settings
conversation_length: Some(32),
// Feature flags
disable_basic_capabilities: false,
advanced_capabilities: true,
// Plugins
plugins: vec![my_plugin()],
..Default::default()
}).await?;
Environment Variables
use std::env;
// Read environment variables
let api_key = env::var("OPENAI_API_KEY")
.expect("OPENAI_API_KEY not set");
let use_multi_step = env::var("USE_MULTI_STEP")
.map(|v| v == "true")
.unwrap_or(false);
let max_iterations = env::var("MAX_MULTISTEP_ITERATIONS")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(6);
Type System
Core Types
use elizaos::{
// Primitive types
UUID,
Content,
Memory,
// Agent types
Character,
Agent,
AgentStatus,
// Component types
ActionDefinition,
ProviderDefinition,
EvaluatorDefinition,
Plugin,
// Environment types
Entity,
Room,
World,
// Model types
ModelType,
GenerateOptions,
};
Serialization
All types serialize to JSON identically to TypeScript:use elizaos::Character;
use serde_json;
let character = Character {
name: "Eliza".to_string(),
username: Some("eliza".to_string()),
bio: Some("AI assistant".to_string()),
..Default::default()
};
// Serialize to JSON
let json = serde_json::to_string(&character)?;
// Deserialize from JSON
let character: Character = serde_json::from_str(&json)?;
Advanced Features
Synchronous Runtime
For ICP and embedded systems:// Enable sync feature in Cargo.toml
// elizaos = { version = "2.0", features = ["sync"] }
use elizaos::{AgentRuntime, RuntimeOptions};
fn main() -> Result<()> {
let runtime = AgentRuntime::new_sync(RuntimeOptions {
character: Some(character),
..Default::default()
})?;
runtime.initialize_sync()?;
let result = runtime.handle_message_sync(message)?;
Ok(())
}
Character Validation
use elizaos::{validate_character, parse_character};
let character_json = std::fs::read_to_string("character.json")?;
// Validate before parsing
let validation = validate_character(&character_json);
if !validation.valid {
eprintln!("Character validation failed:");
for error in validation.errors {
eprintln!(" - {}", error);
}
return Err(anyhow::anyhow!("Invalid character"));
}
// Parse validated character
let character = parse_character(&character_json)?;
Error Handling
use elizaos::{Error, Result};
use anyhow::Context;
async fn process_message(runtime: &AgentRuntime) -> Result<()> {
let result = runtime.handle_message(message).await
.context("Failed to handle message")?;
if !result.success {
return Err(anyhow::anyhow!("Message processing failed"));
}
Ok(())
}
Testing
Unit Tests
#[cfg(test)]
mod tests {
use super::*;
use elizaos::{Character, AgentRuntime, RuntimeOptions};
#[tokio::test]
async fn test_weather_action() {
let character = Character {
name: "TestAgent".to_string(),
..Default::default()
};
let runtime = AgentRuntime::new(RuntimeOptions {
character: Some(character),
plugins: vec![my_plugin()],
..Default::default()
}).await.unwrap();
runtime.initialize().await.unwrap();
let message = create_test_message("What's the weather?");
let result = runtime.handle_message(message).await.unwrap();
assert!(result.success);
assert!(result.content.unwrap().text.unwrap().contains("weather"));
}
}
Integration Tests
#[cfg(test)]
mod integration_tests {
use super::*;
#[tokio::test]
async fn test_full_conversation() {
let runtime = create_test_runtime().await.unwrap();
let messages = vec![
"Hello",
"What's the weather?",
"Thank you",
];
for msg in messages {
let message = create_test_message(msg);
let result = runtime.handle_message(message).await.unwrap();
assert!(result.success);
}
}
}
Build Configurations
Release Build
[profile.release]
lto = true
opt-level = "z" # Optimize for size
codegen-units = 1
panic = "abort"
Cross-Compilation
# Install cross-compilation tools
cargo install cross
# Build for different targets
cross build --target x86_64-unknown-linux-musl --release
cross build --target aarch64-unknown-linux-gnu --release
cross build --target wasm32-unknown-unknown --release
Cross-Language Compatibility
Rust implementation maintains binary compatibility:- Rust
- TypeScript
use elizaos::{Character, Content};
let character = Character {
name: "Eliza".to_string(),
bio: Some("AI assistant".to_string()),
system: Some("You are helpful.".to_string()),
..Default::default()
};
let content = Content {
text: Some("Hello".to_string()),
source: Some("cli".to_string()),
..Default::default()
};
import type { Character, Content } from "@elizaos/core";
const character: Character = {
name: "Eliza",
bio: "AI assistant",
system: "You are helpful.",
};
const content: Content = {
text: "Hello",
source: "cli",
};
Examples
Complete examples in the repository:Resources
Next Steps
TypeScript SDK
Explore the TypeScript implementation
Python SDK
Check out the Python implementation
WebAssembly Guide
Deploy to browsers and edge
Examples
View complete examples