Skip to content

FEAT: GUI view existing scores#2189

Open
jbolor21 wants to merge 1 commit into
microsoft:mainfrom
jbolor21:jbolor/GUI_load_scores
Open

FEAT: GUI view existing scores#2189
jbolor21 wants to merge 1 commit into
microsoft:mainfrom
jbolor21:jbolor/GUI_load_scores

Conversation

@jbolor21

@jbolor21 jbolor21 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Description

Surfaces the attack objectives and scores from existing conversations in the DB into the GUI/CoPyRIT chat view.

Whats new:

  • Objective Header: the conversation objective is now shown in an inline banner above the conversation. Long objectives are truncated with a toggle to view the full objective.
  • Score value: a clickable chip is shown with the score value in the top ribbon
  • When you click on the score value, you can see all scorer information including the rationale, scorer type, etc
  • The chip color is color coded red for 'False', green for 'True' and grey for NA/neutral.
  • Refactored History table outcome icons/colors into a shared utils/attackOutcome helper so the chat and history views stay consistent

Screenshots:
(see the new objective and score)
image

Click on score for details:
image

Tests and Documentation

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

@rlundeen2

Copy link
Copy Markdown
Contributor

Love this already but haven't reviewed in depth yet :)

Comment thread frontend/src/App.tsx
labels: attack.labels ?? {},
target: attack.target ?? null,
relatedConversationIds: attack.related_conversation_ids ?? [],
objective: attack.objective,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: every other branch here defaults objective to '', but the success path assigns attack.objective directly. It's typed as required so this is fine today — worth a ?? '' for consistency/robustness if the backend ever omits it.


const bipolar = normalized * 2 - 1
const magnitude = Math.abs(bipolar)
const target = bipolar < 0 ? RED : GREEN

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This red↔green mapping bakes in "true/high = harmful = attack success," but that assumption is scorer-specific. The fixture used in the chip's own test is a SelfAskRefusalScorer with score_value: 'true' and outcome: 'failure' — a refusal means the attack failed, yet this renders the most saturated green (the same green the outcome badge uses for success). So we can show a green score sitting next to a "failure" outcome in the same popover. Since last_score accepts any scorer (refusal scorers included), the color will mislead for inverted-polarity scorers.

Consider driving the badge color from the attack outcome (success/failure/error/undetermined) rather than raw score polarity, or restricting the harm axis to scorers whose polarity is actually known. The numeric/boolean value can still render as text regardless.

const styles = useAttackVerdictChipStyles()

// Only the score value is surfaced in the ribbon, so there's nothing to show without a score.
if (!score) return null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Early-returning on !score means attacks that have an outcome but no last_score show no ribbon verdict at all — e.g. the sequential and barge-in strategies set last_score=None, and manual GUI attacks have no score. Even when a real outcome exists, the ribbon stays verdict-blank. Since outcome is already passed in, consider rendering an outcome-only chip in that case so those attacks aren't left with nothing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, maybe we can show at least the attack outcome badge (exactly as is shown in the attack table), and if scores are present who them in the click/hover effect?

so attack verdict means (outcome + score), and the AttackVerdictChip shows exactly those

)}
</div>
<div className={styles.ribbonActions}>
<AttackVerdictChip outcome={outcome} score={lastScore} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This chip always renders the attack-level lastScore, but the ribbon sits above whatever conversation is currently active. activeConversationId can be any related conversation (adversarial red-teamer, pruned, or a branch), while lastScore is loaded once per attack_result_id and — per the comment in App.tsx — never refetches on conversation switch. So when a multi-conversation attack is open and you switch to a related conversation, we display a verdict that was computed on the main conversation as if it applied to the one you're looking at.

last_score is specifically the objective score on the main conversation's last turn (and for TAP it's best_objective_score, a single best-across-branches value), so it genuinely doesn't belong to a related conversation. In a tool where the verdict is the payload, that's misleading.

Can we gate this on activeConversationId === mainConversationId (App already has both), or relabel/dim it as the attack-level verdict when a related conversation is active?

@behnam-o behnam-o left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with other comments, approved as long as those are resolved.

const styles = useAttackVerdictChipStyles()

// Only the score value is surfaced in the ribbon, so there's nothing to show without a score.
if (!score) return null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, maybe we can show at least the attack outcome badge (exactly as is shown in the attack table), and if scores are present who them in the click/hover effect?

so attack verdict means (outcome + score), and the AttackVerdictChip shows exactly those

@@ -82,11 +65,11 @@ export default function AttackTable({ attacks, onOpenAttack, formatDate }: Attac
<TableCell>
<Badge

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

assuming you take my comment on the ChatWindow about rendering attack outcome in the chat window, I think should reuse the same components? (show the whole attack verdict chip here too, so a user can hover/click on this table column to see the last score ?)

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.

4 participants