Files
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

539 lines
20 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
import os
from collections.abc import Callable
from typing import Any
import pytest
from pytest import raises
from semantic_kernel.connectors.ai import PromptExecutionSettings
from semantic_kernel.connectors.openapi_plugin.openapi_parser import OpenApiParser
from semantic_kernel.exceptions.function_exceptions import PluginInitializationError
from semantic_kernel.functions import kernel_function
from semantic_kernel.functions.kernel_function import KernelFunction
from semantic_kernel.functions.kernel_function_from_method import KernelFunctionFromMethod
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
from semantic_kernel.functions.kernel_plugin import KernelPlugin
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
@pytest.fixture
def mock_function() -> Callable[..., Any]:
@kernel_function
def mock_function(input: str) -> None:
pass
return mock_function
# region Init
def test_init_fail_no_name():
with raises(TypeError):
KernelPlugin(description="A unit test plugin")
def test_init_with_no_functions():
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert not plugin.functions
def test_init_with_kernel_functions(mock_function):
function_plugin_name = "MockPlugin"
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=function_plugin_name)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=native_function)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
assert native_function.plugin_name == function_plugin_name
def test_init_with_kernel_functions_list(mock_function):
function_plugin_name = "MockPlugin"
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=function_plugin_name)
plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=[native_function]
)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
assert native_function.plugin_name == function_plugin_name
def test_init_with_list_other_fail():
with raises(ValueError):
KernelPlugin(name="test_plugin", description="A unit test plugin", functions=["str"])
def test_init_with_other_fail():
with raises(ValueError):
KernelPlugin(name="test_plugin", description="A unit test plugin", functions="str")
def test_init_with_kernel_functions_dict(mock_function):
function_plugin_name = "MockPlugin"
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=function_plugin_name)
plugin = KernelPlugin(
name=expected_plugin_name,
description=expected_plugin_description,
functions={native_function.name: native_function},
)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
assert native_function.plugin_name == function_plugin_name
def test_init_with_callable_functions(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=mock_function)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
def test_init_with_callable_functions_list(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=[mock_function])
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
def test_init_with_kernel_plugin(mock_function):
function_plugin_name = "MockPlugin"
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=function_plugin_name)
first_plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=native_function
)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=first_plugin)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
assert native_function.plugin_name == function_plugin_name
def test_init_with_kernel_plugin_list(mock_function):
function_plugin_name = "MockPlugin"
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=function_plugin_name)
first_plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=native_function
)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=[first_plugin])
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].plugin_name == expected_plugin_name
assert native_function.plugin_name == function_plugin_name
def test_init_exposes_the_native_function_it_contains(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name="MockPlugin")
plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=[native_function]
)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].name == native_function.name
def test_init_with_prompt_function():
req_settings = PromptExecutionSettings(extension_data={"max_tokens": 2000, "temperature": 0.7, "top_p": 0.8})
prompt = "Use this input: {{$request}}"
prompt_template_config = PromptTemplateConfig(
template=prompt,
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings={"default": req_settings},
)
expected_plugin_name = "test_plugin"
expected_function_name = "mock_function"
semantic_function = KernelFunctionFromPrompt(
prompt=prompt,
prompt_template_config=prompt_template_config,
plugin_name=expected_plugin_name,
function_name=expected_function_name,
)
expected_plugin_description = "A unit test plugin"
plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=[semantic_function]
)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"] == semantic_function
def test_init_with_both_function_types(mock_function):
req_settings = PromptExecutionSettings(extension_data={"max_tokens": 2000, "temperature": 0.7, "top_p": 0.8})
prompt = "Use this input: {{$request}}"
prompt_template_config = PromptTemplateConfig(
template=prompt,
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings={"default": req_settings},
)
expected_plugin_name = "test_plugin"
expected_function_name = "prompt_function"
semantic_function = KernelFunction.from_prompt(
prompt=prompt,
prompt_template_config=prompt_template_config,
plugin_name=expected_plugin_name,
function_name=expected_function_name,
)
native_function = KernelFunctionFromMethod(method=mock_function, plugin_name="MockPlugin")
# Add both types to the default kernel plugin
expected_plugin_description = "A unit test plugin"
plugin = KernelPlugin(
name=expected_plugin_name,
description=expected_plugin_description,
functions=[semantic_function, native_function],
)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 2
for func in [semantic_function, native_function]:
assert func.name in plugin
assert plugin[func.name].name == func.name
def test_init_with_same_function_names(mock_function):
req_settings = PromptExecutionSettings(extension_data={"max_tokens": 2000, "temperature": 0.7, "top_p": 0.8})
prompt = "Use this input: {{$request}}"
prompt_template_config = PromptTemplateConfig(
template=prompt,
name="chat",
template_format="semantic-kernel",
input_variables=[
InputVariable(name="request", description="The user input", is_required=True),
],
execution_settings={"default": req_settings},
)
expected_plugin_name = "test_plugin"
expected_function_name = "mock_function"
semantic_function = KernelFunction.from_prompt(
prompt=prompt,
prompt_template_config=prompt_template_config,
plugin_name=expected_plugin_name,
function_name=expected_function_name,
)
native_function = KernelFunctionFromMethod(method=mock_function, plugin_name="MockPlugin")
plugin = KernelPlugin(name=expected_plugin_name, functions=[semantic_function, native_function])
assert len(plugin.functions) == 1
# region Dict-like methods
def test_set_item(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=expected_plugin_name)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description)
plugin["mock_function"] = native_function
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].metadata == native_function.metadata
function = plugin["mock_function"]
assert function.name == "mock_function"
def test_set(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=expected_plugin_name)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description)
plugin.set("mock_function", native_function)
assert plugin.name == expected_plugin_name
assert plugin.description == expected_plugin_description
assert len(plugin.functions) == 1
assert plugin["mock_function"].metadata == native_function.metadata
function = plugin.get("mock_function", None)
assert function.name == "mock_function"
function2 = plugin.get("mock_function2", None)
assert function2 is None
def test_set_default(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=expected_plugin_name)
native_function2 = KernelFunction.from_method(method=mock_function, plugin_name="other")
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description)
native_function == plugin.setdefault("mock_function", native_function)
native_function == plugin.setdefault("mock_function", native_function2)
assert len(plugin.functions) == 1
with raises(ValueError):
plugin.setdefault("mock_function2", None)
def test_update(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=expected_plugin_name)
native_function2 = KernelFunction.from_method(method=mock_function, plugin_name="p2")
native_function3 = KernelFunction.from_method(method=mock_function, plugin_name="p3")
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description)
plugin.update(mock_function=native_function)
assert len(plugin.functions) == 1
plugin.update([native_function2])
assert len(plugin.functions) == 1
plugin.update({"mock_function": native_function3})
assert len(plugin.functions) == 1
plugin2 = KernelPlugin(name="p2", description="p2")
plugin2.update(plugin)
assert len(plugin2.functions) == 1
plugin2.update([plugin])
assert len(plugin2.functions) == 1
with raises(TypeError):
plugin.update(1)
with raises(TypeError):
plugin.update(1, 2)
def test_iter():
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
func1 = KernelFunctionFromPrompt("test1", expected_plugin_name, None, "test prompt")
func2 = KernelFunctionFromPrompt("test1", expected_plugin_name, None, "test prompt")
func3 = KernelFunctionFromPrompt("test1", expected_plugin_name, None, "test prompt")
plugin = KernelPlugin(
name=expected_plugin_name, description=expected_plugin_description, functions=[func1, func2, func3]
)
for func in plugin:
assert func.metadata in [func1.metadata, func2.metadata, func3.metadata]
# region Properties
def test_get_functions_metadata(mock_function):
expected_plugin_name = "test_plugin"
expected_plugin_description = "A unit test plugin"
native_function = KernelFunction.from_method(method=mock_function, plugin_name=expected_plugin_name)
plugin = KernelPlugin(name=expected_plugin_name, description=expected_plugin_description, functions=native_function)
metadatas = plugin.get_functions_metadata()
assert len(metadatas) == 1
assert metadatas[0] == native_function.metadata
# region Class Methods
def test_from_directory():
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_plugins")
plugin = KernelPlugin.from_directory("TestMixedPlugin", plugins_directory)
assert plugin is not None
assert len(plugin.functions) == 3
assert plugin.name == "TestMixedPlugin"
assert plugin.get("TestFunctionYaml") is not None
assert plugin.get("echoAsync") is not None
assert plugin.get("TestFunction") is not None
def test_from_directory_parent_directory_does_not_exist():
# import plugins
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_plugins_fail")
# path to plugins directory
with raises(PluginInitializationError, match="Plugin directory does not exist"):
KernelPlugin.from_directory("TestPlugin", plugins_directory)
def test_from_python_fail():
with raises(PluginInitializationError, match="No class found in file"):
KernelPlugin.from_python_file(
"TestNativePluginNoClass",
os.path.join(
os.path.dirname(__file__),
"../../assets",
"test_native_plugins",
"TestNativePluginNoClass",
"native_function.py",
),
)
def test_from_python_in_directory_fail():
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_native_plugins")
# path to plugins directory
with raises(PluginInitializationError, match="No functions found in folder"):
KernelPlugin.from_directory("TestNativePluginNoClass", plugins_directory)
def test_from_yaml_in_directory_fail():
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_plugins")
# path to plugins directory
with raises(PluginInitializationError, match="No functions found in folder"):
KernelPlugin.from_directory("TestFunctionBadYaml", plugins_directory)
def test_from_directory_other():
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_plugins")
# path to plugins directory
with raises(PluginInitializationError, match="No functions found in folder"):
KernelPlugin.from_directory("TestNoFunction", plugins_directory)
def test_from_directory_with_args():
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets", "test_native_plugins")
# path to plugins directory
plugin = KernelPlugin.from_directory(
"TestNativePluginArgs",
plugins_directory,
class_init_arguments={"TestNativeEchoBotPlugin": {"static_input": "prefix "}},
)
result = plugin["echo"].method(text="test")
assert result == "prefix test"
def test_from_object_function(decorated_native_function):
plugin = KernelPlugin.from_object("TestPlugin", {"getLightStatusFunc": decorated_native_function})
assert plugin is not None
assert len(plugin.functions) == 1
assert plugin.functions.get("getLightStatus") is not None
def test_from_object_class(custom_plugin_class):
plugin = KernelPlugin.from_object("TestPlugin", custom_plugin_class())
assert plugin is not None
assert len(plugin.functions) == 1
assert plugin.functions.get("getLightStatus") is not None
def test_from_openapi():
openapi_spec_file = os.path.join(
os.path.dirname(__file__), "../../assets/test_plugins", "TestOpenAPIPlugin", "akv-openapi.yaml"
)
plugin = KernelPlugin.from_openapi(
plugin_name="TestOpenAPIPlugin",
openapi_document_path=openapi_spec_file,
)
assert plugin is not None
assert plugin.name == "TestOpenAPIPlugin"
assert plugin.functions.get("GetSecret") is not None
assert plugin.functions.get("SetSecret") is not None
def test_custom_spec_from_openapi():
openapi_spec_file = os.path.join(
os.path.dirname(__file__), "../../assets/test_plugins", "TestOpenAPIPlugin", "akv-openapi.yaml"
)
parser = OpenApiParser()
openapi_spec = parser.parse(openapi_spec_file)
plugin = KernelPlugin.from_openapi(
plugin_name="TestOpenAPIPlugin",
openapi_parsed_spec=openapi_spec,
)
assert plugin is not None
assert plugin.name == "TestOpenAPIPlugin"
assert plugin.functions.get("GetSecret") is not None
assert plugin.functions.get("SetSecret") is not None
def test_from_openapi_missing_document_and_parsed_spec_throws():
with raises(PluginInitializationError):
KernelPlugin.from_openapi(
plugin_name="TestOpenAPIPlugin",
openapi_document_path=None,
)
# region Static Methods
def test_parse_or_copy_fail():
with raises(ValueError):
KernelPlugin._parse_or_copy(None, "test")