A Rust-native AI agent framework for building LLM-powered agents with tool use, memory, and streaming.
Daimon implements the ReAct (Reason-Act-Observe) pattern: the agent calls a model, optionally invokes tools, observes results, and repeats until it produces a final response. It is designed to be easy to use while leveraging Rust's type system, async runtime, and performance characteristics.
- ReAct agent loop with configurable iteration limits
- Multiple LLM providers behind feature flags — OpenAI, Anthropic, AWS Bedrock
- Tool system with async execution, parallel tool calls, and a typed registry
- Streaming with full ReAct loop support (tool calls accumulate and re-invoke within a single stream)
- Conversation memory with pluggable backends (sliding window included)
- Lifecycle hooks for observability and control
- Cancellation via
tokio_util::CancellationToken - Tracing instrumentation on all agent and provider operations
- Retry logic with exponential backoff for transient provider errors
Add Daimon to your Cargo.toml:
[dependencies]
daimon = "0.1"
tokio = { version = "1", features = ["full"] }Create an agent and prompt it:
use daimon::prelude::*;
#[tokio::main]
async fn main() -> daimon::Result<()> {
let agent = Agent::builder()
.model(daimon::model::openai::OpenAi::new("gpt-4o"))
.system_prompt("You are a helpful assistant.")
.build()?;
let response = agent.prompt("What is Rust?").await?;
println!("{}", response.text());
Ok(())
}Define tools by implementing the Tool trait:
use daimon::prelude::*;
struct Calculator;
impl Tool for Calculator {
fn name(&self) -> &str { "calculator" }
fn description(&self) -> &str { "Evaluate math expressions" }
fn parameters_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"] },
"a": { "type": "number" },
"b": { "type": "number" }
},
"required": ["operation", "a", "b"]
})
}
async fn execute(&self, input: &Value) -> daimon::Result<ToolOutput> {
let op = input["operation"].as_str().unwrap_or("add");
let a = input["a"].as_f64().unwrap_or(0.0);
let b = input["b"].as_f64().unwrap_or(0.0);
let result = match op {
"add" => a + b,
"subtract" => a - b,
"multiply" => a * b,
"divide" if b != 0.0 => a / b,
"divide" => return Ok(ToolOutput::error("Division by zero")),
_ => return Ok(ToolOutput::error(format!("Unknown operation: {op}"))),
};
Ok(ToolOutput::text(format!("{result}")))
}
}
#[tokio::main]
async fn main() -> daimon::Result<()> {
let agent = Agent::builder()
.model(daimon::model::openai::OpenAi::new("gpt-4o"))
.system_prompt("Use the calculator tool to solve math problems.")
.tool(Calculator)
.build()?;
let response = agent.prompt("What is 42 * 17 + 3?").await?;
println!("{}", response.text());
println!("Completed in {} iteration(s)", response.iterations);
Ok(())
}Stream responses token-by-token with the full ReAct loop:
use daimon::prelude::*;
#[tokio::main]
async fn main() -> daimon::Result<()> {
let agent = Agent::builder()
.model(daimon::model::openai::OpenAi::new("gpt-4o"))
.build()?;
let mut stream = agent.prompt_stream("Explain quantum computing.").await?;
while let Some(event) = stream.next().await {
match event? {
StreamEvent::TextDelta(text) => print!("{text}"),
StreamEvent::ToolResult { content, .. } => eprintln!("\n[tool result: {content}]"),
StreamEvent::Done => { println!(); break; }
_ => {}
}
}
Ok(())
}| Feature | Default | Description |
|---|---|---|
openai |
Yes | OpenAI Chat Completions API |
anthropic |
Yes | Anthropic Messages API |
bedrock |
No | AWS Bedrock Converse API |
full |
No | All providers |
The core framework compiles with no features enabled. Enable only the providers you need:
# Only Anthropic
daimon = { version = "0.1", default-features = false, features = ["anthropic"] }
# All providers
daimon = { version = "0.1", features = ["full"] }
# Core only (bring your own Model impl)
daimon = { version = "0.1", default-features = false }All providers support configurable timeout, retries, and provider-specific options:
use std::time::Duration;
// OpenAI with custom settings
let model = daimon::model::openai::OpenAi::new("gpt-4o")
.with_timeout(Duration::from_secs(30))
.with_max_retries(5)
.with_response_format("json_object")
.with_parallel_tool_calls(true);
// Anthropic with prompt caching
let model = daimon::model::anthropic::Anthropic::new("claude-sonnet-4-20250514")
.with_timeout(Duration::from_secs(60))
.with_prompt_caching();
// AWS Bedrock with guardrails
let model = daimon::model::bedrock::Bedrock::new("anthropic.claude-3-5-sonnet-20241022-v2:0")
.with_guardrail("my-guardrail-id", "DRAFT");use daimon::prelude::*;
let agent = Agent::builder()
.model(model) // required
.system_prompt("You are helpful.") // optional system prompt
.tool(Calculator) // register tools
.tool(WebSearch)
.memory(SlidingWindowMemory::new(100)) // custom memory (default: 50 messages)
.hooks(MyObserver) // lifecycle hooks
.max_iterations(10) // default: 25
.temperature(0.7) // sampling temperature
.max_tokens(4096) // max output tokens
.build()?;
// Standard prompt
let response = agent.prompt("Hello").await?;
println!("{}", response.text());
println!("Tokens used: {}", response.usage.total_tokens());
// With cancellation
let cancel = CancellationToken::new();
let response = agent.prompt_with_cancellation("Hello", &cancel).await?;
// With pre-built messages
let messages = vec![Message::user("Hello")];
let response = agent.prompt_with_messages(messages).await?;┌──────────────────────────────────────────────────┐
│ Agent │
│ ┌────────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Model │ │ Tools │ │ Memory │ │
│ │ (trait) │ │ Registry │ │ (trait) │ │
│ └─────┬──────┘ └────┬─────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌─────┴──────────────┴───────────────┴───────┐ │
│ │ ReAct Loop │ │
│ │ 1. Load memory → build request │ │
│ │ 2. Call model │ │
│ │ 3. Tool calls? → execute (parallel) → 2 │ │
│ │ 4. Final response → save to memory │ │
│ └────────────────────────────────────────────┘ │
│ │ │
│ ┌─────┴──────┐ ┌──────────┐ │
│ │ Hooks │ │ Streaming │ │
│ │ (lifecycle)│ │ Events │ │
│ └────────────┘ └──────────┘ │
└──────────────────────────────────────────────────┘
Rust 1.85 (edition 2024).
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
See CONTRIBUTING.md for development setup, coding standards, and contribution guidelines. Note that AI-assisted contributions must include proper attribution.