15
15
from ...types .inference_pipelines .data_stream_params import ConfigLlmData
16
16
from .. import utils
17
17
from . import enums , steps , traces
18
+ from ..guardrails .base import GuardrailResult , GuardrailAction
18
19
19
20
logger = logging .getLogger (__name__ )
20
21
@@ -1200,7 +1201,7 @@ def _apply_input_guardrails(
1200
1201
guardrails : List [Any ],
1201
1202
inputs : Dict [str , Any ],
1202
1203
) -> Tuple [Dict [str , Any ], Dict [str , Any ]]:
1203
- """Apply guardrails to function inputs.
1204
+ """Apply guardrails to function inputs, creating guardrail steps .
1204
1205
1205
1206
Args:
1206
1207
guardrails: List of guardrail instances
@@ -1213,9 +1214,9 @@ def _apply_input_guardrails(
1213
1214
return inputs , {}
1214
1215
1215
1216
modified_inputs = inputs .copy ()
1216
- guardrail_metadata = {}
1217
+ overall_metadata = {}
1217
1218
1218
- for guardrail in guardrails :
1219
+ for i , guardrail in enumerate ( guardrails ) :
1219
1220
try :
1220
1221
# Import here to avoid circular imports
1221
1222
from ..guardrails .base import BaseGuardrail , GuardrailBlockedException
@@ -1227,50 +1228,112 @@ def _apply_input_guardrails(
1227
1228
if not guardrail .is_enabled ():
1228
1229
continue
1229
1230
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
+ )
1231
1266
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
+ )
1239
1300
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
+ }
1252
1318
1253
1319
except Exception as e :
1320
+ # Handle exceptions that occur outside the guardrail step context
1254
1321
if hasattr (e , "guardrail_name" ):
1255
- # Re-raise guardrail exceptions
1256
1322
raise
1257
1323
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
+ )
1266
1329
1267
- return modified_inputs , guardrail_metadata
1330
+ return modified_inputs , overall_metadata
1268
1331
1269
1332
1270
1333
def _apply_output_guardrails (
1271
1334
guardrails : List [Any ], output : Any , inputs : Dict [str , Any ]
1272
1335
) -> Tuple [Any , Dict [str , Any ]]:
1273
- """Apply guardrails to function output.
1336
+ """Apply guardrails to function output, creating guardrail steps .
1274
1337
1275
1338
Args:
1276
1339
guardrails: List of guardrail instances
@@ -1284,9 +1347,9 @@ def _apply_output_guardrails(
1284
1347
return output , {}
1285
1348
1286
1349
modified_output = output
1287
- guardrail_metadata = {}
1350
+ overall_metadata = {}
1288
1351
1289
- for guardrail in guardrails :
1352
+ for i , guardrail in enumerate ( guardrails ) :
1290
1353
try :
1291
1354
# Import here to avoid circular imports
1292
1355
from ..guardrails .base import BaseGuardrail , GuardrailBlockedException
@@ -1298,46 +1361,106 @@ def _apply_output_guardrails(
1298
1361
if not guardrail .is_enabled ():
1299
1362
continue
1300
1363
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
+ )
1302
1399
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
+ )
1310
1433
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 ])
1323
1451
1324
1452
except Exception as e :
1453
+ # Handle exceptions that occur outside the guardrail step context
1325
1454
if hasattr (e , "guardrail_name" ):
1326
- # Re-raise guardrail exceptions
1327
1455
raise
1328
1456
else :
1329
- # Log other exceptions but don't fail the trace
1330
1457
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 ,
1332
1461
)
1333
- guardrail_key = f"output_{ guardrail .name .lower ().replace (' ' , '_' )} "
1334
- guardrail_metadata [guardrail_key ] = {
1335
- "action" : "error" ,
1336
- "reason" : str (e ),
1337
- "metadata" : {},
1338
- }
1339
1462
1340
- return modified_output , guardrail_metadata
1463
+ return modified_output , overall_metadata
1341
1464
1342
1465
1343
1466
def _handle_guardrail_block (
0 commit comments