diff --git a/python/semantic_kernel/contents/binary_content.py b/python/semantic_kernel/contents/binary_content.py index d6214bf331f1..72ecc0633fee 100644 --- a/python/semantic_kernel/contents/binary_content.py +++ b/python/semantic_kernel/contents/binary_content.py @@ -114,10 +114,13 @@ def data_uri(self) -> str: @data_uri.setter def data_uri(self, value: str): """Set the data uri.""" - if not self._data_uri: - self._data_uri = DataUri.from_data_uri(value, self.default_mime_type) - else: - self._data_uri.update_data(value) + data_uri = DataUri.from_data_uri(value, self.default_mime_type) + if self._data_uri: + for key, parameter in self._data_uri.parameters.items(): + current_value = self.metadata.get(key) + if isinstance(current_value, str) and current_value == parameter: + self.metadata.pop(key, None) + self._data_uri = data_uri self.metadata.update(self._data_uri.parameters) @property diff --git a/python/tests/unit/contents/test_binary_content.py b/python/tests/unit/contents/test_binary_content.py index 1daacb1f72d3..fa6fc947a156 100644 --- a/python/tests/unit/contents/test_binary_content.py +++ b/python/tests/unit/contents/test_binary_content.py @@ -95,6 +95,60 @@ def test_update_data_uri(): assert binary.metadata == {"foo": "bar"} +@pytest.mark.parametrize( + "initial_data", + [ + {"data_uri": "data:text/plain;foo=old;base64,b2xk"}, + {"data": b"old", "mime_type": "text/plain"}, + {"data": array([1, 2, 3]), "data_format": "base64", "mime_type": "text/plain"}, + ], + ids=["base64", "bytes", "array"], +) +@pytest.mark.parametrize( + "replacement,parameters", + [ + ("data:image/jpeg;foo=new;base64,bmV3", {"foo": "new"}), + ("data:image/jpeg;foo=new;,new", {"foo": "new"}), + ("data:image/jpeg;base64,bmV3", {}), + ("data:image/jpeg;,new", {}), + ], + ids=["base64", "text", "base64-no-parameters", "text-no-parameters"], +) +def test_replace_data_uri(initial_data, replacement, parameters): + binary = BinaryContent(**initial_data, metadata={"source": "test"}) + + binary.data_uri = replacement + + assert binary.data == b"new" + assert binary.mime_type == "image/jpeg" + assert binary.metadata == {"source": "test", **parameters} + assert BinaryContent(data_uri=binary.data_uri).data == b"new" + + +@pytest.mark.parametrize("metadata_value", ["caller", None, array([1, 2]), {"custom": True}]) +def test_replace_data_uri_preserves_updated_caller_metadata(metadata_value): + binary = BinaryContent(data_uri="data:text/plain;foo=old;base64,b2xk") + binary.metadata["foo"] = metadata_value + + binary.data_uri = "data:image/jpeg;base64,bmV3" + + assert set(binary.metadata) == {"foo"} + assert binary.metadata["foo"] is metadata_value + assert binary.data == b"new" + + +def test_replace_data_uri_invalid_preserves_existing_data(): + binary = BinaryContent(data_uri="data:image/jpeg;foo=old;base64,b2xk", metadata={"source": "test"}) + original_data_uri = binary.data_uri + + with pytest.raises(ContentInitializationError): + binary.data_uri = "data:image/jpeg;base64,invalid!" + + assert binary.data == b"old" + assert binary.data_uri == original_data_uri + assert binary.metadata == {"source": "test", "foo": "old"} + + def test_to_str_uri(): binary = BinaryContent(uri="http://test_uri") assert str(binary) == "http://test_uri/" diff --git a/python/tests/unit/contents/test_image_content.py b/python/tests/unit/contents/test_image_content.py index 7d84a1746271..d25b46138cad 100644 --- a/python/tests/unit/contents/test_image_content.py +++ b/python/tests/unit/contents/test_image_content.py @@ -48,6 +48,17 @@ def test_to_str_data(): assert str(image) == "data:image/jpeg;base64,dGVzdF9kYXRh" +def test_replace_image_data_uri(): + image = ImageContent(data=b"old_image", mime_type="image/jpeg", data_format="base64") + replacement = "data:image/png;base64,bmV3X2ltYWdl" + + image.data_uri = replacement + + assert image.data == b"new_image" + assert image.mime_type == "image/png" + assert image.to_dict() == {"type": "image_url", "image_url": {"url": replacement}} + + @pytest.mark.parametrize("image", test_cases) def test_element_roundtrip(image): element = image.to_element()