From 7582776367823b85cd22de5de0812579eac40f1b Mon Sep 17 00:00:00 2001 From: stefan521 Date: Tue, 28 May 2024 23:04:00 +0100 Subject: [PATCH] Python: Verify that plugin names are unique. --- .../functions/kernel_function_extension.py | 26 +++++++++++-------- python/tests/unit/kernel/test_kernel.py | 10 ++++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/python/semantic_kernel/functions/kernel_function_extension.py b/python/semantic_kernel/functions/kernel_function_extension.py index 0bb872a377be..6b05f3451841 100644 --- a/python/semantic_kernel/functions/kernel_function_extension.py +++ b/python/semantic_kernel/functions/kernel_function_extension.py @@ -8,7 +8,7 @@ from pydantic import Field, field_validator from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings -from semantic_kernel.exceptions import KernelFunctionNotFoundError, KernelPluginNotFoundError +from semantic_kernel.exceptions import KernelFunctionNotFoundError, KernelPluginNotFoundError, PluginInvalidNameError from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata from semantic_kernel.functions.kernel_plugin import KernelPlugin from semantic_kernel.kernel_pydantic import KernelBaseModel @@ -81,24 +81,28 @@ def add_plugin( ValidationError: If a KernelPlugin needs to be created, but it is not valid. """ + name = plugin.name if isinstance(plugin, KernelPlugin) else plugin_name + + if name in self.plugins: + raise PluginInvalidNameError(f"A plugin with the name {name} already exists.") if isinstance(plugin, KernelPlugin): - self.plugins[plugin.name] = plugin - return self.plugins[plugin.name] - if not plugin_name: - raise ValueError("plugin_name must be provided if a plugin is not supplied.") + self.plugins[name] = plugin + return self.plugins[name] + if not name: + raise PluginInvalidNameError("plugin_name must be provided if a plugin is not supplied.") if plugin: - self.plugins[plugin_name] = KernelPlugin.from_object( - plugin_name=plugin_name, plugin_instance=plugin, description=description + self.plugins[name] = KernelPlugin.from_object( + plugin_name=name, plugin_instance=plugin, description=description ) - return self.plugins[plugin_name] + return self.plugins[name] if plugin is None and parent_directory is not None: - self.plugins[plugin_name] = KernelPlugin.from_directory( - plugin_name=plugin_name, + self.plugins[name] = KernelPlugin.from_directory( + plugin_name=name, parent_directory=parent_directory, description=description, class_init_arguments=class_init_arguments, ) - return self.plugins[plugin_name] + return self.plugins[name] raise ValueError("plugin or parent_directory must be provided.") def add_plugins(self, plugins: list[KernelPlugin] | dict[str, KernelPlugin | object]) -> None: diff --git a/python/tests/unit/kernel/test_kernel.py b/python/tests/unit/kernel/test_kernel.py index 45f8fc9c46f7..fdab38832d03 100644 --- a/python/tests/unit/kernel/test_kernel.py +++ b/python/tests/unit/kernel/test_kernel.py @@ -18,6 +18,7 @@ from semantic_kernel.exceptions import ( KernelFunctionAlreadyExistsError, KernelServiceNotFoundError, + PluginInvalidNameError, ServiceInvalidTypeError, ) from semantic_kernel.exceptions.kernel_exceptions import KernelFunctionNotFoundError, KernelPluginNotFoundError @@ -185,7 +186,7 @@ def test_plugin_no_plugin(kernel: Kernel): def test_plugin_name_error(kernel: Kernel): - with pytest.raises(ValueError): + with pytest.raises(PluginInvalidNameError, match="plugin_name must be provided if a plugin is not supplied."): kernel.add_plugin(" ", None) @@ -197,6 +198,13 @@ def test_plugins_add_plugins(kernel: Kernel): assert len(kernel.plugins) == 2 +def test_plugins_name_unique(kernel: Kernel): + with pytest.raises(PluginInvalidNameError, match="A plugin with the name TestPlugin already exists."): + plugin1 = KernelPlugin(name="TestPlugin") + plugin2 = KernelPlugin(name="TestPlugin") + kernel.add_plugins([plugin1, plugin2]) + + def test_add_function_from_prompt(kernel: Kernel): prompt = """ Write a short story about two Corgis on an adventure.