diff --git a/doc/code/converters/0_converters.ipynb b/doc/code/converters/0_converters.ipynb
index 0121ff06f..aa77034b9 100644
--- a/doc/code/converters/0_converters.ipynb
+++ b/doc/code/converters/0_converters.ipynb
@@ -32,518 +32,7 @@
"execution_count": null,
"id": "2",
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Found default environment files: ['/home/vscode/.pyrit/.env', '/home/vscode/.pyrit/.env.local']\n",
- "Loaded environment file: /home/vscode/.pyrit/.env\n",
- "Loaded environment file: /home/vscode/.pyrit/.env.local\n"
- ]
- },
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " Input Modality | \n",
- " Output Modality | \n",
- " Converter | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " | 0 | \n",
- " audio_path | \n",
- " audio_path | \n",
- " AudioFrequencyConverter | \n",
- "
\n",
- " \n",
- " | 1 | \n",
- " audio_path | \n",
- " text | \n",
- " AzureSpeechAudioToTextConverter | \n",
- "
\n",
- " \n",
- " | 2 | \n",
- " image_path | \n",
- " image_path | \n",
- " AddTextImageConverter | \n",
- "
\n",
- " \n",
- " | 3 | \n",
- " image_path | \n",
- " image_path | \n",
- " TransparencyAttackConverter | \n",
- "
\n",
- " \n",
- " | 4 | \n",
- " image_path | \n",
- " video_path | \n",
- " AddImageVideoConverter | \n",
- "
\n",
- " \n",
- " | 5 | \n",
- " image_path, url | \n",
- " image_path | \n",
- " ImageCompressionConverter | \n",
- "
\n",
- " \n",
- " | 6 | \n",
- " text | \n",
- " audio_path | \n",
- " AzureSpeechTextToAudioConverter | \n",
- "
\n",
- " \n",
- " | 7 | \n",
- " text | \n",
- " image_path | \n",
- " AddImageTextConverter | \n",
- "
\n",
- " \n",
- " | 8 | \n",
- " text | \n",
- " image_path | \n",
- " QRCodeConverter | \n",
- "
\n",
- " \n",
- " | 9 | \n",
- " text | \n",
- " text | \n",
- " AnsiAttackConverter | \n",
- "
\n",
- " \n",
- " | 10 | \n",
- " text | \n",
- " text | \n",
- " AsciiArtConverter | \n",
- "
\n",
- " \n",
- " | 11 | \n",
- " text | \n",
- " text | \n",
- " AsciiSmugglerConverter | \n",
- "
\n",
- " \n",
- " | 12 | \n",
- " text | \n",
- " text | \n",
- " AskToDecodeConverter | \n",
- "
\n",
- " \n",
- " | 13 | \n",
- " text | \n",
- " text | \n",
- " AtbashConverter | \n",
- "
\n",
- " \n",
- " | 14 | \n",
- " text | \n",
- " text | \n",
- " Base2048Converter | \n",
- "
\n",
- " \n",
- " | 15 | \n",
- " text | \n",
- " text | \n",
- " Base64Converter | \n",
- "
\n",
- " \n",
- " | 16 | \n",
- " text | \n",
- " text | \n",
- " BinAsciiConverter | \n",
- "
\n",
- " \n",
- " | 17 | \n",
- " text | \n",
- " text | \n",
- " BinaryConverter | \n",
- "
\n",
- " \n",
- " | 18 | \n",
- " text | \n",
- " text | \n",
- " BrailleConverter | \n",
- "
\n",
- " \n",
- " | 19 | \n",
- " text | \n",
- " text | \n",
- " CaesarConverter | \n",
- "
\n",
- " \n",
- " | 20 | \n",
- " text | \n",
- " text | \n",
- " CharSwapConverter | \n",
- "
\n",
- " \n",
- " | 21 | \n",
- " text | \n",
- " text | \n",
- " CharacterSpaceConverter | \n",
- "
\n",
- " \n",
- " | 22 | \n",
- " text | \n",
- " text | \n",
- " CodeChameleonConverter | \n",
- "
\n",
- " \n",
- " | 23 | \n",
- " text | \n",
- " text | \n",
- " ColloquialWordswapConverter | \n",
- "
\n",
- " \n",
- " | 24 | \n",
- " text | \n",
- " text | \n",
- " DenylistConverter | \n",
- "
\n",
- " \n",
- " | 25 | \n",
- " text | \n",
- " text | \n",
- " DiacriticConverter | \n",
- "
\n",
- " \n",
- " | 26 | \n",
- " text | \n",
- " text | \n",
- " EcojiConverter | \n",
- "
\n",
- " \n",
- " | 27 | \n",
- " text | \n",
- " text | \n",
- " EmojiConverter | \n",
- "
\n",
- " \n",
- " | 28 | \n",
- " text | \n",
- " text | \n",
- " FirstLetterConverter | \n",
- "
\n",
- " \n",
- " | 29 | \n",
- " text | \n",
- " text | \n",
- " FlipConverter | \n",
- "
\n",
- " \n",
- " | 30 | \n",
- " text | \n",
- " text | \n",
- " HumanInTheLoopConverter | \n",
- "
\n",
- " \n",
- " | 31 | \n",
- " text | \n",
- " text | \n",
- " InsertPunctuationConverter | \n",
- "
\n",
- " \n",
- " | 32 | \n",
- " text | \n",
- " text | \n",
- " LLMGenericTextConverter | \n",
- "
\n",
- " \n",
- " | 33 | \n",
- " text | \n",
- " text | \n",
- " LeetspeakConverter | \n",
- "
\n",
- " \n",
- " | 34 | \n",
- " text | \n",
- " text | \n",
- " MaliciousQuestionGeneratorConverter | \n",
- "
\n",
- " \n",
- " | 35 | \n",
- " text | \n",
- " text | \n",
- " MathObfuscationConverter | \n",
- "
\n",
- " \n",
- " | 36 | \n",
- " text | \n",
- " text | \n",
- " MathPromptConverter | \n",
- "
\n",
- " \n",
- " | 37 | \n",
- " text | \n",
- " text | \n",
- " MorseConverter | \n",
- "
\n",
- " \n",
- " | 38 | \n",
- " text | \n",
- " text | \n",
- " NatoConverter | \n",
- "
\n",
- " \n",
- " | 39 | \n",
- " text | \n",
- " text | \n",
- " NoiseConverter | \n",
- "
\n",
- " \n",
- " | 40 | \n",
- " text | \n",
- " text | \n",
- " PersuasionConverter | \n",
- "
\n",
- " \n",
- " | 41 | \n",
- " text | \n",
- " text | \n",
- " ROT13Converter | \n",
- "
\n",
- " \n",
- " | 42 | \n",
- " text | \n",
- " text | \n",
- " RandomCapitalLettersConverter | \n",
- "
\n",
- " \n",
- " | 43 | \n",
- " text | \n",
- " text | \n",
- " RandomTranslationConverter | \n",
- "
\n",
- " \n",
- " | 44 | \n",
- " text | \n",
- " text | \n",
- " RepeatTokenConverter | \n",
- "
\n",
- " \n",
- " | 45 | \n",
- " text | \n",
- " text | \n",
- " SearchReplaceConverter | \n",
- "
\n",
- " \n",
- " | 46 | \n",
- " text | \n",
- " text | \n",
- " SelectiveTextConverter | \n",
- "
\n",
- " \n",
- " | 47 | \n",
- " text | \n",
- " text | \n",
- " SneakyBitsSmugglerConverter | \n",
- "
\n",
- " \n",
- " | 48 | \n",
- " text | \n",
- " text | \n",
- " StringJoinConverter | \n",
- "
\n",
- " \n",
- " | 49 | \n",
- " text | \n",
- " text | \n",
- " SuffixAppendConverter | \n",
- "
\n",
- " \n",
- " | 50 | \n",
- " text | \n",
- " text | \n",
- " SuperscriptConverter | \n",
- "
\n",
- " \n",
- " | 51 | \n",
- " text | \n",
- " text | \n",
- " TemplateSegmentConverter | \n",
- "
\n",
- " \n",
- " | 52 | \n",
- " text | \n",
- " text | \n",
- " TenseConverter | \n",
- "
\n",
- " \n",
- " | 53 | \n",
- " text | \n",
- " text | \n",
- " TextJailbreakConverter | \n",
- "
\n",
- " \n",
- " | 54 | \n",
- " text | \n",
- " text | \n",
- " ToneConverter | \n",
- "
\n",
- " \n",
- " | 55 | \n",
- " text | \n",
- " text | \n",
- " ToxicSentenceGeneratorConverter | \n",
- "
\n",
- " \n",
- " | 56 | \n",
- " text | \n",
- " text | \n",
- " TranslationConverter | \n",
- "
\n",
- " \n",
- " | 57 | \n",
- " text | \n",
- " text | \n",
- " UnicodeConfusableConverter | \n",
- "
\n",
- " \n",
- " | 58 | \n",
- " text | \n",
- " text | \n",
- " UnicodeReplacementConverter | \n",
- "
\n",
- " \n",
- " | 59 | \n",
- " text | \n",
- " text | \n",
- " UnicodeSubstitutionConverter | \n",
- "
\n",
- " \n",
- " | 60 | \n",
- " text | \n",
- " text | \n",
- " UrlConverter | \n",
- "
\n",
- " \n",
- " | 61 | \n",
- " text | \n",
- " text | \n",
- " VariationConverter | \n",
- "
\n",
- " \n",
- " | 62 | \n",
- " text | \n",
- " text | \n",
- " VariationSelectorSmugglerConverter | \n",
- "
\n",
- " \n",
- " | 63 | \n",
- " text | \n",
- " text | \n",
- " ZalgoConverter | \n",
- "
\n",
- " \n",
- " | 64 | \n",
- " text | \n",
- " text | \n",
- " ZeroWidthConverter | \n",
- "
\n",
- " \n",
- " | 65 | \n",
- " text | \n",
- " url | \n",
- " PDFConverter | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " Input Modality Output Modality Converter\n",
- "0 audio_path audio_path AudioFrequencyConverter\n",
- "1 audio_path text AzureSpeechAudioToTextConverter\n",
- "2 image_path image_path AddTextImageConverter\n",
- "3 image_path image_path TransparencyAttackConverter\n",
- "4 image_path video_path AddImageVideoConverter\n",
- "5 image_path, url image_path ImageCompressionConverter\n",
- "6 text audio_path AzureSpeechTextToAudioConverter\n",
- "7 text image_path AddImageTextConverter\n",
- "8 text image_path QRCodeConverter\n",
- "9 text text AnsiAttackConverter\n",
- "10 text text AsciiArtConverter\n",
- "11 text text AsciiSmugglerConverter\n",
- "12 text text AskToDecodeConverter\n",
- "13 text text AtbashConverter\n",
- "14 text text Base2048Converter\n",
- "15 text text Base64Converter\n",
- "16 text text BinAsciiConverter\n",
- "17 text text BinaryConverter\n",
- "18 text text BrailleConverter\n",
- "19 text text CaesarConverter\n",
- "20 text text CharSwapConverter\n",
- "21 text text CharacterSpaceConverter\n",
- "22 text text CodeChameleonConverter\n",
- "23 text text ColloquialWordswapConverter\n",
- "24 text text DenylistConverter\n",
- "25 text text DiacriticConverter\n",
- "26 text text EcojiConverter\n",
- "27 text text EmojiConverter\n",
- "28 text text FirstLetterConverter\n",
- "29 text text FlipConverter\n",
- "30 text text HumanInTheLoopConverter\n",
- "31 text text InsertPunctuationConverter\n",
- "32 text text LLMGenericTextConverter\n",
- "33 text text LeetspeakConverter\n",
- "34 text text MaliciousQuestionGeneratorConverter\n",
- "35 text text MathObfuscationConverter\n",
- "36 text text MathPromptConverter\n",
- "37 text text MorseConverter\n",
- "38 text text NatoConverter\n",
- "39 text text NoiseConverter\n",
- "40 text text PersuasionConverter\n",
- "41 text text ROT13Converter\n",
- "42 text text RandomCapitalLettersConverter\n",
- "43 text text RandomTranslationConverter\n",
- "44 text text RepeatTokenConverter\n",
- "45 text text SearchReplaceConverter\n",
- "46 text text SelectiveTextConverter\n",
- "47 text text SneakyBitsSmugglerConverter\n",
- "48 text text StringJoinConverter\n",
- "49 text text SuffixAppendConverter\n",
- "50 text text SuperscriptConverter\n",
- "51 text text TemplateSegmentConverter\n",
- "52 text text TenseConverter\n",
- "53 text text TextJailbreakConverter\n",
- "54 text text ToneConverter\n",
- "55 text text ToxicSentenceGeneratorConverter\n",
- "56 text text TranslationConverter\n",
- "57 text text UnicodeConfusableConverter\n",
- "58 text text UnicodeReplacementConverter\n",
- "59 text text UnicodeSubstitutionConverter\n",
- "60 text text UrlConverter\n",
- "61 text text VariationConverter\n",
- "62 text text VariationSelectorSmugglerConverter\n",
- "63 text text ZalgoConverter\n",
- "64 text text ZeroWidthConverter\n",
- "65 text url PDFConverter"
- ]
- },
- "execution_count": null,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
+ "outputs": [],
"source": [
"import pandas as pd\n",
"\n",
@@ -610,24 +99,7 @@
"execution_count": null,
"id": "6",
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "text: gryy zr ubj gb phg qbja n gerr\n",
- "text: tell me HOw to Cut dowN A tRee\n",
- "text: \n",
- "_|_ _ |\\ |\\ _ |) _ _|_ _ _ _|_ _| _ _, _|_ ,_ _ _ \n",
- " | |/ |/ |/ /|/|/| |/ |/\\ / \\_| | |_ | / \\_ / | | | / | / \\_| | |_/|/| / | | / | |/ |/ \n",
- " |_/|_/|_/|_/ | | |_/|_/ | |/\\_/ \\/ \\/ |_/\\_/ \\__/ \\/|_/ |_/ \\/|_/\\_/ \\/ \\/ | |_/ \\/|_/ |_/ |/|_/|_/\n",
- " \n",
- "\n",
- "text: 0000000001110100 0000000001100101 0000000001101100 0000000001101100 0000000000100000 0000000001101101 0000000001100101 0000000000100000 0000000001101000 0000000001101111 0000000001110111 0000000000100000 0000000001110100 0000000001101111 0000000000100000 0000000001100011 0000000001110101 0000000001110100 0000000000100000 0000000001100100 0000000001101111 0000000001110111 0000000001101110 0000000000100000 0000000001100001 0000000000100000 0000000001110100 0000000001110010 0000000001100101 0000000001100101\n",
- "text: t m h t c d a t\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"from pyrit.prompt_converter import (\n",
" AsciiArtConverter,\n",
@@ -671,39 +143,7 @@
"execution_count": null,
"id": "8",
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'__type__': 'TextTarget', '__module__': 'pyrit.prompt_target.text_target'}: user: E-x-p-l-a-i-n t-o m-e t-h-e p-r-o-c-e-s-s o-f c-h-o-p-p-i-n-g d-o-w-n a t-r-e-e-.\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "[PromptSendingAttack (ID: 81e8da7f)] No response received on attempt 1 (likely filtered)\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
- "\u001b[1m\u001b[34m🔹 Turn 1 - USER\u001b[0m\n",
- "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
- "\u001b[36m Original:\u001b[0m\n",
- "\u001b[37m tell me how to cut down a tree\u001b[0m\n",
- "\n",
- "\u001b[36m Converted:\u001b[0m\n",
- "\u001b[37m E-x-p-l-a-i-n t-o m-e t-h-e p-r-o-c-e-s-s o-f c-h-o-p-p-i-n-g d-o-w-n a t-r-e-e-.\u001b[0m\n",
- "\n",
- "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"from pyrit.executor.attack import (\n",
" AttackConverterConfig,\n",
@@ -737,23 +177,85 @@
"printer = ConsoleAttackResultPrinter()\n",
"await printer.print_conversation_async(result=result) # type: ignore"
]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9",
+ "metadata": {},
+ "source": [
+ "## Response Converters\n",
+ "\n",
+ "So far, we've focused on **request converters** that transform prompts before sending them to the target. PyRIT also supports **response converters** that transform the target's response before returning it. This is useful in scenarios like:\n",
+ "\n",
+ "- Translating responses back to the original language after sending prompts in a different language\n",
+ "- Decoding encoded responses\n",
+ "- Normalizing or cleaning up response text\n",
+ "\n",
+ "Response converters use the same `PromptConverterConfiguration` class as request converters. They are configured via the `response_converters` parameter in `AttackConverterConfig`.\n",
+ "\n",
+ "### Translation Round-Trip Example\n",
+ "\n",
+ "A common use case is sending prompts in a different language to test how the target handles non-English input. In this example, we:\n",
+ "\n",
+ "1. Use a **request converter** to translate the prompt from English to French\n",
+ "2. Send the translated prompt to the target\n",
+ "3. Use a **response converter** to translate the response back to English"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "10",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyrit.executor.attack import (\n",
+ " AttackConverterConfig,\n",
+ " ConsoleAttackResultPrinter,\n",
+ " PromptSendingAttack,\n",
+ ")\n",
+ "from pyrit.prompt_converter import TranslationConverter\n",
+ "from pyrit.prompt_normalizer import PromptConverterConfiguration\n",
+ "from pyrit.prompt_target import OpenAIChatTarget\n",
+ "\n",
+ "objective = \"What is the capital of France?\"\n",
+ "\n",
+ "# Create an LLM target for the converters\n",
+ "converter_target = OpenAIChatTarget()\n",
+ "\n",
+ "# Create an LLM target to send prompts to\n",
+ "prompt_target = OpenAIChatTarget()\n",
+ "\n",
+ "# Request converter: translate English to French\n",
+ "request_converter = TranslationConverter(converter_target=converter_target, language=\"French\")\n",
+ "request_converter_config = PromptConverterConfiguration(converters=[request_converter])\n",
+ "\n",
+ "# Response converter: translate response back to English\n",
+ "response_converter = TranslationConverter(converter_target=converter_target, language=\"English\")\n",
+ "response_converter_config = PromptConverterConfiguration(converters=[response_converter])\n",
+ "\n",
+ "# Configure the attack with both request and response converters\n",
+ "converter_config = AttackConverterConfig(\n",
+ " request_converters=[request_converter_config],\n",
+ " response_converters=[response_converter_config],\n",
+ ")\n",
+ "\n",
+ "attack = PromptSendingAttack(\n",
+ " objective_target=prompt_target,\n",
+ " attack_converter_config=converter_config,\n",
+ ")\n",
+ "\n",
+ "result = await attack.execute_async(objective=objective) # type: ignore\n",
+ "\n",
+ "# Print the conversation showing both original and converted values\n",
+ "printer = ConsoleAttackResultPrinter()\n",
+ "await printer.print_conversation_async(result=result) # type: ignore"
+ ]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.14"
}
},
"nbformat": 4,
diff --git a/doc/code/converters/0_converters.py b/doc/code/converters/0_converters.py
index 6be75251d..5731a3982 100644
--- a/doc/code/converters/0_converters.py
+++ b/doc/code/converters/0_converters.py
@@ -136,3 +136,65 @@
printer = ConsoleAttackResultPrinter()
await printer.print_conversation_async(result=result) # type: ignore
+
+# %% [markdown]
+# ## Response Converters
+#
+# So far, we've focused on **request converters** that transform prompts before sending them to the target. PyRIT also supports **response converters** that transform the target's response before returning it. This is useful in scenarios like:
+#
+# - Translating responses back to the original language after sending prompts in a different language
+# - Decoding encoded responses
+# - Normalizing or cleaning up response text
+#
+# Response converters use the same `PromptConverterConfiguration` class as request converters. They are configured via the `response_converters` parameter in `AttackConverterConfig`.
+#
+# ### Translation Round-Trip Example
+#
+# A common use case is sending prompts in a different language to test how the target handles non-English input. In this example, we:
+#
+# 1. Use a **request converter** to translate the prompt from English to French
+# 2. Send the translated prompt to the target
+# 3. Use a **response converter** to translate the response back to English
+
+# %%
+from pyrit.executor.attack import (
+ AttackConverterConfig,
+ ConsoleAttackResultPrinter,
+ PromptSendingAttack,
+)
+from pyrit.prompt_converter import TranslationConverter
+from pyrit.prompt_normalizer import PromptConverterConfiguration
+from pyrit.prompt_target import OpenAIChatTarget
+
+objective = "What is the capital of France?"
+
+# Create an LLM target for the converters
+converter_target = OpenAIChatTarget()
+
+# Create an LLM target to send prompts to
+prompt_target = OpenAIChatTarget()
+
+# Request converter: translate English to French
+request_converter = TranslationConverter(converter_target=converter_target, language="French")
+request_converter_config = PromptConverterConfiguration(converters=[request_converter])
+
+# Response converter: translate response back to English
+response_converter = TranslationConverter(converter_target=converter_target, language="English")
+response_converter_config = PromptConverterConfiguration(converters=[response_converter])
+
+# Configure the attack with both request and response converters
+converter_config = AttackConverterConfig(
+ request_converters=[request_converter_config],
+ response_converters=[response_converter_config],
+)
+
+attack = PromptSendingAttack(
+ objective_target=prompt_target,
+ attack_converter_config=converter_config,
+)
+
+result = await attack.execute_async(objective=objective) # type: ignore
+
+# Print the conversation showing both original and converted values
+printer = ConsoleAttackResultPrinter()
+await printer.print_conversation_async(result=result) # type: ignore