1mod execution_policy;
36mod filters;
37mod tenant_policy;
38mod tool_policy;
39
40mod input_processor;
42mod long_running;
43mod pii_input;
44
45pub use execution_policy::{ExecutionLimits, ExecutionPolicy};
46pub use filters::{ContentFilter, FilterAction, FilterResult};
47pub use long_running::{
48 CheckpointPolicy, ContextStrategy, LongRunningExecutionPolicy, WorkingMemoryPolicy,
49};
50pub use tenant_policy::{FeatureFlags, TenantLimits, TenantPolicy};
51pub use tool_policy::{ToolPermissions, ToolPolicy, ToolTrustLevel};
52
53pub use input_processor::{InputProcessor, InputProcessorPipeline, InputProcessorResult};
55pub use pii_input::{PiiInputMode, PiiInputProcessor};
56
57#[derive(Debug, Clone)]
59pub enum PolicyDecision {
60 Allow,
62 Deny { reason: String },
64 Warn { message: String },
66}
67
68impl PolicyDecision {
69 pub fn is_allowed(&self) -> bool {
70 matches!(self, PolicyDecision::Allow | PolicyDecision::Warn { .. })
71 }
72
73 pub fn is_denied(&self) -> bool {
74 matches!(self, PolicyDecision::Deny { .. })
75 }
76}
77
78pub trait PolicyEvaluator: Send + Sync {
80 fn evaluate(&self, context: &PolicyContext) -> PolicyDecision;
82}
83
84#[derive(Debug, Clone)]
86pub struct PolicyContext {
87 pub tenant_id: Option<String>,
89 pub user_id: Option<String>,
91 pub action: PolicyAction,
93 pub metadata: std::collections::HashMap<String, String>,
95}
96
97#[derive(Debug, Clone)]
99pub enum PolicyAction {
100 StartExecution { graph_id: Option<String> },
102 InvokeTool { tool_name: String },
104 LlmCall { model: String },
106 ExternalAccess { resource: String },
108 OutputContent { content_type: String },
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
119 fn test_policy_decision_allow() {
120 let decision = PolicyDecision::Allow;
121 assert!(decision.is_allowed());
122 assert!(!decision.is_denied());
123 }
124
125 #[test]
126 fn test_policy_decision_deny() {
127 let decision = PolicyDecision::Deny {
128 reason: "Not authorized".to_string(),
129 };
130 assert!(!decision.is_allowed());
131 assert!(decision.is_denied());
132 }
133
134 #[test]
135 fn test_policy_decision_warn() {
136 let decision = PolicyDecision::Warn {
137 message: "Proceed with caution".to_string(),
138 };
139 assert!(decision.is_allowed());
141 assert!(!decision.is_denied());
142 }
143
144 #[test]
147 fn test_policy_context_creation() {
148 let mut metadata = std::collections::HashMap::new();
149 metadata.insert("key".to_string(), "value".to_string());
150
151 let context = PolicyContext {
152 tenant_id: Some("tenant-123".to_string()),
153 user_id: Some("user-456".to_string()),
154 action: PolicyAction::StartExecution {
155 graph_id: Some("graph-789".to_string()),
156 },
157 metadata,
158 };
159
160 assert_eq!(context.tenant_id.as_ref().unwrap(), "tenant-123");
161 assert_eq!(context.user_id.as_ref().unwrap(), "user-456");
162 assert!(matches!(
163 context.action,
164 PolicyAction::StartExecution { .. }
165 ));
166 }
167
168 #[test]
169 fn test_policy_action_variants() {
170 let start = PolicyAction::StartExecution { graph_id: None };
171 assert!(matches!(start, PolicyAction::StartExecution { .. }));
172
173 let invoke = PolicyAction::InvokeTool {
174 tool_name: "web_search".to_string(),
175 };
176 assert!(matches!(invoke, PolicyAction::InvokeTool { .. }));
177
178 let llm = PolicyAction::LlmCall {
179 model: "gpt-4".to_string(),
180 };
181 assert!(matches!(llm, PolicyAction::LlmCall { .. }));
182
183 let external = PolicyAction::ExternalAccess {
184 resource: "https://api.example.com".to_string(),
185 };
186 assert!(matches!(external, PolicyAction::ExternalAccess { .. }));
187
188 let output = PolicyAction::OutputContent {
189 content_type: "text/plain".to_string(),
190 };
191 assert!(matches!(output, PolicyAction::OutputContent { .. }));
192 }
193}