Skip to content

FEAT: Add prefill technique with native assistant continuation - #3283

Open
Dev3 ^~^ (shashank03-dev) wants to merge 9 commits into
microsoft:mainfrom
shashank03-dev:feat/prefill-attack
Open

Dev3 ^~^ (shashank03-dev) wants to merge 9 commits into
microsoft:mainfrom
shashank03-dev:feat/prefill-attack

Conversation

@shashank03-dev

@shashank03-dev Dev3 ^~^ (shashank03-dev) commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3270

Adds an opt-in prefill attack technique built on PromptSendingAttack. The technique prepares the converted user objective followed by an exact assistant prefix and reuses shared sending, scoring, response conversion, retry isolation, and persistence. The unreleased dedicated PrefillAttack API is removed.

  • Registers the technique in extra with native PREFILL, MULTI_TURN, and EDITABLE_HISTORY requirements and objective-only dataset inputs.
  • Adds explicit assistant-continuation seed extraction while preserving ordinary user-ending requests and assistant-ending simulated history.
  • Keeps prefixes as simulated_assistant in memory, preserves distinct evaluation hashes, and retains Hugging Face continuation support. Completion targets remain unsupported.
  • Validates converted history modalities while allowing OpenAI Chat adapters to omit stored audio and replay its transcript. Unsupported objective images still fail before generation.
  • Preserves declared prefill support through capability discovery and JSON serialization.
  • Updates the paired single-turn example and integrates current main.

Validation: 30,302 unit tests pass (12 skipped); 106 discovery/prefill boundary tests and 102 catalog tests pass; changed production-line coverage is 100%; Python typing, frontend TypeScript checking, documentation validation, and pre-commit checks pass. The updated prefill example executed against cached Qwen2.5-0.5B-Instruct using model_path in place of model_id because this environment has no Hugging Face token.

GitHub Actions currently reports action_required for the six PR workflows, with no jobs started. Remote CI results remain pending workflow approval.

@ManoharPaturi

Copy link
Copy Markdown
Contributor

This is a great implementation of the prefill attack. Requiring supports_prefill natively in TargetRequirements is the right design decision because attempting to adapt or fall back on targets that drop trailing assistant messages would disguise a target limitation as an attack refusal. Scoping request converters strictly to the prepended user objective preserves the exact prompt tokens intended for continuation, and tagging the prefill as a simulated assistant role keeps memory history truthful. The HuggingFaceChatTarget chat template continuation tests cover the mechanics nicely.

@shashank03-dev

Copy link
Copy Markdown
Contributor Author

Manohar Paturi (@ManoharPaturi) Thank you :)

Comment thread pyrit/executor/attack/single_turn/prefill.py Outdated
Comment thread pyrit/executor/attack/single_turn/prefill.py Outdated
@romanlutz

Roman Lutz (romanlutz) commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Dev3 ^~^ (@shashank03-dev) Sorry, I missed an architectural point in my earlier reviews, especially after asking you to make fixes around the new attack class. I should have caught this before approving. On a closer look, I think prefill should be an attack technique built on PromptSendingAttack, rather than a separate PrefillAttack. I'd like us to make that change before merging.

The capability and target changes are still useful. The part I'd like to change is how we package the behavior. Prefill does not introduce a new conversation algorithm: it prepares a user objective followed by an assistant prefix, then uses the same sending, scoring, retry, and result-handling flow we already have. That is a better fit for the technique/executor split in framework.md.

Here is the direction I suggest:

  1. Keep the target capability and native continuation support. Keep CapabilityName.PREFILL, the default-false supports_prefill field, and the Hugging Face chat-template change. The target should still use continue_final_message=True and avoid adding a new generation prompt for a trailing assistant message. Keep OpenAICompletionTarget unsupported unless its adapter can preserve the objective as well as the prefix.

  2. Represent prefill as technique configuration. Register a technique using AttackTechniqueFactory(attack_class=PromptSendingAttack, ...), with the prefix supplied as technique-owned seed/configuration. Put the general-purpose technique in pyrit/setup/initializers/techniques/extra.py, rather than adding it to the small default core catalog. Declare native PREFILL, MULTI_TURN, and EDITABLE_HISTORY requirements through TechniqueRequirements and TargetRequirements. Unsupported targets should fail compatibility checks, not silently flatten or drop the prefix.

  3. Add only the missing reusable preparation support. This is not quite a configuration-only replacement with today's code. PromptSendingAttack can send a supplied next_message, but SeedGroup.next_message only extracts a final user turn; an assistant-ending seed group is treated as history. Request converters also normally run on the outgoing message. Please use the existing seed/technique composition and add the smallest explicit, reusable opt-in needed for this shape:

    user:      <the current objective, after request conversion>
    assistant: <the exact configured prefix, left open for continuation>
    

    Do not globally reinterpret every assistant-ending seed group as an outgoing request. Other techniques use assistant-ending seeds as completed simulated history before the real request. The prefill behavior needs an explicit contract, while existing seed extraction and ordinary prompt-sending behavior retain their defaults. Technique code should configure preparation, not implement its own send/scoring/retry loop.

  4. Preserve converter, memory, and scoring semantics. Request converters should act on the objective, not the prefix. Store the caller-written prefix as simulated_assistant, but send it with the assistant API role. Response converters and scorers should still operate on the generated continuation. Reuse the existing executor lifecycle, retry isolation, labels, and persistence. The technique identity must include the prefix so two different prefixes retain different evaluation hashes. Also declare which incoming seed-group shapes the technique accepts; do not silently discard dataset-provided prompts or history.

  5. Replace the dedicated attack API and update the example. Once the shared path works, remove the new PrefillAttack/PrefillAttackParameters class and exports from this PR. Move the relevant coverage to the technique and shared preparation/execution paths. Update both paired single-turn documentation files to show the technique-based usage. There is no need to retain a compatibility wrapper for an API that this unmerged PR has not released.

  6. Keep the original regression coverage and add boundary tests. Exercise the public technique/executor path with a recording target or fake provider. Check the exact user/assistant ordering, unchanged prefix tokens, objective-only request conversion, continuation scoring, simulated memory role, distinct persisted evaluation hashes, unsupported-target rejection, and retry isolation. Add controls showing ordinary user-ending requests and existing assistant-ending history still behave as before. Keep the QRCodeConverter/text-only-target rejection test.

There is also still an audio-history regression to address in the shared validation change. With OpenAIChatTarget(audio_response_config=OpenAIChatAudioConfig(voice="alloy")), the first text request can produce a transcript plus an audio_path response. The next text request now rejects that stored assistant audio as unsupported input, before the adapter can omit the audio and send its transcript. Please validate the content the adapter actually sends, or provide a narrow adapter-aware validation hook, without weakening the rejection of genuinely unsupported objective images. Add a two-turn public-send test with a fake SDK: first an audio response, then a text follow-up, and assert that the second payload carries the transcript rather than input_audio.

I realize this changes the direction after the earlier feedback, and I'm sorry for the extra work. The goal is to keep the useful prefill support while avoiding another attack subclass for behavior that can share our existing execution scaffolding. If there is a constraint in the technique path that I have missed, please point it out so we can address that shared gap explicitly.

@shashank03-dev

Copy link
Copy Markdown
Contributor Author

Roman Lutz (@romanlutz) Thanks for the detailed guidance. Agreed — I’ll move prefill into a PromptSendingAttack technique, fix the audio-history regression, and add coverage for the edge cases. I’ll update the PR once verified.

@shashank03-dev Dev3 ^~^ (shashank03-dev) changed the title FEAT: Add supports_prefill capability and PrefillAttack strategy FEAT: Add prefill technique with native assistant continuation Oct 20, 2026
@shashank03-dev

Copy link
Copy Markdown
Contributor Author

Roman Lutz (@romanlutz) I’ve addressed your feedback and updated the PR. Could you pls review it when you get a chance? Thanks!

This branch has not been deployed

No deployments
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.

FEAT: Add supports_prefill capability and PrefillAttack strategy

3 participants