@@ -158,6 +158,122 @@ def test_escalate_action_is_mapped_with_app_and_recipient(self) -> None:
158158 assert action .version == 2
159159 assert action .assignee == "admin@example.com"
160160
161+ @pytest .mark .parametrize (
162+ "scope,scope_lower" ,
163+ [
164+ ("Llm" , "llm" ),
165+ ("Agent" , "agent" ),
166+ ],
167+ )
168+ def test_deterministic_guardrail_with_invalid_scope_raises_value_error (
169+ self , scope : str , scope_lower : str
170+ ) -> None :
171+ """DeterministicGuardrails with LLM or AGENT scope should raise ValueError."""
172+ guardrail = AgentCustomGuardrail .model_validate (
173+ {
174+ "$guardrailType" : "custom" ,
175+ "id" : f"test-{ scope_lower } -scope" ,
176+ "name" : f"test-guardrail-{ scope_lower } " ,
177+ "description" : f"Test guardrail with { scope } scope" ,
178+ "enabledForEvals" : True ,
179+ "selector" : {
180+ "$selectorType" : "scoped" ,
181+ "scopes" : [scope ], # Invalid scope - should be rejected
182+ "matchNames" : None ,
183+ },
184+ "rules" : [
185+ {
186+ "$ruleType" : "word" ,
187+ "fieldSelector" : {
188+ "$selectorType" : "specific" ,
189+ "fields" : [{"path" : "message.content" , "source" : "input" }],
190+ },
191+ "operator" : "contains" ,
192+ "value" : "forbidden" ,
193+ }
194+ ],
195+ "action" : {"$actionType" : "block" , "reason" : "test" },
196+ }
197+ )
198+
199+ with pytest .raises (
200+ ValueError ,
201+ match = rf"Deterministic guardrail 'test-guardrail-{ scope_lower } ' can only be used with TOOL scope.*Found invalid scopes.*{ scope .upper ()} " ,
202+ ):
203+ build_guardrails_with_actions ([guardrail ])
204+
205+ def test_deterministic_guardrail_with_tool_scope_succeeds (self ) -> None :
206+ """DeterministicGuardrails with TOOL scope should be accepted."""
207+ guardrail = AgentCustomGuardrail .model_validate (
208+ {
209+ "$guardrailType" : "custom" ,
210+ "id" : "test-tool-scope" ,
211+ "name" : "test-guardrail-tool" ,
212+ "description" : "Test guardrail with TOOL scope" ,
213+ "enabledForEvals" : True ,
214+ "selector" : {
215+ "$selectorType" : "scoped" ,
216+ "scopes" : ["Tool" ], # TOOL scope - should be accepted
217+ "matchNames" : ["my_tool" ],
218+ },
219+ "rules" : [
220+ {
221+ "$ruleType" : "word" ,
222+ "fieldSelector" : {
223+ "$selectorType" : "specific" ,
224+ "fields" : [{"path" : "message.content" , "source" : "input" }],
225+ },
226+ "operator" : "contains" ,
227+ "value" : "forbidden" ,
228+ }
229+ ],
230+ "action" : {"$actionType" : "block" , "reason" : "test" },
231+ }
232+ )
233+
234+ result = build_guardrails_with_actions ([guardrail ])
235+
236+ assert len (result ) == 1
237+ converted_guardrail , action = result [0 ]
238+ assert isinstance (converted_guardrail , DeterministicGuardrail )
239+ assert converted_guardrail .name == "test-guardrail-tool"
240+ assert isinstance (action , BlockAction )
241+
242+ def test_deterministic_guardrail_with_mixed_scopes_raises_value_error (self ) -> None :
243+ """DeterministicGuardrails with mixed scopes including non-TOOL should raise ValueError."""
244+ guardrail = AgentCustomGuardrail .model_validate (
245+ {
246+ "$guardrailType" : "custom" ,
247+ "id" : "test-mixed-scope" ,
248+ "name" : "test-guardrail-mixed" ,
249+ "description" : "Test guardrail with mixed scopes" ,
250+ "enabledForEvals" : True ,
251+ "selector" : {
252+ "$selectorType" : "scoped" ,
253+ "scopes" : ["Tool" , "Llm" ], # Mixed scopes - should be rejected
254+ "matchNames" : ["my_tool" ],
255+ },
256+ "rules" : [
257+ {
258+ "$ruleType" : "word" ,
259+ "fieldSelector" : {
260+ "$selectorType" : "specific" ,
261+ "fields" : [{"path" : "message.content" , "source" : "input" }],
262+ },
263+ "operator" : "contains" ,
264+ "value" : "forbidden" ,
265+ }
266+ ],
267+ "action" : {"$actionType" : "block" , "reason" : "test" },
268+ }
269+ )
270+
271+ with pytest .raises (
272+ ValueError ,
273+ match = r"Deterministic guardrail 'test-guardrail-mixed' can only be used with TOOL scope.*Found invalid scopes.*LLM" ,
274+ ):
275+ build_guardrails_with_actions ([guardrail ])
276+
161277
162278class TestCreateWordRuleFunc :
163279 """Tests for _create_word_rule_func."""
0 commit comments