Skip to content

Commit c6d024e

Browse files
UmerkhanGolandazclauderomanlutz
authored
FIX: rejoin words with the configured word_split_separator (#2593)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Roman Lutz <romanlutz13@gmail.com>
1 parent 48384c0 commit c6d024e

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

‎pyrit/converter/word_level_converter.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,19 @@ def join_words(self, words: list[str]) -> str:
8282
"""
8383
Provide a way for subclasses to override the default behavior of joining words.
8484
85+
Words are rejoined with the same separator they were split on, so a custom
86+
``word_split_separator`` survives the round trip. A ``None`` separator splits on
87+
arbitrary whitespace, which has no single representation to restore, so those
88+
words are joined with a space.
89+
8590
Args:
8691
words (list[str]): List of words to join.
8792
8893
Returns:
8994
str: The joined string.
9095
"""
91-
return " ".join(words)
96+
separator = " " if self._word_split_separator is None else self._word_split_separator
97+
return separator.join(words)
9298

9399
async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult:
94100
"""

‎tests/unit/converter/test_word_level_converter.py‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ async def convert_word_async(self, word: str) -> str:
3030
return word.upper()
3131

3232

33+
class SeparatorWordLevelConverter(WordLevelConverter):
34+
"""Exposes word_split_separator, mirroring converters like EmojiConverter."""
35+
36+
def __init__(self, *, word_split_separator=" ", word_selection_strategy=None):
37+
super().__init__(
38+
word_selection_strategy=word_selection_strategy,
39+
word_split_separator=word_split_separator,
40+
)
41+
42+
async def convert_word_async(self, word: str) -> str:
43+
return word.upper()
44+
45+
3346
class TestWordLevelConverter:
3447
async def test_convert_async_all_mode(self):
3548
converter = SimpleWordLevelConverter()
@@ -120,3 +133,30 @@ async def test_default_is_all_words(self):
120133
assert isinstance(converter._word_selection_strategy, AllWordsSelectionStrategy)
121134
result = await converter.convert_async(prompt="test prompt")
122135
assert result.output_text == "TEST PROMPT"
136+
137+
138+
class TestWordLevelConverterSeparator:
139+
@pytest.mark.parametrize("separator", [",", "|", "-", "::"])
140+
async def test_custom_separator_is_preserved(self, separator):
141+
converter = SeparatorWordLevelConverter(word_split_separator=separator)
142+
prompt = separator.join(["alpha", "beta", "gamma"])
143+
result = await converter.convert_async(prompt=prompt)
144+
assert result.output_text == separator.join(["ALPHA", "BETA", "GAMMA"])
145+
146+
async def test_default_space_separator_unchanged(self):
147+
converter = SeparatorWordLevelConverter()
148+
result = await converter.convert_async(prompt="alpha beta gamma")
149+
assert result.output_text == "ALPHA BETA GAMMA"
150+
151+
async def test_none_separator_joins_with_space(self):
152+
converter = SeparatorWordLevelConverter(word_split_separator=None)
153+
result = await converter.convert_async(prompt="alpha\tbeta\ngamma")
154+
assert result.output_text == "ALPHA BETA GAMMA"
155+
156+
async def test_custom_separator_preserved_with_partial_selection(self):
157+
converter = SeparatorWordLevelConverter(
158+
word_split_separator=",",
159+
word_selection_strategy=WordIndexSelectionStrategy(indices=[0, 2]),
160+
)
161+
result = await converter.convert_async(prompt="alpha,beta,gamma")
162+
assert result.output_text == "ALPHA,beta,GAMMA"

0 commit comments

Comments
 (0)