Skip to content

Commit a0ead85

Browse files
committed
Merge remote-tracking branch 'origin/main' into riedgar-ms/selfask-jsonschema-01
2 parents a923a81 + 83b7f87 commit a0ead85

14 files changed

Lines changed: 108 additions & 92 deletions

pyrit/score/audio_transcript_scorer.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import os
66
import tempfile
77
import uuid
8-
from abc import ABC
98
from typing import Optional
109

1110
import av
@@ -88,7 +87,7 @@ def _audio_to_wav(input_path: str, *, sample_rate: int, channels: int) -> str:
8887
return output_path
8988

9089

91-
class AudioTranscriptHelper(ABC): # noqa: B024
90+
class AudioTranscriptHelper: # noqa: B024
9291
"""
9392
Abstract base class for audio scorers that process audio by transcribing and scoring the text.
9493

pyrit/score/float_scale/video_float_scale_scorer.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
)
1212
from pyrit.score.float_scale.float_scale_scorer import FloatScaleScorer
1313
from pyrit.score.scorer_prompt_validator import ScorerPromptValidator
14-
from pyrit.score.video_scorer import _BaseVideoScorer
14+
from pyrit.score.video_scorer import VideoHelper
1515

1616
if TYPE_CHECKING:
1717
from pyrit.score.score_aggregator_result import ScoreAggregatorResult
1818

1919

2020
class VideoFloatScaleScorer(
2121
FloatScaleScorer,
22-
_BaseVideoScorer,
2322
):
2423
"""
2524
A scorer that processes videos by extracting frames and scoring them using a float scale image scorer.
@@ -48,7 +47,7 @@ def __init__(
4847
num_sampled_frames: Optional[int] = None,
4948
validator: Optional[ScorerPromptValidator] = None,
5049
score_aggregator: FloatScaleAggregatorFunc = FloatScaleScorerByCategory.MAX,
51-
image_objective_template: Optional[str] = _BaseVideoScorer._DEFAULT_IMAGE_OBJECTIVE_TEMPLATE,
50+
image_objective_template: Optional[str] = VideoHelper._DEFAULT_IMAGE_OBJECTIVE_TEMPLATE,
5251
audio_objective_template: Optional[str] = None,
5352
) -> None:
5453
"""
@@ -82,8 +81,7 @@ def __init__(
8281
"""
8382
FloatScaleScorer.__init__(self, validator=validator or self._DEFAULT_VALIDATOR)
8483

85-
_BaseVideoScorer.__init__(
86-
self,
84+
self._video_helper = VideoHelper(
8785
image_capable_scorer=image_capable_scorer,
8886
num_sampled_frames=num_sampled_frames,
8987
image_objective_template=image_objective_template,
@@ -92,7 +90,7 @@ def __init__(
9290
self._score_aggregator = score_aggregator
9391

9492
if audio_scorer is not None:
95-
self._validate_audio_scorer(audio_scorer)
93+
VideoHelper._validate_audio_scorer(audio_scorer)
9694
self.audio_scorer = audio_scorer
9795

9896
def _build_identifier(self) -> ComponentIdentifier:
@@ -102,17 +100,17 @@ def _build_identifier(self) -> ComponentIdentifier:
102100
Returns:
103101
ComponentIdentifier: The identifier for this scorer.
104102
"""
105-
sub_scorer_ids = [self.image_scorer.get_identifier()]
103+
sub_scorer_ids = [self._video_helper.image_scorer.get_identifier()]
106104
if self.audio_scorer:
107105
sub_scorer_ids.append(self.audio_scorer.get_identifier())
108106

109107
return self._create_identifier(
110108
params={
111109
"score_aggregator": self._score_aggregator.__name__,
112-
"num_sampled_frames": self.num_sampled_frames,
110+
"num_sampled_frames": self._video_helper.num_sampled_frames,
113111
"has_audio_scorer": self.audio_scorer is not None,
114-
"image_objective_template": self.image_objective_template,
115-
"audio_objective_template": self.audio_objective_template,
112+
"image_objective_template": self._video_helper.image_objective_template,
113+
"audio_objective_template": self._video_helper.audio_objective_template,
116114
},
117115
children={
118116
"sub_scorers": sub_scorer_ids,
@@ -131,14 +129,14 @@ async def _score_piece_async(self, message_piece: MessagePiece, *, objective: Op
131129
List of aggregated scores for the video. Returns one score if using FloatScaleScoreAggregator,
132130
or multiple scores (one per category) if using FloatScaleScorerByCategory.
133131
"""
134-
frame_scores = await self._score_frames_async(message_piece=message_piece, objective=objective)
132+
frame_scores = await self._video_helper._score_frames_async(message_piece=message_piece, objective=objective)
135133

136134
all_scores = list(frame_scores)
137135
audio_scored = False
138136

139137
# Score audio if audio_scorer is provided
140138
if self.audio_scorer:
141-
audio_scores = await self._score_video_audio_async(
139+
audio_scores = await self._video_helper._score_video_audio_async(
142140
message_piece=message_piece, audio_scorer=self.audio_scorer, objective=objective
143141
)
144142
if audio_scores:

pyrit/score/true_false/video_true_false_scorer.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from pyrit.score.scorer_prompt_validator import ScorerPromptValidator
99
from pyrit.score.true_false.true_false_score_aggregator import TrueFalseScoreAggregator
1010
from pyrit.score.true_false.true_false_scorer import TrueFalseScorer
11-
from pyrit.score.video_scorer import _BaseVideoScorer
11+
from pyrit.score.video_scorer import VideoHelper
1212

1313

14-
class VideoTrueFalseScorer(TrueFalseScorer, _BaseVideoScorer):
14+
class VideoTrueFalseScorer(TrueFalseScorer):
1515
"""
1616
A scorer that processes videos by extracting frames and scoring them using a true/false image scorer.
1717
@@ -34,7 +34,7 @@ def __init__(
3434
audio_scorer: Optional[TrueFalseScorer] = None,
3535
num_sampled_frames: Optional[int] = None,
3636
validator: Optional[ScorerPromptValidator] = None,
37-
image_objective_template: Optional[str] = _BaseVideoScorer._DEFAULT_IMAGE_OBJECTIVE_TEMPLATE,
37+
image_objective_template: Optional[str] = VideoHelper._DEFAULT_IMAGE_OBJECTIVE_TEMPLATE,
3838
audio_objective_template: Optional[str] = None,
3939
) -> None:
4040
"""
@@ -59,18 +59,17 @@ def __init__(
5959
Raises:
6060
ValueError: If audio_scorer is provided and does not support audio_path data type.
6161
"""
62-
_BaseVideoScorer.__init__(
63-
self,
62+
super().__init__(validator=validator or self._DEFAULT_VALIDATOR)
63+
64+
self._video_helper = VideoHelper(
6465
image_capable_scorer=image_capable_scorer,
6566
num_sampled_frames=num_sampled_frames,
6667
image_objective_template=image_objective_template,
6768
audio_objective_template=audio_objective_template,
6869
)
6970

70-
TrueFalseScorer.__init__(self, validator=validator or self._DEFAULT_VALIDATOR)
71-
7271
if audio_scorer is not None:
73-
self._validate_audio_scorer(audio_scorer)
72+
VideoHelper._validate_audio_scorer(audio_scorer)
7473
self.audio_scorer = audio_scorer
7574

7675
def _build_identifier(self) -> ComponentIdentifier:
@@ -80,16 +79,16 @@ def _build_identifier(self) -> ComponentIdentifier:
8079
Returns:
8180
ComponentIdentifier: The identifier for this scorer.
8281
"""
83-
sub_scorer_ids = [self.image_scorer.get_identifier()]
82+
sub_scorer_ids = [self._video_helper.image_scorer.get_identifier()]
8483
if self.audio_scorer:
8584
sub_scorer_ids.append(self.audio_scorer.get_identifier())
8685

8786
return self._create_identifier(
8887
params={
89-
"num_sampled_frames": self.num_sampled_frames,
88+
"num_sampled_frames": self._video_helper.num_sampled_frames,
9089
"has_audio_scorer": self.audio_scorer is not None,
91-
"image_objective_template": self.image_objective_template,
92-
"audio_objective_template": self.audio_objective_template,
90+
"image_objective_template": self._video_helper.image_objective_template,
91+
"audio_objective_template": self._video_helper.audio_objective_template,
9392
},
9493
children={
9594
"sub_scorers": sub_scorer_ids,
@@ -114,7 +113,7 @@ async def _score_piece_async(self, message_piece: MessagePiece, *, objective: Op
114113
piece_id = message_piece.id if message_piece.id is not None else message_piece.original_prompt_id
115114

116115
# Get scores for all frames and aggregate with OR (True if ANY frame matches)
117-
frame_scores = await self._score_frames_async(message_piece=message_piece, objective=objective)
116+
frame_scores = await self._video_helper._score_frames_async(message_piece=message_piece, objective=objective)
118117
frame_result = TrueFalseScoreAggregator.OR(frame_scores)
119118

120119
# Create a Score from the frame aggregation result
@@ -132,7 +131,7 @@ async def _score_piece_async(self, message_piece: MessagePiece, *, objective: Op
132131

133132
# Score audio if audio_scorer is provided
134133
if self.audio_scorer:
135-
audio_scores = await self._score_video_audio_async(
134+
audio_scores = await self._video_helper._score_video_audio_async(
136135
message_piece=message_piece, audio_scorer=self.audio_scorer, objective=objective
137136
)
138137
if audio_scores:

pyrit/score/video_scorer.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import random
77
import tempfile
88
import uuid
9-
from abc import ABC
109
from typing import Optional
1110

1211
from pyrit.memory import CentralMemory
@@ -17,13 +16,13 @@
1716
logger = logging.getLogger(__name__)
1817

1918

20-
class _BaseVideoScorer(ABC): # noqa: B024
19+
class VideoHelper:
2120
"""
22-
Abstract base class for video scorers that process videos by extracting frames and scoring them.
21+
Helper class for video scorers that process videos by extracting frames and scoring them.
2322
2423
This class provides common functionality for extracting frames from videos and delegating
25-
scoring to an image-capable scorer. Concrete implementations handle aggregation logic
26-
specific to their scoring type (true/false or float scale).
24+
scoring to an image-capable scorer. Used via composition by VideoTrueFalseScorer and
25+
VideoFloatScaleScorer.
2726
"""
2827

2928
_DEFAULT_VIDEO_FRAMES_SAMPLING_NUM = 5

tests/integration/converter/test_notebooks_converter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
nb_directory_path = pathlib.Path(path.DOCS_CODE_PATH, "converters").resolve()
1414

1515
skipped_files = [
16+
"2_audio_converters.ipynb", # requires Azure Speech API key
1617
"7_human_converter.ipynb", # requires human input
1718
]
1819

tests/integration/datasets/test_notebooks_datasets.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212

1313
nb_directory_path = pathlib.Path(path.DOCS_CODE_PATH, "datasets").resolve()
1414

15+
skipped_files = [
16+
"2_seed_programming.ipynb", # requires OpenAI API credentials
17+
]
18+
1519

1620
@pytest.mark.parametrize(
1721
"file_name",
18-
[file for file in os.listdir(nb_directory_path) if file.endswith(".ipynb")],
22+
[file for file in os.listdir(nb_directory_path) if file.endswith(".ipynb") and file not in skipped_files],
1923
)
2024
def test_execute_notebooks(file_name):
2125
nb_path = pathlib.Path(nb_directory_path, file_name).resolve()

0 commit comments

Comments
 (0)