An LLM agent framework for Rust. Build AI agents with tools, skills, middleware, and pluggable LLM backends — all with multi-layered security.
┌─────────────────────────────────────────────────────┐
│ funera-orchestrate high-level builder API │
│ Agent · AgentRuntime · callbacks · streaming │
├─────────────────────────────────────────────────────┤
│ funera_core core engine │
│ ReActLoop · Session · EventBus · Middleware │
│ Security · Provider · Tools · Skills │
├─────────────────────────────────────────────────────┤
│ builtin_tools default tool implementations │
│ ReadTool · WriteTool · EditTool · ShellTool │
└─────────────────────────────────────────────────────┘
- ReAct loop — iterative tool-calling agent execution with configurable max iterations
- Pluggable providers — OpenAI and DeepSeek backends with streaming support
- Tool system — define custom tools by implementing the
Tooltrait; built-in file I/O and shell - Skill system — load prompt templates from YAML-frontmatter Markdown files
- Middleware pipeline — intercept agent events with inspectors (read-only, parallel) and mutators (pass/modify/block, sequential)
- Security layer — tool/shell policies, path allowlisting, audit logging, secure API key storage
- Type-state session — compile-time enforcement of session ownership (
Idle/Acquired)
Add the root crate to your Cargo.toml:
[dependencies]
funera = { git = "https://github.com/dynamder/Funera" }| Feature | Default | Description |
|---|---|---|
builtin-tools |
❌ | Bundled Read, Write, Edit, Shell tools |
deepseek |
✅ | DeepSeek provider |
openai |
❌ | OpenAI provider |
security |
❌ | Tool policy enforcement, path guards, audit logging |
sandbox |
❌ | Kernel-level subprocess isolation (Landlock/Seatbelt/Token) |
middleware |
❌ | Event interception pipeline |
skill |
❌ | Skill loading and prompt injection |
use funera::{Agent, AgentRuntime, DeepSeekProvider};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(std::env::var("DEEPSEEK_API_KEY")?)
.model("deepseek-v4-flash")
.build()?;
let agent = Agent::builder()
.system_prompt("You are a helpful assistant.")
.build();
let resp = agent.fire("Hello!", &runtime).await?;
println!("{}", resp.content);
Ok(())
}use funera::{Agent, AgentEvent, AgentRuntime, DeepSeekProvider};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(std::env::var("DEEPSEEK_API_KEY")?)
.model("deepseek-v4-flash")
.build()?;
let agent = Agent::builder()
.on_token(|t| print!("{t}"))
.build();
let mut rx = agent.fire_stream("Explain Rust ownership", &runtime).await?;
while let Some(event) = rx.recv().await {
if let AgentEvent::Text(t) = event {
print!("{t}");
}
}
Ok(())
}let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(std::env::var("DEEPSEEK_API_KEY")?)
.model("deepseek-v4-flash")
.build()?;
let agent = Agent::builder()
.system_prompt("You are helpful.")
.build();
let handle = agent.send("Hi, I'm Alice.", runtime).await?;
let (runtime, _resp) = handle.await?;
let handle = agent.send("What's my name?", runtime).await?;
let (_runtime, _resp) = handle.await?;use async_trait::async_trait;
use funera::core::re_act::tool::{Tool, ToolCallError};
use serde_json::{json, Value as JsonValue};
struct Calculator;
#[async_trait]
impl Tool for Calculator {
fn name(&self) -> &str { "calculator" }
fn description(&self) -> &str { "Evaluate a math expression" }
fn schema(&self) -> JsonValue {
json!({
"type": "function",
"function": {
"name": "calculator",
"parameters": {
"type": "object",
"properties": {
"expression": { "type": "string" }
},
"required": ["expression"]
}
}
})
}
async fn execute(&self, args: JsonValue) -> Result<String, ToolCallError> {
let expr = args["expression"].as_str().unwrap_or("");
Ok(format!("TODO: compute {expr}"))
}
}
// Register it:
let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(std::env::var("DEEPSEEK_API_KEY")?)
.model("deepseek-v4-flash")
.with_tool_instance(Box::new(Calculator))
.build()?;Requires the security feature (and optionally builtin-tools, sandbox):
use funera::{Agent, AgentRuntime, DeepSeekProvider, ToolPolicy, ShellPolicy};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(std::env::var("DEEPSEEK_API_KEY")?)
.model("deepseek-v4-flash")
.with_builtin_tools()
.with_tool_policy(ToolPolicy {
denied_tools: ["shell".into()].into_iter().collect(),
shell_policy: Some(ShellPolicy::with_allowed(
vec!["git".into(), "cargo".into()],
)),
..Default::default()
})
.build()?;
let agent = Agent::builder().build();
let resp = agent.fire("List the git log.", &runtime).await?;
println!("{}", resp.content);
Ok(())
}funera/
├── funera_core/ Core agent engine
│ └── src/
│ ├── chat/ Message types, session actor
│ ├── env.rs Shared runtime environment
│ ├── event_bus/ Token, React, EnvState, Tool buses
│ ├── middleware.rs Event interception pipeline
│ ├── provider/ OpenAI & DeepSeek backends
│ ├── re_act/ ReAct loop, Tool trait, Skill system
│ └── security/ Policies, path guard, audit, secrets
├── funera-orchestrate/ High-level builder API
│ ├── src/
│ │ ├── agent.rs Agent & AgentBuilder
│ │ ├── runtime.rs AgentRuntime & AgentRuntimeBuilder
│ │ ├── event.rs AgentEvent enum
│ │ ├── dispatcher.rs Callback dispatch
│ │ └── send_handle.rs Ownership handles
│ └── examples/ Example programs
├── builtin_tools/ Default tool implementations
│ └── src/
│ ├── read.rs ReadTool (file/dir, hashline output)
│ ├── write.rs WriteTool (auto parent dirs)
│ ├── edit.rs EditTool (hashline-anchored editing)
│ └── shell.rs ShellTool (cross-platform, timeout)
└── tui/ TUI frontend (stub)
MIT — see the repository for details.