From 3cf70f88d26dab5c5d6d3a956dcad4195ec624a3 Mon Sep 17 00:00:00 2001 From: Santosh Todkar Date: Thu, 5 Nov 2026 10:43:19 +0530 Subject: [PATCH] Include ordered parameter bindings in TemplateSegmentConverter identity TemplateSegmentConverter uses the ordered parameters list to assign consecutive prompt segments via dict(zip(parameters, segments)). Reversing that list changes the rendered text, but the identifier previously included only the template text hash, parameter count, and seed. Both configurations got the same registry name, preventing registration of both. Add ordered_parameters to the identifier params dict so that different orderings produce distinct identities. Fixes #3902 Co-Authored-By: Claude Opus 4.6 (1M context) --- pyrit/converter/template_segment_converter.py | 7 +++ .../test_template_segment_converter.py | 53 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/pyrit/converter/template_segment_converter.py b/pyrit/converter/template_segment_converter.py index 7140c51e09..adf11c71d8 100644 --- a/pyrit/converter/template_segment_converter.py +++ b/pyrit/converter/template_segment_converter.py @@ -75,14 +75,21 @@ def _build_identifier(self) -> ComponentIdentifier: """ Build identifier with template parameters. + The ordered parameter list is included so that two converters using + the same template but different parameter orderings (which changes + which prompt segment fills which placeholder) produce distinct + identities. + Returns: ComponentIdentifier: The identifier for this converter. """ template_hash = hashlib.sha256(str(self.prompt_template.value).encode("utf-8")).hexdigest()[:16] + ordered_params = list(self.prompt_template.parameters or []) return self._create_identifier( params={ "template_hash": template_hash, "number_parameters": self._number_parameters, + "ordered_parameters": ordered_params, "seed": self._seed, } ) diff --git a/tests/unit/converter/test_template_segment_converter.py b/tests/unit/converter/test_template_segment_converter.py index 73b85b5b9b..d4d35750d1 100644 --- a/tests/unit/converter/test_template_segment_converter.py +++ b/tests/unit/converter/test_template_segment_converter.py @@ -168,3 +168,56 @@ async def test_template_segment_converter_invalid_input_type(): converter = TemplateSegmentConverter() with pytest.raises(ValueError, match="Input type not supported"): await converter.convert_async(prompt="test", input_type="image_path") + + + +def test_template_segment_converter_different_param_order_different_identity(): + """Different parameter orderings must produce different identifiers. + + Reversing the parameter list changes which prompt segment fills which + placeholder, so the two configurations are semantically different and + must not share the same identity. + """ + template_text = "LEFT[{{left}}] RIGHT[{{right}}]" + converter_a = TemplateSegmentConverter( + prompt_template=SeedPrompt(value=template_text, parameters=["left", "right"]), + seed=7, + ) + converter_b = TemplateSegmentConverter( + prompt_template=SeedPrompt(value=template_text, parameters=["right", "left"]), + seed=7, + ) + id_a = converter_a.get_identifier() + id_b = converter_b.get_identifier() + assert id_a != id_b, ( + "Converters with different parameter orderings must have different identifiers" + ) + + +def test_template_segment_converter_same_param_order_same_identity(): + """Identical configuration must produce the same identifier.""" + template_text = "LEFT[{{left}}] RIGHT[{{right}}]" + converter_a = TemplateSegmentConverter( + prompt_template=SeedPrompt(value=template_text, parameters=["left", "right"]), + seed=7, + ) + converter_b = TemplateSegmentConverter( + prompt_template=SeedPrompt(value=template_text, parameters=["left", "right"]), + seed=7, + ) + id_a = converter_a.get_identifier() + id_b = converter_b.get_identifier() + assert id_a == id_b, ( + "Converters with the same configuration must have the same identifier" + ) + + +def test_template_segment_converter_identity_includes_ordered_parameters(): + """The identifier params must contain the ordered_parameters key.""" + template_text = "A[{{a}}] B[{{b}}] C[{{c}}]" + converter = TemplateSegmentConverter( + prompt_template=SeedPrompt(value=template_text, parameters=["a", "b", "c"]), + ) + identifier = converter.get_identifier() + assert "ordered_parameters" in identifier.params + assert identifier.params["ordered_parameters"] == ["a", "b", "c"]