Repository navigation
FEAT: Prompt generation jobs in the backend (-5) - #4134
Richard Lundeen (richlundeen) wants to merge 44 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
Critical storage and cross-backend coordination races can overwrite terminal state, hide jobs, and release dataset-name reservations while original workers remain active, allowing duplicate generation.
5 open issues
Reject cancellation of remotely owned active jobs · New Reserve dataset names atomically across shared-storage backends · New Drain cancelled checkpoint writes before releasing the service lock · New Serialize cache initialization with generation mutations · New Persist and restore generation requests for objective datasets · New
What changed in this PR
This PR adds backend-managed prompt generation jobs so clients can draft, estimate, review, resume, and save generated datasets using PyRIT’s existing generators and dataset providers.
Changes:
- Integrates generation jobs with backend routing, runtime lifecycle, estimates, and dataset packaging.
- Bundles ASSERT behavior and suite presets with attribution.
- Adds supporting ASSERT scoring, trace evidence, analytics, frontend integration, and documentation.
| File | Description |
|---|---|
| THIRD_PARTY_NOTICES.txt | Updates ASSERT attribution. |
| tests/unit/executor/promptgen/test_target_objective_generator.py | Tests generation estimates. |
| tests/unit/datasets/test_target_objective_provider.py | Tests result packaging. |
| tests/unit/common/test_lazy_package_imports.py | Updates analytics export count. |
| tests/unit/assert_mocks.py | Adds checkpoint fixtures. |
| tests/unit/analytics/test_rate_statistics.py | Tests binary and macro rates. |
| pyrit/score/true_false/true_false_fallback_scorer.py | Adds configurable fallback triggers. |
| pyrit/score/scorer.py | Identifies missing-evidence results. |
| pyrit/score/observation/otel_span_exporter.py | Exports richer trace evidence. |
| pyrit/score/observation/message_trace_resolver.py | Resolves conversation trace links. |
| pyrit/score/observation/execution.py | Shares evidence acquisition across scorers. |
| pyrit/score/observation/__init__.py | Exports trace observation APIs. |
| pyrit/score/float_scale/float_scale_fallback_scorer.py | Adds configurable fallback triggers. |
| pyrit/score/__init__.py | Exports ASSERT and tracing APIs. |
| pyrit/scenario/core/scenario.py | Extracts scenario identity parameters. |
| pyrit/prompt_target/common/target_trace_config.py | Adds conversation identifiers to spans. |
| pyrit/prompt_target/common/prompt_target.py | Exposes target tracing configuration. |
| pyrit/models/score/__init__.py | Exports execution-trace models. |
| pyrit/models/prompt_generation_estimate.py | Defines generation estimate contracts. |
| pyrit/models/analytics.py | Adds rate result models. |
| pyrit/executor/promptgen/target_objective_generator.py | Estimates objective-generation work. |
| pyrit/executor/promptgen/__init__.py | Exports objective-generation results. |
| pyrit/datasets/seed_datasets/generated/target_objective_provider.py | Packages existing generation results. |
| pyrit/datasets/score/true_false_question/assert_execution_evidence_instructions.yaml | Guides execution-evidence judgments. |
| pyrit/datasets/score/assert/judge_presets/tool-use.yaml | Adds tool-use judging criteria. |
| pyrit/datasets/score/assert/judge_presets/safety-extended.yaml | Adds extended safety criteria. |
| pyrit/datasets/score/assert/judge_presets/safety-core.yaml | References core safety dimensions. |
| pyrit/datasets/score/assert/judge_presets/robustness.yaml | Adds robustness judging criteria. |
| pyrit/datasets/score/assert/judge_presets/policy-adherence.yaml | Adds policy-adherence criteria. |
| pyrit/datasets/score/assert/judge_presets/operational.yaml | Adds operational judging criteria. |
| pyrit/datasets/score/assert/judge_presets/multi-turn.yaml | Adds multi-turn judging criteria. |
| pyrit/datasets/score/assert/judge_presets/instruction-following.yaml | Adds instruction-following criteria. |
| pyrit/datasets/score/assert/judge_presets/grounding.yaml | Adds grounding judging criteria. |
| pyrit/datasets/score/assert/judge_presets/communication.yaml | Adds communication judging criteria. |
| pyrit/datasets/score/assert/judge_presets/alignment.yaml | Adds alignment judging criteria. |
| pyrit/datasets/score/assert/dimension_system_prompt.yaml | Adds custom-dimension judge template. |
| pyrit/datasets/assert_suites/travel_planner.yaml | Bundles travel-planner suite. |
| pyrit/datasets/assert_suites/travel_planner_benchmark.yaml | Bundles travel-quality benchmark suite. |
| pyrit/datasets/assert_suites/telecom_customer_service.yaml | Bundles telecom support suite. |
| pyrit/datasets/assert_library.py | Loads and validates bundled presets. |
| pyrit/datasets/assert_behaviors/violent_content.yaml | Bundles violence behavior preset. |
| pyrit/datasets/assert_behaviors/unsupported_conclusion_failures.yaml | Bundles unsupported-conclusion preset. |
| pyrit/datasets/assert_behaviors/unit_conversion_failures.yaml | Bundles unit-conversion preset. |
| pyrit/datasets/assert_behaviors/underused_context_failures.yaml | Bundles context-underuse preset. |
| pyrit/datasets/assert_behaviors/unauthorized_action_failures.yaml | Bundles unauthorized-action preset. |
| pyrit/datasets/assert_behaviors/tool_parameter_formatting_failures.yaml | Bundles tool-argument preset. |
| pyrit/datasets/assert_behaviors/tool_output_misinterpretation_failures.yaml | Bundles tool-output interpretation preset. |
| pyrit/datasets/assert_behaviors/tool_orchestration_errors.yaml | Bundles tool-orchestration preset. |
| pyrit/datasets/assert_behaviors/tool_call_turn_protocol_failures.yaml | Bundles tool-turn protocol preset. |
| pyrit/datasets/assert_behaviors/tool_call_error_recovery_failures.yaml | Bundles tool-error recovery preset. |
| pyrit/datasets/assert_behaviors/tabs_vs_spaces.yaml | Bundles indentation-bias preset. |
| pyrit/datasets/assert_behaviors/sycophancy.yaml | Bundles sycophancy preset. |
| pyrit/datasets/assert_behaviors/suicide_self_harm.yaml | Bundles self-harm safety preset. |
| pyrit/datasets/assert_behaviors/success_criteria_ambiguity_failures.yaml | Bundles success-criteria preset. |
| pyrit/datasets/assert_behaviors/stereotyping.yaml | Bundles stereotyping preset. |
| pyrit/datasets/assert_behaviors/stale_state_failures.yaml | Bundles stale-state preset. |
| pyrit/datasets/assert_behaviors/shimano_vs_sram.yaml | Bundles cycling-brand bias preset. |
| pyrit/datasets/assert_behaviors/sexual_content.yaml | Bundles sexual-content safety preset. |
| pyrit/datasets/assert_behaviors/response_completeness_failures.yaml | Bundles response-completeness preset. |
| pyrit/datasets/assert_behaviors/repeated_action_loop_failures.yaml | Bundles repeated-action preset. |
| pyrit/datasets/assert_behaviors/prompt_injection.yaml | Bundles prompt-injection preset. |
| pyrit/datasets/assert_behaviors/procedure_adherence_failures.yaml | Bundles procedure-adherence preset. |
| pyrit/datasets/assert_behaviors/premature_termination_failures.yaml | Bundles premature-termination preset. |
| pyrit/datasets/assert_behaviors/poor_retrieval_failures.yaml | Bundles retrieval-quality preset. |
| pyrit/datasets/assert_behaviors/output_internal_consistency_failures.yaml | Bundles output-consistency preset. |
| pyrit/datasets/assert_behaviors/out_of_scope_request_failures.yaml | Bundles scope-compliance preset. |
| pyrit/datasets/assert_behaviors/observation_neglect_failures.yaml | Bundles observation-neglect preset. |
| pyrit/datasets/assert_behaviors/malicious_cyber_activity.yaml | Bundles cyber-safety preset. |
| pyrit/datasets/assert_behaviors/ios_vs_android.yaml | Bundles platform-bias preset. |
| pyrit/datasets/assert_behaviors/inter_agent_handoff_failures.yaml | Bundles agent-handoff preset. |
| pyrit/datasets/assert_behaviors/intent_misinterpretation_failures.yaml | Bundles intent-interpretation preset. |
| pyrit/datasets/assert_behaviors/insufficient_verification_failures.yaml | Bundles verification-failure preset. |
| pyrit/datasets/assert_behaviors/ineffective_team_communication_failures.yaml | Bundles agent-communication preset. |
| pyrit/datasets/assert_behaviors/incorrect_tool_selection_failures.yaml | Bundles tool-selection preset. |
| pyrit/datasets/assert_behaviors/incomplete_answer_synthesis_failures.yaml | Bundles answer-synthesis preset. |
| pyrit/datasets/assert_behaviors/identity_verification_failures.yaml | Bundles identity-verification preset. |
| pyrit/datasets/assert_behaviors/hate_speech_harassment.yaml | Bundles harassment-safety preset. |
| pyrit/datasets/assert_behaviors/harmful_medical_advice.yaml | Bundles medical-safety preset. |
| pyrit/datasets/assert_behaviors/grounding_attribution_errors.yaml | Bundles grounding-error preset. |
| pyrit/datasets/assert_behaviors/goal_drift_failures.yaml | Bundles goal-drift preset. |
| pyrit/datasets/assert_behaviors/flawed_action_plan_failures.yaml | Bundles action-planning preset. |
| pyrit/datasets/assert_behaviors/explicit_constraint_violation_failures.yaml | Bundles constraint-compliance preset. |
| pyrit/datasets/assert_behaviors/escalation_judgment_failures.yaml | Bundles escalation-decision preset. |
| pyrit/datasets/assert_behaviors/doxxing.yaml | Bundles privacy-safety preset. |
| pyrit/datasets/assert_behaviors/crystal_mountain_vs_snoqualmie.yaml | Bundles destination-bias preset. |
| pyrit/datasets/assert_behaviors/conversation_coherence_breakdown.yaml | Bundles conversation-coherence preset. |
| pyrit/datasets/assert_behaviors/constraint_propagation_failures.yaml | Bundles constraint-propagation preset. |
| pyrit/datasets/assert_behaviors/conflicting_instruction_resolution_failures.yaml | Bundles instruction-conflict preset. |
| pyrit/datasets/assert_behaviors/actionability_failures.yaml | Bundles actionability preset. |
| pyrit/datasets/__init__.py | Exports preset lookup APIs. |
| pyrit/common/path.py | Defines bundled preset paths. |
| pyrit/backend/services/service_lifecycle.py | Resets generation service during cleanup. |
| pyrit/backend/services/runtime_lifecycle.py | Includes generation jobs in lifecycle checks. |
| pyrit/backend/routes/scenarios.py | Exposes ASSERT metrics endpoint. |
| pyrit/backend/README.md | Documents jobs and metrics APIs. |
| pyrit/backend/main.py | Registers generation routes. |
| pyrit/analytics/__init__.py | Exports ASSERT analytics helpers. |
| frontend/src/types/index.ts | Defines ASSERT metrics contracts. |
| frontend/src/services/api.ts | Adds metrics API client. |
| frontend/src/hooks/useAssertPolicyMetrics.ts | Loads metrics with request cancellation. |
| frontend/src/hooks/useAssertPolicyMetrics.test.tsx | Tests metrics loading and retries. |
| 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 navigation. |
| doc/code/scoring/1_true_false_scorers.py | Documents dimensions and sampled judgments. |
| doc/code/scoring/1_true_false_scorers.ipynb | Updates paired scoring documentation. |
| doc/code/scoring/0_scoring.py | Links execution-trace scoring guidance. |
| doc/code/scoring/0_scoring.ipynb | Updates paired scoring overview. |
| doc/code/framework.md | Defines job, trace, and analytics responsibilities. |
| doc/code/executor/7_promptgen.py | Demonstrates checkpoint review and continuation. |
| doc/code/executor/7_promptgen.ipynb | Adds paired generation example. |
| doc/code/analytics/0_attack_results.md | Documents rate helpers and ASSERT metrics. |
Review effort: Balanced
…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>
Roman Lutz (romanlutz)
left a comment
There was a problem hiding this comment.
Reviewed the incremental diff vs the -4 branch (richlundeen-assert-generation-sdk). Solid job layer — the lifecycle edge cases (shielded cancel updates, shutdown waiting for saves, idempotent retry-after-save) are handled with care, and the tests exercise real generators. One small 500-level bug and a handful of polish items below. All 37 new tests pass for me locally.
38e7c2a to
588e3b9
Compare
588e3b9 to
ea4dbc1
Compare
Adds a scenario-local assert_package technique to foundry.assert_policy: PyRIT's RedTeamingAttack whose tester uses ASSERT's own tester system prompt (pinned 04b713fe) filled with each group's stored tester instructions. It is the default technique alongside the baseline; red_teaming stays selectable. Groups without tester instructions are skipped or rejected per incompatible_technique_policy. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve judge presets in a worker thread instead of the synchronous validator. Drop a judge sample whose judgment stays invalid after retries (scoring fails only when every sample does), as ASSERT does. Require native multi-turn targets for turn-taking techniques in foundry.assert_policy, keep turn settings a registered technique sets itself, pass the adversarial chat only to techniques that use one, and warn once per even sample count. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ea4dbc1 to
6f66fd2
Compare
Roman Lutz (romanlutz)
left a comment
There was a problem hiding this comment.
Re-review of ea4dbc1. The simplification pass (drafts, versioning, and the groups API removed; single-backend ownership made explicit) resolves my round-1 points 1, 4, 5, and 6 — point 1's 500 bug went away with the PATCH endpoint itself — and the new cancel-before-start handling fixes a latent bug I'd missed. Still open from round 1: pause_for_review accepted for non-reviewable generators, and the exact preset counts in the route test. Three small new items inline. All 35 tests pass locally on the new head.
| async with self._lock: | ||
| taken = await self._get_taken_dataset_names_async(exclude_id=generation_id) | ||
| if record.dataset_name in taken and not await self._dataset_came_from_async(record=record): | ||
| raise PromptGenerationConflictError(_name_taken_message(record.dataset_name)) |
There was a problem hiding this comment.
With the rename path (PATCH) gone, a retryable generation whose dataset name has since been taken can never be retried — and cancellation releases the reservation, so this is reachable: cancel, someone reuses the name, retry 409s forever, and the checkpoint's paid-for model work is stranded (from-dataset regeneration starts over). Was dropping rename-before-retry a deliberate tradeoff? If so, fine — but maybe the conflict message shouldn't point at "Edit & regenerate" as if the checkpoint were recoverable.
| list[PromptGenerationRecord]: Stored records. | ||
| """ | ||
| records = (await self._load_cache_async()).values() | ||
| return sorted(records, key=lambda record: record.created_at, reverse=True) |
There was a problem hiding this comment.
Records created in one batch share a single created_at, so this sort leaves within-batch order undefined — in-process it happens to be insertion order, but after a restart it becomes file-listing order (random UUID filenames). A stable secondary key (e.g. record.id, or a per-item index carried from the batch request) would keep listings deterministic for the GUI.
| record = await self.get_async(generation_id) | ||
| # A task cancelled before it started never ran its handler, so its record is still queued. | ||
| if record.status != PromptGenerationStatus.QUEUED: | ||
| return record |
There was a problem hiding this comment.
Nice catch on the never-started-task case. It doesn't look covered by a test though — with the default concurrency of 1, queueing two generations and cancelling the second while it waits on the semaphore… actually that one does run its handler; the truly-never-started case is cancelling immediately after create, before the event loop first schedules the task. Worth a test either way, since this branch is what keeps the record from being returned as QUEUED while claiming cancellation succeeded.
Add AssertDimensionResult, AssertCategoryJudgment (with AssertNodeJudgment) and AssertSampleVotes in pyrit.models as the only readers and writers of the ASSERT score metadata keys, keeping the stored key format. Missing keys are unset and malformed present data raises ValueError. The scenario judge wrapper now records the assert_dimension marker on judged and skipped scores. The custom-dimension judge renders its request from a YAML SeedPrompt, and both ASSERT response handlers validate and convert the decoded response through a new JsonSchemaResponseHandler._convert_response hook instead of parsing twice. AssertDimensionMetadata and AssertSampleMetadata are removed. 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>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Withdraw completion when a span starts after it; export and completion share the capture lock; evict any trace when full with explicit loss markers and bounded bookkeeping; render steps in parent cycles; remove the unreachable unattributed block; require a trace source for trace_requirement; document strict snapshot matching and citation behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…mplate additional_instructions is an explicit dimension_system_prompt.yaml parameter rendered in the sandbox instead of appended after rendering; the prompt is unchanged when it is empty. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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>
Checkpoints carry a format version and the identifier of the target that produced each role's work. Resuming with a different target is rejected before any model call, build_result attributes work to the recorded producers, and stored seed-group assignments must match the planned batches. Estimates count the dimensions and target system prompt sent with every seed-group call. Objective datasets record the request that generated them, the objective estimate reads its prompt once, and Azure Blob listings percent-encode names so they round-trip. AssertGenerationStage is a StrEnum. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
PromptGenerationService runs generators in the background: preflight and estimates, review pauses, cancel, retry from the last checkpoint, batch creation for suites, and saving a new named dataset. One backend instance owns the records, stored as JSON documents in the memory's results storage. Adapters translate the ASSERT and target-objective generators; routes live under /api/prompt-generators and /api/prompt-generations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A cancelled local save finishes its file replacement before the cancellation handler writes again, and the first cache load is serialized with saves and deletes. pause_for_review is rejected for generators without a review step, objective datasets can be edited and regenerated, and batch members keep their request order. The README states that several backends sharing one results storage aren't supported. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
6f66fd2 to
7875121
Compare
Brings in microsoft#3920. No textual conflicts. Updates the assert_package resume test for main's earlier rejection of changed scenario role references, which now reports that the run does not match the current configuration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…nto trace evidence
…een-assert-headline-metrics-plan # Conflicts: # pyrit/backend/routes/scenarios.py
…(-4) Brings in main 1dc1d5c (including dataset uploads, microsoft#3920) through -3 78f2477. Storage keeps both list_files_async and main's write_stream_async/canonical_path; Azure write_stream_async now shares the reference-counted container client so it can't close a client other operations are using. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
main's dataset editing and deletion read selection keys through the shared parse_dataset_selection_key, which replaced DatasetService._parse_selection_key in this layer. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in microsoft#4187. No conflicts or integration changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…nto trace evidence
…een-assert-headline-metrics-plan
…ng in the launch form AssertPolicy(objective_scorer=...) uses a supplied true/false scorer unchanged and rejects mixing it with judge_target, a different scoring_target, or non-default legacy judge settings. Default per-policy judge construction moves into the scorer package as build_assert_judge_scorers. The ASSERT launch form hides the objective-scorer picker and ASSERT judge settings in the form, request and preview, and rejects saved custom scoring instead of replaying it as defaults. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…objective scorer Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ts readiness Default ASSERT judges resolve their target through Scenario._resolve_scoring_target, so the explicit scoring_target run parameter takes precedence over judge_target, and AssertPolicy.get_readiness binds that target or reports it missing. A supplied objective scorer needs no default judge target. The ASSERT launch form also treats a carried scoring_target as scorer-owned, and compares saved judge settings to their display defaults by type: an empty list is the default only for list settings, and an empty or list value for target_system_prompt is rejected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Satisfies main's check-keyword-only-params gate. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Merges the published trace evidence layer (7bf65f3) into the reshaped judge dimensions layer. Trace evidence now flows through build_assert_judge_scorers, a supplied objective scorer also owns trace configuration, and the launch form hides and rejects non-default trace settings with the other ASSERT scoring settings. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…only parameters Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…layer Bring the published layer-3 metrics (4848f91) onto the rebuilt layer 2 (06ccdf2). The merge applied without conflicts. The Foundry notebook keeps layer 2's outputs and is synced from foundry.py; the new ASSERT metrics cell has no output because it hasn't been run against this layer. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…distinct ASSERT metrics now let a saved registry name decide whether foundry.assert_policy produced a result, so another scanner's AssertPolicy class isn't mistaken for ASSERT. Older results without a registry name must come from PyRIT's AssertPolicy module. The metrics route answers other scenarios with a 400 problem of type /errors/not-assert-policy-run. The run page mounts the panel on a matching registry name, or tentatively on the class name for older runs, and hides it only for that problem type; compatibility, validation and other errors still show. A supplied objective scorer's uncategorized verdicts are reported as the objective dimension, not as ASSERT's policy judge. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…sions A result's policy now comes only from the stored seed group with exactly the seeds it recorded, and only when that group's taxonomy matches the taxonomy the policy judge recorded in its saved scorer identifier, which survives sampling and trace wrappers. A replaced or edited dataset leaves the conversation without a matching stored policy instead of reporting another group's categories. Unmarked legacy scores count as ASSERT dimensions only with that judge provenance, so a supplied scorer that uses the policy_violation category is reported as the objective dimension. Category rates are kept per policy, with a policy_id, so policies that share a behavior or category name aren't pooled, and the panel names them apart. Results without a scenario module are no longer accepted as AssertPolicy by class name alone. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A stored seed group's policy is used only when the policy score's saved identifier comes from the scenario's built-in judge wrapper and records the same taxonomy; a policy score without that evidence leaves the conversation without a matching stored policy, while its dimension rates still count it. Only questions under that wrapper count as judge evidence, so a supplied SelfAskTrueFalseScorer whose question carries ASSERT-shaped metadata and the policy_violation category is reported as the objective dimension. Category rates are keyed by the judged policy (taxonomy, application context and target instructions), so policies that share a taxonomy but differ in context or instructions aren't pooled. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The metrics no longer read a stored group's assigned category. ASSERT's judges recompute policy_violation from their category judgments, so every violation verdict already names a violated category; ASSERT's attribution of category-less violations to the generated category isn't needed and would depend on seed metadata that can be edited after a run. A stored policy verdict that disagrees with its category judgments is now rejected as malformed instead of being attributed, and AssertCategoryMetrics drops attributed_by_fallback. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Integrates -3 d0b7c2a (typed judgments, provenance and verdict corrections) under the published -4 head; no conflicts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>


Part -5 of a 10-PR ASSERT stack. Design and how to use it: ASSERT in PyRIT.
Each PR targets
mainand includes the earlier layers, so its Files changed tab also shows them. Review this PR's own changes in the -4 to -5 source comparison. Use source comparisons rather than commit lists: -8 keeps prototype commits in its ancestry.Description
The backend runs prompt generators as background jobs, so the GUI can estimate, run, review, resume, and save generated datasets. It isn't tied to ASSERT: the target-objective generator uses the same jobs.
PromptGenerationServiceowns the job lifecycle: admission (one generation at a time by default), checkpoint persistence, review pauses, cancel, retry from the last checkpoint, batch creation for suites, dataset-name reservation, and saving a new named dataset. Shutdown lets a dataset save finish. Retrying after an interruption completes a save that already landed instead of failing on the name.pause_for_reviewis rejected for generators without a review step.<memory results_path>/prompt_generations/<id>.json, next to media output. No database migration is needed./api/prompt-generators: generator descriptors and the bundled ASSERT behaviors and suites./api/prompt-generations: preflight with estimate, create, review, continue/retry/cancel, delete a record, and the request recorded in an ASSERT or objective dataset./api/prompt-generations/batch: queues generations that share settings, such as an ASSERT suite, as ordinary records with a sharedgroup_id, listed in request order.DatasetServiceand the generation service share oneparse_dataset_selection_key, including the dataset delete route, without changing which selection keys are accepted.framework.mddescribe the service's responsibility.Storage and concurrency. One backend instance owns the generation records. It reads them once into an in-process cache and every write updates both; local files are replaced atomically, including when a save is cancelled. Writes are last-write-wins, including review edits. Earlier revisions of this PR checked a record version on edits and periodically refreshed the cache; both were removed. Those checks only worked inside one process and never coordinated separate backends. Several backends sharing one results storage, such as one blob container, aren't supported: record writes, cancellation and dataset-name reservations aren't coordinated across processes, so two backends could accept the same dataset name or cancel each other's work.
GUI and scope
Own GUI changes: none. This layer changes no frontend files; its routes back the GUI added in #4135.
Shared / non-ASSERT impact (backend only):
Unchanged / deferred:
The job page below is the later #4135 frontend that uses these routes, captured with synthetic offline fixture data on -6
8f878a58e; it is not UI added by this PR: