Skip to content

FEAT: Load Scores Per Message in GUI - #2352

Open
jbolor21 wants to merge 10 commits into
microsoft:mainfrom
jbolor21:jbolor/GUI_load_message_scores
Open

FEAT: Load Scores Per Message in GUI#2352
jbolor21 wants to merge 10 commits into
microsoft:mainfrom
jbolor21:jbolor/GUI_load_message_scores

Conversation

@jbolor21

@jbolor21 jbolor21 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

Loads scores in the GUI for each message. Loads the last message if there are multiple and adds objective when present above the conversation.

Screenshots:
image

image

multiple scores:
image

multiple pieces:
image

Tests and Documentation

Ran frontend tests & added new tests for changes

@romanlutz Roman Lutz (romanlutz) 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.

Can you attach screenshots or videos?

Other comments AI generated but they apply nontheless :-)

Comment thread frontend/src/components/Chat/ObjectiveHeader.styles.ts
Comment thread frontend/src/App.tsx
Comment thread frontend/src/components/Chat/MessageList.styles.ts Outdated
Comment thread frontend/e2e/touch-targets.spec.ts
Comment thread frontend/src/utils/messageMapper.test.ts Outdated
@romanlutz

Copy link
Copy Markdown
Contributor

What does this look like with multiple scores?

Also the spacing is not right. This doesn't look very good with "Score" and no space between.

Comment thread frontend/src/utils/messageMapper.ts Outdated
Comment thread frontend/src/types/index.ts Outdated
Comment thread pyrit/backend/services/attack_service.py Outdated
Comment thread frontend/src/components/Chat/MessageList.styles.ts
Comment thread frontend/e2e/touch-targets.spec.ts Outdated
Comment thread frontend/src/utils/messageMapper.ts Outdated
Comment thread frontend/src/components/Chat/MessageList.test.tsx Outdated
@romanlutz

Copy link
Copy Markdown
Contributor

I think would be preferable if the score was at the bottom of a piece rather than vertically centered.

I also think the multiple score experience is a bit confusing.

Image

I had to look back and forth a few times to realize the 0.91 was the same as the one presented to the left and not a separate score.

Wild idea: Would it be hard to show them visually stacked (can be static with 3 maybe, doesn't need to adjust the number of levels to the number of scores)
Image
and if you click on it it shows the same score details card as it shows for the first score right now, but the card has a pivot/tablist item at the top to switch between the various scores? [In theory, that could also overflow but there's a "with overflow" option if you check the doc link below
https://storybooks.fluentui.dev/react/?path=/docs/components-tablist--docs&globals=storybook_fluentui-react-addon_dir:rtl ]

@romanlutz Roman Lutz (romanlutz) 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.

These are GHCP generated and at least worth a look. Making another manual pass as well

appearance="subtle"
size="small"
className={styles.scoreChip}
aria-label="View score details"

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.

Every multi-score group has the same accessible name, View score details, so text and attachment score groups in one message produce indistinguishable buttons. Please include the piece/source label in this trigger's name, and in the score chip name too, since identical scorer/value pairs can otherwise collide.


return (
<div className={styles.scoreList}>
<MessageScore score={selectedScore} groupId={groupId} scoreIndex={selectedIndex} />

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 still makes only the selected/default score visible; every other score is hidden behind +, and selecting one replaces the current chip. We should keep every score visible inline, optionally highlighting the objective score, rather than making users discover and swap them one at a time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes designed that way on purpose so we wouldn't overflow with scores!

clients don't have to dig into ``scorer_class_identifier``.
"""

is_objective_score: bool = Field(

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.

Message responses can mark the canonical objective score, but attack_result_to_summary_async builds ar.last_score without setting this flag, so the same score is serialized as false in every AttackSummary. Please pass is_objective_score=True for the summary's last_score and cover that mapper path.

# Historical unnamed GUI attacks persisted this sentinel. Normalize it
# at the API boundary so clients can use the objective value directly,
# without a separate metadata or has_explicit_objective flag.
objective="" if ar.objective == _LEGACY_MANUAL_ATTACK_PLACEHOLDER else ar.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.

This string-only compatibility check erases a legitimate explicit objective named exactly Manual attack via GUI, even for non-manual attacks; the current test uses a Crescendo attack and codifies that collision. Please scope the fallback to legacy unnamed ManualAttack identity/metadata instead of normalizing every matching objective.

<MenuPopover>
<MenuList>
{scores.map((score, scoreIndex) => (
<MenuItem

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.

These menu items are the controls users tap after opening +, but Fluent's default MenuItem rows are only 32px high on the coarse-pointer viewport. Please apply the mobile 44px minimum here and extend the E2E test to open the menu and measure its items; it currently measures only the chip and trigger.

{/* Text content (converted / primary), with any scores for the
text piece(s) shown alongside it at the same height rather
than stacked above or below. */}
{message.content && (

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.

message.scores is inside the message.content truthiness guard, so a scored text piece whose converted response is "" renders no score at all. Please render this row when either content or scores are present and add an empty-content scored-response test.


return {
...score,
sourcePieceId: piece.id,

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.

ScoreView already serializes the canonical message_piece_id, and each score also arrives nested under its originating BackendMessagePiece. Can we add message_piece_id to BackendScore instead of duplicating it as sourcePieceId, keep the backend DTO free of frontend-only optional fields, and use a separate display type only for derived fields such as pieceIndex, pieceType, and sourceLabel?


is_objective_score: bool = Field(
default=False,
description="Whether this is the attack's canonical objective score.",

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 description is ambiguous because "objective score" could mean any score produced by the objective scorer. Since the flag is set only when the score ID matches AttackResult.last_score.id, please describe it explicitly as "Whether this is the score referenced by AttackResult.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.

2 participants