FEAT: GUI view last_score of Attack Results - #2189
Conversation
|
Love this already but haven't reviewed in depth yet :) |
Behnam (behnam-o)
left a comment
There was a problem hiding this comment.
agreed with other comments, approved as long as those are resolved.
There was a problem hiding this comment.
Pull request overview
This PR enhances the CoPyRIT GUI by surfacing persisted attack objectives and scoring details from stored conversations, enabling users to quickly understand an attack’s goal and outcome and to inspect scorer details directly from the chat/history UI.
Changes:
- Backend: expose a
scale_scorecomputed field onScoreViewto surface the underlying float score for thresholded true/false scorers. - Frontend: add an objective header and a reusable verdict/score chip with a details popover; introduce shared outcome + score-color utilities to keep chat/history consistent.
- Tests: add/extend unit tests across backend response contracts and frontend components/utils to validate rendering and color behavior.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/backend/test_response_contracts.py | Adds contract tests ensuring scale_score is serialized as expected. |
| pyrit/backend/models/attacks.py | Adds ScoreView.scale_score computed field derived from score metadata. |
| frontend/src/utils/scoreColor.ts | New utility to compute score badge colors based on outcome + score intensity. |
| frontend/src/utils/scoreColor.test.ts | Unit tests for score normalization and color selection. |
| frontend/src/utils/attackOutcome.tsx | New shared outcome icons/colors + resolver for history/chat consistency. |
| frontend/src/types/index.ts | Extends shared types with AttackOutcome, ScoreView, and new AttackSummary fields (objective, last_score). |
| frontend/src/components/Home/Home.test.tsx | Updates mocks to include required objective on AttackSummary. |
| frontend/src/components/History/AttackTable.tsx | Replaces static outcome badge with shared AttackVerdictChip and score popover support. |
| frontend/src/components/History/AttackTable.test.tsx | Adds tests asserting score-tinted outcome chip + popover behavior without row navigation. |
| frontend/src/components/History/AttackHistory.test.tsx | Updates sample attack data to include objective. |
| frontend/src/components/Chat/ObjectiveHeader.tsx | New component displaying the conversation objective with overflow-aware expand/collapse. |
| frontend/src/components/Chat/ObjectiveHeader.test.tsx | Tests objective header rendering and overflow toggle behavior. |
| frontend/src/components/Chat/ObjectiveHeader.styles.ts | Styling for the objective header banner and expanded/collapsed states. |
| frontend/src/components/Chat/ChatWindow.tsx | Wires objective header and verdict chip into the chat window ribbon and layout. |
| frontend/src/components/Chat/AttackVerdictChip.tsx | New verdict chip component: outcome-only badge or score-tinted interactive popover. |
| frontend/src/components/Chat/AttackVerdictChip.test.tsx | Tests chip visibility, popover contents, and scale-score row behavior. |
| frontend/src/components/Chat/AttackVerdictChip.styles.ts | Styling for chip layout and popover surface content. |
| frontend/src/App.tsx | Threads objective/outcome/lastScore through loaded-attack state into ChatWindow. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
frontend/src/utils/attackOutcome.tsx:17
OUTCOME_COLORStreatserroras a warning state, but the error icon is currently colored with the red failure token. This makes error outcomes visually indistinguishable from failures and diverges from the amber/orange error hue used elsewhere (e.g. score badge tinting). Use the dark-orange warning token for the error icon.
success: <CheckmarkCircleRegular style={{ color: tokens.colorPaletteGreenForeground1 }} />,
failure: <DismissCircleRegular style={{ color: tokens.colorPaletteRedForeground1 }} />,
error: <ErrorCircleRegular style={{ color: tokens.colorPaletteRedForeground1 }} />,
undetermined: <QuestionCircleRegular style={{ color: tokens.colorNeutralForeground3 }} />,
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
frontend/src/utils/attackOutcome.tsx:17
- The shared outcome presentation uses a warning-colored badge for
error(OUTCOME_COLORS mapserror->warning), but the error icon is still hard-coded to the red foreground token. This makes the error outcome visually inconsistent (and can reduce contrast on a warning/amber background). Use the dark-orange foreground token for the error icon to match the warning semantics and the amber hue used elsewhere.
export const OUTCOME_ICONS: Record<AttackOutcome, React.ReactElement> = {
success: <CheckmarkCircleRegular style={{ color: tokens.colorPaletteGreenForeground1 }} />,
failure: <DismissCircleRegular style={{ color: tokens.colorPaletteRedForeground1 }} />,
error: <ErrorCircleRegular style={{ color: tokens.colorPaletteRedForeground1 }} />,
undetermined: <QuestionCircleRegular style={{ color: tokens.colorNeutralForeground3 }} />,
…jbolor-PyRIT into jbolor/GUI_load_scores
Resolve conflict in ChatWindow.tsx by keeping both the AttackVerdictChip (this branch) and the Markdown toggle + conversation export controls (main). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 35d82e7e-fb5c-440e-a385-05faa99575a4
| ``score_metadata``; surface it here so clients don't have to know the | ||
| metadata key. ``None`` when the score is not a thresholded float scale. | ||
| """ | ||
| value = (self.score_metadata or {}).get(ORIGINAL_FLOAT_VALUE_KEY) |
There was a problem hiding this comment.
This is a bit worrisome. If we change anything about that it would break. Do you have an example of how that's shown?
There was a problem hiding this comment.
this is just for color shading (mostly for float, since t/f is just boolean rather than having multiple possibilities - ust thought shading might be nice for something higher scored vs lower scored ie "mildly offensive" vs "severely offensive" were diff shades but still "success" colored - my thought there for just a visualization! if that's too complex I can just leave as two distinct colors no shading!
But I think this is what I think you're asking:
this key in scale_score is a shared constant in the ORIGINAL_FLOAT_VALUE_KEY set in the utils.py file, which all the scorers import, so changing it is propogated everywhere. It will also return None if key is missing rather than breaking!
There was a problem hiding this comment.
Yes, the comment was about the latter 🙂
I think we can do case-based (bool vs float) handling in the GUI (?) and normalized score should be 0-1 so that shouldn't be that tricky?
Description
Surfaces the attack objectives and scores from existing conversations in the DB into the GUI/CoPyRIT chat view.
Whats new:
utils/attackOutcomehelper so the chat and history views stay consistentScreenshots:

(see the new objective and score)
Click on score for details:

Tests and Documentation
New unit tests for the score chip, objective header, and the color. Existing full frontend test suite passes.