with_macros/
with_macros.rs

1/// Example: Complete BAML with All Macros
2///
3/// This example demonstrates the full power of BAML macros:
4/// - `#[derive(BamlSchema)]` for automatic class/enum IR generation
5/// - `#[baml_function]` for function definition
6/// - `#[derive(BamlClient)]` for client configuration
7///
8/// Compare this with the manual approach to see the dramatic improvement!
9///
10/// To run this example:
11/// ```bash
12/// export OPENAI_API_KEY="your-api-key-here"
13/// cargo run --example with_macros
14/// ```
15
16use simplify_baml::*;
17use simplify_baml_macros::{BamlSchema, BamlClient};
18use std::collections::HashMap;
19use std::env;
20
21// Step 1: Define types using derive macros
22#[derive(BamlSchema)]
23#[baml(description = "Calendar month of the year")]
24enum Month {
25    January,
26    February,
27    March,
28    April,
29    May,
30    June,
31    July,
32    August,
33    September,
34    October,
35    November,
36    December,
37}
38
39#[derive(BamlSchema)]
40#[baml(description = "Information about a person")]
41struct Person {
42    #[baml(description = "Full name of the person")]
43    name: String,
44
45    #[baml(description = "Age in years")]
46    age: i64,
47
48    #[baml(description = "Month of birth, if mentioned")]
49    #[baml(rename = "birthMonth")]
50    birth_month: Option<Month>,
51
52    #[baml(description = "Job title or profession, if mentioned")]
53    occupation: Option<String>,
54}
55
56// Step 2: Define BAML function using macro
57#[baml_function(client = "openai")]
58fn extract_person(
59    #[baml(description = "Text containing person information to extract")]
60    text: String
61) -> Person {
62    r#"Extract the person's information from the following text:
63
64{{ text }}
65
66Please extract: name, age, birth month (if mentioned), and occupation (if mentioned)."#
67}
68
69// Step 3: Define client using derive macro
70#[derive(BamlClient)]
71#[baml(provider = "OpenAI", model = "gpt-4o-mini")]
72struct OpenAIClient;
73
74#[tokio::main]
75async fn main() -> anyhow::Result<()> {
76    println!("=== BAML Complete Macro Example ===\n");
77    println!("✨ Using all three macros for maximum productivity!\n");
78
79    // Build IR automatically from types
80    let ir = BamlSchemaRegistry::new()
81        .register::<Month>()
82        .register::<Person>()
83        .build_with_functions(vec![extract_person()]);
84
85    println!("📊 IR built with {} classes, {} enums, {} functions",
86        ir.classes.len(),
87        ir.enums.len(),
88        ir.functions.len()
89    );
90
91    // Print what was generated
92    for class in &ir.classes {
93        println!("   - Class: {} ({} fields)", class.name, class.fields.len());
94    }
95    for enum_def in &ir.enums {
96        println!("   - Enum: {} ({} variants)", enum_def.name, enum_def.values.len());
97    }
98    for func in &ir.functions {
99        println!("   - Function: {} ({} inputs -> {})",
100            func.name,
101            func.inputs.len(),
102            func.output.to_string()
103        );
104    }
105    println!();
106
107    // Configure the LLM client using macro-generated struct
108    let api_key = env::var("OPENAI_API_KEY").unwrap_or_else(|_| {
109        eprintln!("Warning: OPENAI_API_KEY not set. Using mock mode.");
110        "mock".to_string()
111    });
112
113    let client = OpenAIClient::new(api_key.clone());
114
115    // Build the runtime
116    let runtime = RuntimeBuilder::new()
117        .ir(ir)
118        .client("openai", client)
119        .build();
120
121    // Prepare input parameters
122    let mut params = HashMap::new();
123    params.insert(
124        "text".to_string(),
125        BamlValue::String(
126            "John Smith is 30 years old and was born in March. He works as a software engineer."
127                .to_string(),
128        ),
129    );
130
131    println!("Input text: {}", params.get("text").unwrap().as_string().unwrap());
132    println!("\nExecuting BAML function 'ExtractPerson'...\n");
133
134    // Execute the function
135    match runtime.execute("ExtractPerson", params).await {
136        Ok(result) => {
137            println!("✅ Success! Parsed result:");
138            print_result(&result);
139        }
140        Err(e) => {
141            eprintln!("❌ Error: {}", e);
142            eprintln!("\nNote: If you haven't set OPENAI_API_KEY, this is expected.");
143            eprintln!("Set it with: export OPENAI_API_KEY='your-key-here'");
144        }
145    }
146
147    println!("\n=== Code Comparison ===");
148    println!("Manual approach:");
149    println!("  - Class definition: ~15 lines");
150    println!("  - Enum definition: ~15 lines");
151    println!("  - Function definition: ~20 lines");
152    println!("  - Client setup: ~3 lines");
153    println!("  - Total: ~53 lines");
154    println!();
155    println!("With macros:");
156    println!("  - #[derive(BamlSchema)] on struct: ~12 lines");
157    println!("  - #[derive(BamlSchema)] on enum: ~10 lines");
158    println!("  - #[baml_function]: ~8 lines");
159    println!("  - #[derive(BamlClient)]: ~3 lines");
160    println!("  - Total: ~33 lines");
161    println!();
162    println!("💡 Savings: ~38% less code, more readable, and type-safe!");
163    println!("💡 All macros now use consistent #[derive(...)] syntax!");
164
165    Ok(())
166}
167
168/// Pretty print the result
169fn print_result(value: &BamlValue) {
170    match value {
171        BamlValue::Map(map) => {
172            println!("{{");
173            for (key, val) in map {
174                print!("  {}: ", key);
175                match val {
176                    BamlValue::String(s) => println!("\"{}\"", s),
177                    BamlValue::Int(i) => println!("{}", i),
178                    BamlValue::Float(f) => println!("{}", f),
179                    BamlValue::Bool(b) => println!("{}", b),
180                    BamlValue::Null => println!("null"),
181                    _ => println!("{:?}", val),
182                }
183            }
184            println!("}}");
185        }
186        _ => println!("{:?}", value),
187    }
188}