FEAT: GUI view existing scores#2189
Conversation
|
Love this already but haven't reviewed in depth yet :) |
behnam-o
left a comment
There was a problem hiding this comment.
agreed with other comments, approved as long as those are resolved.
| <AttackVerdictChip | ||
| outcome={outcome} | ||
| score={lastScore} | ||
| appliesToActiveConversation={activeConversationId === conversationId} |
There was a problem hiding this comment.
I don't think we should render the verdict here at all when a related (non-main) conversation is active, rather than dimming it and adding a caveat. outcome/last_score are attack-level, computed on the main conversation's final turn — so putting a colored success/failure badge in the ribbon next to a different conversation's messages is misleading. Someone scanning the ribbon reads the badge color first; the "this is attack-level, not this conversation" note in the popover is easy to miss and only shows after a click.
The dimming + appliesToActiveConversation machinery is really working around the fact that this value doesn't belong on a related-conversation view. I'd gate it so the chip only shows on the main conversation (activeConversationId === conversationId) and drop the dimmed/attack-level-note branch entirely — simpler component, and no misleading state.
(If we do want related conversations to show their verdict eventually, the per-piece scores are already in the API — MessagePieceView.scores — so that'd be a separate, more accurate feature rather than surfacing the main conversation's score everywhere.)
[For attacks, it's not unreasonable to think that we might have an attack result page at some point, too. ]
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 }} />,
| <Button | ||
| appearance="subtle" | ||
| className={styles.chip} | ||
| data-testid="attack-score-chip" | ||
| aria-label={`Verdict ${resolvedOutcome}, score ${score.score_value}`} | ||
| > | ||
| <Badge appearance="filled" size="medium" style={scoreBadgeStyle}> | ||
| {resolvedOutcome} | ||
| </Badge> |
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.