FEAT: Output-layer Refactor and CLI Results Printing Rewire - #2548
FEAT: Output-layer Refactor and CLI Results Printing Rewire#2548Justin Song (jsong468) wants to merge 1 commit into
Conversation
|
(GHCP Generated): Cross-linking something from #2374 (live scenario progress dashboard) that overlaps with this refactor — no action needed here, just worth considering while the output layer is already open. The overlap
_SCORER_DISPLAY_PARAMS = frozenset({"scorer_type", "score_aggregator"})
_TARGET_DISPLAY_PARAMS = frozenset({"model_name", "temperature"})plus hardcoded child names ( #2374 originally had the same pattern in from pyrit.models import project_behavioral_identity # pyrit/models/identifiers/identifier_projection.py
projected = project_behavioral_identity(identifier, identifier_type=ScorerIdentifier)It honors each component's own markers ( Why it might matter for this PRThe copies have already drifted.
There's also a responsibility angle: If CaveatThis would change user-visible CLI output (deployment name → model name, |
Description
This branch makes the
pyrit_scan/pyrit_shellscenario-resultsviews render through theframework's
pyrit.outputprinters instead of a parallel CLI-owned rendering stack. It introduces a Source seam so the same printers can render from memory (notebook/library) or REST (the thin CLI client), and moves the last CLI-only renderer (the attacks table) intopyrit.output.No user-visible output changes — pretty output is byte-for-byte the same.
ConversationSourceProtocol seam (pyrit/output/conversation/source.py) with aMemoryConversationSourceimplementation.Protocol(structural typing) means the REST source needs no import ofpyrit.output, andpyrit.outputnever importspyrit.cli, so the dependency direction (and the no-cycle rule) is preserved._get_scores_asynchook from the conversation base; the*MemoryPrinterleaves injectMemoryConversationSourceinternally so their public signatures are unchanged (no notebook/helper breakage).conversations(and the transcript half offull) now reusePrettyConversationPrintervia aRestApiConversationSource(pyrit/cli/_sources.py) that hydratespyrit.modelsobjects from the/messagesview JSON and serves the inline objective score (no new endpoint).--limitgate network calls).pyrit.outputand consolidated it intorender_asynconPrettyScenarioResultPrintervia a typedviewparameter (ScenarioView = Literal["overview", "attacks"]), plus a notebook-friendlyoutput_scenario_attacks_asynchelper.render_async). Theviewliteral mirrors the existingOutputFormat = Literal["pretty", "markdown"]precedent and matches how the CLI already models these as views of one result. Living inpyrit.outputmeans notebooks get the same compact per-attack view, and the CLI becomes one consumer of the shared renderer.AttackRow/AttacksTablePayload/build_attacks_table_payloadfrompyrit/cli/_results.pyandprint_attacks_tablefrompyrit/cli/_output.py;_results.pynow holds only CLI-side flag policy (resolve_view,apply_view_limit_policy,_select_attacks).build_scripts/export_adversarial_benchmark_result.py) off the deleted CLI payload — it now dumpsattacks.jsonfrom a small local helper and rendersattacks.txtthrough the framework printer with colors off.source-requiring base; switched them to the memory printer.Tests and Documentation
tests/unit/cli/test_sources.py(REST source hydration + inline-score capture);render_async(view="attacks")cases intests/unit/output/scenario_result/test_pretty.py(list,--limittruncation, id filtering, empty).print_conversations_async/output_scenario_attacks_asyncpaths; removed the deleted-payload builder/drift tests;tests/unit/output/test_blur_images.pyinjects a null source instead of overriding the removed hook.tests/unit/cli+tests/unit/output/scenario_result(527 passed) andtests/unit/output(all passing);ruffand thecheck_async_suffix.pygate are clean.Next Steps
--output jsonas a format-printer. AddJson*Printersiblings that call the same source hooks, assemble a structure, and serialize (model_dump_json()), starting withscenario_result(already a value object) then conversation/attack-result — retiring any remaining CLI-side JSON assembly so console and json can't diverge.--output html+ output-file destination. AddHtml*Printersiblings (cheapest first cut: a template around the same structure the json printer assembles) that render to a file viaFileSink; make--output htmlrequire--output-file(never raw terminal) and inline media via the piece media URLs.read_image_bytes_asyncso each format keeps its own presentation (IPython display vs. blurred file + link vs. inline<img>) — the last seam needed before html can inline media.