Skip to content

Commit a923a81

Browse files
committed
Merge remote-tracking branch 'origin/main' into riedgar-ms/selfask-jsonschema-01
2 parents 4551f23 + ba944ee commit a923a81

36 files changed

Lines changed: 959 additions & 533 deletions

.github/copilot-instructions.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,13 @@ Do NOT leave comments about:
2828

2929
Aim for fewer, higher-signal comments. A review with 2-3 important comments is better than 15 trivial ones.
3030

31-
Follow `.github/instructions/style-guide.instructions.md` for style guidelines. And look in `.github/instructions/` for specific instructions on the different components.
31+
## Instruction Files
32+
33+
BEFORE editing or code-reviewing any file, you MUST read the `.github/instructions/` files whose `applyTo` patterns match the files you are about to edit. For example:
34+
- Editing/code-reviewing `pyrit/**/*.py` → read `style-guide.instructions.md` and `user-custom.instructions.md`
35+
- Editing/code-reviewing `pyrit/scenario/**` → also read `scenarios.instructions.md`
36+
- Editing/code-reviewing `pyrit/prompt_converter/**` → also read `converters.instructions.md`
37+
- Editing/code-reviewing `tests/**` → also read `test.instructions.md`
38+
- Editing/code-reviewing `doc/**/*.py` or `doc/**/*.ipynb` → also read `docs.instructions.md`
39+
40+
Follow every rule in the applicable instruction files. Do not skip this step.

.pyrit_conf_example

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ memory_db_type: sqlite
2424
# Available initializers:
2525
# - simple: Basic OpenAI configuration (requires OPENAI_CHAT_* env vars)
2626
# - airt: AI Red Team setup with Azure OpenAI (requires AZURE_OPENAI_* env vars)
27+
# - targets: Registers available prompt targets into the TargetRegistry
28+
# - scorers: Registers pre-configured scorers into the ScorerRegistry
2729
# - load_default_datasets: Loads default datasets for all registered scenarios
2830
# - objective_list: Sets default objectives for scenarios
29-
# - openai_objective_target: Sets up OpenAI target for scenarios
3031
#
3132
# Each initializer can be specified as:
3233
# - A simple string (name only)
@@ -38,13 +39,14 @@ memory_db_type: sqlite
3839
# Example:
3940
# initializers:
4041
# - simple
41-
# - name: target
42+
# - name: targets
4243
# args:
4344
# tags:
4445
# - default
4546
# - scorer
4647
initializers:
4748
- name: simple
49+
- name: load_default_datasets
4850
- name: scorers
4951
- name: targets
5052
args:

doc/code/scenarios/1_configuring_scenarios.ipynb

Lines changed: 24 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
"name": "stdout",
3737
"output_type": "stream",
3838
"text": [
39-
"Found default environment files: ['./.pyrit/.env']\n",
40-
"Loaded environment file: ./.pyrit/.env\n"
39+
"Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']\n",
40+
"Loaded environment file: ./.pyrit/.env\n",
41+
"Loaded environment file: ./.pyrit/.env.local\n"
4142
]
4243
}
4344
],
@@ -74,47 +75,7 @@
7475
"name": "stderr",
7576
"output_type": "stream",
7677
"text": [
77-
"\r",
78-
"Loading datasets - this can take a few minutes: 0%| | 0/46 [00:00<?, ?dataset/s]"
79-
]
80-
},
81-
{
82-
"name": "stderr",
83-
"output_type": "stream",
84-
"text": [
85-
"\r",
86-
"Loading datasets - this can take a few minutes: 2%|▋ | 1/46 [00:00<00:20, 2.20dataset/s]"
87-
]
88-
},
89-
{
90-
"name": "stderr",
91-
"output_type": "stream",
92-
"text": [
93-
"\r",
94-
"Loading datasets - this can take a few minutes: 43%|████████████▌ | 20/46 [00:00<00:00, 45.31dataset/s]"
95-
]
96-
},
97-
{
98-
"name": "stderr",
99-
"output_type": "stream",
100-
"text": [
101-
"\r",
102-
"Loading datasets - this can take a few minutes: 67%|███████████████████▌ | 31/46 [00:00<00:00, 39.43dataset/s]"
103-
]
104-
},
105-
{
106-
"name": "stderr",
107-
"output_type": "stream",
108-
"text": [
109-
"\r",
110-
"Loading datasets - this can take a few minutes: 100%|█████████████████████████████| 46/46 [00:00<00:00, 51.19dataset/s]"
111-
]
112-
},
113-
{
114-
"name": "stderr",
115-
"output_type": "stream",
116-
"text": [
117-
"\n"
78+
"Loading datasets - this can take a few minutes: 100%|██████████| 58/58 [00:00<00:00, 69.09dataset/s]\n"
11879
]
11980
}
12081
],
@@ -222,7 +183,7 @@
222183
{
223184
"data": {
224185
"application/vnd.jupyter.widget-view+json": {
225-
"model_id": "fe242b7b0aaa476187fb5643e41cdd94",
186+
"model_id": "811109e1b31543f3835ee76ed5c708bd",
226187
"version_major": 2,
227188
"version_minor": 0
228189
},
@@ -268,7 +229,7 @@
268229
"\u001b[1m 📋 Scenario Details\u001b[0m\n",
269230
"\u001b[36m • Name: RedTeamAgent\u001b[0m\n",
270231
"\u001b[36m • Scenario Version: 1\u001b[0m\n",
271-
"\u001b[36m • PyRIT Version: 0.10.1.dev0\u001b[0m\n",
232+
"\u001b[36m • PyRIT Version: 0.11.1.dev0\u001b[0m\n",
272233
"\u001b[36m • Description:\u001b[0m\n",
273234
"\u001b[36m RedTeamAgent is a preconfigured scenario that automatically generates multiple AtomicAttack instances based on\u001b[0m\n",
274235
"\u001b[36m the specified attack strategies. It supports both single-turn attacks (with various converters) and multi-turn\u001b[0m\n",
@@ -280,58 +241,49 @@
280241
"\u001b[1m 🎯 Target Information\u001b[0m\n",
281242
"\u001b[36m • Target Type: OpenAIChatTarget\u001b[0m\n",
282243
"\u001b[36m • Target Model: gpt-4o\u001b[0m\n",
283-
"\u001b[36m • Target Endpoint: https://pyrit-japan-test.openai.azure.com/openai/v1\u001b[0m\n",
244+
"\u001b[36m • Target Endpoint: https://pyrit-dev.openai.azure.com/openai/v1\u001b[0m\n",
284245
"\n",
285246
"\u001b[1m 📊 Scorer Information\u001b[0m\n",
286247
"\u001b[37m ▸ Scorer Identifier\u001b[0m\n",
287-
"\u001b[36m • Scorer Type: TrueFalseCompositeScorer\u001b[0m\n",
288-
"\u001b[36m • Score Aggregator: AND_\u001b[0m\n",
289-
"\u001b[36m └─ Composite of 2 scorer(s):\u001b[0m\n",
290-
"\u001b[36m • Scorer Type: FloatScaleThresholdScorer\u001b[0m\n",
291-
"\u001b[36m • Score Aggregator: OR_\u001b[0m\n",
292-
"\u001b[36m • threshold: 0.5\u001b[0m\n",
293-
"\u001b[36m • float_scale_aggregator: MAX_\u001b[0m\n",
294-
"\u001b[36m └─ Composite of 1 scorer(s):\u001b[0m\n",
295-
"\u001b[36m • Scorer Type: AzureContentFilterScorer\u001b[0m\n",
296-
"\u001b[36m • score_categories: ['Hate', 'SelfHarm', 'Sexual', 'Violence']\u001b[0m\n",
297-
"\u001b[36m • Scorer Type: TrueFalseInverterScorer\u001b[0m\n",
298-
"\u001b[36m • Score Aggregator: OR_\u001b[0m\n",
299-
"\u001b[36m └─ Composite of 1 scorer(s):\u001b[0m\n",
300-
"\u001b[36m • Scorer Type: SelfAskRefusalScorer\u001b[0m\n",
301-
"\u001b[36m • Target Model: gpt-4o\u001b[0m\n",
302-
"\u001b[36m • Temperature: 0.9\u001b[0m\n",
303-
"\u001b[36m • Score Aggregator: OR_\u001b[0m\n",
248+
"\u001b[36m • Scorer Type: TrueFalseInverterScorer\u001b[0m\n",
249+
"\u001b[36m • scorer_type: true_false\u001b[0m\n",
250+
"\u001b[36m • score_aggregator: OR_\u001b[0m\n",
251+
"\u001b[36m └─ Composite of 1 scorer(s):\u001b[0m\n",
252+
"\u001b[36m • Scorer Type: SelfAskRefusalScorer\u001b[0m\n",
253+
"\u001b[36m • scorer_type: true_false\u001b[0m\n",
254+
"\u001b[36m • score_aggregator: OR_\u001b[0m\n",
255+
"\u001b[36m • model_name: gpt-4o\u001b[0m\n",
304256
"\n",
305257
"\u001b[37m ▸ Performance Metrics\u001b[0m\n",
306-
"\u001b[31m • Accuracy: 54.05%\u001b[0m\n",
307-
"\u001b[36m • Accuracy Std Error: ±0.0410\u001b[0m\n",
308-
"\u001b[31m • F1 Score: 0.2273\u001b[0m\n",
309-
"\u001b[36m • Precision: 0.7143\u001b[0m\n",
310-
"\u001b[31m • Recall: 0.1351\u001b[0m\n",
311-
"\u001b[36m • Average Score Time: 0.76s\u001b[0m\n",
258+
"\u001b[36m • Accuracy: 84.84%\u001b[0m\n",
259+
"\u001b[36m • Accuracy Std Error: ±0.0185\u001b[0m\n",
260+
"\u001b[36m • F1 Score: 0.8606\u001b[0m\n",
261+
"\u001b[36m • Precision: 0.7928\u001b[0m\n",
262+
"\u001b[32m • Recall: 0.9412\u001b[0m\n",
263+
"\u001b[36m • Average Score Time: 1.27s\u001b[0m\n",
312264
"\n",
313265
"\u001b[1m\u001b[36m▼ Overall Statistics\u001b[0m\n",
314266
"\u001b[36m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
315267
"\u001b[1m 📈 Summary\u001b[0m\n",
316268
"\u001b[32m • Total Strategies: 4\u001b[0m\n",
317269
"\u001b[32m • Total Attack Results: 8\u001b[0m\n",
318-
"\u001b[32m • Overall Success Rate: 0%\u001b[0m\n",
270+
"\u001b[36m • Overall Success Rate: 25%\u001b[0m\n",
319271
"\u001b[32m • Unique Objectives: 4\u001b[0m\n",
320272
"\n",
321273
"\u001b[1m\u001b[36m▼ Per-Strategy Breakdown\u001b[0m\n",
322274
"\u001b[36m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
323275
"\n",
324276
"\u001b[1m 🔸 Strategy: baseline\u001b[0m\n",
325277
"\u001b[33m • Number of Results: 2\u001b[0m\n",
326-
"\u001b[32m • Success Rate: 0%\u001b[0m\n",
278+
"\u001b[33m • Success Rate: 50%\u001b[0m\n",
327279
"\n",
328280
"\u001b[1m 🔸 Strategy: base64\u001b[0m\n",
329281
"\u001b[33m • Number of Results: 2\u001b[0m\n",
330282
"\u001b[32m • Success Rate: 0%\u001b[0m\n",
331283
"\n",
332284
"\u001b[1m 🔸 Strategy: binary\u001b[0m\n",
333285
"\u001b[33m • Number of Results: 2\u001b[0m\n",
334-
"\u001b[32m • Success Rate: 0%\u001b[0m\n",
286+
"\u001b[33m • Success Rate: 50%\u001b[0m\n",
335287
"\n",
336288
"\u001b[1m 🔸 Strategy: ComposedStrategy(caesar, char_swap)\u001b[0m\n",
337289
"\u001b[33m • Number of Results: 2\u001b[0m\n",

pyrit/cli/_banner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def add(line: str, role: ColorRole, segments: Optional[list[tuple[int, int, Colo
296296
quick_start = [
297297
"Quick Start:",
298298
" pyrit> list-scenarios",
299-
" pyrit> run foundry --initializers openai_objective_target load_default_datasets",
299+
" pyrit> run foundry --target my_target --initializers targets load_default_datasets",
300300
]
301301
for qs in quick_start:
302302
full_line = _box_line(" " + qs)

pyrit/cli/_cli_args.py

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ def parse_memory_labels(json_string: str) -> dict[str, str]:
286286
"Creates a new dataset config; fetches all items unless --max-dataset-size is also specified",
287287
"max_dataset_size": "Maximum number of items to use from the dataset (must be >= 1). "
288288
"Limits new datasets if --dataset-names provided, otherwise overrides scenario's default limit",
289+
"target": "Name of a registered target from the TargetRegistry to use as the objective target. "
290+
"Targets are registered by initializers (e.g., 'targets' initializer). "
291+
"Use --list-targets to see available target names after initializers have run",
289292
}
290293

291294

@@ -372,15 +375,14 @@ def parse_run_arguments(*, args_string: str) -> dict[str, Any]:
372375
"scenario_name": parts[0],
373376
"initializers": None,
374377
"initialization_scripts": None,
375-
"env_files": None,
376378
"scenario_strategies": None,
377379
"max_concurrency": None,
378380
"max_retries": None,
379381
"memory_labels": None,
380-
"database": None,
381382
"log_level": None,
382383
"dataset_names": None,
383384
"max_dataset_size": None,
385+
"target": None,
384386
}
385387

386388
i = 1
@@ -399,13 +401,6 @@ def parse_run_arguments(*, args_string: str) -> dict[str, Any]:
399401
while i < len(parts) and not parts[i].startswith("--"):
400402
result["initialization_scripts"].append(parts[i])
401403
i += 1
402-
elif parts[i] == "--env-files":
403-
# Collect env file paths until next flag
404-
result["env_files"] = []
405-
i += 1
406-
while i < len(parts) and not parts[i].startswith("--"):
407-
result["env_files"].append(parts[i])
408-
i += 1
409404
elif parts[i] in ("--strategies", "-s"):
410405
# Collect strategies until next flag
411406
result["scenario_strategies"] = []
@@ -431,12 +426,6 @@ def parse_run_arguments(*, args_string: str) -> dict[str, Any]:
431426
raise ValueError("--memory-labels requires a value")
432427
result["memory_labels"] = parse_memory_labels(parts[i])
433428
i += 1
434-
elif parts[i] == "--database":
435-
i += 1
436-
if i >= len(parts):
437-
raise ValueError("--database requires a value")
438-
result["database"] = validate_database(database=parts[i])
439-
i += 1
440429
elif parts[i] == "--log-level":
441430
i += 1
442431
if i >= len(parts):
@@ -456,6 +445,12 @@ def parse_run_arguments(*, args_string: str) -> dict[str, Any]:
456445
raise ValueError("--max-dataset-size requires a value")
457446
result["max_dataset_size"] = validate_integer(parts[i], name="--max-dataset-size", min_value=1)
458447
i += 1
448+
elif parts[i] == "--target":
449+
i += 1
450+
if i >= len(parts):
451+
raise ValueError("--target requires a value")
452+
result["target"] = parts[i]
453+
i += 1
459454
else:
460455
raise ValueError(f"Unknown argument: {parts[i]}")
461456

@@ -470,24 +465,12 @@ def parse_run_arguments(*, args_string: str) -> dict[str, Any]:
470465
def add_common_arguments(parser: argparse.ArgumentParser) -> None:
471466
"""Add arguments shared between pyrit_shell and pyrit_scan."""
472467
parser.add_argument("--config-file", type=Path, help=ARG_HELP["config_file"])
473-
parser.add_argument(
474-
"--database",
475-
type=validate_database_argparse,
476-
default=None,
477-
help=f"Database type to use ({IN_MEMORY}, {SQLITE}, {AZURE_SQL}). Defaults to config or {SQLITE}.",
478-
)
479468
parser.add_argument(
480469
"--log-level",
481470
type=validate_log_level_argparse,
482471
default=logging.WARNING,
483472
help="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) (default: WARNING)",
484473
)
485-
parser.add_argument(
486-
"--env-files",
487-
type=str,
488-
nargs="+",
489-
help=ARG_HELP["env_files"],
490-
)
491474

492475

493476
# Module-level logger (stdlib only — no heavy deps)

0 commit comments

Comments
 (0)