use async_trait::async_trait;
#[derive(Debug)]
pub enum GuardrailAction {
Allow,
Block(String),
Sanitize(String),
}
#[async_trait]
pub trait GuardrailRunner: Send + Sync {
async fn check_input(&self, text: &str) -> GuardrailAction;
async fn check_output(&self, text: &str) -> GuardrailAction;
}
pub struct NoopGuardrailRunner;
#[async_trait]
impl GuardrailRunner for NoopGuardrailRunner {
async fn check_input(&self, _text: &str) -> GuardrailAction {
GuardrailAction::Allow
}
async fn check_output(&self, _text: &str) -> GuardrailAction {
GuardrailAction::Allow
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_noop_allows_input() {
let runner = NoopGuardrailRunner;
let action = runner.check_input("some input text").await;
assert!(matches!(action, GuardrailAction::Allow));
}
#[tokio::test]
async fn test_noop_allows_output() {
let runner = NoopGuardrailRunner;
let action = runner.check_output("some output text").await;
assert!(matches!(action, GuardrailAction::Allow));
}
#[tokio::test]
async fn test_custom_block_guardrail() {
struct BlockAll;
#[async_trait]
impl GuardrailRunner for BlockAll {
async fn check_input(&self, _text: &str) -> GuardrailAction {
GuardrailAction::Block("blocked".to_string())
}
async fn check_output(&self, _text: &str) -> GuardrailAction {
GuardrailAction::Allow
}
}
let runner = BlockAll;
let action = runner.check_input("anything").await;
assert!(matches!(action, GuardrailAction::Block(_)));
if let GuardrailAction::Block(reason) = action {
assert_eq!(reason, "blocked");
}
}
#[tokio::test]
async fn test_custom_sanitize_guardrail() {
struct RedactPii;
#[async_trait]
impl GuardrailRunner for RedactPii {
async fn check_input(&self, text: &str) -> GuardrailAction {
GuardrailAction::Sanitize(text.replace("secret", "[REDACTED]"))
}
async fn check_output(&self, _text: &str) -> GuardrailAction {
GuardrailAction::Allow
}
}
let runner = RedactPii;
let action = runner.check_input("my secret is 42").await;
assert!(matches!(action, GuardrailAction::Sanitize(_)));
if let GuardrailAction::Sanitize(clean) = action {
assert_eq!(clean, "my [REDACTED] is 42");
}
}
}