FIX: Integration Test Fixes#1907
Conversation
Some adversarial LLMs return ationale_behind_question instead of ationale_behind_jailbreak — semantically equivalent but different key name. This caused all JSON retries to fail with InvalidJsonException, eventually raising _StrategyRuntimeError and crashing the 4_sequential_attack.ipynb notebook. The fix extends _parse_adversarial_response with a key-alias step (applied after camelCase normalisation) that renames ationale_behind_question -> ationale_behind_jailbreak before schema validation, matching the same pattern used to accept both camelCase and snake_case keys. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Everything looks good for this PR, I was just wondering if you considered tweaking the system prompt instead to enforce adherence to the key? Just wondering at what point we reach "hacky" territory to parse the adversarial LLM response haha |
Replaces the parser-level alias workaround (microsoft#1907 review feedback) with upstream prevention: all 7 Crescendo system prompt variants now explicitly instruct the adversarial LLM to use the exact key names and not to rename, abbreviate, or rephrase them. The example JSON block is also normalized to valid JSON syntax (quoted keys and string values) to reduce ambiguity. Changes: - Revert _parse_adversarial_response to its pre-aliasing state; remove _key_aliases dict and rename loop. - Tighten format instructions in all 7 crescendo YAMLs (variants 1-5, escalation_crisis, therapist). - Drop the alias-specific unit test. - Trim the parser docstring; the camelCase tolerance is the only remaining nuance worth calling out. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aVC/PyRIT into integration_test_fixes_2 Merging to ensure integration test fixes apply to latest main.
…43855b) referenced in barge_in_attack.ipynb.
Description
This PR bundles three integration-test fixes plus a related notebook cleanup.
1. Crescendo system prompts hardened (replaces the original aliasing patch)
Original failure:
4_sequential_attack.ipynbintegration test —CrescendoAttack._get_attack_prompt_asyncexhausted all retries and raised_StrategyRuntimeError. Root cause: the adversarial LLM (Azure OpenAI GPT-4o in CI) consistently returnedrationale_behind_questioninstead of the schema-requiredrationale_behind_jailbreak, particularly on early turns where the "jailbreak" framing doesn't apply yet. Each retry failed identically withInvalidJsonException: Missing required keys {'rationale_behind_jailbreak'}.Initial approach (reverted): A parser-level alias step that renamed
rationale_behind_question→rationale_behind_jailbreak. Reviewers raised — fairly — that this kind of per-drift patch would snowball as models invent new variants (reasoning_behind_jailbreak,jailbreak_rationale, etc.).Adopted fix: Tighten the upstream source instead. All 7 Crescendo system prompt YAMLs (
crescendo_variant_1..5,escalation_crisis,therapist) now use explicit anti-paraphrase wording in the task section:The example JSON block is also normalized to valid JSON (quoted keys + string values) to remove a second source of model ambiguity. The parser itself was reverted to its pre-PR state — keeping only the camelCase tolerance added in #1862. Long-term, Hannah suggested wiring
json_schemathroughprompt_metadatafor targets advertisingsupports_json_schema; that's tracked separately.2.
barge_in_attack.ipynb—_roleAttributeErrorFailure:
'MessagePiece' object has no attribute '_role'. After theMessage→ Pydantic migration (#1885), Pydantic's__getattr__rejects unknown attributes, and the notebook had a debug print loop accessing the privatepiece._role.Fix: Updated the notebook (and its
.pyjupytext source) to use the publicpiece.roleattribute.3.
3_adaptive_scenarios.ipynb—AttackTechniqueRegistry is emptyFailure:
RuntimeError: AttackTechniqueRegistry is empty. Register attack technique factories before executing scenarios...raised fromLoadDefaultDatasets.initialize_async→ScenarioRegistry.list_metadata→ instantiation ofCyber→_build_cyber_strategy→AttackTechniqueRegistry.get_factories_or_raise.Root cause: The shared
doc/scanner/pyrit_conf.yamlconfig used by 4 notebooks was missing thescenario_techniqueinitializer in its initializer list. With nothing populatingAttackTechniqueRegistrybeforeLoadDefaultDatasetsran, every scenario instantiation that walks_build_cyber_strategyblew up.Fix: Added
- name: scenario_techniquetodoc/scanner/pyrit_conf.yaml, ordered betweenscorerandload_default_datasets. Initializers now execute in YAML list order (the oldexecution_orderproperty is deprecated → removed in 0.16.0), so listing it beforeload_default_datasetsguarantees the registry is populated in time. Benefits all 4 consumers of the YAML (garak,foundry,1_common_scenario_parameters,3_adaptive_scenarios).4. Removed redundant workaround in
1_common_scenario_parameters.pyPR #1897 added a notebook-level
await ScenarioTechniqueInitializer().initialize_async()call as a stopgap for the same registry-empty bug. Now thatpyrit_conf.yamlis the source of truth, that workaround is dead code. Removed the import + call. Left0_scenarios.pyand2_custom_scenario_parameters.pyalone — they use manualinitialize_pyrit_async("InMemory")rather than the YAML config, so the explicitScenarioTechniqueInitializercall is still the right pattern there (preserves the "minimal manual setup" demo).Tests and Documentation
test_parse_adversarial_response_accepts_rationale_behind_question_aliaswas removed alongside the alias logic.tests/integration/executors/test_executor_notebooks.py::test_execute_notebooks[4_sequential_attack.ipynb]✅ (178s)tests/integration/executors/test_executor_notebooks.py::test_execute_notebooks[barge_in_attack.ipynb]✅ (104s)tests/integration/scenarios/test_notebooks_scenarios.py— all 4 notebooks ✅ (480s)uv run jupytext --execute --to notebookon1_common_scenario_parameters.pyand3_adaptive_scenarios.pyto verify both regenerated.ipynbfiles execute end-to-end against live endpoints.