Repository navigation
FEAT: Staged ASSERT generation and the ASSERT policy library (-4) - #4133
Richard Lundeen (richlundeen) wants to merge 8 commits into
Conversation
Adds ASSERT judge dimension/preset models, ships ASSERT's 11 judge presets, judges custom boolean and ordinal dimensions (with not-applicable) through build_assert_policy_scorer, aggregates repeated judge samples as ASSERT does, and exposes judge_presets, disabled_judge_dimensions, judge_samples, and objective_dimension on foundry.assert_policy. Default identities are unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ge presets to built-in names - objective_dimension and other non-default judge settings are recorded in scenario identity params, so resume detects changes and saved GUI/API launches restore them. Default identities are unchanged. - Mixed boolean/ordinal judge sets are ordered by policy for a stable identity. - judge_presets accepts only shipped preset names; preset files move to the Python-only judge_preset_files (list[Path]) parameter, which the API guard rejects. Preset load errors no longer echo file contents. - Adds a Scenario._get_identity_params hook. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues can misattribute checkpoint generation, understate token cost estimates, and cause listed blob URLs to read or delete the wrong blob.
3 open issues
What changed in this PR
This PR extends PyRIT’s ASSERT integration with staged, reviewable generation and bundled policy presets. It also adds supporting scoring, trace-evidence, and analytics interfaces.
Changes:
- Supports checkpoint-based generation, policy review, cost estimates, and packaging generated objectives.
- Bundles ASSERT behavior, suite, and judge presets with attribution.
- Extends execution-trace scoring and exposes ASSERT metrics through the backend and run page.
| File | Description |
|---|---|
| THIRD_PARTY_NOTICES.txt | Updates ASSERT redistribution and adaptation notices. |
| tests/unit/executor/promptgen/test_target_objective_generator.py | Tests generation estimates and input validation. |
| tests/unit/datasets/test_target_objective_provider.py | Tests generated-objective packaging. |
| tests/unit/common/test_lazy_package_imports.py | Updates analytics export expectations. |
| tests/unit/assert_mocks.py | Adds checkpoint and generated-group fixtures. |
| tests/unit/analytics/test_rate_statistics.py | Tests Wilson intervals and macro rates. |
| pyrit/score/true_false/true_false_fallback_scorer.py | Exposes configurable fallback triggers. |
| pyrit/score/scorer.py | Identifies missing-evidence results. |
| pyrit/score/observation/otel_span_exporter.py | Captures span events and trace-specific failures. |
| pyrit/score/observation/message_trace_resolver.py | Resolves conversation request trace links. |
| pyrit/score/observation/execution.py | Shares evidence acquisition within scoring operations. |
| pyrit/score/observation/__init__.py | Exports execution-trace observation APIs. |
| pyrit/score/float_scale/float_scale_fallback_scorer.py | Exposes configurable fallback triggers. |
| pyrit/score/__init__.py | Exports ASSERT judging and trace APIs. |
| pyrit/scenario/core/scenario.py | Extracts scenario identity parameter handling. |
| pyrit/prompt_target/common/target_trace_config.py | Adds conversation identifiers to target spans. |
| pyrit/prompt_target/common/prompt_target.py | Exposes target tracing configuration. |
| pyrit/models/score/__init__.py | Exports execution-trace data models. |
| pyrit/models/prompt_generation_estimate.py | Defines generation-call and token estimates. |
| pyrit/models/analytics.py | Adds binary and macro rate models. |
| pyrit/executor/promptgen/target_objective_generator.py | Adds provider-free generation estimates. |
| pyrit/executor/promptgen/__init__.py | Exports generated-objective results. |
| pyrit/datasets/seed_datasets/generated/target_objective_provider.py | Adds reusable result packaging. |
| pyrit/datasets/score/true_false_question/assert_execution_evidence_instructions.yaml | Defines trace-evidence judging instructions. |
| pyrit/datasets/score/assert/judge_presets/tool-use.yaml | Adds tool-use judging dimensions. |
| pyrit/datasets/score/assert/judge_presets/safety-extended.yaml | Adds harm and privacy judging dimensions. |
| pyrit/datasets/score/assert/judge_presets/safety-core.yaml | References built-in safety dimensions. |
| pyrit/datasets/score/assert/judge_presets/robustness.yaml | Adds robustness judging dimensions. |
| pyrit/datasets/score/assert/judge_presets/policy-adherence.yaml | Adds policy-adherence judging criteria. |
| pyrit/datasets/score/assert/judge_presets/operational.yaml | Adds workflow and escalation criteria. |
| pyrit/datasets/score/assert/judge_presets/multi-turn.yaml | Adds conversation continuity criteria. |
| pyrit/datasets/score/assert/judge_presets/instruction-following.yaml | Adds constraint and completion criteria. |
| pyrit/datasets/score/assert/judge_presets/grounding.yaml | Adds grounding and attribution criteria. |
| pyrit/datasets/score/assert/judge_presets/communication.yaml | Adds communication-quality criteria. |
| pyrit/datasets/score/assert/judge_presets/alignment.yaml | Adds honesty and verification criteria. |
| pyrit/datasets/score/assert/dimension_system_prompt.yaml | Adds custom-dimension judging template. |
| pyrit/datasets/assert_suites/travel_planner.yaml | Bundles travel-planning evaluation context. |
| pyrit/datasets/assert_suites/travel_planner_benchmark.yaml | Bundles quality-focused travel benchmark context. |
| pyrit/datasets/assert_suites/telecom_customer_service.yaml | Bundles telecom service evaluation context. |
| pyrit/datasets/assert_library.py | Loads and validates bundled presets. |
| pyrit/datasets/assert_behaviors/violent_content.yaml | Bundles violent-content policy. |
| pyrit/datasets/assert_behaviors/unsupported_conclusion_failures.yaml | Bundles unsupported-conclusion behavior. |
| pyrit/datasets/assert_behaviors/unit_conversion_failures.yaml | Bundles unit-conversion behavior. |
| pyrit/datasets/assert_behaviors/underused_context_failures.yaml | Bundles retrieved-context usage behavior. |
| pyrit/datasets/assert_behaviors/unauthorized_action_failures.yaml | Bundles action-authorization behavior. |
| pyrit/datasets/assert_behaviors/tool_parameter_formatting_failures.yaml | Bundles tool-argument behavior. |
| pyrit/datasets/assert_behaviors/tool_output_misinterpretation_failures.yaml | Bundles tool-output interpretation behavior. |
| pyrit/datasets/assert_behaviors/tool_orchestration_errors.yaml | Bundles tool-orchestration behavior. |
| pyrit/datasets/assert_behaviors/tool_call_turn_protocol_failures.yaml | Bundles tool-turn protocol behavior. |
| pyrit/datasets/assert_behaviors/tool_call_error_recovery_failures.yaml | Bundles tool-error recovery behavior. |
| pyrit/datasets/assert_behaviors/tabs_vs_spaces.yaml | Bundles indentation preference-bias behavior. |
| pyrit/datasets/assert_behaviors/sycophancy.yaml | Bundles sycophancy behavior. |
| pyrit/datasets/assert_behaviors/suicide_self_harm.yaml | Bundles self-harm safety policy. |
| pyrit/datasets/assert_behaviors/success_criteria_ambiguity_failures.yaml | Bundles task-completion criteria behavior. |
| pyrit/datasets/assert_behaviors/stereotyping.yaml | Bundles stereotyping behavior. |
| pyrit/datasets/assert_behaviors/stale_state_failures.yaml | Bundles stale-state behavior. |
| pyrit/datasets/assert_behaviors/shimano_vs_sram.yaml | Bundles bicycle-component preference-bias behavior. |
| pyrit/datasets/assert_behaviors/sexual_content.yaml | Bundles sexual-content safety policy. |
| pyrit/datasets/assert_behaviors/response_completeness_failures.yaml | Bundles response-completeness behavior. |
| pyrit/datasets/assert_behaviors/repeated_action_loop_failures.yaml | Bundles repeated-action behavior. |
| pyrit/datasets/assert_behaviors/prompt_injection.yaml | Bundles prompt-injection resistance behavior. |
| pyrit/datasets/assert_behaviors/procedure_adherence_failures.yaml | Bundles procedure-adherence behavior. |
| pyrit/datasets/assert_behaviors/premature_termination_failures.yaml | Bundles premature-termination behavior. |
| pyrit/datasets/assert_behaviors/poor_retrieval_failures.yaml | Bundles retrieval-quality behavior. |
| pyrit/datasets/assert_behaviors/output_internal_consistency_failures.yaml | Bundles output-consistency behavior. |
| pyrit/datasets/assert_behaviors/out_of_scope_request_failures.yaml | Bundles scope-compliance behavior. |
| pyrit/datasets/assert_behaviors/observation_neglect_failures.yaml | Bundles observation-handling behavior. |
| pyrit/datasets/assert_behaviors/malicious_cyber_activity.yaml | Bundles cyber-safety policy. |
| pyrit/datasets/assert_behaviors/ios_vs_android.yaml | Bundles mobile-platform preference-bias behavior. |
| pyrit/datasets/assert_behaviors/inter_agent_handoff_failures.yaml | Bundles agent-handoff behavior. |
| pyrit/datasets/assert_behaviors/intent_misinterpretation_failures.yaml | Bundles intent-interpretation behavior. |
| pyrit/datasets/assert_behaviors/insufficient_verification_failures.yaml | Bundles verification behavior. |
| pyrit/datasets/assert_behaviors/ineffective_team_communication_failures.yaml | Bundles agent-team communication behavior. |
| pyrit/datasets/assert_behaviors/incorrect_tool_selection_failures.yaml | Bundles tool-selection behavior. |
| pyrit/datasets/assert_behaviors/incomplete_answer_synthesis_failures.yaml | Bundles answer-synthesis behavior. |
| pyrit/datasets/assert_behaviors/identity_verification_failures.yaml | Bundles identity-verification behavior. |
| pyrit/datasets/assert_behaviors/hate_speech_harassment.yaml | Bundles hate and harassment safety policy. |
| pyrit/datasets/assert_behaviors/harmful_medical_advice.yaml | Bundles medical-advice safety behavior. |
| pyrit/datasets/assert_behaviors/grounding_attribution_errors.yaml | Bundles grounding and attribution behavior. |
| pyrit/datasets/assert_behaviors/goal_drift_failures.yaml | Bundles goal-drift behavior. |
| pyrit/datasets/assert_behaviors/flawed_action_plan_failures.yaml | Bundles planning-quality behavior. |
| pyrit/datasets/assert_behaviors/explicit_constraint_violation_failures.yaml | Bundles explicit-constraint behavior. |
| pyrit/datasets/assert_behaviors/escalation_judgment_failures.yaml | Bundles escalation-decision behavior. |
| pyrit/datasets/assert_behaviors/doxxing.yaml | Bundles personal-information protection behavior. |
| pyrit/datasets/assert_behaviors/crystal_mountain_vs_snoqualmie.yaml | Bundles resort preference-bias behavior. |
| pyrit/datasets/assert_behaviors/conversation_coherence_breakdown.yaml | Bundles conversation-coherence behavior. |
| pyrit/datasets/assert_behaviors/constraint_propagation_failures.yaml | Bundles cross-agent constraint behavior. |
| pyrit/datasets/assert_behaviors/conflicting_instruction_resolution_failures.yaml | Bundles instruction-conflict behavior. |
| pyrit/datasets/assert_behaviors/actionability_failures.yaml | Bundles answer-actionability behavior. |
| pyrit/datasets/__init__.py | Exports behavior and suite loaders. |
| pyrit/common/path.py | Defines bundled library paths. |
| pyrit/backend/routes/scenarios.py | Adds the ASSERT metrics endpoint. |
| pyrit/backend/README.md | Documents ASSERT metrics responses. |
| pyrit/analytics/rate_statistics.py | Implements Wilson intervals and macro averages. |
| pyrit/analytics/__init__.py | Exports ASSERT metrics and rate helpers. |
| frontend/src/types/index.ts | Defines ASSERT metrics response types. |
| frontend/src/services/api.ts | Adds the ASSERT metrics client call. |
| frontend/src/hooks/useAssertPolicyMetrics.ts | Loads metrics with cancellation and retry. |
| frontend/src/hooks/useAssertPolicyMetrics.test.tsx | Tests metrics loading and runtime changes. |
| frontend/src/components/Scenarios/ScenarioRunPage.tsx | Displays metrics for completed ASSERT runs. |
| frontend/src/components/Scenarios/ScenarioRunPage.test.tsx | Tests conditional metrics display. |
| frontend/src/components/Scenarios/assertPolicyRun.ts | Identifies ASSERT policy runs. |
| frontend/src/components/Scenarios/AssertPolicyMetricsPanel.styles.ts | Styles metrics presentation. |
| doc/myst.yml | Adds trace-scoring documentation navigation. |
| doc/code/scoring/1_true_false_scorers.py | Documents custom dimensions and repeated judgments. |
| doc/code/scoring/1_true_false_scorers.ipynb | Mirrors updated ASSERT scoring guidance. |
| doc/code/scoring/0_scoring.py | Links execution-trace scoring guidance. |
| doc/code/scoring/0_scoring.ipynb | Mirrors the trace-scoring documentation link. |
| doc/code/framework.md | Documents trace and analytics responsibilities. |
| doc/code/executor/7_promptgen.py | Demonstrates staged policy review and generation. |
| doc/code/executor/7_promptgen.ipynb | Adds the staged-generation notebook example. |
| doc/code/analytics/0_attack_results.md | Documents rates and ASSERT policy metrics. |
Review effort: Balanced
| generation_targets={ | ||
| "generation": self._target.get_identifier().model_dump(mode="json"), | ||
| "research": self._research_target.get_identifier().model_dump(mode="json"), | ||
| self.GENERATION_ROLE: self._target.get_identifier().model_dump(mode="json"), | ||
| self.RESEARCH_ROLE: self._research_target.get_identifier().model_dump(mode="json"), | ||
| }, |
| stages = cls._estimate_policy_stages(checkpoint=checkpoint, request_characters=request) | ||
| group_stage, exact = cls._estimate_groups( | ||
| checkpoint=checkpoint, | ||
| input_characters=len(_load_stage_prompts()["seed_groups"]) + request + taxonomy_chars + 600, |
| container_url = ( | ||
| f"{parsed.scheme}://{parsed.netloc}/{path_parts[0]}" if path_parts else self._container_url.rstrip("/") | ||
| ) | ||
| return sorted(f"{container_url}/{name}" for name in names if "/" not in name[len(prefix) :]) |
…converters - Custom judge dimensions are boolean bad-event flags judged by SelfAskTrueFalseScorer with an ASSERT dimension template; ASSERT's ordinal scale and allow_not_applicable options are rejected. - Repeated samples use TrueFalseCompositeScorer over the repeated judge with an ASSERT vote (ties count as failures, undetermined samples excluded, category re-vote with override warning), replacing the bespoke sampled scorers. - Public exports trimmed to AssertJudgeDimension, AssertJudgePreset, AssertSampleMetadata, and the preset list/load functions. - foundry.assert_policy accepts technique_converters (none by default) for both techniques. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- AssertTechnique is removed. The technique class is built from the AttackTechniqueRegistry, with red_teaming as the default (ASSERT's scenario case). - The baseline, now on by default, sends each group's stored system and user prompts (ASSERT's prompt case). - max_turns applies to techniques whose attack takes a turn count; results are grouped by technique. - AssertPolicy.VERSION is 2, so runs from version 1 do not resume. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- allow_not_applicable: a not-applicable answer is undetermined with assert_not_applicable metadata; repeated samples exclude it and keep the marker only when every sample is not applicable. - ASSERT's ordinal scale is accepted with a PyRIT violation_at grade. The judge's grade position is thresholded by FloatScaleThresholdScorer, so every dimension still yields a true/false verdict, and the grade is kept in assert_grade metadata. Graded dimensions can be the objective. - AssertDimensionMetadata is exported from pyrit.score. Default identities are unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ence ASSERT judges can read what the application did, not only what it said: tool calls with arguments and results, model calls, and agent steps captured with OpenTelemetry, rendered after the turns they produced with citable step IDs. - LocalTraceCapture: in-process provider and store that keeps target-request traces, completes them when their spans end, and evicts the oldest when full. - OtelExecutionTraceSource: OpenInference and GenAI normalization into a durable execution_trace observation with bounded, redactable content. - ConversationScorer trace_source and trace_requirement; AssertTraceEvidence for build_assert_policy_scorer; foundry.assert_policy trace_source and trace_requirement, kept in identity when non-default. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| self._target.validate_web_search_config(WebSearchConfig(mode=WebSearchMode.DISABLED)) | ||
| mode = WebSearchMode.REQUIRED if context.config.web_search else WebSearchMode.DISABLED | ||
| self._research_target.validate_web_search_config(WebSearchConfig(mode=mode)) | ||
| async def advance_async( |
There was a problem hiding this comment.
advance_async bypasses execute_with_context_async, so strategy event handlers get setup/teardown but never ON_PRE_VALIDATE/ON_PRE_EXECUTE/ON_POST_EXECUTE/ON_ERROR, and no RetryCollector is installed (retry stats are silently uncollected on this path). Since the backend job runner in -5 will drive generation through this method, event-based telemetry won't see those runs. If that's intentional, a comment here would help; if not, consider factoring a result-type-agnostic lifecycle wrapper both entry points share.
| Returns: | ||
| AsyncContainerClient: The shared client, open until the last user releases it. | ||
| """ | ||
| if not self._client_async: |
There was a problem hiding this comment.
This lazy create is race-free only because _create_container_client_async happens to contain no await — the check/create/increment sequence can't interleave on the event loop today. If anyone adds an awaited call there (say async credential acquisition), two concurrent acquires will both create clients: the first client and its DefaultAzureCredential leak unclosed, and the refcount closes only the survivor. An asyncio.Lock around creation (or at minimum a comment pinning the no-await invariant on _create_container_client_async) would make the fix durable.
| batches = _plan_batches(checkpoint=checkpoint, batch_size=self._BATCH_SIZE) | ||
| done = checkpoint.completed_batches | ||
| if done > len(batches) or sum(count for _, count in batches[:done]) != len(checkpoint.groups): | ||
| raise ValueError("The checkpoint's seed groups don't match the planned coverage assignments.") |
There was a problem hiding this comment.
This guard checks that the count of stored groups matches the first completed_batches planned batches, but not that their assignments do. A hand-edited or version-skewed checkpoint whose groups carry valid-but-different assignments resumes silently, and the final coverage then deviates from the sampling plan (some cells double-covered, others missed) while looking legitimate. Comparing the stored groups' assignments against batches[:done] would close that off cheaply.
| conversation_id: _Name | ||
|
|
||
|
|
||
| class AssertGenerationCheckpoint(_AssertModel): |
There was a problem hiding this comment.
Checkpoints are designed to be persisted (that's what list_files_async is for, and -5 stores them), but there's no schema-version field. When this model evolves, previously stored checkpoints will fail with a generic pydantic validation error. A version field (like the presets carry) would let the backend distinguish "stale checkpoint, regenerate" from genuine corruption. Worth deciding before -5 freezes the stored format.
| return self | ||
|
|
||
|
|
||
| class AssertGenerationStage(str, Enum): |
There was a problem hiding this comment.
Nit: main just migrated SeedOrigin to StrEnum (P311-17, #4126), so a new enum here will need re-migration after rebase. The rest of this file predates that effort, so maybe a post-rebase cleanup — but new code could start as StrEnum.
| Estimate the remaining model calls and tokens of a checkpoint without calling a target. | ||
|
|
||
| Seed-group calls are exact once the taxonomy and dimensions exist. Before that, they're | ||
| bounded: one call per distinct category and dimension combination, up to five groups each. |
There was a problem hiding this comment.
Docstring nit: this says pre-policy seed-group calls are bounded by "one call per distinct category and dimension combination, up to five groups each", but _estimate_groups computes min = ceil(remaining / 5) and max = remaining (one call per group). The text describes neither bound — consider restating it as "between one call per five groups and one call per group".
Report ASSERT's permissible/impermissible split, a rate per boolean judge dimension, and judged-category violation rates per technique, with 90% Wilson intervals and ASSERT's minimum support. Expose them through a backend route and the GUI run page. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…RT behavior library AssertGenerator gains a checkpointed staged API (advance_async, build_result, estimate, validate_config): stop after a stage, apply a reviewed taxonomy and dimensions (recorded in each group's provenance), and resume after interruption without repeating model calls. TargetObjectiveGenerator.estimate and the provider-free PromptGenerationEstimate report remaining calls and a token range. The ASSERT v0.3.0 behavior (52) and suite (3) presets are vendored with loaders, and StorageIO gains list_files_async. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
6146e1b to
ac96336
Compare

Part -4 of a 7-PR ASSERT stack. Design and how to use it: ASSERT in PyRIT.
Each PR targets
mainand contains the PRs above it in this list, so review only this PR's own commits.Description
ASSERT generation can now pause after the taxonomy for a person to review and edit it, resume after an interruption without repeating model calls, and estimate its cost before calling a model. It can start from ASSERT's bundled behavior library.
AssertGenerator.advance_asyncruns the remaining stages of anAssertGenerationCheckpointand can stop after any stage.with_reviewed_policyapplies an edited taxonomy and dimension levels, and each seed group's provenance records which stages were edited.on_checkpointreports progress after each stage and seed-group batch.execute_asynckeeps its signature and runs every stage through the same staged path.AssertGenerator.estimateandTargetObjectiveGenerator.estimatereturn a provider-freePromptGenerationEstimate: model calls by stage and an approximate token range. Seed-group calls are a range until the taxonomy exists, then exact.pyrit/datasets/assert_behaviorsandpyrit/datasets/assert_suites, with loaders inpyrit.datasets, attribution headers, provenance entries, and third-party notices.StorageIO.list_files_asyncfor disk and Azure Blob storage. Concurrent Azure Blob operations no longer close each other's shared container client.TargetObjectiveProvider.package_resultand theTargetObjectiveGeneratorResultexport, used by the backend layer.