microsoft/PyRIT v1.0.1
microsoft/PyRIT
Captured source
source ↗v1.0.1
Repository: microsoft/PyRIT
Tag: v1.0.1
Published: 2026-07-30T05:11:54Z
Prerelease: no
Release notes:
What's changed?
PyRIT v1.0.1 is a patch release with a single targeted bug fix. It contains no new features and no intentional breaking changes; upgrading from v1.0.0 should be a drop-in replacement for nearly all users.
Reason for the patch: OpenAI Responses and Chat Completions can return *structured refusals* — a valid model response that carries a refusal explanation instead of ordinary text content. PyRIT treated these as parser/runtime failures. In practice that meant a refusal from the target could leave attack objectives incomplete and surface a misleading scenario-level ValueError, rather than being recorded and scored as the refusal it actually was.
You should upgrade if you
- Use
OpenAIResponseTargetorOpenAIChatTargetagainst models that emit structured refusals (this is common with recent OpenAI and Azure OpenAI models). - Run scenarios that can legitimately be refused by the target — refusals no longer abort the atomic attack, and genuine partial execution now raises the dedicated
ScenarioPartialFailureExceptionwith structured counts, the incomplete objectives, and the original cause. - Rely on true/false scorers — a syntactically valid but out-of-domain
score_valuesuch as"refusal"is now rejected inside response parsing asInvalidJsonException, so it participates in the existing clean-history JSON retry flow instead of failing after the retry boundary.
ScenarioPartialFailureException subclasses ValueError, so existing except ValueError handlers keep working. Genuine transport and runtime failures are still reported as incomplete failures, not as success-shaped refusal results.
⚠️ Compatibility note: response piece ordering
To make refusals scorable, OpenAIResponseTarget now orders reasoning pieces after the actionable response pieces. For reasoning models (e.g. GPT-5) that returned [reasoning, text], the response is now [text, reasoning].
This is the correct behavior — Message.get_piece() defaults to index 0, so previously a reasoning model could hand the reasoning blob to a scorer instead of the model's actual answer. But if your own code indexes message_pieces[1] to read the answer, it will now read the reasoning piece instead. PyRIT's own tests and one doc notebook made exactly this assumption and were updated in this release.
Prefer selecting by data type rather than position:
text_piece = response[0].get_piece_by_type(data_type="text")
Full list of changes
- FIX Handle structured refusals and scenario partial failures by @romanlutz in https://github.com/microsoft/PyRIT/pull/2283
Full Changelog: https://github.com/microsoft/PyRIT/compare/v1.0.0...v1.0.1