Skip to content

MAINT: Refactor GUI and REST api to use pyrit.models#1941

Merged
rlundeen2 merged 12 commits into
microsoft:mainfrom
rlundeen2:rlundeen2/phase-10-plan
Jun 12, 2026
Merged

MAINT: Refactor GUI and REST api to use pyrit.models#1941
rlundeen2 merged 12 commits into
microsoft:mainfrom
rlundeen2:rlundeen2/phase-10-plan

Conversation

@rlundeen2

@rlundeen2 rlundeen2 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Refactoring the backend and GUI to use pyrit.models.

Phase 10 of pyrit.models refactor here: https://gist.github.com/rlundeen2/3e8daa8e12a11b4b6e52587b3c9b1dca

Breaking changes to /api/attacks* wire shape

This 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.pieces removed. Use MessageView.message_pieces (now the only canonical key). The deprecated pieces computed_field was duplicating the entire piece array on every message — a ~24% wire-size hit per conversation.
  • AttackSummary.last_response type change. Was a flattened str | None preview; is now a MessagePieceView | None carrying the full last piece (so the client gets media URLs, MIME types, etc. without a second round trip). Reach .original_value / .converted_value_url on the object for the previous string content.
  • AttackSummary.score_value removed. The top-level convenience field is gone; read last_score.score_value (with a null-check on last_score).
  • AttackSummary.retry_events default flipped. Now defaults to [] (canonical pyrit.models.AttackResult shape) instead of null.
  • pyrit.backend.models re-exports dropped. Score, Message, MessagePiece are no longer re-exported from pyrit.backend.models. Import them directly from pyrit.models. The backend's view classes (ScoreView, MessageView, MessagePieceView, AttackSummary) live in pyrit.backend.models.attacks and inherit from the canonical models.

Cheap scalar aliases are still emitted for one release as deprecated computed_fields so dashboards keep working: Score.score_idid, Score.scored_attimestamp, MessagePieceView.piece_idid. These will be removed in 0.17.0.

rlundeen2 and others added 7 commits June 3, 2026 21:27
…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>
Comment thread pyrit/backend/models/attacks.py
Comment thread pyrit/backend/models/attacks.py Outdated
Comment thread pyrit/backend/mappers/attack_mappers.py
Comment thread pyrit/backend/models/attacks.py Outdated
Comment thread pyrit/backend/models/attacks.py
@romanlutz romanlutz self-assigned this Jun 5, 2026
rlundeen2 and others added 2 commits June 5, 2026 16:11
# 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>
rlundeen2 and others added 3 commits June 11, 2026 16:15
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>
@rlundeen2 rlundeen2 added this pull request to the merge queue Jun 12, 2026
Merged via the queue into microsoft:main with commit f36693e Jun 12, 2026
53 checks passed
@rlundeen2 rlundeen2 deleted the rlundeen2/phase-10-plan branch June 12, 2026 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants