@@ -974,27 +974,30 @@ class TestGuardrailFunctionality:
974
974
async def _wait_for_guardrail_tasks (self , session ):
975
975
"""Wait for all pending guardrail tasks to complete."""
976
976
import asyncio
977
+
977
978
if session ._guardrail_tasks :
978
979
await asyncio .gather (* session ._guardrail_tasks , return_exceptions = True )
979
980
980
981
@pytest .fixture
981
982
def triggered_guardrail (self ):
982
983
"""Creates a guardrail that always triggers"""
984
+
983
985
def guardrail_func (context , agent , output ):
984
986
return GuardrailFunctionOutput (
985
- output_info = {"reason" : "test trigger" },
986
- tripwire_triggered = True
987
+ output_info = {"reason" : "test trigger" }, tripwire_triggered = True
987
988
)
989
+
988
990
return OutputGuardrail (guardrail_function = guardrail_func , name = "triggered_guardrail" )
989
991
990
992
@pytest .fixture
991
993
def safe_guardrail (self ):
992
994
"""Creates a guardrail that never triggers"""
995
+
993
996
def guardrail_func (context , agent , output ):
994
997
return GuardrailFunctionOutput (
995
- output_info = {"reason" : "safe content" },
996
- tripwire_triggered = False
998
+ output_info = {"reason" : "safe content" }, tripwire_triggered = False
997
999
)
1000
+
998
1001
return OutputGuardrail (guardrail_function = guardrail_func , name = "safe_guardrail" )
999
1002
1000
1003
@pytest .mark .asyncio
@@ -1004,7 +1007,7 @@ async def test_transcript_delta_triggers_guardrail_at_threshold(
1004
1007
"""Test that guardrails run when transcript delta reaches debounce threshold"""
1005
1008
run_config : RealtimeRunConfig = {
1006
1009
"output_guardrails" : [triggered_guardrail ],
1007
- "guardrails_settings" : {"debounce_text_length" : 10 }
1010
+ "guardrails_settings" : {"debounce_text_length" : 10 },
1008
1011
}
1009
1012
1010
1013
session = RealtimeSession (mock_model , mock_agent , None , run_config = run_config )
@@ -1041,20 +1044,20 @@ async def test_transcript_delta_multiple_thresholds_same_item(
1041
1044
"""Test guardrails run at 1x, 2x, 3x thresholds for same item_id"""
1042
1045
run_config : RealtimeRunConfig = {
1043
1046
"output_guardrails" : [triggered_guardrail ],
1044
- "guardrails_settings" : {"debounce_text_length" : 5 }
1047
+ "guardrails_settings" : {"debounce_text_length" : 5 },
1045
1048
}
1046
1049
1047
1050
session = RealtimeSession (mock_model , mock_agent , None , run_config = run_config )
1048
1051
1049
1052
# First delta - reaches 1x threshold (5 chars)
1050
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1051
- item_id = "item_1" , delta = "12345" , response_id = "resp_1"
1052
- ))
1053
+ await session .on_event (
1054
+ RealtimeModelTranscriptDeltaEvent ( item_id = "item_1" , delta = "12345" , response_id = "resp_1" )
1055
+ )
1053
1056
1054
1057
# Second delta - reaches 2x threshold (10 chars total)
1055
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1056
- item_id = "item_1" , delta = "67890" , response_id = "resp_1"
1057
- ))
1058
+ await session .on_event (
1059
+ RealtimeModelTranscriptDeltaEvent ( item_id = "item_1" , delta = "67890" , response_id = "resp_1" )
1060
+ )
1058
1061
1059
1062
# Wait for async guardrail tasks to complete
1060
1063
await self ._wait_for_guardrail_tasks (session )
@@ -1070,28 +1073,32 @@ async def test_transcript_delta_different_items_tracked_separately(
1070
1073
"""Test that different item_ids are tracked separately for debouncing"""
1071
1074
run_config : RealtimeRunConfig = {
1072
1075
"output_guardrails" : [safe_guardrail ],
1073
- "guardrails_settings" : {"debounce_text_length" : 10 }
1076
+ "guardrails_settings" : {"debounce_text_length" : 10 },
1074
1077
}
1075
1078
1076
1079
session = RealtimeSession (mock_model , mock_agent , None , run_config = run_config )
1077
1080
1078
1081
# Add text to item_1 (8 chars - below threshold)
1079
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1080
- item_id = "item_1" , delta = "12345678" , response_id = "resp_1"
1081
- ))
1082
+ await session .on_event (
1083
+ RealtimeModelTranscriptDeltaEvent (
1084
+ item_id = "item_1" , delta = "12345678" , response_id = "resp_1"
1085
+ )
1086
+ )
1082
1087
1083
1088
# Add text to item_2 (8 chars - below threshold)
1084
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1085
- item_id = "item_2" , delta = "abcdefgh" , response_id = "resp_2"
1086
- ))
1089
+ await session .on_event (
1090
+ RealtimeModelTranscriptDeltaEvent (
1091
+ item_id = "item_2" , delta = "abcdefgh" , response_id = "resp_2"
1092
+ )
1093
+ )
1087
1094
1088
1095
# Neither should trigger guardrails yet
1089
1096
assert mock_model .interrupts_called == 0
1090
1097
1091
1098
# Add more text to item_1 (total 12 chars - above threshold)
1092
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1093
- item_id = "item_1" , delta = "90ab" , response_id = "resp_1"
1094
- ))
1099
+ await session .on_event (
1100
+ RealtimeModelTranscriptDeltaEvent ( item_id = "item_1" , delta = "90ab" , response_id = "resp_1" )
1101
+ )
1095
1102
1096
1103
# item_1 should have triggered guardrail run (but not interrupted since safe)
1097
1104
assert session ._item_guardrail_run_counts ["item_1" ] == 1
@@ -1107,15 +1114,17 @@ async def test_turn_ended_clears_guardrail_state(
1107
1114
"""Test that turn_ended event clears guardrail state for next turn"""
1108
1115
run_config : RealtimeRunConfig = {
1109
1116
"output_guardrails" : [triggered_guardrail ],
1110
- "guardrails_settings" : {"debounce_text_length" : 5 }
1117
+ "guardrails_settings" : {"debounce_text_length" : 5 },
1111
1118
}
1112
1119
1113
1120
session = RealtimeSession (mock_model , mock_agent , None , run_config = run_config )
1114
1121
1115
1122
# Trigger guardrail
1116
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1117
- item_id = "item_1" , delta = "trigger" , response_id = "resp_1"
1118
- ))
1123
+ await session .on_event (
1124
+ RealtimeModelTranscriptDeltaEvent (
1125
+ item_id = "item_1" , delta = "trigger" , response_id = "resp_1"
1126
+ )
1127
+ )
1119
1128
1120
1129
# Wait for async guardrail tasks to complete
1121
1130
await self ._wait_for_guardrail_tasks (session )
@@ -1132,31 +1141,30 @@ async def test_turn_ended_clears_guardrail_state(
1132
1141
assert len (session ._item_guardrail_run_counts ) == 0
1133
1142
1134
1143
@pytest .mark .asyncio
1135
- async def test_multiple_guardrails_all_triggered (
1136
- self , mock_model , mock_agent
1137
- ):
1144
+ async def test_multiple_guardrails_all_triggered (self , mock_model , mock_agent ):
1138
1145
"""Test that all triggered guardrails are included in the event"""
1146
+
1139
1147
def create_triggered_guardrail (name ):
1140
1148
def guardrail_func (context , agent , output ):
1141
- return GuardrailFunctionOutput (
1142
- output_info = {"name" : name },
1143
- tripwire_triggered = True
1144
- )
1149
+ return GuardrailFunctionOutput (output_info = {"name" : name }, tripwire_triggered = True )
1150
+
1145
1151
return OutputGuardrail (guardrail_function = guardrail_func , name = name )
1146
1152
1147
1153
guardrail1 = create_triggered_guardrail ("guardrail_1" )
1148
1154
guardrail2 = create_triggered_guardrail ("guardrail_2" )
1149
1155
1150
1156
run_config : RealtimeRunConfig = {
1151
1157
"output_guardrails" : [guardrail1 , guardrail2 ],
1152
- "guardrails_settings" : {"debounce_text_length" : 5 }
1158
+ "guardrails_settings" : {"debounce_text_length" : 5 },
1153
1159
}
1154
1160
1155
1161
session = RealtimeSession (mock_model , mock_agent , None , run_config = run_config )
1156
1162
1157
- await session .on_event (RealtimeModelTranscriptDeltaEvent (
1158
- item_id = "item_1" , delta = "trigger" , response_id = "resp_1"
1159
- ))
1163
+ await session .on_event (
1164
+ RealtimeModelTranscriptDeltaEvent (
1165
+ item_id = "item_1" , delta = "trigger" , response_id = "resp_1"
1166
+ )
1167
+ )
1160
1168
1161
1169
# Wait for async guardrail tasks to complete
1162
1170
await self ._wait_for_guardrail_tasks (session )
0 commit comments