Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f1b607d
FEAT: Custom ASSERT judge dimensions, presets, and repeated samples
richlundeen Nov 10, 2026
1b753ee
FIX: Keep non-default ASSERT judge settings in identity; restrict jud…
richlundeen Nov 10, 2026
54b1aba
MAINT: Simplify ASSERT judge dimensions and samples; allow technique …
richlundeen Nov 10, 2026
fe1ff0e
FEAT: Run registered attack techniques in foundry.assert_policy
richlundeen Nov 10, 2026
fb0caff
FEAT: Support ASSERT not-applicable answers and graded judge dimensions
richlundeen Nov 10, 2026
aed1b20
FEAT: assert_package technique with ASSERT's tester prompt
richlundeen Nov 10, 2026
a99189d
FIX: address #4132 review feedback
richlundeen Nov 10, 2026
a18bc4f
MAINT: typed ASSERT judgment metadata and YAML dimension framing
richlundeen Nov 11, 2026
3c26aec
FEAT: Trace-grounded ASSERT judging with OpenTelemetry execution evid…
richlundeen Nov 10, 2026
053dd5c
FIX: Order ASSERT trace steps deterministically when start times tie
richlundeen Nov 10, 2026
9780189
FIX: Address ASSERT trace evidence review feedback
richlundeen Nov 10, 2026
e2c965b
FIX: Version ASSERT trace rendering for cyclic steps
richlundeen Nov 10, 2026
4bb2163
FIX: Render ASSERT dimension trace instructions through the prompt te…
richlundeen Nov 11, 2026
ee00756
MAINT: merge main 1dc1d5cda into ASSERT judge dimensions (-1)
richlundeen Nov 11, 2026
9e697fd
MAINT: Merge refreshed ASSERT judge dimensions layer (main 1dc1d5cda)…
richlundeen Nov 11, 2026
0012d16
MAINT: merge main c3cff1831 into ASSERT judge dimensions (-1)
richlundeen Nov 11, 2026
7bf65f3
MAINT: Merge refreshed ASSERT judge dimensions layer (main c3cff1831)…
richlundeen Nov 11, 2026
6932faa
FEAT: accept a supplied ASSERT objective scorer and use default scori…
richlundeen Nov 11, 2026
2804b41
DOC: note default ASSERT scoring in the launch form and the supplied …
richlundeen Nov 11, 2026
8859dea
FIX: resolve the ASSERT judge target with scoring_target and report i…
richlundeen Nov 11, 2026
f696a86
MAINT: make ASSERT judgment metadata helpers keyword-only
richlundeen Nov 11, 2026
4c8ceda
MAINT: Rebuild ASSERT trace evidence layer on the supplied-scorer layer
richlundeen Nov 11, 2026
06ccdf2
MAINT: Exempt the OpenTelemetry span processor callback from keyword-…
richlundeen Nov 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions THIRD_PARTY_NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ PyRIT includes adapted assignment-sampling algorithms, generation schemas,
policy-scoring rules, and prompt templates from ASSERT v0.3.0, revision
04b713fe73879208369750b3bd5ad53544f3eecc (2026-09-04). Microsoft Corporation adapted these
portions to native PyRIT targets, generation strategies, typed seed datasets,
and true/false conversation scorers. Scoring templates are adaptations, not
verbatim copies of ASSERT's full judge or its XML citation protocol.
conversation scorers, and judge presets. Scoring templates are adaptations, not
verbatim copies of ASSERT's full judge or its XML citation protocol. The tester
prompt keeps ASSERT's text except for its turn-counter sentence.
ASSERT is not a runtime dependency.

The provenance inventory in third_party/assert-provenance.json identifies the
Expand Down
8 changes: 7 additions & 1 deletion doc/code/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,11 @@ If you are contributing to PyRIT, that work will most likely land in one of the
undetermined, not false. For a `MessageScorable`, the scoring layer resolves
outbound request trace links, regardless of chat role, through the scored response.
Attacks pass message evidence and route expectations according to scorer support.
- `OtelExecutionTraceSource` normalizes model, tool, and agent spans, with bounded tool
arguments, results, and messages, into an execution snapshot. A conversation scorer given a
trace source renders those steps after the turns they produced and owns citation and coverage
handling. A `trace_requirement` lets it decline to judge, with an undetermined result, when
the evidence it needs is missing.
- Surface sources read what a location holds for a `SurfaceScorable`, such as files under one
root directory. `FileWriteScorer` evaluates its `ContentWritten` condition against that
evidence. The caller owns workspace isolation; a snapshot does not identify its writer.
Expand Down Expand Up @@ -335,7 +340,8 @@ If you are contributing to PyRIT, that work will most likely land in one of the
When the judge's response is blocked, conversation scoring handles direct and message-triggered
calls the same way. If it returns an undetermined score, it retains the evidence snapshot.
- The local SDK exporter supports caller-owned, in-process capture, not a remote collector or
durable store.
durable store. `LocalTraceCapture` bundles it with a provider and completion tracking for
in-process targets.
- Observation capture requires durable scored evidence. A custom general-scorer template that reads `message_piece` fields does not emit an observation for a loose `ContentScorable`.
- `Score.scored_expectation` records the complete expectation used for the verdict. `Score.objective` is its read-only compatibility view.
- Scorer trees check that all conditions have a matching leaf. Wrappers route supported subsets
Expand Down
3 changes: 2 additions & 1 deletion doc/code/scoring/0_scoring.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,8 @@
"Media observation capture remains deferred until its evidence can be snapshotted.\n",
"Trace-backed tool observations are covered in [Tool-call scoring](5_tool_call_scorer.ipynb), and file-system\n",
"observations in [File-write scoring](6_file_write_scorer.ipynb), and sandboxed program runs in\n",
"[Program-execution scoring](7_program_execution_scorer.ipynb).\n",
"[Program-execution scoring](7_program_execution_scorer.ipynb). Execution traces with model calls,\n",
"tool arguments, and results are covered in [Judging with execution traces](8_trace_evidence_scoring.ipynb).\n",
"\n",
"Replaying a judgment is different from evaluating a stored run against a new expectation.\n",
"A retained target judgment answers the original expectation; changing that expectation\n",
Expand Down
3 changes: 2 additions & 1 deletion doc/code/scoring/0_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@
# Media observation capture remains deferred until its evidence can be snapshotted.
# Trace-backed tool observations are covered in [Tool-call scoring](5_tool_call_scorer.ipynb), and file-system
# observations in [File-write scoring](6_file_write_scorer.ipynb), and sandboxed program runs in
# [Program-execution scoring](7_program_execution_scorer.ipynb).
# [Program-execution scoring](7_program_execution_scorer.ipynb). Execution traces with model calls,
# tool arguments, and results are covered in [Judging with execution traces](8_trace_evidence_scoring.ipynb).
#
# Replaying a judgment is different from evaluating a stored run against a new expectation.
# A retained target judgment answers the original expectation; changing that expectation
Expand Down
25 changes: 17 additions & 8 deletions doc/code/scoring/1_true_false_scorers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@
"import uuid\n",
"\n",
"from pyrit.memory import CentralMemory\n",
"from pyrit.models import AssertTaxonomy, ConversationScorable\n",
"from pyrit.models import AssertCategoryJudgment, AssertTaxonomy, ConversationScorable\n",
"from pyrit.score import AssertJudgmentMode, AssertScoringDimension, build_assert_policy_scorer\n",
"\n",
"taxonomy = AssertTaxonomy.model_validate(\n",
Expand Down Expand Up @@ -811,8 +811,9 @@
" )[0]\n",
" assert_scores.append(score)\n",
" print(f\"[ASSERT/{mode.value}/{dimension.value}] {case}: {score.get_value()}\")\n",
" if \"assert_correction_reason\" in score.score_metadata:\n",
" print(f\" Correction: {score.score_metadata['assert_correction_reason']}\")"
" judgment = AssertCategoryJudgment.from_metadata(metadata=score.score_metadata)\n",
" if judgment is not None and judgment.correction_reason:\n",
" print(f\" Correction: {judgment.correction_reason}\")"
]
},
{
Expand All @@ -823,15 +824,23 @@
},
"source": [
"Each score has its dimension category, rationale, judge metadata, and saved observations.\n",
"Category-mode metadata also contains `assert_node_judgments` as JSON, `assert_original_score_value`,\n",
"and any `assert_correction_reason` or `assert_judgment_warnings`. The raw response remains retained.\n",
"In category mode, `AssertCategoryJudgment.from_metadata(metadata=score.score_metadata)` returns the\n",
"per-category judgments, the judge's original verdict, and any correction or warnings. The raw response\n",
"remains retained.\n",
"`get_assert_failure_criterion(dimension)` exposes the shared failure definition for other consumers.\n",
"\n",
"Category validation establishes structural consistency, not whether the model selected the right\n",
"categories or interpreted the evidence correctly. Verdicts can still conflict with their reasoning.\n",
"This does not port ASSERT's XML citation resolution or multi-judge aggregation, and it does not\n",
"establish accuracy parity or calibration. A scenario can configure repeated judgments with\n",
"`TrueFalseCompositeScorer` without changing this scorer's policy or merging the two dimensions.\n"
"This does not port ASSERT's XML citation resolution, and it does not establish accuracy parity or\n",
"calibration.\n",
"\n",
"`dimension` also accepts a custom `AssertJudgeDimension`, such as one from ASSERT's judge presets\n",
"(`list_assert_judge_presets()` or `load_assert_judge_preset()`) or your own preset file\n",
"(`load_assert_judge_preset_file()`). A dimension with a graded `scale` is a violation at or past\n",
"its `violation_at` grade, and a not-applicable answer is undetermined. `samples` repeats the judge\n",
"call and combines the verdicts as ASSERT does: a majority vote with ties counted as failures, leaving\n",
"out undetermined samples and samples whose judgment is still invalid after retries. See\n",
"[Foundry scenarios](../../scanner/foundry.ipynb) for the preset file format.\n"
]
},
{
Expand Down
25 changes: 17 additions & 8 deletions doc/code/scoring/1_true_false_scorers.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@
import uuid

from pyrit.memory import CentralMemory
from pyrit.models import AssertTaxonomy, ConversationScorable
from pyrit.models import AssertCategoryJudgment, AssertTaxonomy, ConversationScorable
from pyrit.score import AssertJudgmentMode, AssertScoringDimension, build_assert_policy_scorer

taxonomy = AssertTaxonomy.model_validate(
Expand Down Expand Up @@ -474,20 +474,29 @@
)[0]
assert_scores.append(score)
print(f"[ASSERT/{mode.value}/{dimension.value}] {case}: {score.get_value()}")
if "assert_correction_reason" in score.score_metadata:
print(f" Correction: {score.score_metadata['assert_correction_reason']}")
judgment = AssertCategoryJudgment.from_metadata(metadata=score.score_metadata)
if judgment is not None and judgment.correction_reason:
print(f" Correction: {judgment.correction_reason}")

# %% [markdown]
# Each score has its dimension category, rationale, judge metadata, and saved observations.
# Category-mode metadata also contains `assert_node_judgments` as JSON, `assert_original_score_value`,
# and any `assert_correction_reason` or `assert_judgment_warnings`. The raw response remains retained.
# In category mode, `AssertCategoryJudgment.from_metadata(metadata=score.score_metadata)` returns the
# per-category judgments, the judge's original verdict, and any correction or warnings. The raw response
# remains retained.
# `get_assert_failure_criterion(dimension)` exposes the shared failure definition for other consumers.
#
# Category validation establishes structural consistency, not whether the model selected the right
# categories or interpreted the evidence correctly. Verdicts can still conflict with their reasoning.
# This does not port ASSERT's XML citation resolution or multi-judge aggregation, and it does not
# establish accuracy parity or calibration. A scenario can configure repeated judgments with
# `TrueFalseCompositeScorer` without changing this scorer's policy or merging the two dimensions.
# This does not port ASSERT's XML citation resolution, and it does not establish accuracy parity or
# calibration.
#
# `dimension` also accepts a custom `AssertJudgeDimension`, such as one from ASSERT's judge presets
# (`list_assert_judge_presets()` or `load_assert_judge_preset()`) or your own preset file
# (`load_assert_judge_preset_file()`). A dimension with a graded `scale` is a violation at or past
# its `violation_at` grade, and a not-applicable answer is undetermined. `samples` repeats the judge
# call and combines the verdicts as ASSERT does: a majority vote with ties counted as failures, leaving
# out undetermined samples and samples whose judgment is still invalid after retries. See
# [Foundry scenarios](../../scanner/foundry.ipynb) for the preset file format.
#
# %% [markdown]
# ## External classifier integrations
Expand Down
Loading
Loading