Source code for pyrit.score.composite_scorer
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from typing import List, Optional
from pyrit.models import PromptRequestPiece, Score
from pyrit.score.score_aggregator import ScoreAggregator
from pyrit.score.scorer import Scorer
[docs]
class CompositeScorer(Scorer):
"""A scorer that aggregates other true_false scorers using a specified aggregation function.
It returns a single score of True or False based on the aggregation of the scores of the constituent
scorers.
Args:
aggregator: The aggregation function to use (e.g. `AND_`, `OR_`, `MAJORITY_`)
scorers: List of true_false scorers to combine
score_category: Optional category for the score
"""
[docs]
def __init__(
self, *, aggregator: ScoreAggregator, scorers: List[Scorer], score_category: Optional[str] = None
) -> None:
self.scorer_type = "true_false"
self._aggregator = aggregator
self._score_category = score_category
if not scorers:
raise ValueError("At least one scorer must be provided.")
for scorer in scorers:
if scorer.scorer_type != "true_false":
raise ValueError("All scorers must be true_false scorers.")
self._scorers = scorers
[docs]
async def score_async(self, request_response: PromptRequestPiece, *, task: Optional[str] = None) -> List[Score]:
"""Scores the request response by combining results from all constituent scorers.
Args:
request_response: The request response to be scored
task: Optional task description for scoring context
Returns:
List containing a single Score object representing the combined result
"""
self.validate(request_response, task=task)
scores = await self._score_all_async(request_response, task=task)
identifier_dict = self.get_identifier()
identifier_dict["sub_identifier"] = [scorer.get_identifier() for scorer in self._scorers]
result = self._aggregator(scores)
return_score = Score(
score_value=str(result.value),
score_value_description=None,
score_type=self.scorer_type,
score_category=self._score_category,
score_metadata=None,
score_rationale=result.rationale,
scorer_class_identifier=identifier_dict,
prompt_request_response_id=request_response.id,
task=task,
)
return [return_score]
async def _score_all_async(
self, request_response: PromptRequestPiece, *, task: Optional[str] = None
) -> List[Score]:
"""Scores the request_response using all constituent scorers sequentially.
Args:
request_response: The request response to be scored
task: Optional task description for scoring context
Returns:
List of scores from all constituent scorers
"""
if not self._scorers:
return []
all_scores = []
for scorer in self._scorers:
scores = await scorer.score_async(request_response=request_response, task=task)
all_scores.extend(scores)
return all_scores
[docs]
def validate(self, request_response: PromptRequestPiece, *, task: Optional[str] = None) -> None:
"""Validates the request response for scoring.
Args:
request_response: The request response to validate
task: Optional task description for validation context
"""
pass