use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GuardrailType {
#[serde(rename = "pre_agent")]
PreAgent,
#[serde(rename = "post_agent")]
PostAgent,
#[serde(rename = "pre_tool")]
PreTool,
#[serde(rename = "post_tool")]
PostTool,
#[serde(rename = "pre_workflow")]
PreWorkflow,
#[serde(rename = "post_workflow")]
PostWorkflow,
}
impl std::fmt::Display for GuardrailType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GuardrailType::PreAgent => write!(f, "pre_agent"),
GuardrailType::PostAgent => write!(f, "post_agent"),
GuardrailType::PreTool => write!(f, "pre_tool"),
GuardrailType::PostTool => write!(f, "post_tool"),
GuardrailType::PreWorkflow => write!(f, "pre_workflow"),
GuardrailType::PostWorkflow => write!(f, "post_workflow"),
}
}
}
impl GuardrailType {
pub fn parse_str(s: &str) -> Option<Self> {
match s {
"pre_agent" => Some(GuardrailType::PreAgent),
"post_agent" => Some(GuardrailType::PostAgent),
"pre_tool" => Some(GuardrailType::PreTool),
"post_tool" => Some(GuardrailType::PostTool),
"pre_workflow" => Some(GuardrailType::PreWorkflow),
"post_workflow" => Some(GuardrailType::PostWorkflow),
_ => None,
}
}
pub fn all() -> Vec<Self> {
vec![
GuardrailType::PreAgent,
GuardrailType::PostAgent,
GuardrailType::PreTool,
GuardrailType::PostTool,
GuardrailType::PreWorkflow,
GuardrailType::PostWorkflow,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ViolationAction {
#[serde(rename = "block")]
Block,
#[serde(rename = "warn")]
Warn,
#[serde(rename = "log")]
Log,
#[serde(rename = "alert")]
Alert,
}
impl std::fmt::Display for ViolationAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ViolationAction::Block => write!(f, "block"),
ViolationAction::Warn => write!(f, "warn"),
ViolationAction::Log => write!(f, "log"),
ViolationAction::Alert => write!(f, "alert"),
}
}
}
impl ViolationAction {
pub fn parse_str(s: &str) -> Option<Self> {
match s {
"block" => Some(ViolationAction::Block),
"warn" => Some(ViolationAction::Warn),
"log" => Some(ViolationAction::Log),
"alert" => Some(ViolationAction::Alert),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum LogicalOperator {
#[serde(rename = "and")]
#[default]
And,
#[serde(rename = "or")]
Or,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Condition {
Inline(String),
Code { language: String, content: String },
Composite {
operator: LogicalOperator,
conditions: Vec<Condition>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GuardrailDef {
pub name: String,
#[serde(rename = "type")]
pub guardrail_type: GuardrailType,
pub condition: Condition,
pub on_violation: ViolationAction,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub severity: Option<GuardrailSeverity>,
#[serde(default)]
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
pub enum GuardrailSeverity {
#[serde(rename = "info")]
Info,
#[serde(rename = "low")]
Low,
#[serde(rename = "medium")]
#[default]
Medium,
#[serde(rename = "high")]
High,
#[serde(rename = "critical")]
Critical,
}
impl std::fmt::Display for GuardrailSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GuardrailSeverity::Info => write!(f, "info"),
GuardrailSeverity::Low => write!(f, "low"),
GuardrailSeverity::Medium => write!(f, "medium"),
GuardrailSeverity::High => write!(f, "high"),
GuardrailSeverity::Critical => write!(f, "critical"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct GuardrailContext {
pub state: HashMap<String, serde_json::Value>,
pub agent_outputs: HashMap<String, String>,
pub current_agent: Option<String>,
pub current_tool: Option<String>,
pub tool_input: Option<String>,
pub tool_output: Option<String>,
pub workflow_inputs: HashMap<String, serde_json::Value>,
pub agent_output: Option<String>,
}
impl GuardrailContext {
pub fn new() -> Self {
Self::default()
}
pub fn with_state(
mut self,
key: impl Into<String>,
value: impl Into<serde_json::Value>,
) -> Self {
self.state.insert(key.into(), value.into());
self
}
pub fn with_agent_output(
mut self,
agent: impl Into<String>,
output: impl Into<String>,
) -> Self {
let agent_name = agent.into();
let output_str = output.into();
self.agent_outputs
.insert(agent_name.clone(), output_str.clone());
self.current_agent = Some(agent_name);
self.agent_output = Some(output_str);
self
}
pub fn with_current_agent(mut self, agent: impl Into<String>) -> Self {
self.current_agent = Some(agent.into());
self
}
pub fn with_current_tool(mut self, tool: impl Into<String>) -> Self {
self.current_tool = Some(tool.into());
self
}
pub fn with_tool_input(mut self, input: impl Into<String>) -> Self {
self.tool_input = Some(input.into());
self
}
pub fn with_workflow_input(
mut self,
key: impl Into<String>,
value: impl Into<serde_json::Value>,
) -> Self {
self.workflow_inputs.insert(key.into(), value.into());
self
}
pub fn to_json(&self) -> serde_json::Value {
let mut map = serde_json::Map::new();
map.insert(
"state".to_string(),
serde_json::to_value(&self.state).unwrap_or_default(),
);
map.insert(
"agent_outputs".to_string(),
serde_json::to_value(&self.agent_outputs).unwrap_or_default(),
);
map.insert(
"workflow_inputs".to_string(),
serde_json::to_value(&self.workflow_inputs).unwrap_or_default(),
);
if let Some(agent) = &self.current_agent {
map.insert("current_agent".to_string(), agent.clone().into());
}
if let Some(tool) = &self.current_tool {
map.insert("current_tool".to_string(), tool.clone().into());
}
if let Some(input) = &self.tool_input {
map.insert("tool_input".to_string(), input.clone().into());
}
if let Some(output) = &self.tool_output {
map.insert("tool_output".to_string(), output.clone().into());
}
if let Some(output) = &self.agent_output {
map.insert("agent_output".to_string(), output.clone().into());
}
serde_json::Value::Object(map)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EvaluationResult {
Pass,
Fail { reason: String },
Error { message: String },
}
impl EvaluationResult {
pub fn is_pass(&self) -> bool {
matches!(self, EvaluationResult::Pass)
}
pub fn is_fail(&self) -> bool {
matches!(self, EvaluationResult::Fail { .. })
}
pub fn is_error(&self) -> bool {
matches!(self, EvaluationResult::Error { .. })
}
}
#[derive(Debug, Clone)]
pub struct GuardrailOutcome {
pub guardrail_name: String,
pub guardrail_type: GuardrailType,
pub result: EvaluationResult,
pub action: ViolationAction,
pub timestamp: std::time::Instant,
pub evaluation_duration_ms: u64,
}
#[derive(Debug, Clone, Default)]
pub struct GuardrailSummary {
pub total_checked: usize,
pub passed: usize,
pub failed: usize,
pub errors: usize,
pub blocked: usize,
pub warnings: usize,
pub outcomes: Vec<GuardrailOutcome>,
}
impl GuardrailSummary {
pub fn should_block(&self) -> bool {
self.outcomes
.iter()
.any(|o| matches!(o.action, ViolationAction::Block) && o.result.is_fail())
}
pub fn blocking_violations(&self) -> Vec<&GuardrailOutcome> {
self.outcomes
.iter()
.filter(|o| matches!(o.action, ViolationAction::Block) && o.result.is_fail())
.collect()
}
pub fn warnings(&self) -> Vec<&GuardrailOutcome> {
self.outcomes
.iter()
.filter(|o| matches!(o.action, ViolationAction::Warn) && o.result.is_fail())
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuardrailTelemetryEvent {
pub timestamp: String,
pub guardrail_name: String,
pub guardrail_type: String,
pub result: String,
pub action: String,
pub duration_ms: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub failure_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workflow_name: Option<String>,
}
#[cfg(test)]
#[path = "../../../tests/unit/swl/guardrails/types/types_test.rs"]
mod tests;