From e4db1b43e7db3f3432b5fcf200c954028a7222aa Mon Sep 17 00:00:00 2001 From: po-et <42566883+po-et@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:24:38 +0800 Subject: [PATCH 1/2] fix(python): parse replacement binary data URIs --- .../contents/binary_content.py | 5 +-- .../unit/contents/test_binary_content.py | 35 +++++++++++++++++++ .../tests/unit/contents/test_image_content.py | 11 ++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/python/semantic_kernel/contents/binary_content.py b/python/semantic_kernel/contents/binary_content.py index d6214bf331f1..c24ad3d0bcd3 100644 --- a/python/semantic_kernel/contents/binary_content.py +++ b/python/semantic_kernel/contents/binary_content.py @@ -114,10 +114,7 @@ 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) + self._data_uri = DataUri.from_data_uri(value, self.default_mime_type) 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..d4f56a76f823 100644 --- a/python/tests/unit/contents/test_binary_content.py +++ b/python/tests/unit/contents/test_binary_content.py @@ -95,6 +95,41 @@ 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", + ["data:image/jpeg;foo=new;base64,bmV3", "data:image/jpeg;foo=new;,new"], + ids=["base64", "text"], +) +def test_replace_data_uri(initial_data, replacement): + 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", "foo": "new"} + assert BinaryContent(data_uri=binary.data_uri).data == b"new" + + +def test_replace_data_uri_invalid_preserves_existing_data(): + binary = BinaryContent(data_uri="data:image/jpeg;base64,b2xk") + + with pytest.raises(ContentInitializationError): + binary.data_uri = "data:image/jpeg;base64,invalid!" + + assert binary.data == b"old" + assert binary.data_uri == "data:image/jpeg;base64,b2xk" + + 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() From 7ab43b303a0cdfb6b578de0898e215cda28aa9ce Mon Sep 17 00:00:00 2001 From: po-et <42566883+po-et@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:18:26 +0800 Subject: [PATCH 2/2] fix(python): clear stale parameters when replacing data URIs --- .../contents/binary_content.py | 8 ++++- .../unit/contents/test_binary_content.py | 33 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/python/semantic_kernel/contents/binary_content.py b/python/semantic_kernel/contents/binary_content.py index c24ad3d0bcd3..72ecc0633fee 100644 --- a/python/semantic_kernel/contents/binary_content.py +++ b/python/semantic_kernel/contents/binary_content.py @@ -114,7 +114,13 @@ def data_uri(self) -> str: @data_uri.setter def data_uri(self, value: str): """Set the data uri.""" - self._data_uri = DataUri.from_data_uri(value, self.default_mime_type) + 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 d4f56a76f823..fa6fc947a156 100644 --- a/python/tests/unit/contents/test_binary_content.py +++ b/python/tests/unit/contents/test_binary_content.py @@ -105,29 +105,48 @@ def test_update_data_uri(): ids=["base64", "bytes", "array"], ) @pytest.mark.parametrize( - "replacement", - ["data:image/jpeg;foo=new;base64,bmV3", "data:image/jpeg;foo=new;,new"], - ids=["base64", "text"], + "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): +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", "foo": "new"} + 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;base64,b2xk") + 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 == "data:image/jpeg;base64,b2xk" + assert binary.data_uri == original_data_uri + assert binary.metadata == {"source": "test", "foo": "old"} def test_to_str_uri():