1use crate::{AsyncPolicy, Policy, ToolCall, Vote};
2use std::collections::HashSet;
3use std::future::Future;
4
5pub struct AllowList {
9 names: HashSet<String>,
10}
11
12impl AllowList {
13 pub fn new(names: impl IntoIterator<Item = impl Into<String>>) -> Self {
14 Self {
15 names: names.into_iter().map(Into::into).collect(),
16 }
17 }
18}
19
20impl Policy for AllowList {
21 fn name(&self) -> &str {
22 "allow_list"
23 }
24
25 fn vote(&self, call: &ToolCall) -> Vote {
26 if self.names.contains(&call.name) {
27 Vote::Allow
28 } else {
29 Vote::Abstain
30 }
31 }
32}
33
34pub struct DenyList {
37 names: HashSet<String>,
38}
39
40impl DenyList {
41 pub fn new(names: impl IntoIterator<Item = impl Into<String>>) -> Self {
42 Self {
43 names: names.into_iter().map(Into::into).collect(),
44 }
45 }
46}
47
48impl Policy for DenyList {
49 fn name(&self) -> &str {
50 "deny_list"
51 }
52
53 fn vote(&self, call: &ToolCall) -> Vote {
54 if self.names.contains(&call.name) {
55 Vote::Deny(format!("{} is on the deny list", call.name))
56 } else {
57 Vote::Abstain
58 }
59 }
60}
61
62pub struct ArgMatchesRegex {
67 tool: String,
68 argument: String,
69 pattern: regex::Regex,
70}
71
72impl ArgMatchesRegex {
73 pub fn new(
74 tool: impl Into<String>,
75 argument: impl Into<String>,
76 pattern: &str,
77 ) -> Result<Self, regex::Error> {
78 Ok(Self {
79 tool: tool.into(),
80 argument: argument.into(),
81 pattern: regex::Regex::new(pattern)?,
82 })
83 }
84}
85
86impl Policy for ArgMatchesRegex {
87 fn name(&self) -> &str {
88 "arg_matches_regex"
89 }
90
91 fn vote(&self, call: &ToolCall) -> Vote {
92 if call.name != self.tool {
93 return Vote::Abstain;
94 }
95 match call.argument_str(&self.argument) {
96 Some(value) if self.pattern.is_match(value) => Vote::Allow,
97 Some(value) => Vote::Deny(format!(
98 "{}={value:?} does not match /{}/",
99 self.argument,
100 self.pattern.as_str()
101 )),
102 None => Vote::Deny(format!(
103 "missing or non-string argument {:?}",
104 self.argument
105 )),
106 }
107 }
108}
109
110pub struct CustomAsyncPolicy<F> {
114 name: String,
115 vote: F,
116}
117
118impl<F, Fut> CustomAsyncPolicy<F>
119where
120 F: Fn(ToolCall) -> Fut + Send + Sync,
121 Fut: Future<Output = Vote> + Send,
122{
123 pub fn new(name: impl Into<String>, vote: F) -> Self {
124 Self {
125 name: name.into(),
126 vote,
127 }
128 }
129}
130
131#[async_trait::async_trait]
132impl<F, Fut> AsyncPolicy for CustomAsyncPolicy<F>
133where
134 F: Fn(ToolCall) -> Fut + Send + Sync,
135 Fut: Future<Output = Vote> + Send,
136{
137 fn name(&self) -> &str {
138 &self.name
139 }
140
141 async fn vote(&self, call: &ToolCall) -> Vote {
142 (self.vote)(call.clone()).await
143 }
144}