FEAT: GUI view existing scores#2189
Conversation
|
Love this already but haven't reviewed in depth yet :) |
| labels: attack.labels ?? {}, | ||
| target: attack.target ?? null, | ||
| relatedConversationIds: attack.related_conversation_ids ?? [], | ||
| objective: attack.objective, |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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} /> |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
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 ?)
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.