-
Hey! I've been working with SK for some time, and I'm having a bit of trouble handling the import of plugin functions. Here's what I've encountered: When you want to import a plugin with prompt functions (formerly known as semantic functions), you can use: kernel.ImportPluginFromPromptDirectory("Plugins/SomeSpecificPlugin", "PluginName"); On the other hand, if you want to import a plugin with native functions, a simple way to do that is: kernel.ImportPluginFromType<Plugins.MyNativeFunctions>("PluginName"); That’s pretty straightforward. However, the problem arises when you want to combine native and prompt functions in the same plugin. I found that there is no easy way to do this. Additionally, if you want to separate native functions into different classes, you end up creating a new plugin for each native function due to the same issue. I’d appreciate some advice if I’m missing something here, but I believe the goal of plugins should be to group related native and semantic functions. My Approach kernel.CustomImportPlugin("PluginName"); (For simplicity, assume CustomKernelExtensions.cs is placed in the "Plugins" directory, and the plugin name must match the specific plugin folder name.) Here’s the code for the CustomKernelExtensions class: internal static class CustomKernelExtensions
{
private static IEnumerable<KernelFunction> CreatePromptFunctions(Kernel kernel, string pluginName)
{
var scanDirectory = Path.Combine(Environment.CurrentDirectory, nameof(Plugins), pluginName);
// Creating plugin from directory (but not importing it into the kernel)
return kernel.CreatePluginFromPromptDirectory(scanDirectory);
}
private static IEnumerable<KernelFunction> CreateNativeFunctions(Kernel kernel, string pluginName)
{
List<KernelFunction> result = new List<KernelFunction>();
string namespaceToGetPlugins = typeof(CustomKernelExtensions).Namespace!;
namespaceToGetPlugins = $"{namespaceToGetPlugins}.{pluginName}";
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace != null && x.Namespace.StartsWith(namespaceToGetPlugins)))
{
var containsAnyNativeFunction = type.GetMethods().Any(m => m.GetCustomAttributes(typeof(KernelFunctionAttribute), false).Length > 0);
if (containsAnyNativeFunction)
{
string functionName = type.FullName!.Replace(type.Assembly!.GetName().Name!, string.Empty).Trim('.').Replace($"{nameof(Plugins)}.", string.Empty);
// Creating an instance, ensuring to inject dependencies
var instance = ActivatorUtilities.CreateInstance(kernel.Services, type);
// Creating plugin from object (but not importing it into the kernel)
result.AddRange(kernel.CreatePluginFromObject(instance, type.Name!));
}
}
return result;
}
internal static void CustomImportPlugin(this Kernel kernel, string pluginName)
{
if (string.IsNullOrWhiteSpace(pluginName))
throw new ArgumentNullException(nameof(pluginName), "You must provide a plugin name to import.");
// Merging all kernel functions (native and prompt) into the same plugin
kernel.ImportPluginFromFunctions(pluginName,
CreatePromptFunctions(kernel, pluginName)
.Concat(CreateNativeFunctions(kernel, pluginName)));
}
} Does anyone have any feedback on this approach? I’m wondering if I’m overcomplicating things. Is there a simpler way to achieve the same result? I would appreciate any insights or suggestions! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
@gsubiran - thanks for putting this in. We are going to take a look at building a sample on how to do this and/or updating the functionality. |
Beta Was this translation helpful? Give feedback.
@gsubiran - thanks for putting this in. We are going to take a look at building a sample on how to do this and/or updating the functionality.