Repository navigation
Description
Describe the bug
The KernelPlugin.from_directory method says (emphasis mine):
A .py file is parsed and a plugin created,
the functions within as [sic] then combined with any other functions found.
The python file needs to contain a class with one or more kernel_function decorated methods.
If this class has a__init__method, it will be called with the arguments provided in the
class_init_argumentsdictionary, the key needs to be the same as the name of the class,
with the value being a dictionary of arguments to pass to the class (using kwargs).
If a .py file is present in the directory, and it has a class with no kernel_function decorated methods, the class will still be initialised when loading the plugin. Since the class is not expected to be initialised, no class_init_arguments are set and it will cause an error.
The use case for this is lazily calling kernel.add_plugin(plugin_name=Path(__file__).parent, parent_directory=Path(__file__).parent.parent) to pick up a prompts.yaml file that is sitting next to the code file. The code file that calls this is also picked up as a plugin, and causes an error since its __init__ parameters were not passed.
I can and will use KernelFunctionFromPrompt.from_yaml instead, since I only want to load the yaml file, but the behaviour still surprised me. I do understand that checking the class definition for decorators before initialising it might be too much/not possible.
To Reproduce
Run the following script:
from pathlib import Path
from semantic_kernel import Kernel
class Example:
def __init__(self, kernel):
this_directory = Path(__file__).parent
kernel.add_plugin(plugin_name=this_directory, parent_directory=this_directory.parent)
if __name__ == "__main__":
Example(Kernel())Expected behavior
Since the class has no kernel_function decorated methods, I would expect it to be skipped entirely.
Screenshots
Stack trace:
Traceback (most recent call last):
File "C:\Users\...\test.py", line 10, in <module>
Example(Kernel())
File "C:\Users\...\test.py", line 7, in __init__
kernel.add_plugin(plugin_name=this_directory, parent_directory=this_directory.parent)
File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_function_extension.py", line 95, in add_plugin
self.plugins[plugin_name] = KernelPlugin.from_directory(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_plugin.py", line 325, in from_directory
cls.from_python_file(
File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_plugin.py", line 401, in from_python_file
instance = getattr(module, name)(**class_init_arguments.get(name, {}) if class_init_arguments else {})
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Example.__init__() missing 1 required positional argument: 'kernel'
Platform
- OS: Windows
- IDE: gVim + PowerShell
- Language: Python
- Source: pip package version 1.19.0
Additional context
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
[-]Bug: KernelPlugin.from_directory initialises Python classes with no @kernel_function methods[/-][+]Python: Bug: KernelPlugin.from_directory initialises Python classes with no @kernel_function methods[/+]moonbox3 commentedon Jan 23, 2025
Thanks for reporting Billy Brown (@Druid-of-Luhn). I agree with your expected behavior. We'll have a look.
moonbox3 commentedon Jan 24, 2025
Here's my proposed fix: #10286
Python: Improve handling for kernel plugin from file. (#10286)
Python: Improve handling for kernel plugin from file. (microsoft#10286)