MAINT: Refactor GUI and REST api to use pyrit.models#1941
Merged
Conversation
…re fields Make backend response DTOs (ScoreView/MessagePieceView/MessageView/ AttackSummary) inherit their pyrit.models counterparts so they expose every canonical field, adding presentation data via computed fields / mappers. Drop the RetryEventResponse DTO and the old deprecation shims. Soft-deprecate the renamed wire fields instead of hard-breaking external API clients: re-expose score_id/scored_at/piece_id/pieces as deprecated read-only computed-field aliases mirroring id/timestamp/message_pieces. Pydantic flags them deprecated in the OpenAPI schema; slated for removal in 0.17.0. Frontend + CLI updated/verified; add test_response_contracts.py wire-shape and deprecated-alias guards. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Following the cleanup of attacks.py, remove the legacy 'from pyrit.models import X as PyritX' aliasing in attack_mappers.py, test_mappers.py, and test_api_routes.py. There's no name collision in those files, so import Message/MessagePiece/Score plainly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously MessagePieceView.from_domain re-used the inherited original_value / converted_value fields to carry the client-fetchable URL (a /api/media path or a SAS-signed blob URL). Two different concepts shared one slot, and the override-style signature gave readers no signal which kwarg was a narrow vs. a transform of the inherited field. Make the two concepts independent: - original_value / converted_value (inherited) keep the raw stored value the database has — text, a file path, a blob URL, or a data URI. - original_value_url / converted_value_url are new optional presentation fields populated by the mapper for media pieces, and None for plain text. _resolve_media_url now returns None for non-media data types (it's a media resolver — text has no fetchable URL). The mapper gates URL population on the piece data type so text never carries a stale URL string. Frontend: BackendMessagePiece gains the two *_value_url fields. pieceToAttachment prefers *_value_url when present and falls back to the existing base64 / raw-value detection so older payload shapes keep working. A new test exercises the URL-precedence path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename pieces -> message_pieces and piece_id -> id in mock response payloads. The frontend types only read the canonical names; the old aliases are deprecated and emitted by the backend serializer for back-compat but unused by the client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
romanlutz
reviewed
Jun 5, 2026
# Conflicts: # pyrit/backend/mappers/attack_mappers.py # pyrit/backend/models/attacks.py
…igning, inline MessageView) - Drop `MessageView.from_domain` classmethod: callers (mapper + 2 tests) now use `MessageView.model_construct(message_pieces=...)` directly. Removes the signature ambiguity Roman flagged where `from_domain` took pre-mapped views instead of a domain Message. (microsoft#1941 comment 3362992954) - Promote `attack_result_to_summary` to async and SAS-sign blob URLs in the summary's `last_response` (matches the messages path). The sync helper silently returned unsigned blob URLs from list/detail endpoints, which would 403 on Azure-backed deployments. Both call sites in attack_service are already async, so the await is a one-line change. (microsoft#1941 comment 3362992959) - Drop the deprecated `pieces` computed_field on `MessageView`: it duplicated the heavy `message_pieces` array per piece (~24% extra wire size on a 2-piece conversation, per Roman's measurement). The wire-shape simplicity wins over the soft-alias break; cheap scalar aliases (`score_id`, `piece_id`, `scored_at`) are kept. Flips the contract test to assert `pieces` is absent. (microsoft#1941 comment 3362992961) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
romanlutz
approved these changes
Jun 10, 2026
Integrates the Conversation refactor (microsoft#1950) and memory-based score fetching (microsoft#1992) into the Phase 10 view-model mapper. - MessagePiece no longer carries scores/prompt_target_identifier/ attack_identifier; scores are now batch-fetched from memory via get_prompt_scores(prompt_ids=...) and linked by original_prompt_id. - attack_mappers.pyrit_messages_to_dto_async grafts the score-fetch pattern onto Phase 10's MessagePieceView/MessageView shape and takes an optional memory kwarg. - MessagePieceView.from_domain takes scores explicitly instead of reading the removed piece.scores. - attack_service uses the renamed get_conversation_messages and passes memory into the mapper. - Tests updated for real MessagePiece/Score objects, the memory-fetch path, and .pieces -> .message_pieces. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Memory is a singleton everywhere else in the backend (services grab it in __init__, routes call get_memory_instance() inline). The mapper's optional memory kwarg was redundant: the only production caller passed self._memory (the singleton) and the real-object tests passed sqlite_instance (also the singleton, since the fixture calls set_memory_instance). The mapper now grabs CentralMemory.get_memory_instance() inline like the routes do. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ew docstring
Two review fixes on the canonical-model-backed response views:
- The deprecated wire aliases (score_id, scored_at, piece_id) used
@computed_field(deprecated="..."), which emits a DeprecationWarning every
time the field is serialized. Since computed fields are dumped on every
response, the server warned against its own serialization on the hot path
(consumers read JSON, not server logs). Switched to
json_schema_extra={"deprecated": True}, which keeps the OpenAPI deprecated
flag (and the removal note in the description) without the runtime warning.
- MessagePieceView's docstring claimed response_error_description is "derived
from the raw values at map time"; it is never populated (defaults to None,
matching prior behavior). Corrected the docstring to say so.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refactoring the backend and GUI to use
pyrit.models.Phase 10 of
pyrit.modelsrefactor here: https://gist.github.com/rlundeen2/3e8daa8e12a11b4b6e52587b3c9b1dcaBreaking changes to
/api/attacks*wire shapeThis PR intentionally breaks a few response fields rather than keeping soft-aliases for them — the old shapes were either misleading or expensive to mirror. Frontend code is updated in this PR; external clients of the attack API should adapt before pulling this in.
MessageView.piecesremoved. UseMessageView.message_pieces(now the only canonical key). The deprecatedpiecescomputed_field was duplicating the entire piece array on every message — a ~24% wire-size hit per conversation.AttackSummary.last_responsetype change. Was a flattenedstr | Nonepreview; is now aMessagePieceView | Nonecarrying the full last piece (so the client gets media URLs, MIME types, etc. without a second round trip). Reach.original_value/.converted_value_urlon the object for the previous string content.AttackSummary.score_valueremoved. The top-level convenience field is gone; readlast_score.score_value(with a null-check onlast_score).AttackSummary.retry_eventsdefault flipped. Now defaults to[](canonicalpyrit.models.AttackResultshape) instead ofnull.pyrit.backend.modelsre-exports dropped.Score,Message,MessagePieceare no longer re-exported frompyrit.backend.models. Import them directly frompyrit.models. The backend's view classes (ScoreView,MessageView,MessagePieceView,AttackSummary) live inpyrit.backend.models.attacksand inherit from the canonical models.Cheap scalar aliases are still emitted for one release as deprecated computed_fields so dashboards keep working:
Score.score_id→id,Score.scored_at→timestamp,MessagePieceView.piece_id→id. These will be removed in0.17.0.