From 84b9347f8e8f99494f42e96681a00394c0c8ba31 Mon Sep 17 00:00:00 2001 From: Bolor Date: Thu, 16 May 2024 13:25:01 -0700 Subject: [PATCH] initial commit adding changed converters --- pyrit/prompt_converter/ascii_art_converter.py | 10 +++++++++- pyrit/prompt_converter/base64_converter.py | 11 ++++++++++- pyrit/prompt_converter/leetspeak_converter.py | 12 +++++++++++- pyrit/prompt_converter/prompt_converter.py | 13 +++++++++++++ .../random_capital_letters_converter.py | 10 ++++++++++ pyrit/prompt_converter/rot13_converter.py | 11 +++++++++++ .../prompt_converter/search_replace_converter.py | 9 +++++++++ pyrit/prompt_converter/string_join_converter.py | 12 +++++++++++- .../unicode_confusable_converter.py | 9 +++++++++ pyrit/prompt_converter/unicode_sub_converter.py | 10 ++++++++++ pyrit/prompt_converter/variation_converter.py | 15 +++++++++++++-- 11 files changed, 116 insertions(+), 6 deletions(-) diff --git a/pyrit/prompt_converter/ascii_art_converter.py b/pyrit/prompt_converter/ascii_art_converter.py index b8b03c54d..7f0916c82 100644 --- a/pyrit/prompt_converter/ascii_art_converter.py +++ b/pyrit/prompt_converter/ascii_art_converter.py @@ -1,5 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -11,8 +13,14 @@ class AsciiArtConverter(PromptConverter): def __init__(self, font="rand"): self.font_value = font - def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Converter that uses art to convert strings to ASCII art. This can sometimes bypass LLM filters diff --git a/pyrit/prompt_converter/base64_converter.py b/pyrit/prompt_converter/base64_converter.py index 0b7e3f559..23e58d9b6 100644 --- a/pyrit/prompt_converter/base64_converter.py +++ b/pyrit/prompt_converter/base64_converter.py @@ -2,6 +2,8 @@ # Licensed under the MIT license. import base64 +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -9,6 +11,13 @@ class Base64Converter(PromptConverter): def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that just base64 encodes the prompt """ @@ -19,6 +28,6 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver encoded_bytes = base64.b64encode(string_bytes) return ConverterResult(output_text=encoded_bytes.decode("utf-8"), output_type="text") - + def input_supported(self, input_type: PromptDataType) -> bool: return input_type == "text" diff --git a/pyrit/prompt_converter/leetspeak_converter.py b/pyrit/prompt_converter/leetspeak_converter.py index 7f37ffdca..bff69d76b 100644 --- a/pyrit/prompt_converter/leetspeak_converter.py +++ b/pyrit/prompt_converter/leetspeak_converter.py @@ -1,9 +1,12 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import random +import concurrent.futures +import asyncio + from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult -import random class LeetspeakConverter(PromptConverter): @@ -25,6 +28,13 @@ def __init__(self) -> None: } def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter to generate leatspeak version of a prompt. Since there are multiple character variations, this is non-deterministic. diff --git a/pyrit/prompt_converter/prompt_converter.py b/pyrit/prompt_converter/prompt_converter.py index cf10dc250..ab60f264a 100644 --- a/pyrit/prompt_converter/prompt_converter.py +++ b/pyrit/prompt_converter/prompt_converter.py @@ -34,6 +34,19 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver str: The converted representation of the prompts. """ pass + + @abc.abstractmethod + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Converts the given prompts into a different representation + + Args: + prompt: The prompt to be converted. + + Returns: + str: The converted representation of the prompts. + """ + pass @abc.abstractmethod def input_supported(self, input_type: PromptDataType) -> bool: diff --git a/pyrit/prompt_converter/random_capital_letters_converter.py b/pyrit/prompt_converter/random_capital_letters_converter.py index 8e8d740c6..92d20dae2 100644 --- a/pyrit/prompt_converter/random_capital_letters_converter.py +++ b/pyrit/prompt_converter/random_capital_letters_converter.py @@ -3,6 +3,8 @@ import random import logging +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -64,6 +66,13 @@ def string_to_upper_case_by_percentage(self, percentage, prompt): return "".join(output) def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that converts the prompt to capital letters via a percentage . """ @@ -73,3 +82,4 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver output = self.string_to_upper_case_by_percentage(self.percentage, prompt) return ConverterResult(output_text=output, output_type="text") + diff --git a/pyrit/prompt_converter/rot13_converter.py b/pyrit/prompt_converter/rot13_converter.py index ac7337eab..e5f291963 100644 --- a/pyrit/prompt_converter/rot13_converter.py +++ b/pyrit/prompt_converter/rot13_converter.py @@ -2,13 +2,23 @@ # Licensed under the MIT license. import codecs +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult class ROT13Converter(PromptConverter): + def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that just ROT13 encodes the prompts """ @@ -17,5 +27,6 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver return ConverterResult(output_text=codecs.encode(prompt, "rot13"), output_type="text") + def input_supported(self, input_type: PromptDataType) -> bool: return input_type == "text" diff --git a/pyrit/prompt_converter/search_replace_converter.py b/pyrit/prompt_converter/search_replace_converter.py index e20c76420..cdbe648cb 100644 --- a/pyrit/prompt_converter/search_replace_converter.py +++ b/pyrit/prompt_converter/search_replace_converter.py @@ -1,5 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -18,6 +20,13 @@ def __init__(self, old_value: str, new_value: str) -> None: self.new_value = new_value def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that just replaces character in string with a chosen new character diff --git a/pyrit/prompt_converter/string_join_converter.py b/pyrit/prompt_converter/string_join_converter.py index ca0840f0e..b1b1ceaa4 100644 --- a/pyrit/prompt_converter/string_join_converter.py +++ b/pyrit/prompt_converter/string_join_converter.py @@ -1,6 +1,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import concurrent.futures +import asyncio + from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -9,8 +12,15 @@ class StringJoinConverter(PromptConverter): def __init__(self, *, join_value="-"): self.join_value = join_value - + def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that uses str join for letters between. E.g. with a `-` it converts a prompt of `test` to `t-e-s-t` diff --git a/pyrit/prompt_converter/unicode_confusable_converter.py b/pyrit/prompt_converter/unicode_confusable_converter.py index cdd117edb..23cba0f49 100644 --- a/pyrit/prompt_converter/unicode_confusable_converter.py +++ b/pyrit/prompt_converter/unicode_confusable_converter.py @@ -2,6 +2,8 @@ # Licensed under the MIT license. import random +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -14,6 +16,13 @@ def __init__(self, deterministic: bool = False): self.deterministic = deterministic def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Converts the given prompts into things that look similar, but are actually different, using Unicode confusables -- e.g., replacing a Latin 'a' with a Cyrillic 'а'. diff --git a/pyrit/prompt_converter/unicode_sub_converter.py b/pyrit/prompt_converter/unicode_sub_converter.py index 1bf416143..0bfe3fba9 100644 --- a/pyrit/prompt_converter/unicode_sub_converter.py +++ b/pyrit/prompt_converter/unicode_sub_converter.py @@ -1,6 +1,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import concurrent.futures +import asyncio + from pyrit.models import PromptDataType from pyrit.prompt_converter import PromptConverter, ConverterResult @@ -10,6 +13,13 @@ def __init__(self, *, start_value=0xE0000): self.startValue = start_value def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Simple converter that just encodes the prompt using any unicode starting point. Default is to use invisible flag emoji characters. diff --git a/pyrit/prompt_converter/variation_converter.py b/pyrit/prompt_converter/variation_converter.py index f73febbbd..b2e617550 100644 --- a/pyrit/prompt_converter/variation_converter.py +++ b/pyrit/prompt_converter/variation_converter.py @@ -2,6 +2,8 @@ import logging import uuid import pathlib +import concurrent.futures +import asyncio from pyrit.models import PromptDataType from pyrit.models import PromptRequestPiece, PromptRequestResponse @@ -33,8 +35,15 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: Promp prompt_template.apply_custom_metaprompt_parameters(number_iterations=str(self.number_variations)) ) - @retry(stop=stop_after_attempt(2), wait=wait_fixed(1)) def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Deprecated. Use async_convert instead. + """ + pool = concurrent.futures.ThreadPoolExecutor() + return pool.submit(asyncio.run, self.async_convert(prompt=prompt, input_type=input_type)).result() + + @retry(stop=stop_after_attempt(2), wait=wait_fixed(1)) + async def async_convert(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Generates variations of the input prompts using the converter target. Parameters: @@ -42,6 +51,7 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver Return: target_responses: list of prompt variations generated by the converter target """ + if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -69,7 +79,8 @@ def convert(self, *, prompt: str, input_type: PromptDataType = "text") -> Conver ] ) - response_msg = self.converter_target.send_prompt(prompt_request=request).request_pieces[0].converted_value + response = await self.converter_target.send_prompt_async(prompt_request=request) + response_msg = response.request_pieces[0].converted_value try: ret_text = json.loads(response_msg)[0]