Skip to content

Commit 698d848

Browse files
rlundeen2romanlutz
andauthored
FEAT: Scenario DatasetConfiguration (microsoft#1288)
Co-authored-by: Roman Lutz <romanlutz13@gmail.com>
1 parent e8ec417 commit 698d848

40 files changed

Lines changed: 1649 additions & 422 deletions

doc/_toc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ chapters:
129129
- file: code/auxiliary_attacks/1_gcg_azure_ml
130130
- file: code/scenarios/0_scenarios
131131
sections:
132-
- file: code/scenarios/1_composite_scenario
132+
- file: code/scenarios/1_configuring_scenarios
133133
- file: code/front_end/0_front_end
134134
sections:
135135
- file: code/front_end/1_pyrit_scan

doc/code/converters/3_image_converters.ipynb

Lines changed: 18 additions & 44 deletions
Large diffs are not rendered by default.

doc/code/converters/3_image_converters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
# This converter adds text to an existing image
6666
# Using the roakey image from the doc folder
67-
sample_image = pathlib.Path("/workspace/doc/roakey.png")
67+
sample_image = pathlib.Path(".").resolve().parent.parent / "roakey.png"
6868
text_to_image_converter = AddImageTextConverter(img_to_add=str(sample_image))
6969
output_image_file = await text_to_image_converter.convert_async(prompt=text_prompt) # type: ignore
7070

@@ -89,7 +89,7 @@
8989
text_to_add: str = jailbreak.get_jailbreak(prompt="How to create a Molotov cocktail?")
9090

9191
image_converter = AddTextImageConverter(text_to_add=text_to_add)
92-
image_location = str(pathlib.Path("/workspace/doc/roakey.png"))
92+
image_location = str(pathlib.Path(".").resolve().parent.parent / "roakey.png")
9393
output_image_file = await image_converter.convert_async(prompt=image_location) # type: ignore
9494

9595
print(output_image_file)

doc/code/scenarios/0_scenarios.ipynb

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
"\n",
3333
"These Scenarios can be updated and added to as you refine what you are testing for.\n",
3434
"\n",
35+
"## How to Run Scenarios\n",
36+
"\n",
37+
"Scenarios should take almost no effort to run with default values. [pyrit_scan](../front_end/1_pyrit_scan.ipynb) and [pyrit_shell](../front_end/2_pyrit_shell.md) both use scenarios to execute.\n",
38+
"\n",
3539
"## How It Works\n",
3640
"\n",
3741
"Each `Scenario` contains a collection of `AtomicAttack` objects. When executed:\n",
@@ -80,16 +84,33 @@
8084
"execution_count": null,
8185
"id": "1",
8286
"metadata": {},
83-
"outputs": [],
87+
"outputs": [
88+
{
89+
"name": "stdout",
90+
"output_type": "stream",
91+
"text": [
92+
"Found default environment files: ['C:\\\\Users\\\\rlundeen\\\\.pyrit\\\\.env', 'C:\\\\Users\\\\rlundeen\\\\.pyrit\\\\.env.local']\n",
93+
"Loaded environment file: C:\\Users\\rlundeen\\.pyrit\\.env\n",
94+
"Loaded environment file: C:\\Users\\rlundeen\\.pyrit\\.env.local\n"
95+
]
96+
}
97+
],
8498
"source": [
8599
"from typing import List, Optional, Type\n",
86100
"\n",
87101
"from pyrit.common import apply_defaults\n",
88102
"from pyrit.executor.attack import AttackScoringConfig, PromptSendingAttack\n",
89-
"from pyrit.models import SeedGroup, SeedObjective\n",
90-
"from pyrit.scenario import AtomicAttack, Scenario, ScenarioStrategy\n",
103+
"from pyrit.scenario import (\n",
104+
" AtomicAttack,\n",
105+
" DatasetConfiguration,\n",
106+
" Scenario,\n",
107+
" ScenarioStrategy,\n",
108+
")\n",
91109
"from pyrit.scenario.core.scenario_strategy import ScenarioCompositeStrategy\n",
92110
"from pyrit.score.true_false.true_false_scorer import TrueFalseScorer\n",
111+
"from pyrit.setup import initialize_pyrit_async\n",
112+
"\n",
113+
"await initialize_pyrit_async(memory_db_type=\"InMemory\") # type: ignore [top-level-await]\n",
93114
"\n",
94115
"\n",
95116
"class MyStrategy(ScenarioStrategy):\n",
@@ -101,6 +122,7 @@
101122
"class MyScenario(Scenario):\n",
102123
" version: int = 1\n",
103124
"\n",
125+
" # A strategy defintion helps callers define how to run your scenario (e.g. from the front_end)\n",
104126
" @classmethod\n",
105127
" def get_strategy_class(cls) -> Type[ScenarioStrategy]:\n",
106128
" return MyStrategy\n",
@@ -109,6 +131,11 @@
109131
" def get_default_strategy(cls) -> ScenarioStrategy:\n",
110132
" return MyStrategy.ALL\n",
111133
"\n",
134+
" # This is the default dataset configuration for this scenario (e.g. prompts to send)\n",
135+
" @classmethod\n",
136+
" def default_dataset_config(cls) -> DatasetConfiguration:\n",
137+
" return DatasetConfiguration(dataset_names=[\"dataset_name\"])\n",
138+
"\n",
112139
" @apply_defaults\n",
113140
" def __init__(\n",
114141
" self,\n",
@@ -146,11 +173,8 @@
146173
" )\n",
147174
"\n",
148175
" for strategy in selected_strategies:\n",
149-
" # Create seed groups with objectives\n",
150-
" seed_groups = [\n",
151-
" SeedGroup(seeds=[SeedObjective(value=\"objective1\")]),\n",
152-
" SeedGroup(seeds=[SeedObjective(value=\"objective2\")]),\n",
153-
" ]\n",
176+
" # self._dataset_config is set by the parent class\n",
177+
" seed_groups = self._dataset_config.get_all_seed_groups()\n",
154178
"\n",
155179
" # Create attack instances based on strategy\n",
156180
" attack = PromptSendingAttack(\n",
@@ -165,7 +189,10 @@
165189
" memory_labels=self._memory_labels,\n",
166190
" )\n",
167191
" )\n",
168-
" return atomic_attacks"
192+
" return atomic_attacks\n",
193+
" \n",
194+
"\n",
195+
"scenario = MyScenario()"
169196
]
170197
},
171198
{
@@ -205,7 +232,7 @@
205232
" Available Strategies (7):\n",
206233
" hate, fairness, violence, sexual, harassment, misinformation, leakage\n",
207234
" Default Strategy: all\n",
208-
" Required Datasets (7):\n",
235+
" Default Datasets (7, max 4 per dataset):\n",
209236
" airt_hate, airt_fairness, airt_violence, airt_sexual, airt_harassment,\n",
210237
" airt_misinformation, airt_leakage\n",
211238
"\u001b[1m\u001b[36m\n",
@@ -221,7 +248,7 @@
221248
" Available Strategies (2):\n",
222249
" single_turn, multi_turn\n",
223250
" Default Strategy: all\n",
224-
" Required Datasets (1):\n",
251+
" Default Datasets (1):\n",
225252
" airt_malware\n",
226253
"\u001b[1m\u001b[36m\n",
227254
" foundry_scenario\u001b[0m\n",
@@ -245,7 +272,7 @@
245272
" suffix_append, string_join, unicode_confusable, unicode_substitution,\n",
246273
" url, jailbreak, tense, multi_turn, crescendo, pair, tap\n",
247274
" Default Strategy: easy\n",
248-
" Required Datasets (1):\n",
275+
" Default Datasets (1, max 4 per dataset):\n",
249276
" harmbench\n",
250277
"\u001b[1m\u001b[36m\n",
251278
" garak.encoding_scenario\u001b[0m\n",
@@ -268,7 +295,7 @@
268295
" uuencode, rot13, braille, atbash, morse_code, nato, ecoji, zalgo,\n",
269296
" leet_speak, ascii_smuggler\n",
270297
" Default Strategy: all\n",
271-
" Required Datasets (2):\n",
298+
" Default Datasets (2, max 4 per dataset):\n",
272299
" garak_slur_terms_en, garak_web_html_js\n",
273300
"\n",
274301
"================================================================================\n",

doc/code/scenarios/0_scenarios.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
# extension: .py
66
# format_name: percent
77
# format_version: '1.3'
8-
# jupytext_version: 1.17.3
8+
# jupytext_version: 1.18.1
9+
# kernelspec:
10+
# display_name: pyrit (3.13.5)
11+
# language: python
12+
# name: python3
913
# ---
1014

1115
# %% [markdown]
@@ -34,6 +38,10 @@
3438
#
3539
# These Scenarios can be updated and added to as you refine what you are testing for.
3640
#
41+
# ## How to Run Scenarios
42+
#
43+
# Scenarios should take almost no effort to run with default values. [pyrit_scan](../front_end/1_pyrit_scan.ipynb) and [pyrit_shell](../front_end/2_pyrit_shell.md) both use scenarios to execute.
44+
#
3745
# ## How It Works
3846
#
3947
# Each `Scenario` contains a collection of `AtomicAttack` objects. When executed:
@@ -80,10 +88,17 @@
8088

8189
from pyrit.common import apply_defaults
8290
from pyrit.executor.attack import AttackScoringConfig, PromptSendingAttack
83-
from pyrit.models import SeedGroup, SeedObjective
84-
from pyrit.scenario import AtomicAttack, Scenario, ScenarioStrategy
91+
from pyrit.scenario import (
92+
AtomicAttack,
93+
DatasetConfiguration,
94+
Scenario,
95+
ScenarioStrategy,
96+
)
8597
from pyrit.scenario.core.scenario_strategy import ScenarioCompositeStrategy
8698
from pyrit.score.true_false.true_false_scorer import TrueFalseScorer
99+
from pyrit.setup import initialize_pyrit_async
100+
101+
await initialize_pyrit_async(memory_db_type="InMemory") # type: ignore [top-level-await]
87102

88103

89104
class MyStrategy(ScenarioStrategy):
@@ -95,6 +110,7 @@ class MyStrategy(ScenarioStrategy):
95110
class MyScenario(Scenario):
96111
version: int = 1
97112

113+
# A strategy defintion helps callers define how to run your scenario (e.g. from the front_end)
98114
@classmethod
99115
def get_strategy_class(cls) -> Type[ScenarioStrategy]:
100116
return MyStrategy
@@ -103,6 +119,11 @@ def get_strategy_class(cls) -> Type[ScenarioStrategy]:
103119
def get_default_strategy(cls) -> ScenarioStrategy:
104120
return MyStrategy.ALL
105121

122+
# This is the default dataset configuration for this scenario (e.g. prompts to send)
123+
@classmethod
124+
def default_dataset_config(cls) -> DatasetConfiguration:
125+
return DatasetConfiguration(dataset_names=["dataset_name"])
126+
106127
@apply_defaults
107128
def __init__(
108129
self,
@@ -140,11 +161,8 @@ async def _get_atomic_attacks_async(self) -> List[AtomicAttack]:
140161
)
141162

142163
for strategy in selected_strategies:
143-
# Create seed groups with objectives
144-
seed_groups = [
145-
SeedGroup(seeds=[SeedObjective(value="objective1")]),
146-
SeedGroup(seeds=[SeedObjective(value="objective2")]),
147-
]
164+
# self._dataset_config is set by the parent class
165+
seed_groups = self._dataset_config.get_all_seed_groups()
148166

149167
# Create attack instances based on strategy
150168
attack = PromptSendingAttack(
@@ -162,6 +180,8 @@ async def _get_atomic_attacks_async(self) -> List[AtomicAttack]:
162180
return atomic_attacks
163181

164182

183+
scenario = MyScenario()
184+
165185
# %% [markdown]
166186
#
167187
# ## Existing Scenarios

0 commit comments

Comments
 (0)