use molo::agent::{Agent, MessageChunk};
use molo::provider::{FakeProvider, FakeReply};
use molo::tool::{SharedState, ToolError};
use molo::{ToolCall, ToolRegistry, react_agent};
use futures::StreamExt;
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Debug, Deserialize, JsonSchema)]
struct CalcArgs {
#[schemars(description = "The math expression to evaluate, e.g. \"1 + 2 * 3\"")]
expression: String,
}
#[molo::tool(
description = "Evaluates a math expression; supports basic arithmetic and parentheses"
)]
async fn calculator(args: CalcArgs) -> Result<String, ToolError> {
let value =
evalexpr::eval(&args.expression).map_err(|e| ToolError::Execution(e.to_string()))?;
Ok(value.to_string())
}
#[molo::tool(description = "Greets the user")]
async fn hello(name: String) -> Result<String, ToolError> {
Ok(format!("Hello, {name}!"))
}
#[molo::tool(description = "Returns a fixed demo text")]
async fn ping() -> Result<String, ToolError> {
Ok("pong".into())
}
#[molo::tool(description = "Accumulates the call count and returns the current counter")]
async fn counter(state: &SharedState) -> Result<String, ToolError> {
state.with_mut::<usize>(|n| *n += 1);
Ok(format!("count={}", state.get::<usize>().unwrap_or(0)))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let state = SharedState::new();
state.insert(0usize);
let mut registry = ToolRegistry::new();
registry
.register(Calculator)
.register(Hello)
.register(Ping)
.register(Counter);
let fake = FakeProvider::new([
FakeReply::ToolCalls {
content: "".into(),
calls: vec![
ToolCall {
id: "c1".into(),
name: "calculator".into(),
arguments: r#"{"expression":"(1 + 2) * 3"}"#.into(),
},
ToolCall {
id: "c2".into(),
name: "counter".into(),
arguments: "{}".into(),
},
],
},
FakeReply::ToolCalls {
content: "".into(),
calls: vec![ToolCall {
id: "c3".into(),
name: "hello".into(),
arguments: r#"{"name":"molo"}"#.into(),
}],
},
FakeReply::Text("task complete".into()),
]);
let mut agent = react_agent!(fake, registry, "You are an assistant").with_state(state);
let mut stream = agent.run_stream("start the task").await?;
while let Some(event) = stream.next().await {
match event? {
MessageChunk::ToolResult { name, content, .. } => println!("→ tool {name}: {content}"),
MessageChunk::Delta(delta) => println!("→ reply: {delta}"),
_ => {}
}
}
Ok(())
}