From 40a94b497b778efb144dffb3a65233a382c3926e Mon Sep 17 00:00:00 2001 From: barry <91018388+barry166@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:37:19 +0800 Subject: [PATCH 1/2] FIX preserve rationale-less float scores --- .../float_scale/float_scale_score_aggregator.py | 3 +-- .../unit/score/test_float_scale_score_aggregator.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pyrit/score/float_scale/float_scale_score_aggregator.py b/pyrit/score/float_scale/float_scale_score_aggregator.py index a9752f7c43..ed4fed7311 100644 --- a/pyrit/score/float_scale/float_scale_score_aggregator.py +++ b/pyrit/score/float_scale/float_scale_score_aggregator.py @@ -94,8 +94,7 @@ def _build_rationale(scores: list[Score], *, aggregate_description: str) -> tupl rationale = scores[0].score_rationale or "" else: description = aggregate_description - # Only include scores with non-empty rationales - rationale_parts = [format_score_for_rationale(s) for s in scores if s.score_rationale] + rationale_parts = [format_score_for_rationale(s) for s in scores] rationale = "\n".join(rationale_parts) if rationale_parts else "" return description, rationale diff --git a/tests/unit/score/test_float_scale_score_aggregator.py b/tests/unit/score/test_float_scale_score_aggregator.py index 37909e9b10..5568302158 100644 --- a/tests/unit/score/test_float_scale_score_aggregator.py +++ b/tests/unit/score/test_float_scale_score_aggregator.py @@ -94,6 +94,18 @@ def test_float_scale_aggregator_category_deduplication(): assert results[0].category == ["Hate"] # Should be deduplicated +def test_float_scale_aggregator_preserves_scores_without_rationales(): + scores = [ + _mk_score(0.5, rationale="explained"), + _mk_score(0.7), + ] + + result = FloatScaleScoreAggregator.MAX(scores)[0] + + assert "explained" in result.rationale + assert result.rationale.count("UnitTestScorer") == 2 + + def test_float_scale_aggregator_multiple_categories_preserved(): """Test that multiple unique categories are preserved and sorted.""" scores = [ From 7d6236b05de54028421249213a84c7640cbab736 Mon Sep 17 00:00:00 2001 From: Roman Lutz Date: Tue, 29 Sep 2026 12:55:07 -0700 Subject: [PATCH 2/2] FIX label categories in aggregated score rationales Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- pyrit/score/score_utils.py | 6 +++-- .../test_float_scale_score_aggregator.py | 10 ++++---- tests/unit/score/test_score_utils.py | 24 +++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pyrit/score/score_utils.py b/pyrit/score/score_utils.py index f64ba099db..b1e00d3e83 100644 --- a/pyrit/score/score_utils.py +++ b/pyrit/score/score_utils.py @@ -77,11 +77,13 @@ def format_score_for_rationale(score: Score) -> str: score: The Score object to format. Returns: - Formatted string with scorer class, value, and rationale. + Formatted string with scorer class, value, categories (if any), and rationale. """ class_type = score.scorer_class_identifier.class_name or "Unknown" if score.scorer_class_identifier else "Unknown" value = score.score_value if score.score_value is not None else "undetermined" - return f" - {class_type} {value}: {score.score_rationale or ''}" + categories = [category for category in score.score_category or [] if category] + category_label = f" (Category: {', '.join(categories)})" if categories else "" + return f" - {class_type} {value}{category_label}: {score.score_rationale or ''}" def normalize_score_to_float(score: Score | None) -> float: diff --git a/tests/unit/score/test_float_scale_score_aggregator.py b/tests/unit/score/test_float_scale_score_aggregator.py index 5568302158..276ab51dcb 100644 --- a/tests/unit/score/test_float_scale_score_aggregator.py +++ b/tests/unit/score/test_float_scale_score_aggregator.py @@ -96,14 +96,16 @@ def test_float_scale_aggregator_category_deduplication(): def test_float_scale_aggregator_preserves_scores_without_rationales(): scores = [ - _mk_score(0.5, rationale="explained"), - _mk_score(0.7), + _mk_score(0.5, category=["Hate"], rationale="explained"), + _mk_score(0.7, category=["Violence"]), ] result = FloatScaleScoreAggregator.MAX(scores)[0] - assert "explained" in result.rationale - assert result.rationale.count("UnitTestScorer") == 2 + assert result.rationale.splitlines() == [ + " - UnitTestScorer 0.5 (Category: Hate): explained", + " - UnitTestScorer 0.7 (Category: Violence): ", + ] def test_float_scale_aggregator_multiple_categories_preserved(): diff --git a/tests/unit/score/test_score_utils.py b/tests/unit/score/test_score_utils.py index 880e5618f7..de222fb340 100644 --- a/tests/unit/score/test_score_utils.py +++ b/tests/unit/score/test_score_utils.py @@ -284,3 +284,27 @@ def test_handles_missing_rationale(self) -> None: assert "TestScorer" in result assert "False" in result + + def test_formats_categories_without_rationale(self) -> None: + score = Score( + score_type="float_scale", + score_value="0.8", + score_category=["Violence", "Hate"], + score_rationale="", + message_piece_id=str(uuid.uuid4()), + scorer_class_identifier=_make_scorer_id("TestScorer"), + ) + + assert format_score_for_rationale(score) == " - TestScorer 0.8 (Category: Violence, Hate): " + + def test_uncategorized_score_keeps_original_format(self) -> None: + score = Score( + score_type="float_scale", + score_value="0.2", + score_category=None, + score_rationale="explained", + message_piece_id=str(uuid.uuid4()), + scorer_class_identifier=_make_scorer_id("TestScorer"), + ) + + assert format_score_for_rationale(score) == " - TestScorer 0.2: explained"