Skip to content

Commit ed67a1a

Browse files
rlundeen2CopilotValbuenaVC
authored
MAINT Breaking: Convert ScenarioResult to Pydantic (#1908)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Victor Valbuena <50061128+ValbuenaVC@users.noreply.github.com>
1 parent 1a15cb8 commit ed67a1a

8 files changed

Lines changed: 213 additions & 263 deletions

File tree

pyrit/memory/memory_models.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -401,14 +401,17 @@ def __init__(self, *, entry: Score) -> None:
401401
self.score_rationale = entry.score_rationale
402402
self.score_metadata = entry.score_metadata or {}
403403
normalized_scorer = entry.scorer_class_identifier
404-
# Ensure eval_hash is set before truncation so it survives the DB round-trip
405-
if normalized_scorer.eval_hash is None:
406-
normalized_scorer = normalized_scorer.with_eval_hash(
407-
ScorerEvaluationIdentifier(normalized_scorer).eval_hash
404+
if normalized_scorer is None:
405+
self.scorer_class_identifier = {}
406+
else:
407+
# Ensure eval_hash is set before truncation so it survives the DB round-trip
408+
if normalized_scorer.eval_hash is None:
409+
normalized_scorer = normalized_scorer.with_eval_hash(
410+
ScorerEvaluationIdentifier(normalized_scorer).eval_hash
411+
)
412+
self.scorer_class_identifier = normalized_scorer.model_dump(
413+
context={"max_value_length": MAX_IDENTIFIER_VALUE_LENGTH},
408414
)
409-
self.scorer_class_identifier = normalized_scorer.model_dump(
410-
context={"max_value_length": MAX_IDENTIFIER_VALUE_LENGTH},
411-
)
412415
self.prompt_request_response_id = entry.message_piece_id if entry.message_piece_id else None
413416
self.timestamp = entry.timestamp
414417
# Store in both columns for backward compatibility
@@ -439,7 +442,7 @@ def get_score(self) -> Score:
439442
score_category=self.score_category,
440443
score_rationale=self.score_rationale,
441444
score_metadata=self.score_metadata,
442-
scorer_class_identifier=scorer_identifier, # type: ignore[ty:invalid-argument-type]
445+
scorer_class_identifier=scorer_identifier,
443446
message_piece_id=self.prompt_request_response_id,
444447
timestamp=_ensure_utc(self.timestamp),
445448
objective=self.objective,
@@ -975,8 +978,8 @@ class ScenarioResultEntry(Base):
975978
scenario_init_data (dict): Optional initialization parameters used to configure the scenario.
976979
objective_target_identifier (dict): Identifier for the target being evaluated in the scenario.
977980
objective_scorer_identifier (dict): Optional identifier for the scorer used to evaluate results.
978-
scenario_run_state (Literal["CREATED", "IN_PROGRESS", "COMPLETED", "FAILED"]): Current execution state
979-
of the scenario.
981+
scenario_run_state (str): Current execution state of the scenario
982+
(one of CREATED, IN_PROGRESS, COMPLETED, FAILED, CANCELLED).
980983
attack_results_json (str): JSON-serialized dictionary mapping attack names to conversation IDs.
981984
Format: {"attack_name": ["conversation_id1", "conversation_id2", ...]}.
982985
The full AttackResult objects are stored in AttackResultEntries and can be queried by conversation_id.
@@ -1003,9 +1006,7 @@ class ScenarioResultEntry(Base):
10031006
scenario_init_data: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
10041007
objective_target_identifier: Mapped[dict[str, str]] = mapped_column(JSON, nullable=False)
10051008
objective_scorer_identifier: Mapped[dict[str, str] | None] = mapped_column(JSON, nullable=True)
1006-
scenario_run_state: Mapped[Literal["CREATED", "IN_PROGRESS", "COMPLETED", "FAILED", "CANCELLED"]] = mapped_column(
1007-
String, nullable=False, default="CREATED"
1008-
)
1009+
scenario_run_state: Mapped[str] = mapped_column(String, nullable=False, default="CREATED")
10091010
attack_results_json: Mapped[str] = mapped_column(Unicode, nullable=False)
10101011
display_group_map_json: Mapped[str | None] = mapped_column(Unicode, nullable=True)
10111012
labels: Mapped[dict[str, str] | None] = mapped_column(JSON, nullable=True)
@@ -1072,7 +1073,7 @@ def __init__(self, *, entry: ScenarioResult) -> None:
10721073
self.attack_results_json = json.dumps(serialized_attack_results)
10731074

10741075
# Serialize display_group_map if present
1075-
self.display_group_map_json = json.dumps(entry._display_group_map) if entry._display_group_map else None
1076+
self.display_group_map_json = json.dumps(entry.display_group_map) if entry.display_group_map else None
10761077

10771078
self.error_message = entry.error_message
10781079
self.error_type = entry.error_type
@@ -1126,14 +1127,14 @@ def get_scenario_result(self) -> ScenarioResult:
11261127
attack_results=attack_results,
11271128
objective_scorer_identifier=scorer_identifier,
11281129
scenario_run_state=self.scenario_run_state,
1129-
labels=self.labels,
1130+
labels=self.labels or {},
11301131
creation_time=self.timestamp,
11311132
number_tries=self.number_tries,
11321133
completion_time=self.completion_time,
1133-
display_group_map=display_group_map,
1134+
display_group_map=display_group_map or {},
11341135
error_message=self.error_message,
11351136
error_type=self.error_type,
1136-
metadata=dict(self.scenario_metadata) if self.scenario_metadata else None,
1137+
metadata=dict(self.scenario_metadata) if self.scenario_metadata else {},
11371138
)
11381139

11391140
def get_conversation_ids_by_attack_name(self) -> dict[str, list[str]]:

pyrit/models/identifiers/component_identifier.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class ComponentIdentifier(BaseModel):
168168
#: may have been truncated.
169169
hash: Optional[str] = None
170170
#: Version tag for storage. Not included in the content hash.
171-
pyrit_version: str = Field(default_factory=lambda: pyrit.__version__)
171+
pyrit_version: str = Field(default=pyrit.__version__)
172172
#: Evaluation hash. Computed by EvaluationIdentifier subclasses and attached
173173
#: to the identifier so it survives DB round-trips with truncated params.
174174
eval_hash: Optional[str] = None

0 commit comments

Comments
 (0)