Skip to content

FIX: reject a threshold aggregator that does not combine scores - #2877

Open
fei (feiiiiii5) wants to merge 3 commits into
microsoft:mainfrom
feiiiiii5:fix/float-scale-threshold-multi-aggregate
Open

fei (feiiiiii5) wants to merge 3 commits into
microsoft:mainfrom
feiiiiii5:fix/float-scale-threshold-multi-aggregate

Conversation

@feiiiiii5

Copy link
Copy Markdown
Contributor

Description

FloatScaleThresholdScorer accepts any FloatScaleAggregatorFunc but only ever used the first result:

aggregate_results = self._float_scale_aggregator(scores)
aggregate_score = aggregate_results[0]

FloatScaleScorerByCategory.MAX is exported from pyrit.score and is the same FloatScaleAggregatorFunc type as the default FloatScaleScoreAggregator.MAX, but returns one result per harm category instead of combining them. Wrapping a per-category scorer in a threshold scorer produced a single True/False verdict decided by whichever category sorted first, the other categories dropped with no log, metadata key or second score. Thresholding Hate: 0.0 and Violence: 0.9 at 0.5 returns:

score: False | category: ['Hate'] | meta: {'original_float_value': 0.0}

In a red-teaming run that reads as "not harmful" when a category is well over the threshold.

Two components here already refuse this instead of guessing: TrueFalseCompositeScorer raises ValueError("Each TrueFalseScorer must return exactly one score.") and FallbackScorer raises "...aggregate multiple results first.". This makes the threshold scorer consistent with them, and turns the empty-aggregate case into the same clear error instead of IndexError: list index out of range.

Tests and Documentation

Two tests in tests/unit/score/test_float_scale_threshold_scorer.py: a by-category aggregator is rejected with a message naming it, and an aggregator returning nothing is rejected rather than raising IndexError. Docstrings for float_scale_aggregator and _apply_threshold state the requirement.

Command output

With only the tests added, on main at 7b533109:

$ pytest tests/unit/score/test_float_scale_threshold_scorer.py -q
FAILED ...::test_float_scale_threshold_scorer_rejects_aggregator_that_does_not_combine
FAILED ...::test_float_scale_threshold_scorer_rejects_aggregator_that_returns_nothing
2 failed, 36 passed in 2.50s

The second failed with Actual message: 'Error in scorer FloatScaleThresholdScorer: list index out of range'; the first did not raise at all and returned the False verdict above.

After:

$ pytest tests/unit/score/test_float_scale_threshold_scorer.py -q
38 passed in 2.75s

$ pytest tests/unit/score -q
2794 passed, 14 warnings in 33.81s

$ ruff check pyrit/score/true_false/float_scale_threshold_scorer.py tests/unit/score/test_float_scale_threshold_scorer.py
All checks passed!

$ ruff format --check <same two files>
2 files already formatted

The pre-commit ty check flags every __name__ read on a callable, and this
file already silences the two pre-existing ones with the same rule id.
auto-merge was automatically disabled September 28, 2026 16:40

Head branch was pushed to by a user without write access

This branch has not been deployed

No deployments
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