55import logging
66import uuid
77from datetime import datetime , timezone
8- from typing import Any , Literal , Optional , Union
8+ from typing import Any , Literal
99
1010from pydantic import BaseModel , ConfigDict
1111from sqlalchemy import (
6464MAX_IDENTIFIER_VALUE_LENGTH : int = 80
6565
6666
67- def _ensure_utc (dt : Optional [ datetime ] ) -> Optional [ datetime ] :
67+ def _ensure_utc (dt : datetime | None ) -> datetime | None :
6868 """
6969 Attach UTC tzinfo to a naive datetime (as returned by SQLite).
7070
7171 Args:
72- dt (Optional[ datetime] ): The datetime to normalize, or None.
72+ dt (datetime | None ): The datetime to normalize, or None.
7373
7474 Returns:
75- Optional[ datetime] : The datetime with UTC tzinfo attached if it was naive, or None.
75+ datetime | None : The datetime with UTC tzinfo attached if it was naive, or None.
7676 """
7777 if dt is not None and dt .tzinfo is None :
7878 return dt .replace (tzinfo = timezone .utc )
@@ -103,7 +103,7 @@ def load_dialect_impl(self, dialect: Any) -> Any:
103103 return dialect .type_descriptor (CHAR (36 ))
104104 return dialect .type_descriptor (Uuid ())
105105
106- def process_bind_param (self , value : Optional [ uuid .UUID ] , dialect : Any ) -> Optional [ str ] :
106+ def process_bind_param (self , value : uuid .UUID | None , dialect : Any ) -> str | None :
107107 """
108108 Process a parameter value before binding it to a database statement.
109109
@@ -116,7 +116,7 @@ def process_bind_param(self, value: Optional[uuid.UUID], dialect: Any) -> Option
116116 """
117117 return str (value ) if value else None
118118
119- def process_result_value (self , value : uuid .UUID | str | None , dialect : Any ) -> Optional [ uuid .UUID ] :
119+ def process_result_value (self , value : uuid .UUID | str | None , dialect : Any ) -> uuid .UUID | None :
120120 """
121121 Process a result value after it has been retrieved from the database.
122122
@@ -187,9 +187,9 @@ class PromptMemoryEntry(Base):
187187 sequence = mapped_column (INTEGER , nullable = False )
188188 timestamp = mapped_column (DateTime , nullable = False )
189189 labels : Mapped [dict [str , str ]] = mapped_column (JSON )
190- prompt_metadata : Mapped [dict [str , Union [ str , int ] ]] = mapped_column (JSON )
191- targeted_harm_categories : Mapped [Optional [ list [str ]] ] = mapped_column (JSON )
192- converter_identifiers : Mapped [Optional [ list [dict [str , str ]]] ] = mapped_column (JSON )
190+ prompt_metadata : Mapped [dict [str , str | int ]] = mapped_column (JSON )
191+ targeted_harm_categories : Mapped [list [str ] | None ] = mapped_column (JSON )
192+ converter_identifiers : Mapped [list [dict [str , str ]] | None ] = mapped_column (JSON )
193193 prompt_target_identifier : Mapped [dict [str , str ]] = mapped_column (JSON )
194194 attack_identifier : Mapped [dict [str , str ]] = mapped_column (JSON )
195195 response_error : Mapped [Literal ["blocked" , "none" , "processing" , "unknown" ]] = mapped_column (String , nullable = True )
@@ -268,7 +268,7 @@ def get_message_piece(self) -> MessagePiece:
268268 MessagePiece: The reconstructed message piece with all its data and scores.
269269 """
270270 # Reconstruct ComponentIdentifiers with the stored pyrit_version
271- converter_ids : Optional [ list [ComponentIdentifier ]] = None
271+ converter_ids : list [ComponentIdentifier ] | None = None
272272 stored_version = self .pyrit_version or LEGACY_PYRIT_VERSION
273273 if self .converter_identifiers :
274274 converter_ids = [
@@ -277,14 +277,14 @@ def get_message_piece(self) -> MessagePiece:
277277 ]
278278
279279 # Reconstruct ComponentIdentifier with the stored pyrit_version
280- target_id : Optional [ ComponentIdentifier ] = None
280+ target_id : ComponentIdentifier | None = None
281281 if self .prompt_target_identifier :
282282 target_id = ComponentIdentifier .from_dict (
283283 {** self .prompt_target_identifier , "pyrit_version" : stored_version }
284284 )
285285
286286 # Reconstruct ComponentIdentifier with the stored pyrit_version
287- attack_id : Optional [ ComponentIdentifier ] = None
287+ attack_id : ComponentIdentifier | None = None
288288 if self .attack_identifier :
289289 attack_id = ComponentIdentifier .from_dict ({** self .attack_identifier , "pyrit_version" : stored_version })
290290
@@ -370,9 +370,9 @@ class ScoreEntry(Base):
370370 score_value = mapped_column (String , nullable = False )
371371 score_value_description = mapped_column (String , nullable = True )
372372 score_type : Mapped [Literal ["true_false" , "float_scale" , "unknown" ]] = mapped_column (String , nullable = False )
373- score_category : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
373+ score_category : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
374374 score_rationale = mapped_column (String , nullable = True )
375- score_metadata : Mapped [Optional [ dict [str , Union [ str , int , float ]]]] = mapped_column (JSON , nullable = True )
375+ score_metadata : Mapped [dict [str , str | int | float ]] = mapped_column (JSON )
376376 scorer_class_identifier : Mapped [dict [str , Any ]] = mapped_column (JSON )
377377 prompt_request_response_id = mapped_column (CustomUUID , ForeignKey (f"{ PromptMemoryEntry .__tablename__ } .id" ))
378378 timestamp = mapped_column (DateTime , nullable = False )
@@ -396,7 +396,7 @@ def __init__(self, *, entry: Score) -> None:
396396 self .score_type = entry .score_type
397397 self .score_category = entry .score_category
398398 self .score_rationale = entry .score_rationale
399- self .score_metadata = entry .score_metadata
399+ self .score_metadata = entry .score_metadata or {}
400400 normalized_scorer = entry .scorer_class_identifier
401401 # Ensure eval_hash is set before truncation so it survives the DB round-trip
402402 if normalized_scorer .eval_hash is None :
@@ -546,18 +546,18 @@ class SeedEntry(Base):
546546 data_type : Mapped [PromptDataType ] = mapped_column (String , nullable = False )
547547 name = mapped_column (String , nullable = True )
548548 dataset_name = mapped_column (String , nullable = True )
549- harm_categories : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
549+ harm_categories : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
550550 description = mapped_column (String , nullable = True )
551- authors : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
552- groups : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
551+ authors : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
552+ groups : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
553553 source = mapped_column (String , nullable = True )
554554 date_added = mapped_column (DateTime , nullable = False )
555555 added_by = mapped_column (String , nullable = False )
556- prompt_metadata : Mapped [Optional [ dict [str , Union [ str , int ]]] ] = mapped_column (JSON , nullable = True )
557- parameters : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
558- prompt_group_id : Mapped [Optional [ uuid .UUID ] ] = mapped_column (CustomUUID , nullable = True )
559- sequence : Mapped [Optional [ int ] ] = mapped_column (INTEGER , nullable = True )
560- role : Mapped [Optional [ ChatMessageRole ] ] = mapped_column (String , nullable = True )
556+ prompt_metadata : Mapped [dict [str , str | int ] | None ] = mapped_column (JSON , nullable = True )
557+ parameters : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
558+ prompt_group_id : Mapped [uuid .UUID | None ] = mapped_column (CustomUUID , nullable = True )
559+ sequence : Mapped [int | None ] = mapped_column (INTEGER , nullable = True )
560+ role : Mapped [ChatMessageRole | None ] = mapped_column (String , nullable = True )
561561 seed_type : Mapped [SeedType ] = mapped_column (String , nullable = False , default = "prompt" )
562562
563563 def __init__ (self , * , entry : Seed ) -> None :
@@ -709,12 +709,12 @@ class AttackResultEntry(Base):
709709 conversation_id = mapped_column (String , nullable = False )
710710 objective = mapped_column (Unicode , nullable = False )
711711 attack_identifier : Mapped [dict [str , str ]] = mapped_column (JSON , nullable = False )
712- atomic_attack_identifier : Mapped [Optional [ dict [str , Any ]] ] = mapped_column (JSON , nullable = True )
712+ atomic_attack_identifier : Mapped [dict [str , Any ] | None ] = mapped_column (JSON , nullable = True )
713713 objective_sha256 = mapped_column (String , nullable = True )
714- last_response_id : Mapped [Optional [ uuid .UUID ] ] = mapped_column (
714+ last_response_id : Mapped [uuid .UUID | None ] = mapped_column (
715715 CustomUUID , ForeignKey (f"{ PromptMemoryEntry .__tablename__ } .id" ), nullable = True
716716 )
717- last_score_id : Mapped [Optional [ uuid .UUID ] ] = mapped_column (
717+ last_score_id : Mapped [uuid .UUID | None ] = mapped_column (
718718 CustomUUID , ForeignKey (f"{ ScoreEntry .__tablename__ } .id" ), nullable = True
719719 )
720720 executed_turns = mapped_column (INTEGER , nullable = False , default = 0 )
@@ -723,10 +723,10 @@ class AttackResultEntry(Base):
723723 String , nullable = False , default = "undetermined"
724724 )
725725 outcome_reason = mapped_column (String , nullable = True )
726- attack_metadata : Mapped [dict [str , Union [ str , int , float , bool ] ]] = mapped_column (JSON , nullable = True )
726+ attack_metadata : Mapped [dict [str , str | int | float | bool ]] = mapped_column (JSON , nullable = True )
727727 labels : Mapped [dict [str , str ]] = mapped_column (JSON , nullable = True )
728- pruned_conversation_ids : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
729- adversarial_chat_conversation_ids : Mapped [Optional [ list [str ]] ] = mapped_column (JSON , nullable = True )
728+ pruned_conversation_ids : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
729+ adversarial_chat_conversation_ids : Mapped [list [str ] | None ] = mapped_column (JSON , nullable = True )
730730 timestamp = mapped_column (DateTime , nullable = False )
731731 # Version of PyRIT used when this attack result was created
732732 # Nullable for backwards compatibility with existing databases
@@ -738,14 +738,14 @@ class AttackResultEntry(Base):
738738 error_traceback = mapped_column (Unicode , nullable = True )
739739
740740 # Retry events (JSON-serialized list of RetryEvent dicts)
741- retry_events_json : Mapped [Optional [ str ] ] = mapped_column (Unicode , nullable = True )
741+ retry_events_json : Mapped [str | None ] = mapped_column (Unicode , nullable = True )
742742 total_retries = mapped_column (INTEGER , nullable = True , default = 0 )
743743
744- last_response : Mapped [Optional [ "PromptMemoryEntry" ] ] = relationship (
744+ last_response : Mapped ["PromptMemoryEntry | None" ] = relationship (
745745 "PromptMemoryEntry" ,
746746 foreign_keys = [last_response_id ],
747747 )
748- last_score : Mapped [Optional [ "ScoreEntry" ] ] = relationship (
748+ last_score : Mapped ["ScoreEntry | None" ] = relationship (
749749 "ScoreEntry" ,
750750 foreign_keys = [last_score_id ],
751751 )
@@ -816,7 +816,7 @@ def __init__(self, *, entry: AttackResult) -> None:
816816 self .total_retries = entry .total_retries
817817
818818 @staticmethod
819- def _get_id_as_uuid (obj : Any ) -> Optional [ uuid .UUID ] :
819+ def _get_id_as_uuid (obj : Any ) -> uuid .UUID | None :
820820 """
821821 Safely extract and convert an object's id to UUID.
822822
@@ -975,25 +975,25 @@ class ScenarioResultEntry(Base):
975975 scenario_description = mapped_column (Unicode , nullable = True )
976976 scenario_version = mapped_column (INTEGER , nullable = False , default = 1 )
977977 pyrit_version = mapped_column (String , nullable = False )
978- scenario_init_data : Mapped [Optional [ dict [str , Any ]] ] = mapped_column (JSON , nullable = True )
978+ scenario_init_data : Mapped [dict [str , Any ] | None ] = mapped_column (JSON , nullable = True )
979979 objective_target_identifier : Mapped [dict [str , str ]] = mapped_column (JSON , nullable = False )
980- objective_scorer_identifier : Mapped [Optional [ dict [str , str ]] ] = mapped_column (JSON , nullable = True )
980+ objective_scorer_identifier : Mapped [dict [str , str ] | None ] = mapped_column (JSON , nullable = True )
981981 scenario_run_state : Mapped [Literal ["CREATED" , "IN_PROGRESS" , "COMPLETED" , "FAILED" , "CANCELLED" ]] = mapped_column (
982982 String , nullable = False , default = "CREATED"
983983 )
984984 attack_results_json : Mapped [str ] = mapped_column (Unicode , nullable = False )
985- display_group_map_json : Mapped [Optional [ str ] ] = mapped_column (Unicode , nullable = True )
986- labels : Mapped [Optional [ dict [str , str ]] ] = mapped_column (JSON , nullable = True )
985+ display_group_map_json : Mapped [str | None ] = mapped_column (Unicode , nullable = True )
986+ labels : Mapped [dict [str , str ] | None ] = mapped_column (JSON , nullable = True )
987987 number_tries : Mapped [int ] = mapped_column (INTEGER , nullable = False , default = 0 )
988988 completion_time = mapped_column (DateTime , nullable = False )
989989 timestamp = mapped_column (DateTime , nullable = False )
990990
991991 # Pointer to failed attack result(s) — avoids scanning all attacks for error info
992- error_attack_result_ids_json : Mapped [Optional [ str ] ] = mapped_column (Unicode , nullable = True )
992+ error_attack_result_ids_json : Mapped [str | None ] = mapped_column (Unicode , nullable = True )
993993
994994 # Scenario-level error info (persisted so it survives process restarts)
995- error_message : Mapped [Optional [ str ] ] = mapped_column (Unicode , nullable = True )
996- error_type : Mapped [Optional [ str ] ] = mapped_column (String , nullable = True )
995+ error_message : Mapped [str | None ] = mapped_column (Unicode , nullable = True )
996+ error_type : Mapped [str | None ] = mapped_column (String , nullable = True )
997997
998998 def __init__ (self , * , entry : ScenarioResult ) -> None :
999999 """
0 commit comments