Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using RepoUtils;

// ReSharper disable once InconsistentNaming
Expand Down Expand Up @@ -68,7 +68,7 @@ public static async Task RunAsync()
}
}";
var templateConfig = JsonSerializer.Deserialize<PromptTemplateConfig>(configPayload);
var func = kernel.CreateSemanticFunction(prompt, config: templateConfig!, "HelloAI");
var func = kernel.CreateSemanticFunction(prompt, templateConfig!, "HelloAI");

result = await kernel.RunAsync(func);
Console.WriteLine(result.GetValue<string>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using Microsoft.SemanticKernel.Text;

#pragma warning disable IDE0130
Expand All @@ -28,14 +28,16 @@ public static class KernelSemanticFunctionExtensions
/// </summary>
/// <param name="kernel">Semantic Kernel instance</param>
/// <param name="functionName">Name of the semantic function. The name can contain only alphanumeric chars + underscore.</param>
/// <param name="functionConfig">Function configuration, e.g. I/O params, AI settings, localization details, etc.</param>
/// <param name="promptTemplateConfig">Prompt template configuration.</param>
/// <param name="promptTemplate">Prompt template.</param>
/// <returns>A C# function wrapping AI logic, usually defined with natural language</returns>
public static ISKFunction RegisterSemanticFunction(
this IKernel kernel,
string functionName,
SemanticFunctionConfig functionConfig)
PromptTemplateConfig promptTemplateConfig,
IPromptTemplate promptTemplate)
{
return kernel.RegisterSemanticFunction(FunctionCollection.GlobalFunctionsPluginName, functionName, functionConfig);
return kernel.RegisterSemanticFunction(FunctionCollection.GlobalFunctionsPluginName, functionName, promptTemplateConfig, promptTemplate);
}

/// <summary>
Expand All @@ -44,18 +46,20 @@ public static ISKFunction RegisterSemanticFunction(
/// <param name="kernel">Semantic Kernel instance</param>
/// <param name="pluginName">Name of the plugin containing the function. The name can contain only alphanumeric chars + underscore.</param>
/// <param name="functionName">Name of the semantic function. The name can contain only alphanumeric chars + underscore.</param>
/// <param name="functionConfig">Function configuration, e.g. I/O params, AI settings, localization details, etc.</param>
/// <param name="promptTemplateConfig">Prompt template configuration.</param>
/// <param name="promptTemplate">Prompt template.</param>
/// <returns>A C# function wrapping AI logic, usually defined with natural language</returns>
public static ISKFunction RegisterSemanticFunction(
this IKernel kernel,
string pluginName,
string functionName,
SemanticFunctionConfig functionConfig)
PromptTemplateConfig promptTemplateConfig,
IPromptTemplate promptTemplate)
{
// Future-proofing the name not to contain special chars
Verify.ValidFunctionName(functionName);

ISKFunction function = kernel.CreateSemanticFunction(pluginName, functionName, functionConfig);
ISKFunction function = kernel.CreateSemanticFunction(pluginName, functionName, promptTemplateConfig, promptTemplate);
return kernel.RegisterCustomFunction(function);
}

Expand All @@ -81,19 +85,19 @@ public static ISKFunction CreateSemanticFunction(
{
functionName ??= RandomFunctionName();

var config = new PromptTemplateConfig
var promptTemplateConfig = new PromptTemplateConfig
{
Description = description ?? "Generic function, unknown purpose",
};

if (requestSettings is not null)
{
config.ModelSettings.Add(requestSettings);
promptTemplateConfig.ModelSettings.Add(requestSettings);
}

return kernel.CreateSemanticFunction(
promptTemplate: promptTemplate,
config: config,
promptTemplateConfig: promptTemplateConfig,
functionName: functionName,
pluginName: pluginName);
}
Expand All @@ -103,53 +107,50 @@ public static ISKFunction CreateSemanticFunction(
/// </summary>
/// <param name="kernel">Semantic Kernel instance</param>
/// <param name="promptTemplate">Plain language definition of the semantic function, using SK template language</param>
/// <param name="config">Optional function settings</param>
/// <param name="promptTemplateConfig">Prompt template configuration.</param>
/// <param name="functionName">A name for the given function. The name can be referenced in templates and used by the pipeline planner.</param>
/// <param name="pluginName">An optional plugin name, e.g. to namespace functions with the same name. When empty,
/// the function is added to the global namespace, overwriting functions with the same name</param>
/// <returns>A function ready to use</returns>
public static ISKFunction CreateSemanticFunction(
this IKernel kernel,
string promptTemplate,
PromptTemplateConfig config,
PromptTemplateConfig promptTemplateConfig,
string? functionName = null,
string? pluginName = null)
{
functionName ??= RandomFunctionName();
Verify.ValidFunctionName(functionName);
if (!string.IsNullOrEmpty(pluginName)) { Verify.ValidPluginName(pluginName); }

var template = new PromptTemplate(promptTemplate, config, kernel.PromptTemplateEngine);

// Prepare lambda wrapping AI logic
var functionConfig = new SemanticFunctionConfig(config, template);
var template = new PromptTemplate(promptTemplate, promptTemplateConfig, kernel.PromptTemplateEngine);

// TODO: manage overwrites, potentially error out
return string.IsNullOrEmpty(pluginName)
? kernel.RegisterSemanticFunction(functionName, functionConfig)
: kernel.RegisterSemanticFunction(pluginName!, functionName, functionConfig);
? kernel.RegisterSemanticFunction(functionName, promptTemplateConfig, template)
: kernel.RegisterSemanticFunction(pluginName!, functionName, promptTemplateConfig, template);
}

/// <summary>
/// Invoke a semantic function using the provided prompt template.
/// </summary>
/// <param name="kernel">Semantic Kernel instance</param>
/// <param name="promptTemplate">Plain language definition of the semantic function, using SK template language</param>
/// <param name="template">Plain language definition of the semantic function, using SK template language</param>
/// <param name="functionName">A name for the given function. The name can be referenced in templates and used by the pipeline planner.</param>
/// <param name="pluginName">Optional plugin name, for namespacing and avoid collisions</param>
/// <param name="description">Optional description, useful for the planner</param>
/// <param name="requestSettings">Optional LLM request settings</param>
/// <returns>Kernel execution result</returns>
public static Task<KernelResult> InvokeSemanticFunctionAsync(
this IKernel kernel,
string promptTemplate,
string template,
string? functionName = null,
string? pluginName = null,
string? description = null,
AIRequestSettings? requestSettings = null)
{
var skFunction = kernel.CreateSemanticFunction(
promptTemplate,
template,
functionName,
pluginName,
description,
Expand Down Expand Up @@ -256,14 +257,12 @@ public static IDictionary<string, ISKFunction> ImportSemanticFunctionsFromDirect
// Load prompt template
var template = new PromptTemplate(File.ReadAllText(promptPath), config, kernel.PromptTemplateEngine);

var functionConfig = new SemanticFunctionConfig(config, template);

if (logger.IsEnabled(LogLevel.Trace))
{
logger.LogTrace("Registering function {0}.{1} loaded from {2}", pluginDirectoryName, functionName, dir);
}

functions[functionName] = kernel.RegisterSemanticFunction(pluginDirectoryName, functionName, functionConfig);
functions[functionName] = kernel.RegisterSemanticFunction(pluginDirectoryName, functionName, config, template);
}
}

Expand All @@ -276,23 +275,25 @@ private static ISKFunction CreateSemanticFunction(
this IKernel kernel,
string pluginName,
string functionName,
SemanticFunctionConfig functionConfig)
PromptTemplateConfig promptTemplateConfig,
IPromptTemplate promptTemplate)
{
ISKFunction func = SemanticFunction.FromSemanticConfig(
pluginName,
functionName,
functionConfig,
promptTemplateConfig,
promptTemplate,
kernel.LoggerFactory
);

// Connect the function to the current kernel function collection, in case the function
// is invoked manually without a context and without a way to find other functions.
func.SetDefaultFunctionCollection(kernel.Functions);

func.SetAIConfiguration(functionConfig.PromptTemplateConfig.GetDefaultRequestSettings());
func.SetAIConfiguration(promptTemplateConfig.GetDefaultRequestSettings());

// Note: the service is instantiated using the kernel configuration state when the function is invoked
func.SetAIService(() => kernel.GetService<ITextCompletion>(functionConfig.PromptTemplateConfig.GetDefaultRequestSettings()?.ServiceId ?? null));
func.SetAIService(() => kernel.GetService<ITextCompletion>(promptTemplateConfig.GetDefaultRequestSettings()?.ServiceId ?? null));

return func;
}
Expand Down
17 changes: 10 additions & 7 deletions dotnet/src/Functions/Functions.Semantic/SemanticFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;

#pragma warning disable IDE0130
// ReSharper disable once CheckNamespace - Using the main namespace
Expand Down Expand Up @@ -51,27 +51,30 @@ internal sealed class SemanticFunction : ISKFunction, IDisposable
/// </summary>
/// <param name="pluginName">Name of the plugin to which the function being created belongs.</param>
/// <param name="functionName">Name of the function to create.</param>
/// <param name="functionConfig">Semantic function configuration.</param>
/// <param name="promptTemplateConfig">Prompt template configuration.</param>
/// <param name="promptTemplate">Prompt template.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>SK function instance.</returns>
public static ISKFunction FromSemanticConfig(
string pluginName,
string functionName,
SemanticFunctionConfig functionConfig,
PromptTemplateConfig promptTemplateConfig,
IPromptTemplate promptTemplate,
ILoggerFactory? loggerFactory = null,
CancellationToken cancellationToken = default)
{
Verify.NotNull(functionConfig);
Verify.NotNull(promptTemplateConfig);
Verify.NotNull(promptTemplate);

var func = new SemanticFunction(
template: functionConfig.PromptTemplate,
description: functionConfig.PromptTemplateConfig.Description,
template: promptTemplate,
description: promptTemplateConfig.Description,
pluginName: pluginName,
functionName: functionName,
loggerFactory: loggerFactory
);
func.SetAIConfiguration(functionConfig.PromptTemplateConfig.GetDefaultRequestSettings());
func.SetAIConfiguration(promptTemplateConfig.GetDefaultRequestSettings());

return func;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.TemplateEngine;

namespace Microsoft.SemanticKernel.SemanticFunctions;
namespace Microsoft.SemanticKernel.TemplateEngine;

/// <summary>
/// Prompt template.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Microsoft.SemanticKernel.AI;
using Microsoft.SemanticKernel.Text;

namespace Microsoft.SemanticKernel.SemanticFunctions;
namespace Microsoft.SemanticKernel.TemplateEngine;

/// <summary>
/// Prompt template configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

using System.Text.Json;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using Xunit;

namespace SemanticKernel.Functions.UnitTests.SemanticFunctions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Events;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using Moq;
using Xunit;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.Reliability.Basic;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using SemanticKernel.IntegrationTests.TestSettings;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -356,14 +356,12 @@ public async Task MultipleServiceLoadPromptConfigTestAsync()

var defaultFunc = target.RegisterSemanticFunction(
"WherePlugin", "FishMarket1",
new SemanticFunctionConfig(
defaultConfig,
new PromptTemplate(prompt, defaultConfig, target.PromptTemplateEngine)));
defaultConfig,
new PromptTemplate(prompt, defaultConfig, target.PromptTemplateEngine));
var azureFunc = target.RegisterSemanticFunction(
"WherePlugin", "FishMarket2",
new SemanticFunctionConfig(
azureConfig,
new PromptTemplate(prompt, azureConfig, target.PromptTemplateEngine)));
azureConfig,
new PromptTemplate(prompt, azureConfig, target.PromptTemplateEngine));

// Act
await Assert.ThrowsAsync<HttpOperationException>(() => target.RunAsync(defaultFunc));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;
using Moq;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -87,7 +87,8 @@ private void CreateKernelAndFunctionCreateMocks(List<(string name, string plugin
kernelMock.Setup(x => x.RegisterSemanticFunction(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<SemanticFunctionConfig>()
It.IsAny<PromptTemplateConfig>(),
It.IsAny<IPromptTemplate>()
)).Returns(mockFunction.Object);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// ReSharper disable once CheckNamespace - Using the namespace of IKernel
using Microsoft.SemanticKernel.AI;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;

#pragma warning disable IDE0130
namespace Microsoft.SemanticKernel.Planners;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.Planning;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.TemplateEngine;
using Microsoft.SemanticKernel.TemplateEngine.Basic;

#pragma warning disable IDE0130
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.TemplateEngine;

#pragma warning disable IDE0130
// ReSharper disable once CheckNamespace - Using NS of Plan
Expand Down
Loading