use guardflow::validators::NoPii;
use guardflow::{AsyncGuard, CustomAsyncValidator, Guard, guard_with_retry_async};
async fn passes_refund_policy(text: String) -> Result<(), String> {
if text.to_lowercase().contains("guaranteed refund") {
Err("promises a refund outside policy; only Tier 2 support can authorize refunds".into())
} else {
Ok(())
}
}
fn fake_llm_reply(attempt: usize) -> String {
match attempt {
1 => "Email us at support@ourcompany.com and we'll take a look.".to_string(),
2 => "I can offer you a guaranteed refund right now, no questions asked.".to_string(),
_ => "Thanks for reaching out — I've logged this and a specialist will follow up shortly."
.to_string(),
}
}
#[tokio::main]
async fn main() {
let base = Guard::new().with(NoPii::default());
let guard = AsyncGuard::from_guard(base).with_async(CustomAsyncValidator::new(
"refund_policy",
passes_refund_policy,
));
let mut attempt = 0;
let result = guard_with_retry_async(&guard, 3, |last_failure| {
attempt += 1;
if let Some(failure) = last_failure {
println!("attempt {attempt} rejected, retrying:");
for f in &failure.failures {
println!(" - {}: {}", f.validator, f.message);
}
}
let reply = fake_llm_reply(attempt);
async move { reply }
})
.await;
match result {
Ok(reply) => println!("\napproved reply (attempt {attempt}): {reply}"),
Err(failure) => println!("\ngave up after {attempt} attempts: {failure:?}"),
}
}