Skip to content

FEAT: Adding Json Schema Pipeline to SeedPrompts#1432

Merged
rlundeen2 merged 95 commits into
microsoft:mainfrom
riedgar-ms:riedgar-ms/selfask-jsonschema-01
Jun 9, 2026
Merged

FEAT: Adding Json Schema Pipeline to SeedPrompts#1432
rlundeen2 merged 95 commits into
microsoft:mainfrom
riedgar-ms:riedgar-ms/selfask-jsonschema-01

Conversation

@riedgar-ms

@riedgar-ms riedgar-ms commented Mar 2, 2026

Copy link
Copy Markdown
Contributor
  • Added JSON Schema that can be added directly in yaml, by putting a json_schema inline or using a name that references datasets or (so diff datasets can easily share)
  • Added a message normalizer so that targets that support it can use the schema in the request or add the text to the request if it's not supported
  • Updated refusal scorers to use json schema (followup could have all JSON schemas use this)
  • Moved json_response_config to pyrit.prompt_target.common, which makes more sense since it's tied to targets

co-authored/hijacked by rlundeen

Copilot AI review requested due to automatic review settings March 2, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR embeds a JSON response schema into the SelfAskRefusalScorer’s seed YAML and plumbs that schema through scoring so compatible prompt targets can request schema-constrained JSON output.

Changes:

  • Add response_json_schema to SeedPrompt and populate it in the default refusal scorer YAML.
  • Update SelfAskRefusalScorer to load and forward the schema into _score_value_with_llm.
  • Extend _score_value_with_llm to accept an optional schema and attach it to prompt_metadata for JSON-response-capable targets.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
pyrit/score/true_false/self_ask_refusal_scorer.py Loads schema from the seed prompt and forwards it into LLM scoring + identifier params.
pyrit/score/scorer.py Adds schema parameter and injects it into request metadata for JSON response formatting.
pyrit/models/seeds/seed_prompt.py Introduces a new optional response_json_schema field on SeedPrompt.
pyrit/datasets/score/refusal/refusal_default.yaml Defines the refusal scorer response JSON schema and tightens the schema text in the prompt.

Comment thread pyrit/score/true_false/self_ask_refusal_scorer.py
Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/models/seeds/seed_prompt.py Outdated
Comment thread pyrit/score/true_false/self_ask_refusal_scorer.py Outdated
@riedgar-ms

Copy link
Copy Markdown
Contributor Author

I'm not massively keen on how I've hidden the type of the schema (and this hiding has provoked most of CoPilot's comments). The solution would be defining a JSONObject type, in the manner of:
python/typing#182
However, that could have quite a blast radius.

Comment thread pyrit/datasets/score/refusal/refusal_default.yaml Outdated
Copilot AI review requested due to automatic review settings March 3, 2026 16:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings March 4, 2026 14:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/score/scorer.py Outdated
Comment thread pyrit/score/scorer.py Outdated
Copilot AI review requested due to automatic review settings March 5, 2026 11:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

Comment thread pyrit/datasets/score/refusal/refusal_default.yaml Outdated
@riedgar-ms riedgar-ms force-pushed the riedgar-ms/selfask-jsonschema-01 branch from c679a55 to 04795e5 Compare March 26, 2026 18:18
Comment thread pyrit/datasets/score/refusal/refusal_no_objective_lenient.yaml
rlundeen2 and others added 8 commits June 4, 2026 12:42
rom typing import override is 3.12+; PyRIT supports >=3.10. The
project intentionally suppresses the missing-override-decorator rule
(see pyproject.toml [tool.ty.rules]) precisely so we don't have to
pull in the typing_extensions backport. Aligning this normalizer with
the rest of pyrit/message_normalizer/, which override methods without
the decorator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
JSON Schema 'description' is the canonical place for field-level
guidance. Adding them here means:

* Native JSON_SCHEMA targets (OpenAI, Anthropic) pass field
  descriptions through to the model as part of the schema metadata.
* When JsonSchemaNormalizer falls back to injecting the pretty-printed
  schema into the prompt body, the descriptions ride along automatically
  via {schema_json}.
* Scorer prompt YAMLs no longer need to redundantly describe the shape
  of each field; that responsibility moves to the schema (single source
  of truth) so scorer prompts can focus on the task framing.

Descriptions are intentionally generic so the schema stays reusable
across any future self-ask True/False scorer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
build_scripts/check_no_rest_roles.py rejects :class:/:func:/:meth:/
:data: cross-reference roles in docstrings and comments; the docs
pipeline auto-links bare `Foo` spans against known PyRIT symbols
via build_scripts/gen_api_md.py. Switching all sites I introduced
in pyrit/models/json_schema_definition.py and
pyrit/message_normalizer/json_schema_normalizer.py to plain
double-backticks so the pre-commit hook passes and the rendered
docs link the same way as the rest of the codebase.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Single ValueError string now fits within line length; ruff-format
auto-reformats this on pre-commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pyrit/prompt_target/common/conversation_normalization_pipeline.py
Comment thread pyrit/models/json_schema_definition.py
riedgar-ms and others added 10 commits June 5, 2026 12:38
…lizer

When both MULTI_TURN and JSON_SCHEMA need adaptation, the pipeline ran HistorySquashNormalizer first and produced a fresh Message.from_prompt(...) with empty prompt_metadata. The subsequent JsonSchemaNormalizer then saw no schema key and no-oped, so the schema instructions were silently dropped from the squashed text.

HistorySquashNormalizer now carries the last message's prompt_metadata onto the squashed piece so downstream normalizers still see request-level metadata.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same shape as the HistorySquashNormalizer fix: a fresh Message.from_prompt(...) produced by squashing carried empty prompt_metadata, so when both SYSTEM_PROMPT and JSON_SCHEMA needed adaptation, the schema key was silently dropped before JsonSchemaNormalizer ran.

Single-system case carries the system piece's metadata; system+user squash carries the user piece's metadata (the user piece represents the current request being sent).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both squash normalizers (history and system) need to carry the last subsumed message's prompt_metadata onto the fresh Message.from_prompt(...) so downstream normalizers in the pipeline still see request-level metadata such as the JSON schema key.

Centralize that rule in pyrit/message_normalizer/_helpers.py so any future squash-style normalizer gets correct propagation by default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@riedgar-ms

Copy link
Copy Markdown
Contributor Author

@rlundeen2 @romanlutz first, apologies for accidentally pinging you both on #1346 ; I was updating both PRs, and didn't notice that I was writing in the wrong tab until after I'd hit 'comment' (not that I wouldn't appreciate a review on that PR as well).

I think I've merged in the latest changes from main and updated everything for the latest set of linting rule updates correctly. The type checker also started complaining about memory_models.py, and I had to ask Copilot for help fixing that. As such, those changes might bear closer examination.

…onschema-01

# Conflicts:
#	pyrit/memory/memory_models.py
@rlundeen2 rlundeen2 force-pushed the riedgar-ms/selfask-jsonschema-01 branch from d280fa9 to 478a33d Compare June 9, 2026 21:07
@rlundeen2 rlundeen2 enabled auto-merge June 9, 2026 21:09
@rlundeen2 rlundeen2 added this pull request to the merge queue Jun 9, 2026
Merged via the queue into microsoft:main with commit 37468e5 Jun 9, 2026
53 checks passed
@riedgar-ms riedgar-ms deleted the riedgar-ms/selfask-jsonschema-01 branch June 10, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants