use std::sync::Arc;
use agent_sdk::{
AgentEvent, AgentInput, CancellationToken, EventStore, InMemoryEventStore, ThreadId,
ToolContext, ToolRegistry, ToolResult, ToolTier, TypedTool, builder,
providers::AnthropicProvider,
};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
#[derive(Debug, Serialize, Deserialize)]
struct WeatherArgs {
city: String,
#[serde(default)]
unit: Unit,
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Unit {
#[default]
Celsius,
Fahrenheit,
}
struct WeatherTool;
impl TypedTool<()> for WeatherTool {
type Input = WeatherArgs;
fn name(&self) -> &'static str {
"get_weather"
}
fn display_name(&self) -> &'static str {
"Weather"
}
fn description(&self) -> &'static str {
"Get the current weather for a city. Always call this before answering weather questions."
}
fn input_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit (default: celsius)"
}
},
"required": ["city"]
})
}
fn tier(&self) -> ToolTier {
ToolTier::Observe
}
async fn execute(
&self,
_ctx: &ToolContext<()>,
input: WeatherArgs,
) -> anyhow::Result<ToolResult> {
let (temp, unit) = match input.unit {
Unit::Celsius => (18, "°C"),
Unit::Fahrenheit => (64, "°F"),
};
Ok(ToolResult::success(format!(
"{}: {temp}{unit}, light rain, wind 12 km/h",
input.city
)))
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let api_key = std::env::var("ANTHROPIC_API_KEY")
.map_err(|_| anyhow::anyhow!("ANTHROPIC_API_KEY environment variable must be set"))?;
let mut tools = ToolRegistry::new();
tools.register_typed(WeatherTool);
let event_store = Arc::new(InMemoryEventStore::new());
let agent = builder::<()>()
.provider(AnthropicProvider::sonnet(api_key))
.tools(tools)
.event_store(event_store.clone())
.build();
let thread_id = ThreadId::new();
let final_state = agent.run(
thread_id.clone(),
AgentInput::Text(
"What's the weather in Lisbon right now? Should I take an umbrella?".to_string(),
),
ToolContext::new(()),
CancellationToken::new(),
);
let _ = final_state.await?;
for envelope in event_store.get_events(&thread_id).await? {
match envelope.event {
AgentEvent::ToolCallStart { name, input, .. } => {
println!("→ tool call: {name}({input})");
}
AgentEvent::ToolCallEnd { name, result, .. } => {
println!("← tool result: {name} => {}", result.output);
}
AgentEvent::Text { text, .. } => {
println!("\nAgent: {text}");
}
AgentEvent::Done { total_turns, .. } => {
println!("\n(completed in {total_turns} turns)");
}
AgentEvent::Error { message, .. } => {
eprintln!("error: {message}");
}
_ => {}
}
}
Ok(())
}