use rstructor::{Instructor, RStructorError, SchemaType};
use serde::{Deserialize, Serialize};
#[derive(Instructor, Serialize, Deserialize)]
#[llm(validate = "validate_weather")]
struct WeatherInfo {
#[llm(description = "City name to get weather for")]
city: String,
#[llm(description = "Current temperature in Celsius", example = 22.5)]
temperature: f32,
#[llm(description = "Weather description")]
description: Option<String>,
}
fn validate_weather(info: &WeatherInfo) -> rstructor::Result<()> {
if info.city.trim().is_empty() {
return Err(RStructorError::ValidationError(
"City name cannot be empty".to_string(),
));
}
if info.temperature < -100.0 || info.temperature > 70.0 {
return Err(RStructorError::ValidationError(format!(
"Temperature must be between -100°C and 70°C, got {}°C",
info.temperature
)));
}
Ok(())
}
#[tokio::main]
async fn main() -> rstructor::Result<()> {
let schema = WeatherInfo::schema();
println!("Weather Info Schema (auto-generated by Instructor derive):");
println!("{}", schema);
let valid_weather = WeatherInfo {
city: "Paris".to_string(),
temperature: 25.5,
description: Some("Sunny".to_string()),
};
println!("\nValidating valid weather info...");
match valid_weather.validate() {
Ok(_) => println!("Validation passed!"),
Err(e) => println!("Validation failed: {}", e),
}
println!("\nTesting validation with invalid data...");
let invalid_weather = WeatherInfo {
city: "Madrid".to_string(),
temperature: 120.0, description: Some("Extremely hot".to_string()),
};
match invalid_weather.validate() {
Ok(_) => println!("Validation passed (should not happen)"),
Err(e) => println!("Validation failed as expected: {}", e),
}
if let Ok(api_key) = std::env::var("OPENAI_API_KEY") {
println!("\nAPI key found! Trying an actual weather query with OpenAI...");
use rstructor::{LLMClient, OpenAIClient};
let client = OpenAIClient::new(api_key)?.temperature(0.0);
let prompt = "What's the weather like in Paris right now?";
println!("Prompt: {}", prompt);
match client.materialize::<WeatherInfo>(prompt).await {
Ok(weather) => {
println!("\nReceived weather info from OpenAI:");
println!("Weather for {}: {} °C", weather.city, weather.temperature);
if let Some(desc) = weather.description {
println!("Description: {}", desc);
}
println!("\nNote: The data has already been validated by the materialize method!");
}
Err(e) => println!("Error getting weather from OpenAI: {}", e),
}
} else {
println!("\nNo OPENAI_API_KEY found in environment variables.");
println!("To test with a real API call, set this variable and run again.");
}
println!("\nSample Weather Info:");
println!("{}", serde_json::to_string_pretty(&valid_weather).unwrap());
Ok(())
}