What's the best way to use a semantic function within a native function? #2754
-
I have a native function that needs to call a sematic function. public class MyPlugin
{
private readonly IKernel _kernel;
private readonly ISKFunction _getCommonFolderPathFunction;
public MyPlugin(IKernel kernel)
{
_kernel = kernel;
_getCommonFolderPathFunction = GetCommonFolderPathFunction();
}
private ISKFunction GetCommonFolderPathFunction()
{
var prompt = "What is the path for the common folder {{$commonFolderName}} " +
$"on operating platform {Environment.OSVersion.Platform.ToString()} " +
$"with user profile path {Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}?";
var promptConfig = new PromptTemplateConfig
{
Schema = 1,
Type = "completion",
Description = "Gets the path for the given common folder.",
Completion =
{
MaxTokens = 500,
Temperature = 0.0,
TopP = 0.0,
PresencePenalty = 0.0,
FrequencyPenalty = 0.0
},
Input =
{
Parameters = new List<PromptTemplateConfig.InputParameter>
{
new()
{
Name = "commonFolderName",
Description = "Name of the common folder"
}
}
}
};
var promptTemplate = new PromptTemplate(prompt, promptConfig, _kernel);
var functionConfig = new SemanticFunctionConfig(promptConfig, promptTemplate);
return _kernel.RegisterSemanticFunction("MyPluginInternal", "GetCommonFolderPath", functionConfig);
}
private async Task<string?> GetCommonFolderPath(string commonFolderName)
{
var variables = new ContextVariables
{
["commonFolderName"] = commonFolderName
};
var context = await _getCommonFolderPathFunction.InvokeAsync(variables);
var match = Regex.Match(context.Result, @"([a-zA-Z]?\:?[\/\\].*[\/\\][\S-[""'\.]]*)", RegexOptions.IgnoreCase);
return match.Success ? match.Captures[0].Value : null;
}
[SKFunction, Description("Foobar")]
public async Task<string> LocateFile(SKContext context)
{
var foo = await GetCommonFolderPath(commonFolderName);
...
}
} Is there a way to have the inline function be more in line with the native function? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
The only way I've gotten this to work is to manually pass in the running kernel instance:
But this also means I cannot register this Native class at the same time I'm building out my Kernel instance. Which means whatever your firing mechanism is, it will need to register your Native class at runtime:
This is what we currently do for Plugins that serve and orchestrations of other Semantic Functions, where an individual code path (for us, a REST endpoint) triggers the above per request. Note Singleton Kernels are not recommended (per documentation). Transient ones are wasteful, and I haven't been able to get Scoped ones working (though I may need to wrap it in an abstraction first, I don't know...I'll have to try it). My preference would be that every Kernel built through the builder registers an instance to itself in its own service collection. Then the above would work more intuitively. However, note that what we're asking for is the Kernel to inject itself into a Native Function that itself is trying to call. We don't ask a Controller to inject an instance of itself to execute a Controller method...that's some weird circular dependency blackhole. Perhaps it is cleaner/better to pass the dependency in to the function call while keeping the Native class's state free of a Kernel instance? |
Beta Was this translation helpful? Give feedback.
-
Yes. Going through SK documentation it's never explicitly state whether or not a Native Function is intended to run Kernel functions internally (clearly it can but DI was awkward). Since the abstraction (IKernel) was removed, I figured you guys decided against the concept of Native functions serving as workflow orchestrations (of other Semantic and/or Native functions). The above knowledge you've provided I think would be super valuable to readers who are looking at docs and may not necessarily be looking through SK code 😜👍 |
Beta Was this translation helpful? Give feedback.
Why do you need to pass the Kernel to the plugin's constructor? If you have a method like:
the Kernel being used when that method is invoked will be automatically passed in as that Kernel argument.