Skip to content

MAINT: Refactor printers into lightweight and flexible output module#1732

Merged
rlundeen2 merged 37 commits into
microsoft:mainfrom
rlundeen2:users/rlundeen/2026_05_13_printer_refactor
May 18, 2026
Merged

MAINT: Refactor printers into lightweight and flexible output module#1732
rlundeen2 merged 37 commits into
microsoft:mainfrom
rlundeen2:users/rlundeen/2026_05_13_printer_refactor

Conversation

@rlundeen2
Copy link
Copy Markdown
Contributor

@rlundeen2 rlundeen2 commented May 14, 2026

Introduce pyrit/output/ module — format × sink separation for all result rendering

Adds a new outputmodule that decouples what output looks like (pretty ANSI, markdown, JSON) from where it goes
(stdout, file, Jupyter, etc.) and where data comes from (CentralMemory, REST, test fixtures).

  • Decoupled Sink (StdoutSink, FileSink, IPythonMarkdownSink) — pluggable output destinations
  • PrinterBase with render_async()str (abstract) and write_async()None (concrete, routes through sink)
  • Three-layer hierarchy per domain: abstract base → format class → memory leaf class
  • Domain printers for attack results, conversations, scores, scorer metrics, and scenario results
  • Convenience functions in helpers.py for one-line usage: await print_attack_result_async(result, format="markdown", sink=FileSink(...))
  • Composition: attack result printer composes conversation + score sub-printers

What this replaces: scattered print() calls and format-fused rendering logic across executors and models. The same
printer classes work against memory, REST endpoints, or test fixtures by swapping only the leaf data-fetching layer

All functionality is the same. you can even call the methods imported using the same arguments with the same behavior (except a deprecation message); but this allows us to use the same printing once we replace our cli frontend to use the REST API and more consistently add to output modules

TODO

  • Currently ran deprecated AttackResult and ScenarioResult notebooks. Plan on running all before merge
  • Need to update tests and docs to use non-deprecated patterns

rlundeen2 and others added 9 commits May 13, 2026 19:09
Create new pyrit/printer/ module with abstract base classes that contain
all formatting logic. Data-fetching operations (CentralMemory calls) are
abstract methods implemented by framework subclasses.

This enables thin clients to reuse all pretty-printing by subclassing
the base printers and implementing data-fetching via REST endpoints.
The thin client only needs pyrit.models + pyrit.identifiers + colorama.

Changes:
- New pyrit/printer/ module with attack_result, scenario_result, scorer subpackages
- ConsoleAttackPrinterBase: all attack console formatting, abstract get_conversation/get_scores
- ConsoleScenarioPrinterBase: all scenario console formatting
- ConsoleScorerPrinterBase: all scorer formatting, abstract get_objective/harm_metrics
- Existing framework printers refactored to thin subclasses (backward compatible)
- Added to_dict()/from_dict() to AttackResult, ScenarioResult, ScenarioIdentifier,
  ConversationReference, Score, MessagePiece, Message for serialization round-tripping
- Message.to_full_dict() added for rich serialization (to_dict() unchanged for compat)

All 675 existing tests pass with no modifications.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move framework CentralMemory implementations into pyrit/printer/ alongside
their base classes. CentralMemory is imported lazily inside constructors,
so thin clients importing the module never pay the SQLAlchemy cost.

- ConsoleAttackResultPrinter now lives in pyrit.printer.attack_result.console
- ConsoleScenarioResultPrinter now lives in pyrit.printer.scenario_result.console
- ConsoleScorerPrinter now lives in pyrit.printer.scorer.console
- Old locations (executor/attack/printer/, scenario/printer/, score/printer/)
  become pure re-exports for backward compatibility
- Updated test patch paths to match new module locations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…6.0)

Old locations now use PEP 562 __getattr__ lazy re-exports with
DeprecationWarning. Only concrete classes are re-exported (not bases).

- pyrit.executor.attack.printer → pyrit.printer.attack_result
- pyrit.scenario.printer → pyrit.printer.scenario_result
- pyrit.score.printer → pyrit.printer.scorer
- Updated all internal callers to new canonical paths
- Old ABC files (attack_result_printer.py, scenario_result_printer.py,
  scorer_printer.py) kept for now but deprecated via __init__.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…of bases

Concrete classes that use CentralMemory/scorer registry are now named
to clearly indicate their data source:
- ConsoleAttackResultPrinter → ConsoleAttackMemoryPrinter
- ConsoleScenarioResultPrinter → ConsoleScenarioMemoryPrinter
- ConsoleScorerPrinter → ConsoleScorerMemoryPrinter

Moved ScorerEvaluationIdentifier (pyrit internal) from base class into
the concrete ConsoleScorerMemoryPrinter. Base classes now contain only
formatting logic with no pyrit-internal imports beyond models/identifiers.

Deprecated re-exports at old paths still work (mapping old names to new),
scheduled for removal in 0.16.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Created MarkdownAttackPrinterBase + MarkdownAttackMemoryPrinter in
  pyrit/printer/attack_result/markdown.py (same pattern as console)
- Deleted dead old ABC files:
  - pyrit/executor/attack/printer/attack_result_printer.py
  - pyrit/scenario/printer/scenario_result_printer.py
  - pyrit/score/printer/scorer_printer.py
- Old markdown_printer.py now a deprecation re-export shim
- Updated all internal imports and test patches

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Changed dict[str, object] to dict[str, Any] in MessagePiece.from_dict()
  and Message.from_dict() to satisfy pyright (dict.get returns object otherwise)
- Added Any import to message_piece.py and message.py
- Wrapped get_prompt_scores return in list() for Sequence -> list coercion
- Added isinstance check in display_image_async for type safety

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Added return type annotation (-> type) to all __getattr__ deprecation shims
- Added noqa: B027 to display_image_async intentional no-op default
- Added Returns/Raises sections to short docstrings (DOC201, DOC501)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
objective_target_identifier and objective_scorer_identifier may be None
when deserializing from dicts. The printer bases already handle None.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pyrit/models/message.py Outdated
Comment thread pyrit/printer/attack_result/console.py Outdated
Comment thread pyrit/printer/attack_result/markdown.py Outdated
Comment thread pyrit/executor/attack/__init__.py Outdated
Comment thread pyrit/printer/scenario_result/console.py Outdated
Comment thread pyrit/printer/scorer/console.py Outdated
Comment thread pyrit/printer/attack_result/base.py Outdated
Comment thread pyrit/models/attack_result.py Outdated
rlundeen2 and others added 13 commits May 14, 2026 16:27
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n warnings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rlundeen2 and others added 2 commits May 14, 2026 23:57
…nversation/score printers, refactor display_image_response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread doc/code/printer/0_printer.md Outdated
Comment thread doc/code/output/0_output.ipynb
rlundeen2 and others added 2 commits May 15, 2026 10:38
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rlundeen2 rlundeen2 changed the title MAINT: Refactor printers into lightweight printer module MAINT: Refactor printers into lightweight and flexible output module May 15, 2026
rlundeen2 and others added 4 commits May 15, 2026 12:24
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread doc/generate_docs/pct_to_ipynb.py
Comment thread pyrit/output/helpers.py Outdated
Comment thread pyrit/output/helpers.py
Comment thread pyrit/output/helpers.py Outdated
Comment thread pyrit/output/helpers.py Outdated
@rlundeen2 rlundeen2 added this pull request to the merge queue May 18, 2026
Merged via the queue into microsoft:main with commit 2a97244 May 18, 2026
48 checks passed
@rlundeen2 rlundeen2 deleted the users/rlundeen/2026_05_13_printer_refactor branch May 18, 2026 18:21
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.

3 participants