Skip to content

MAINT: Simplifying building atomic attacks#2107

Open
rlundeen2 wants to merge 6 commits into
microsoft:mainfrom
rlundeen2:rlundeen2-rethink-scenarios
Open

MAINT: Simplifying building atomic attacks#2107
rlundeen2 wants to merge 6 commits into
microsoft:mainfrom
rlundeen2:rlundeen2-rethink-scenarios

Conversation

@rlundeen2

Copy link
Copy Markdown
Contributor

Problem

The Scenario base class bakes in a single construction shape — know strategy_class + one objective_scorer + default_dataset_config at __init__, then cross-product techniques × datasets in a default _get_atomic_attacks_async. That shape only fits the trivial scenarios; every important one (Encoding, RedTeamAgent, AdversarialBenchmark, AdaptiveScenario) fully overrides that method, so it inherits base hooks (_get_attack_technique_factories, _build_display_group, _prepare_strategies, the default cross-product, automatic baseline injection) it never uses but must understand and work around — and it has to remember the "self._objective_target / self._dataset_config are only valid after initialize_async" footgun. Writing a new scenario means fighting machinery that doesn't generalize (there isn't always a single default scorer, a single baseline, or a single default strategy).

This PR

Introduces one explicit extension point, async _build_atomic_attacks_async(self, *, context: ScenarioContext), where ScenarioContext is a frozen dataclass carrying the resolved run inputs (objective_target, scenario_strategies, dataset_config, memory_labels, include_baseline) — which kills the "before initialize_async" footgun because the method can't be called without a populated context. The reusable technique × dataset (× optional adversarial-target) cross-product moves into a standalone MatrixAtomicAttackBuilder that scenarios call (composition) instead of override around (inheritance); scenarios whose construction isn't a cross-product build their AtomicAttacks directly. All overriding scenarios (Encoding, RedTeamAgent, AdaptiveScenario, Jailbreak, Psychosocial, Scam, AdversarialBenchmark) migrate by the same minimal rename-and-read-from-context pattern. A bridge keeps the legacy _get_atomic_attacks_async entry point working, so this is non-breaking and required zero test edits (full tests/unit/scenario/ green). Deleting the bridge + dead hooks, making _build_atomic_attacks_async abstract, and rewriting the scenario authoring docs are deferred to follow-up PRs.

Design RFC / full phased plan: https://gist.github.com/rlundeen2/d2fa5f91eb411ba9b1d0bbf98412f700

rlundeen2 and others added 4 commits June 30, 2026 13:56
…n point

Slim the Scenario base class toward pure orchestration by adding one explicit
extension point, `_build_atomic_attacks_async(self, *, context: ScenarioContext)`,
and extracting the technique x dataset cross-product into a standalone
MatrixAtomicAttackBuilder.

ScenarioContext is a frozen dataclass carrying the run-resolved inputs
(objective_target, scenario_strategies, dataset_config, memory_labels,
include_baseline). A bridge in the legacy `_get_atomic_attacks_async` builds the
context from resolved self state and forwards to the new method, so every existing
call site keeps working with zero test edits.

Migrated AdversarialBenchmark, RedTeamAgent, AdaptiveScenario, Jailbreak,
Psychosocial, Scam, and Encoding onto the new method. objective_scorer is treated
as scenario configuration (read from self) rather than a universal context field,
since scenarios like Psychosocial select scorers per harm category and strategy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolved import-line conflicts in two files, both stemming from main's
parameter/registry refactor (Parameter moved to pyrit.models, coerce_value/
validate_param_type became Parameter methods) overlapping this branch's
ScenarioContext + MatrixAtomicAttackBuilder refactor:

- pyrit/scenario/core/scenario.py: typing import — dropped both `cast`
  (this branch removed the scoring code that used it) and `get_origin`
  (main removed the choices-validation block that used it). Merged body
  uses neither.
- pyrit/scenario/scenarios/benchmark/adversarial.py: dropped the deprecated
  `from pyrit.common import Parameter` (now sourced from pyrit.models.parameter)
  and the now-unused `AttackScoringConfig` import (scoring moved into the builder).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…narios

# Conflicts:
#	pyrit/scenario/core/scenario.py
#	pyrit/scenario/scenarios/adaptive/adaptive_scenario.py
#	pyrit/scenario/scenarios/benchmark/adversarial.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pyrit/scenario/core/scenario_context.py Outdated
Comment on lines +32 to +35
``initialize_async`` and handed to ``_build_atomic_attacks_async``. Because the
method receives a fully-populated context, scenarios no longer depend on the
"set ``self._x`` in ``initialize_async``, then read it in the override" sequencing
that historically caused "accessed before initialize_async" bugs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this class description needs to talk about what it doesn't do 😆 "no longer"

rlundeen2 and others added 2 commits June 30, 2026 21:13
Describe ScenarioContext, MatrixAtomicAttackBuilder, and the bridge by what
they are rather than by what scenarios previously did, and refresh two stale
_get_atomic_attacks_async references to the current method name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move seed-group resolution and baseline emission into the base Scenario bridge so custom scenarios stop duplicating _resolve_seed_groups_async, self._seed_groups state, and the include_baseline block.

Add seed_groups and seed_groups_by_dataset to ScenarioContext, resolve seeds once via the new _resolve_seed_groups_by_dataset_async hook, and prepend the baseline centrally from the same sample (fixes the ADO 9012 double-sampling inconsistency). Simplify jailbreak, scam, encoding, red_team_agent, adaptive, adversarial, and psychosocial accordingly, and update coupled tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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