1515from ...types .inference_pipelines .data_stream_params import ConfigLlmData
1616from .. import utils
1717from . import enums , steps , traces
18+ from ..guardrails .base import GuardrailResult , GuardrailAction
1819
1920logger = logging .getLogger (__name__ )
2021
@@ -1200,7 +1201,7 @@ def _apply_input_guardrails(
12001201 guardrails : List [Any ],
12011202 inputs : Dict [str , Any ],
12021203) -> Tuple [Dict [str , Any ], Dict [str , Any ]]:
1203- """Apply guardrails to function inputs.
1204+ """Apply guardrails to function inputs, creating guardrail steps .
12041205
12051206 Args:
12061207 guardrails: List of guardrail instances
@@ -1213,9 +1214,9 @@ def _apply_input_guardrails(
12131214 return inputs , {}
12141215
12151216 modified_inputs = inputs .copy ()
1216- guardrail_metadata = {}
1217+ overall_metadata = {}
12171218
1218- for guardrail in guardrails :
1219+ for i , guardrail in enumerate ( guardrails ) :
12191220 try :
12201221 # Import here to avoid circular imports
12211222 from ..guardrails .base import BaseGuardrail , GuardrailBlockedException
@@ -1227,50 +1228,112 @@ def _apply_input_guardrails(
12271228 if not guardrail .is_enabled ():
12281229 continue
12291230
1230- result = guardrail .check_input (modified_inputs )
1231+ # Create a guardrail step for this check
1232+ with create_step (
1233+ name = f"{ guardrail .name } - Input" ,
1234+ step_type = enums .StepType .GUARDRAIL ,
1235+ ) as guardrail_step :
1236+ try :
1237+ # Apply the guardrail
1238+ result = guardrail .check_input (modified_inputs )
1239+
1240+ # Store guardrail metadata for main function step
1241+ guardrail_key = f"input_{ guardrail .name .lower ().replace (' ' , '_' )} "
1242+ overall_metadata [guardrail_key ] = {
1243+ "action" : result .action .value ,
1244+ "reason" : result .reason ,
1245+ "metadata" : result .metadata or {},
1246+ }
1247+
1248+ # Prepare step logging data
1249+ step_log_data = {
1250+ "action" : result .action .value ,
1251+ "reason" : result .reason ,
1252+ "data_type" : "input" ,
1253+ "inputs" : {"original_data" : modified_inputs },
1254+ }
1255+
1256+ if result .action .value == "block" :
1257+ # Handle the block according to strategy
1258+ final_inputs , block_metadata = _handle_guardrail_block (
1259+ guardrail = guardrail ,
1260+ result = result ,
1261+ modified_inputs = modified_inputs ,
1262+ guardrail_metadata = overall_metadata ,
1263+ guardrail_key = guardrail_key ,
1264+ is_input = True ,
1265+ )
12311266
1232- # Store guardrail metadata
1233- guardrail_key = f"input_{ guardrail .name .lower ().replace (' ' , '_' )} "
1234- guardrail_metadata [guardrail_key ] = {
1235- "action" : result .action .value ,
1236- "reason" : result .reason ,
1237- "metadata" : result .metadata or {},
1238- }
1267+ # Add final output if different
1268+ if final_inputs != modified_inputs :
1269+ step_log_data ["output" ] = final_inputs
1270+
1271+ # Log once with all data
1272+ guardrail_step .log (** step_log_data )
1273+ return final_inputs , overall_metadata
1274+
1275+ elif (
1276+ result .action .value == "modify"
1277+ and result .modified_data is not None
1278+ ):
1279+ step_log_data ["output" ] = result .modified_data
1280+ modified_inputs = result .modified_data
1281+ logger .debug ("Guardrail %s modified inputs" , guardrail .name )
1282+
1283+ else : # allow
1284+ step_log_data ["output" ] = modified_inputs
1285+
1286+ # Single log call with all data
1287+ guardrail_step .log (** step_log_data )
1288+
1289+ except Exception as e :
1290+ # Create error result for the guardrail step
1291+ error_result = GuardrailResult (
1292+ action = GuardrailAction .ALLOW , # Default to allow on error
1293+ reason = f"Guardrail error: { str (e )} " ,
1294+ metadata = {"error" : str (e ), "error_type" : type (e ).__name__ },
1295+ )
1296+ guardrail_step .log (
1297+ inputs = {"original_data" : modified_inputs },
1298+ output = modified_inputs ,
1299+ )
12391300
1240- if result .action .value == "block" :
1241- return _handle_guardrail_block (
1242- guardrail = guardrail ,
1243- result = result ,
1244- modified_inputs = modified_inputs ,
1245- guardrail_metadata = guardrail_metadata ,
1246- guardrail_key = guardrail_key ,
1247- is_input = True ,
1248- )
1249- elif result .action .value == "modify" and result .modified_data is not None :
1250- modified_inputs = result .modified_data
1251- logger .debug ("Guardrail %s modified inputs" , guardrail .name )
1301+ if hasattr (e , "guardrail_name" ):
1302+ # Re-raise guardrail exceptions
1303+ raise
1304+ else :
1305+ # Log other exceptions but don't fail the trace
1306+ logger .error (
1307+ "Error applying input guardrail %s: %s" , guardrail .name , e
1308+ )
1309+ guardrail_key = (
1310+ f"input_{ guardrail .name .lower ().replace (' ' , '_' )} "
1311+ )
1312+ overall_metadata [guardrail_key ] = {
1313+ "action" : "error" ,
1314+ "reason" : str (e ),
1315+ "metadata" : {"error_type" : type (e ).__name__ },
1316+ "guardrail_name" : guardrail .name ,
1317+ }
12521318
12531319 except Exception as e :
1320+ # Handle exceptions that occur outside the guardrail step context
12541321 if hasattr (e , "guardrail_name" ):
1255- # Re-raise guardrail exceptions
12561322 raise
12571323 else :
1258- # Log other exceptions but don't fail the trace
1259- logger .error ("Error applying input guardrail %s: %s" , guardrail .name , e )
1260- guardrail_key = f"input_{ guardrail .name .lower ().replace (' ' , '_' )} "
1261- guardrail_metadata [guardrail_key ] = {
1262- "action" : "error" ,
1263- "reason" : str (e ),
1264- "metadata" : {},
1265- }
1324+ logger .error (
1325+ "Error setting up input guardrail %s: %s" ,
1326+ getattr (guardrail , "name" , f"guardrail_{ i } " ),
1327+ e ,
1328+ )
12661329
1267- return modified_inputs , guardrail_metadata
1330+ return modified_inputs , overall_metadata
12681331
12691332
12701333def _apply_output_guardrails (
12711334 guardrails : List [Any ], output : Any , inputs : Dict [str , Any ]
12721335) -> Tuple [Any , Dict [str , Any ]]:
1273- """Apply guardrails to function output.
1336+ """Apply guardrails to function output, creating guardrail steps .
12741337
12751338 Args:
12761339 guardrails: List of guardrail instances
@@ -1284,9 +1347,9 @@ def _apply_output_guardrails(
12841347 return output , {}
12851348
12861349 modified_output = output
1287- guardrail_metadata = {}
1350+ overall_metadata = {}
12881351
1289- for guardrail in guardrails :
1352+ for i , guardrail in enumerate ( guardrails ) :
12901353 try :
12911354 # Import here to avoid circular imports
12921355 from ..guardrails .base import BaseGuardrail , GuardrailBlockedException
@@ -1298,46 +1361,106 @@ def _apply_output_guardrails(
12981361 if not guardrail .is_enabled ():
12991362 continue
13001363
1301- result = guardrail .check_output (modified_output , inputs )
1364+ # Create a guardrail step for this check
1365+ with create_step (
1366+ name = f"{ guardrail .name } - Output" ,
1367+ step_type = enums .StepType .GUARDRAIL ,
1368+ ) as guardrail_step :
1369+ try :
1370+ # Apply the guardrail
1371+ result = guardrail .check_output (modified_output , inputs )
1372+
1373+ # Store guardrail metadata for main function step
1374+ guardrail_key = f"output_{ guardrail .name .lower ().replace (' ' , '_' )} "
1375+ overall_metadata [guardrail_key ] = {
1376+ "action" : result .action .value ,
1377+ "reason" : result .reason ,
1378+ "metadata" : result .metadata or {},
1379+ }
1380+
1381+ # Prepare step logging data
1382+ step_log_data = {
1383+ "action" : result .action .value ,
1384+ "reason" : result .reason ,
1385+ "data_type" : "output" ,
1386+ "inputs" : {"original_data" : modified_output },
1387+ }
1388+
1389+ if result .action .value == "block" :
1390+ # Handle the block according to strategy
1391+ final_output , block_metadata = _handle_guardrail_block (
1392+ guardrail = guardrail ,
1393+ result = result ,
1394+ modified_output = modified_output ,
1395+ guardrail_metadata = overall_metadata ,
1396+ guardrail_key = guardrail_key ,
1397+ is_input = False ,
1398+ )
13021399
1303- # Store guardrail metadata
1304- guardrail_key = f"output_{ guardrail .name .lower ().replace (' ' , '_' )} "
1305- guardrail_metadata [guardrail_key ] = {
1306- "action" : result .action .value ,
1307- "reason" : result .reason ,
1308- "metadata" : result .metadata or {},
1309- }
1400+ # Add final output if different
1401+ if final_output != modified_output :
1402+ step_log_data ["output" ] = final_output
1403+
1404+ # Log once with all data
1405+ guardrail_step .log (** step_log_data )
1406+ return final_output , overall_metadata
1407+
1408+ elif (
1409+ result .action .value == "modify"
1410+ and result .modified_data is not None
1411+ ):
1412+ step_log_data ["output" ] = result .modified_data
1413+ modified_output = result .modified_data
1414+ logger .debug ("Guardrail %s modified output" , guardrail .name )
1415+
1416+ else : # allow
1417+ step_log_data ["output" ] = modified_output
1418+
1419+ # Single log call with all data
1420+ guardrail_step .log (** step_log_data )
1421+
1422+ except Exception as e :
1423+ # Create error result for the guardrail step
1424+ error_result = GuardrailResult (
1425+ action = GuardrailAction .ALLOW , # Default to allow on error
1426+ reason = f"Guardrail error: { str (e )} " ,
1427+ metadata = {"error" : str (e ), "error_type" : type (e ).__name__ },
1428+ )
1429+ guardrail_step .log (
1430+ inputs = {"original_data" : modified_output },
1431+ output = modified_output ,
1432+ )
13101433
1311- if result .action .value == "block" :
1312- return _handle_guardrail_block (
1313- guardrail = guardrail ,
1314- result = result ,
1315- modified_output = modified_output ,
1316- guardrail_metadata = guardrail_metadata ,
1317- guardrail_key = guardrail_key ,
1318- is_input = False ,
1319- )
1320- elif result .action .value == "modify" and result .modified_data is not None :
1321- modified_output = result .modified_data
1322- logger .debug ("Guardrail %s modified output" , guardrail .name )
1434+ if hasattr (e , "guardrail_name" ):
1435+ # Re-raise guardrail exceptions
1436+ raise
1437+ else :
1438+ # Log other exceptions but don't fail the trace
1439+ logger .error (
1440+ "Error applying output guardrail %s: %s" , guardrail .name , e
1441+ )
1442+ guardrail_key = (
1443+ f"output_{ guardrail .name .lower ().replace (' ' , '_' )} "
1444+ )
1445+ overall_metadata [guardrail_key ] = {
1446+ "action" : "error" ,
1447+ "reason" : str (e ),
1448+ "metadata" : {"error_type" : type (e ).__name__ },
1449+ }
1450+ guardrail_step .log (** overall_metadata [guardrail_key ])
13231451
13241452 except Exception as e :
1453+ # Handle exceptions that occur outside the guardrail step context
13251454 if hasattr (e , "guardrail_name" ):
1326- # Re-raise guardrail exceptions
13271455 raise
13281456 else :
1329- # Log other exceptions but don't fail the trace
13301457 logger .error (
1331- "Error applying output guardrail %s: %s" , guardrail .name , e
1458+ "Error setting up output guardrail %s: %s" ,
1459+ getattr (guardrail , "name" , f"guardrail_{ i } " ),
1460+ e ,
13321461 )
1333- guardrail_key = f"output_{ guardrail .name .lower ().replace (' ' , '_' )} "
1334- guardrail_metadata [guardrail_key ] = {
1335- "action" : "error" ,
1336- "reason" : str (e ),
1337- "metadata" : {},
1338- }
13391462
1340- return modified_output , guardrail_metadata
1463+ return modified_output , overall_metadata
13411464
13421465
13431466def _handle_guardrail_block (
0 commit comments