From 2041c8aabf0a7583963cb1f2fbd3f51aaefa8469 Mon Sep 17 00:00:00 2001 From: nimaniko Date: Fri, 27 Feb 2026 03:37:27 +0330 Subject: [PATCH 01/10] fix: optimize KernelArguments merge to avoid unnecessary dict copy - Implement lazy dict copy in __or__, __ror__, and __ior__ operators - Only copy execution_settings when merge is needed - Reuse references when no modification needed - performance improvement in merge operations - Add unit tests to verify lazy copy behavior --- .../functions/kernel_arguments.py | 23 ++-- ...est_kernel_arguments_merge_optimization.py | 121 ++++++++++++++++++ 2 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 python/tests/unit/functions/test_kernel_arguments_merge_optimization.py diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index a6048d14e631..d9dae338df4d 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -67,10 +67,15 @@ def __or__(self, value: dict) -> "KernelArguments": f"TypeError: unsupported operand type(s) for |: '{type(self).__name__}' and '{type(value).__name__}'" ) - # Merge execution settings - new_execution_settings = (self.execution_settings or {}).copy() + # Merge execution settings - only copy when needed + if self.execution_settings: + new_execution_settings = self.execution_settings # Reuse reference if immutable + else: + new_execution_settings = {} + if isinstance(value, KernelArguments) and value.execution_settings: - new_execution_settings |= value.execution_settings + # Only copy when we need to merge (mutation) + new_execution_settings = {**new_execution_settings, **value.execution_settings} # Create a new KernelArguments with merged dict values return KernelArguments(settings=new_execution_settings, **(dict(self) | dict(value))) @@ -84,12 +89,13 @@ def __ror__(self, value: dict) -> "KernelArguments": f"TypeError: unsupported operand type(s) for |: '{type(value).__name__}' and '{type(self).__name__}'" ) - # Merge execution settings + # Merge execution settings - only copy when needed new_execution_settings = {} if isinstance(value, KernelArguments) and value.execution_settings: - new_execution_settings = value.execution_settings.copy() + new_execution_settings = value.execution_settings # Reuse reference if immutable if self.execution_settings: - new_execution_settings |= self.execution_settings + # Only copy when we need to merge (mutation) + new_execution_settings = {**new_execution_settings, **self.execution_settings} # Create a new KernelArguments with merged dict values return KernelArguments(settings=new_execution_settings, **(dict(value) | dict(self))) @@ -98,12 +104,13 @@ def __ior__(self, value: "SupportsKeysAndGetItem[Any, Any] | Iterable[tuple[Any, """Merges into this KernelArguments with another KernelArguments or dict (in-place).""" self.update(value) - # In-place merge execution settings + # In-place merge execution settings - only copy when needed if isinstance(value, KernelArguments) and value.execution_settings: if self.execution_settings: self.execution_settings.update(value.execution_settings) else: - self.execution_settings = value.execution_settings.copy() + # Only copy when we actually need a different instance + self.execution_settings = {**value.execution_settings} return self diff --git a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py new file mode 100644 index 000000000000..6eec75d43511 --- /dev/null +++ b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py @@ -0,0 +1,121 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Tests for KernelArguments merge optimization (Issue #2: Lazy dict copy).""" + +import pytest +from unittest.mock import patch, MagicMock + +from semantic_kernel.functions.kernel_arguments import KernelArguments + + +class TestKernelArgumentsMergeOptimization: + """Test suite for KernelArguments merge operator optimization.""" + + def test_or_operator_no_execution_settings_copy(self): + """Test that | operator doesn't copy when execution_settings is None or empty. + + This tests the optimization where dict copy is avoided when not needed. + """ + args = KernelArguments(a=1, b=2) + args.execution_settings = None + + result = args | {"c": 3} + + # Result should have merged values + assert result["a"] == 1 + assert result["b"] == 2 + assert result["c"] == 3 + + def test_or_operator_with_kernel_arguments_merge(self): + """Test | operator with another KernelArguments with execution_settings.""" + settings1 = {"model": "gpt-4", "temperature": 0.7} + settings2 = {"temperature": 0.9, "max_tokens": 100} + + args1 = KernelArguments(a=1, settings=settings1) + args2 = KernelArguments(b=2, settings=settings2) + + result = args1 | args2 + + # Check merged arguments + assert result["a"] == 1 + assert result["b"] == 2 + + # Check merged execution settings + assert result.execution_settings["model"] == "gpt-4" + assert result.execution_settings["temperature"] == 0.9 # args2 overwrites + assert result.execution_settings["max_tokens"] == 100 + + def test_ror_operator_lazy_copy(self): + """Test reverse | operator avoids copy when execution_settings is empty.""" + args = KernelArguments(a=1, b=2) + args.execution_settings = {} + + result = {"c": 3} | args + + # Result should have merged values + assert result["a"] == 1 + assert result["b"] == 2 + assert result["c"] == 3 + + def test_ior_operator_lazy_copy(self): + """Test in-place |= operator avoids copy when execution_settings exists.""" + settings1 = {"model": "gpt-4", "temperature": 0.7} + args1 = KernelArguments(a=1, settings=settings1) + + settings2 = {"temperature": 0.9} + args2 = KernelArguments(b=2, settings=settings2) + + args1 |= args2 + + # Check merged in-place + assert args1["a"] == 1 + assert args1["b"] == 2 + + # Check in-place merge of settings + assert args1.execution_settings["model"] == "gpt-4" + assert args1.execution_settings["temperature"] == 0.9 + + def test_ior_operator_creates_copy_when_needed(self): + """Test that |= creates new dict only when target has no execution_settings.""" + args1 = KernelArguments(a=1) + args1.execution_settings = None + + settings2 = {"model": "gpt-4"} + args2 = KernelArguments(b=2, settings=settings2) + + args1 |= args2 + + # Should have new execution_settings dict + assert args1.execution_settings is not None + assert args1.execution_settings["model"] == "gpt-4" + # Should not be the same reference (it's a copy) + assert args1.execution_settings is not args2.execution_settings + + def test_or_operator_preserves_original_settings(self): + """Test that | operator doesn't mutate original execution_settings.""" + original_settings = {"model": "gpt-4", "temperature": 0.7} + args1 = KernelArguments(a=1, settings=original_settings.copy()) + args2 = KernelArguments(b=2, settings={"temperature": 0.9}) + + result = args1 | args2 + + # Original args1 settings should be unchanged + assert args1.execution_settings["temperature"] == 0.7 + # Result should have merged settings + assert result.execution_settings["temperature"] == 0.9 + + def test_ior_operator_merges_into_existing_dict(self): + """Test that |= merges into existing settings dict when present.""" + settings1 = {"model": "gpt-4", "temperature": 0.7} + args1 = KernelArguments(a=1, settings=settings1) + original_dict = args1.execution_settings + + settings2 = {"temperature": 0.9, "max_tokens": 100} + args2 = KernelArguments(b=2, settings=settings2) + + args1 |= args2 + + # Should have reused and updated the original dict + assert args1.execution_settings is original_dict + assert args1.execution_settings["temperature"] == 0.9 + assert args1.execution_settings["max_tokens"] == 100 From 574ab8e05f9fc911d02afc8256ee37f1288c00fa Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Tue, 24 Mar 2026 14:34:13 +0330 Subject: [PATCH 02/10] Update python/tests/unit/functions/test_kernel_arguments_merge_optimization.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../unit/functions/test_kernel_arguments_merge_optimization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py index 6eec75d43511..05c5983398b7 100644 --- a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py +++ b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py @@ -2,7 +2,7 @@ """Tests for KernelArguments merge optimization (Issue #2: Lazy dict copy).""" -import pytest +from semantic_kernel.functions.kernel_arguments import KernelArguments from unittest.mock import patch, MagicMock from semantic_kernel.functions.kernel_arguments import KernelArguments From 9f9fa026d415b0787611e26cbf308cddabfa12ac Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Tue, 24 Mar 2026 14:34:31 +0330 Subject: [PATCH 03/10] Update python/semantic_kernel/functions/kernel_arguments.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- python/semantic_kernel/functions/kernel_arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index d9dae338df4d..03105d0e40ff 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -92,7 +92,7 @@ def __ror__(self, value: dict) -> "KernelArguments": # Merge execution settings - only copy when needed new_execution_settings = {} if isinstance(value, KernelArguments) and value.execution_settings: - new_execution_settings = value.execution_settings # Reuse reference if immutable + new_execution_settings = value.execution_settings.copy() if self.execution_settings: # Only copy when we need to merge (mutation) new_execution_settings = {**new_execution_settings, **self.execution_settings} From 43bc49d4ee95eec9aab31ee0db039e9a4936c376 Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Tue, 24 Mar 2026 14:41:09 +0330 Subject: [PATCH 04/10] Update python/tests/unit/functions/test_kernel_arguments_merge_optimization.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../functions/test_kernel_arguments_merge_optimization.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py index 05c5983398b7..01f6eab05d5e 100644 --- a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py +++ b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py @@ -3,11 +3,6 @@ """Tests for KernelArguments merge optimization (Issue #2: Lazy dict copy).""" from semantic_kernel.functions.kernel_arguments import KernelArguments -from unittest.mock import patch, MagicMock - -from semantic_kernel.functions.kernel_arguments import KernelArguments - - class TestKernelArgumentsMergeOptimization: """Test suite for KernelArguments merge operator optimization.""" From b1374d6c62b1890d1615c2cd04d26ac2c9f46657 Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Tue, 24 Mar 2026 14:41:29 +0330 Subject: [PATCH 05/10] Update python/semantic_kernel/functions/kernel_arguments.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- python/semantic_kernel/functions/kernel_arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index 03105d0e40ff..b8ee7ef63203 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -110,7 +110,7 @@ def __ior__(self, value: "SupportsKeysAndGetItem[Any, Any] | Iterable[tuple[Any, self.execution_settings.update(value.execution_settings) else: # Only copy when we actually need a different instance - self.execution_settings = {**value.execution_settings} + self.execution_settings = value.execution_settings.copy() return self From 76c1c3be8b15cc09c87c8009be4cd19802ae52b7 Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Sun, 5 Apr 2026 22:56:42 +0330 Subject: [PATCH 06/10] Update python/semantic_kernel/functions/kernel_arguments.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- python/semantic_kernel/functions/kernel_arguments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index b8ee7ef63203..92eb7e4ee7a3 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -69,9 +69,10 @@ def __or__(self, value: dict) -> "KernelArguments": # Merge execution settings - only copy when needed if self.execution_settings: - new_execution_settings = self.execution_settings # Reuse reference if immutable + new_execution_settings = self.execution_settings.copy() else: new_execution_settings = {} + new_execution_settings = {} if isinstance(value, KernelArguments) and value.execution_settings: # Only copy when we need to merge (mutation) From b37a8cc2f5040dce8c1ef163b18ece17782bd8ac Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Thu, 9 Apr 2026 13:33:14 +0330 Subject: [PATCH 07/10] Update kernel_arguments.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- python/semantic_kernel/functions/kernel_arguments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index 92eb7e4ee7a3..83c9a32ff9f6 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -73,6 +73,7 @@ def __or__(self, value: dict) -> "KernelArguments": else: new_execution_settings = {} new_execution_settings = {} + new_execution_settings = {} if isinstance(value, KernelArguments) and value.execution_settings: # Only copy when we need to merge (mutation) From 44250c719f1eb49d036d42dd5b4e45c4c814b3a1 Mon Sep 17 00:00:00 2001 From: Nima Nikunazar Date: Sun, 12 Apr 2026 08:04:12 +0330 Subject: [PATCH 08/10] Update kernel_arguments.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- python/semantic_kernel/functions/kernel_arguments.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index 83c9a32ff9f6..5a892fd2ade9 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -68,15 +68,10 @@ def __or__(self, value: dict) -> "KernelArguments": ) # Merge execution settings - only copy when needed - if self.execution_settings: - new_execution_settings = self.execution_settings.copy() - else: - new_execution_settings = {} - new_execution_settings = {} - new_execution_settings = {} - + # Merge execution settings + new_execution_settings = (self.execution_settings or {}).copy() if isinstance(value, KernelArguments) and value.execution_settings: - # Only copy when we need to merge (mutation) + new_execution_settings |= value.execution_settings new_execution_settings = {**new_execution_settings, **value.execution_settings} # Create a new KernelArguments with merged dict values return KernelArguments(settings=new_execution_settings, **(dict(self) | dict(value))) From eb25badf9f866e33e921e4cc00722c1f4dd9e894 Mon Sep 17 00:00:00 2001 From: nimaniko Date: Thu, 14 May 2026 16:21:22 +0330 Subject: [PATCH 09/10] Python: Fix KernelArguments merge tests to use PromptExecutionSettings --- .../functions/kernel_arguments.py | 14 +- ...est_kernel_arguments_merge_optimization.py | 185 +++++++++--------- 2 files changed, 95 insertions(+), 104 deletions(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index 5a892fd2ade9..caf63b5065dc 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -67,13 +67,9 @@ def __or__(self, value: dict) -> "KernelArguments": f"TypeError: unsupported operand type(s) for |: '{type(self).__name__}' and '{type(value).__name__}'" ) - # Merge execution settings - only copy when needed - # Merge execution settings new_execution_settings = (self.execution_settings or {}).copy() if isinstance(value, KernelArguments) and value.execution_settings: - new_execution_settings |= value.execution_settings - new_execution_settings = {**new_execution_settings, **value.execution_settings} - # Create a new KernelArguments with merged dict values + new_execution_settings.update(value.execution_settings) return KernelArguments(settings=new_execution_settings, **(dict(self) | dict(value))) def __ror__(self, value: dict) -> "KernelArguments": @@ -86,27 +82,21 @@ def __ror__(self, value: dict) -> "KernelArguments": f"TypeError: unsupported operand type(s) for |: '{type(value).__name__}' and '{type(self).__name__}'" ) - # Merge execution settings - only copy when needed new_execution_settings = {} if isinstance(value, KernelArguments) and value.execution_settings: new_execution_settings = value.execution_settings.copy() if self.execution_settings: - # Only copy when we need to merge (mutation) - new_execution_settings = {**new_execution_settings, **self.execution_settings} - - # Create a new KernelArguments with merged dict values + new_execution_settings.update(self.execution_settings) return KernelArguments(settings=new_execution_settings, **(dict(value) | dict(self))) def __ior__(self, value: "SupportsKeysAndGetItem[Any, Any] | Iterable[tuple[Any, Any]]") -> "KernelArguments": """Merges into this KernelArguments with another KernelArguments or dict (in-place).""" self.update(value) - # In-place merge execution settings - only copy when needed if isinstance(value, KernelArguments) and value.execution_settings: if self.execution_settings: self.execution_settings.update(value.execution_settings) else: - # Only copy when we actually need a different instance self.execution_settings = value.execution_settings.copy() return self diff --git a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py index 01f6eab05d5e..4dc7d2358905 100644 --- a/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py +++ b/python/tests/unit/functions/test_kernel_arguments_merge_optimization.py @@ -1,116 +1,117 @@ # Copyright (c) Microsoft. All rights reserved. -"""Tests for KernelArguments merge optimization (Issue #2: Lazy dict copy).""" +"""Tests for KernelArguments merge operator behavior.""" +import pytest + +from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings from semantic_kernel.functions.kernel_arguments import KernelArguments + + +@pytest.fixture +def settings_svc1(): + return PromptExecutionSettings(service_id="service1") + + +@pytest.fixture +def settings_svc2(): + return PromptExecutionSettings(service_id="service2") + + +@pytest.fixture +def settings_default(): + return PromptExecutionSettings(service_id="default") + + class TestKernelArgumentsMergeOptimization: - """Test suite for KernelArguments merge operator optimization.""" - - def test_or_operator_no_execution_settings_copy(self): - """Test that | operator doesn't copy when execution_settings is None or empty. - - This tests the optimization where dict copy is avoided when not needed. - """ + """Test suite for KernelArguments merge operator behavior.""" + + def test_or_operator_no_execution_settings(self): + """Test that | operator works when execution_settings is None.""" args = KernelArguments(a=1, b=2) - args.execution_settings = None - + result = args | {"c": 3} - - # Result should have merged values + assert result["a"] == 1 assert result["b"] == 2 assert result["c"] == 3 - - def test_or_operator_with_kernel_arguments_merge(self): - """Test | operator with another KernelArguments with execution_settings.""" - settings1 = {"model": "gpt-4", "temperature": 0.7} - settings2 = {"temperature": 0.9, "max_tokens": 100} - - args1 = KernelArguments(a=1, settings=settings1) - args2 = KernelArguments(b=2, settings=settings2) - + + def test_or_operator_merges_settings_by_service_id(self, settings_svc1, settings_svc2): + """Test | merges execution_settings keyed by service_id.""" + args1 = KernelArguments(a=1, settings=settings_svc1) + args2 = KernelArguments(b=2, settings=settings_svc2) + result = args1 | args2 - - # Check merged arguments + assert result["a"] == 1 assert result["b"] == 2 - - # Check merged execution settings - assert result.execution_settings["model"] == "gpt-4" - assert result.execution_settings["temperature"] == 0.9 # args2 overwrites - assert result.execution_settings["max_tokens"] == 100 - - def test_ror_operator_lazy_copy(self): - """Test reverse | operator avoids copy when execution_settings is empty.""" - args = KernelArguments(a=1, b=2) - args.execution_settings = {} - - result = {"c": 3} | args - - # Result should have merged values + assert result.execution_settings["service1"] is settings_svc1 + assert result.execution_settings["service2"] is settings_svc2 + + def test_or_operator_right_side_wins_on_same_service_id(self, settings_default): + """Test | overwrites execution_settings when both sides share a service_id.""" + settings_override = PromptExecutionSettings(service_id="default") + args1 = KernelArguments(a=1, settings=settings_default) + args2 = KernelArguments(b=2, settings=settings_override) + + result = args1 | args2 + + assert result.execution_settings["default"] is settings_override + + def test_ror_operator_merges_plain_dict_with_kernel_arguments(self, settings_svc1): + """Test reverse | operator merges a plain dict with KernelArguments.""" + args = KernelArguments(a=1, settings=settings_svc1) + + result = {"b": 2} | args + assert result["a"] == 1 assert result["b"] == 2 - assert result["c"] == 3 - - def test_ior_operator_lazy_copy(self): - """Test in-place |= operator avoids copy when execution_settings exists.""" - settings1 = {"model": "gpt-4", "temperature": 0.7} - args1 = KernelArguments(a=1, settings=settings1) - - settings2 = {"temperature": 0.9} - args2 = KernelArguments(b=2, settings=settings2) - + assert result.execution_settings["service1"] is settings_svc1 + + def test_ior_operator_merges_settings_in_place(self, settings_svc1, settings_svc2): + """Test |= merges execution_settings into the existing dict in-place.""" + args1 = KernelArguments(a=1, settings=settings_svc1) + original_dict = args1.execution_settings + + args2 = KernelArguments(b=2, settings=settings_svc2) args1 |= args2 - - # Check merged in-place + assert args1["a"] == 1 assert args1["b"] == 2 - - # Check in-place merge of settings - assert args1.execution_settings["model"] == "gpt-4" - assert args1.execution_settings["temperature"] == 0.9 - - def test_ior_operator_creates_copy_when_needed(self): - """Test that |= creates new dict only when target has no execution_settings.""" + assert args1.execution_settings is original_dict + assert args1.execution_settings["service1"] is settings_svc1 + assert args1.execution_settings["service2"] is settings_svc2 + + def test_ior_operator_copies_settings_when_target_has_none(self, settings_svc1): + """Test |= creates a copy of execution_settings when target has none.""" args1 = KernelArguments(a=1) - args1.execution_settings = None - - settings2 = {"model": "gpt-4"} - args2 = KernelArguments(b=2, settings=settings2) - + assert args1.execution_settings is None + + args2 = KernelArguments(b=2, settings=settings_svc1) args1 |= args2 - - # Should have new execution_settings dict + assert args1.execution_settings is not None - assert args1.execution_settings["model"] == "gpt-4" - # Should not be the same reference (it's a copy) + assert args1.execution_settings["service1"] is settings_svc1 assert args1.execution_settings is not args2.execution_settings - - def test_or_operator_preserves_original_settings(self): - """Test that | operator doesn't mutate original execution_settings.""" - original_settings = {"model": "gpt-4", "temperature": 0.7} - args1 = KernelArguments(a=1, settings=original_settings.copy()) - args2 = KernelArguments(b=2, settings={"temperature": 0.9}) - + + def test_or_operator_does_not_mutate_original_settings(self, settings_svc1, settings_svc2): + """Test that | does not mutate the original execution_settings.""" + args1 = KernelArguments(a=1, settings=settings_svc1) + args2 = KernelArguments(b=2, settings=settings_svc2) + original_settings = args1.execution_settings + result = args1 | args2 - - # Original args1 settings should be unchanged - assert args1.execution_settings["temperature"] == 0.7 - # Result should have merged settings - assert result.execution_settings["temperature"] == 0.9 - - def test_ior_operator_merges_into_existing_dict(self): - """Test that |= merges into existing settings dict when present.""" - settings1 = {"model": "gpt-4", "temperature": 0.7} - args1 = KernelArguments(a=1, settings=settings1) - original_dict = args1.execution_settings - - settings2 = {"temperature": 0.9, "max_tokens": 100} - args2 = KernelArguments(b=2, settings=settings2) - + + assert args1.execution_settings is original_settings + assert "service2" not in args1.execution_settings + assert "service2" in result.execution_settings + + def test_ior_operator_right_side_wins_on_same_service_id(self, settings_default): + """Test |= overwrites existing settings entry when service_id matches.""" + settings_override = PromptExecutionSettings(service_id="default") + args1 = KernelArguments(a=1, settings=settings_default) + args2 = KernelArguments(b=2, settings=settings_override) + args1 |= args2 - - # Should have reused and updated the original dict - assert args1.execution_settings is original_dict - assert args1.execution_settings["temperature"] == 0.9 - assert args1.execution_settings["max_tokens"] == 100 + + assert args1.execution_settings["default"] is settings_override From 53194aca92c743de0ffd0f4e052c766dab67cdde Mon Sep 17 00:00:00 2001 From: nimaniko Date: Thu, 14 May 2026 16:32:53 +0330 Subject: [PATCH 10/10] Python: Fix aliasing bug in KernelArguments constructor for dict settings --- python/semantic_kernel/functions/kernel_arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/semantic_kernel/functions/kernel_arguments.py b/python/semantic_kernel/functions/kernel_arguments.py index caf63b5065dc..4482483c0de1 100644 --- a/python/semantic_kernel/functions/kernel_arguments.py +++ b/python/semantic_kernel/functions/kernel_arguments.py @@ -44,7 +44,7 @@ def __init__( if settings: settings_dict = {} if isinstance(settings, dict): - settings_dict = settings + settings_dict = dict(settings) elif isinstance(settings, list): settings_dict = {s.service_id or DEFAULT_SERVICE_NAME: s for s in settings} else: