use dotenvy::dotenv;
use langgraph::prelude::RunnableConfig;
use langgraph::tool;
use langgraph::prebuilt::{create_react_agent, prepare_tools, print_result, ReActAgentConfig};
use langgraph::providers::openai::{OpenAIModel, OpenAIModelConfig};
use std::sync::Arc;
fn load_openai_config() -> (String, Option<String>, String) {
dotenv().ok();
let api_key =
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY must be set in .env or environment");
let api_base = std::env::var("OPENAI_API_BASE").ok();
let model_name = std::env::var("OPENAI_MODEL").unwrap_or_else(|_| "mimo-v2.5-pro".to_string());
(api_key, api_base, model_name)
}
#[tool(
"get_weather",
"Get the current weather for a given location. Returns temperature, humidity and conditions."
)]
fn get_weather(location: String) -> Result<String, String> {
Ok(format!(
"Weather for {}: sunny, 22°C, humidity 45%, wind 10km/h",
location
))
}
#[tool(
"calculator",
"Evaluate a mathematical expression. Supports basic arithmetic: +, -, *, /"
)]
fn calculator(expression: String) -> Result<String, String> {
let result = match expression.replace(" ", "").as_str() {
"2+2" => "4",
"10*5" => "50",
"100/4" => "25",
_ => return Ok(format!("Cannot evaluate: {}", expression)),
};
Ok(format!("{} = {}", expression, result))
}
#[tool(
"search_knowledge",
"Search the internal knowledge base for information about a topic."
)]
fn search_knowledge(query: String) -> Result<String, String> {
let answer = match query.to_lowercase().as_str() {
q if q.contains("rust") => {
"Rust is a systems programming language focused on safety, speed, and concurrency."
}
q if q.contains("langgraph") => {
"LangGraph is a library for building stateful, multi-actor applications with LLMs."
}
_ => "No relevant information found in the knowledge base.",
};
Ok(answer.to_string())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("========================================");
println!(" LangGraph ReAct Agent (Macro Tools)");
println!("========================================\n");
let prepared = prepare_tools(vec![
Arc::new(GetWeather::new()),
Arc::new(Calculator::new()),
Arc::new(SearchKnowledge::new()),
]);
let (api_key, api_base, model_name) = load_openai_config();
let model = OpenAIModel::new(OpenAIModelConfig {
model: model_name,
api_key,
api_base,
temperature: Some(0.7),
..Default::default()
});
let agent = create_react_agent(
Arc::new(model),
prepared.tools,
Some(ReActAgentConfig {
system_prompt: Some(
"You are a helpful assistant with access to tools. \
Use the tools when needed to answer user questions."
.to_string(),
),
max_steps: Some(10),
handle_tool_errors: true,
}),
)?;
let queries = vec![
"What's the weather like in Beijing?",
"What is 100 divided by 4?",
"Tell me about the Rust programming language.",
"What's the weather in Shanghai and what is 2+2?",
];
for query in queries {
println!("--- Query: {} ---", query);
let input = serde_json::json!({
"messages": [{"type": "human", "content": query}]
});
let result = agent.ainvoke(&input, &RunnableConfig::new()).await?;
print_result(&result);
println!();
}
Ok(())
}