Skip to content

Commit

Permalink
Fix to use semantic function default values (#1772)
Browse files Browse the repository at this point in the history
### Motivation and Context

Please help reviewers and future users, providing the following
information:
1. Why is this change required? Default values specified in a semantic
function config.json are not being used when executing the semantic
function


### Description

1. Add logic to use default values when executing a semantic function
2. Add a new extension method to allow execution of the text prompt

### Contribution Checklist

- [x] The code builds clean without any errors or warnings
- [x] The PR follows SK Contribution Guidelines
(https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
- [x] The code follows the .NET coding conventions
(https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions)
verified with `dotnet format`
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
  • Loading branch information
markwallace-microsoft committed Jun 29, 2023
1 parent 16d52c1 commit 537bc49
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,50 @@ public async Task CompletionWithDifferentLineEndingsAsync(string lineEnding, AIS
Assert.Contains(ExpectedAnswerContains, actual.Result, StringComparison.OrdinalIgnoreCase);
}

[Fact]
public async Task AzureOpenAIInvokePromptTestAsync()
{
// Arrange
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
Assert.NotNull(azureOpenAIConfiguration);

var builder = Kernel.Builder.WithLogger(this._logger);
this.ConfigureAzureOpenAI(builder);
IKernel target = builder.Build();

var prompt = "Where is the most famous fish market in Seattle, Washington, USA?";

// Act
SKContext actual = await target.InvokeSemanticFunctionAsync(prompt, maxTokens: 150);

// Assert
Assert.Empty(actual.LastErrorDescription);
Assert.False(actual.ErrorOccurred);
Assert.Contains("Pike Place", actual.Result, StringComparison.OrdinalIgnoreCase);
}

[Fact]
public async Task AzureOpenAIDefaultValueTestAsync()
{
// Arrange
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
Assert.NotNull(azureOpenAIConfiguration);

var builder = Kernel.Builder.WithLogger(this._logger);
this.ConfigureAzureOpenAI(builder);
IKernel target = builder.Build();

IDictionary<string, ISKFunction> skill = TestHelpers.GetSkills(target, "FunSkill");

// Act
SKContext actual = await target.RunAsync(skill["Limerick"]);

// Assert
Assert.Empty(actual.LastErrorDescription);
Assert.False(actual.ErrorOccurred);
Assert.Contains("Bob", actual.Result, StringComparison.OrdinalIgnoreCase);
}

#region internals

private readonly XunitLogger<Kernel> _logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.Security;
using Microsoft.SemanticKernel.SemanticFunctions;
using Microsoft.SemanticKernel.SkillDefinition;
Expand Down Expand Up @@ -112,5 +114,54 @@ public static class InlineFunctionsDefinitionExtension
: kernel.RegisterSemanticFunction(skillName!, functionName, functionConfig, trustService);
}

/// <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="functionName">A name for the given function. The name can be referenced in templates and used by the pipeline planner.</param>
/// <param name="skillName">Optional skill name, for namespacing and avoid collisions</param>
/// <param name="description">Optional description, useful for the planner</param>
/// <param name="maxTokens">Max number of tokens to generate</param>
/// <param name="temperature">Temperature parameter passed to LLM</param>
/// <param name="topP">Top P parameter passed to LLM</param>
/// <param name="presencePenalty">Presence Penalty parameter passed to LLM</param>
/// <param name="frequencyPenalty">Frequency Penalty parameter passed to LLM</param>
/// <param name="isSensitive">Whether the function is set to be sensitive or not (default false)</param>
/// <param name="trustService">Service used for trust checks (if null will use the default registered in the kernel)</param>
/// <param name="stopSequences">Strings the LLM will detect to stop generating (before reaching max tokens)</param>
/// <returns>A function ready to use</returns>
public static Task<SKContext> InvokeSemanticFunctionAsync(
this IKernel kernel,
string promptTemplate,
string? functionName = null,
string? skillName = null,
string? description = null,
int maxTokens = 256,
double temperature = 0,
double topP = 0,
double presencePenalty = 0,
double frequencyPenalty = 0,
bool isSensitive = false,
ITrustService? trustService = null,
IEnumerable<string>? stopSequences = null)
{
var skfunction = kernel.CreateSemanticFunction(
promptTemplate,
functionName,
skillName,
description,
maxTokens,
temperature,
topP,
presencePenalty,
frequencyPenalty,
isSensitive,
trustService,
stopSequences);

return skfunction.InvokeAsync();
}

private static string RandomFunctionName() => "func" + Guid.NewGuid().ToString("N");
}
14 changes: 14 additions & 0 deletions dotnet/src/SemanticKernel/SkillDefinition/SKFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ public async Task<SKContext> InvokeAsync(SKContext context, CompleteRequestSetti

if (this.IsSemantic)
{
this.AddDefaultValues(context.Variables);

var resultContext = await this._function(this._aiService?.Value, settings ?? this._aiRequestSettings, context).ConfigureAwait(false);
context.Variables.Update(resultContext.Variables);
}
Expand Down Expand Up @@ -1033,5 +1035,17 @@ private static void TrackUniqueParameterType(ref bool hasParameterType, MethodIn
/// <summary>Formatter functions for converting parameter types to strings.</summary>
private static readonly ConcurrentDictionary<Type, Func<object?, CultureInfo, string>?> s_formatters = new();

/// <summary>Add default values to the context variables if the variable is not defined</summary>
private void AddDefaultValues(ContextVariables variables)
{
foreach (var parameter in this.Parameters)
{
if (!variables.ContainsKey(parameter.Name) && parameter.DefaultValue != null)
{
variables[parameter.Name] = parameter.DefaultValue;
}
}
}

#endregion
}
20 changes: 17 additions & 3 deletions samples/skills/FunSkill/Limerick/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@
"completion": {
"max_tokens": 100,
"temperature": 0.7,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
"top_p": 0,
"presence_penalty": 0,
"frequency_penalty": 0
},
"input": {
"parameters": [
{
"name": "name",
"description": "",
"defaultValue": "Bob"
},
{
"name": "input",
"description": "",
"defaultValue": "Dogs"
}
]
}
}

0 comments on commit 537bc49

Please sign in to comment.