Skip to content

Commit

Permalink
Add support for multiple native function arguments of many types (#1195)
Browse files Browse the repository at this point in the history
### Motivation and Context

Move native functions closer to a normal C# experience.

### Description

- Native skills can now have any number of parameters. The parameters
are populated from context variables of the same name. If no context
variable exists for that name, it'll be populated with a default value
if one was supplied via either an attribute or a default parameter
value, or if there is none, the function will fail to be invoked. The
first parameter may also be populated from "input" if it fails to get
input by its name or default value.
- Descriptions are now specified with the .NET DescriptionAttribute, and
DefaultValue with the DefaultValueAttribute. The C# compiler is aware of
the DefaultValueAttribute and ensures the type of the value provided
matches that of the type of the parameter. Default values can now also
be specified using optional parameter values.
- SKFunction is now purely a marker attribute, other than for
sensitivity. It's sole purpose is to subset which public members are
imported as native functions when a skill is imported. It was already
the case that the attribute wasn't needed when importing a function
directly from a delegate; that requirement has also been lifted when
importing from a MethodInfo.
- SKFunctionContextParameterAttribute has been obsoleted and will be
removed subsequently. DescriptionAttribute, DefaultValueAttribute, and
SKName attribute are used instead. In rare situations where the method
needs access to a variable that's not defined in its signature, it can
use the SKParameter attribute on the method, which does have Description
and DefaultValue optional properties.
- SKFunctionInputAttribute has been obsoleted and will be removed
subsequently. DescriptionAttribute, DefaultValueAttribute, and SKName
attribute are used instead (the latter with "Input" as the name).
However, the need to use SKName should be exceedingly rare.
- InvokeAsync will now catch exceptions and store the exception into the
context. This means native skills should handle all failures by throwing
exceptions rather than by directly interacting with the context.
- Updated name selection heuristic to strip off an "Async" suffix for
async methods. There are now very few reasons to use [SKName] on a
method.
- Added support for ValueTasks as return types, just for completeness so
that developers don't need to think about it. It just works.
- Added ability to accept an ILogger or CancellationToken into a method;
they're populated from the SKContext. With that, there are very few
reasons left to pass an SKContext into a native function.
- Added support for non-string arguments. All C# primitive types and
many core .NET types are supported, with their corresponding
TypeConverters used to parse the string context variable into the
appropriate type. Custom types attributed with TypeConverterAttribute
may also be used, and the associated TypeConverter will be used as is
appropriate. It's the same mechanism used by UI frameworks like WinForms
as well as ASP.NET MVC.
- Similarly, added support for non-string return types.

**Example**
_Before_:
```C#
[SKFunction("Adds value to a value")]
[SKFunctionName("Add")]
[SKFunctionInput(Description = "The value to add")]
[SKFunctionContextParameter(Name = "Amount", Description = "Amount to add")]
public Task<string> AddAsync(string initialValueText, SKContext context)
{
    if (!int.TryParse(initialValueText, NumberStyles.Any, CultureInfo.InvariantCulture, out var initialValue))
    {
        return Task.FromException<string>(new ArgumentOutOfRangeException(
            nameof(initialValueText), initialValueText, "Initial value provided is not in numeric format"));
    }

    string contextAmount = context["Amount"];
    if (!int.TryParse(contextAmount, NumberStyles.Any, CultureInfo.InvariantCulture, out var amount))
    {
        return Task.FromException<string>(new ArgumentOutOfRangeException(
            nameof(context), contextAmount, "Context amount provided is not in numeric format"));
    }

    var result = initialValue + amount;
    return Task.FromResult(result.ToString(CultureInfo.InvariantCulture));
}
```

_After_:
```C#
[SKFunction, Description("Adds an amount to a value")]
public int Add(
    [Description("The value to add")] int value,
    [Description("Amount to add")] int amount) =>
    value + amount;
```

**Example**
_Before_:
```C#
[SKFunction("Wait a given amount of seconds")]
[SKFunctionName("Seconds")]
[SKFunctionInput(DefaultValue = "0", Description = "The number of seconds to wait")]
public async Task SecondsAsync(string secondsText)
{
    if (!decimal.TryParse(secondsText, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
    {
        throw new ArgumentException("Seconds provided is not in numeric format", nameof(secondsText));
    }

    var milliseconds = seconds * 1000;
    milliseconds = (milliseconds > 0) ? milliseconds : 0;

    await this._waitProvider.DelayAsync((int)milliseconds).ConfigureAwait(false);
}
```

_After_:
```C#
[SKFunction, Description("Wait a given amount of seconds")]
public async Task SecondsAsync([Description("The number of seconds to wait")] decimal seconds)
{
    var milliseconds = seconds * 1000;
    milliseconds = (milliseconds > 0) ? milliseconds : 0;

    await this._waitProvider.DelayAsync((int)milliseconds).ConfigureAwait(false);
}
```

**Example**
_Before_:
```C#
[SKFunction("Add an event to my calendar.")]
[SKFunctionInput(Description = "Event subject")]
[SKFunctionContextParameter(Name = Parameters.Start, Description = "Event start date/time as DateTimeOffset")]
[SKFunctionContextParameter(Name = Parameters.End, Description = "Event end date/time as DateTimeOffset")]
[SKFunctionContextParameter(Name = Parameters.Location, Description = "Event location (optional)")]
[SKFunctionContextParameter(Name = Parameters.Content, Description = "Event content/body (optional)")]
[SKFunctionContextParameter(Name = Parameters.Attendees, Description = "Event attendees, separated by ',' or ';'.")]
public async Task AddEventAsync(string subject, SKContext context)
{
    ContextVariables variables = context.Variables;

    if (string.IsNullOrWhiteSpace(subject))
    {
        context.Fail("Missing variables input to use as event subject.");
        return;
    }

    if (!variables.TryGetValue(Parameters.Start, out string? start))
    {
        context.Fail($"Missing variable {Parameters.Start}.");
        return;
    }

    if (!variables.TryGetValue(Parameters.End, out string? end))
    {
        context.Fail($"Missing variable {Parameters.End}.");
        return;
    }

    CalendarEvent calendarEvent = new()
    {
        Subject = variables.Input,
        Start = DateTimeOffset.Parse(start, CultureInfo.InvariantCulture.DateTimeFormat),
        End = DateTimeOffset.Parse(end, CultureInfo.InvariantCulture.DateTimeFormat)
    };

    if (variables.TryGetValue(Parameters.Location, out string? location))
    {
        calendarEvent.Location = location;
    }

    if (variables.TryGetValue(Parameters.Content, out string? content))
    {
        calendarEvent.Content = content;
    }

    if (variables.TryGetValue(Parameters.Attendees, out string? attendees))
    {
        calendarEvent.Attendees = attendees.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
    }

    this._logger.LogInformation("Adding calendar event '{0}'", calendarEvent.Subject);
    await this._connector.AddEventAsync(calendarEvent).ConfigureAwait(false);
}
```

_After_:
```C#
[SKFunction, Description("Add an event to my calendar.")]
public async Task AddEventAsync(
    [Description("Event subject"), SKName("input")] string subject,
    [Description("Event start date/time as DateTimeOffset")] DateTimeOffset start,
    [Description("Event end date/time as DateTimeOffset")] DateTimeOffset end,
    [Description("Event location (optional)")] string? location = null,
    [Description("Event content/body (optional)")] string? content = null,
    [Description("Event attendees, separated by ',' or ';'.")] string? attendees = null)
{
    if (string.IsNullOrWhiteSpace(subject))
    {
        throw new ArgumentException($"{nameof(subject)} variable was null or whitespace", nameof(subject));
    }

    CalendarEvent calendarEvent = new()
    {
        Subject = subject,
        Start = start,
        End = end,
        Location = location,
        Content = content,
        Attendees = attendees is not null ? attendees.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) : Enumerable.Empty<string>(),
    };

    this._logger.LogInformation("Adding calendar event '{0}'", calendarEvent.Subject);
    await this._connector.AddEventAsync(calendarEvent).ConfigureAwait(false);
}
```

### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->
- [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
- [ ] I didn't break anyone 😄

cc: @dluc, @shawncal

---------

Co-authored-by: Shawn Callegari <[email protected]>
Co-authored-by: name <email>
  • Loading branch information
stephentoub and shawncal committed Jun 16, 2023
1 parent 4a6e57f commit b31e36b
Show file tree
Hide file tree
Showing 99 changed files with 2,055 additions and 1,585 deletions.
47 changes: 44 additions & 3 deletions dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,51 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.AI.HuggingFace", "src\Connectors\Connectors.AI.HuggingFace\Connectors.AI.HuggingFace.csproj", "{136823BE-8665-4D57-87E0-EF41535539E2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InternalUtilities", "InternalUtilities", "{4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\InternalUtilities.props = src\InternalUtilities\InternalUtilities.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Weaviate", "src\Connectors\Connectors.Memory.Weaviate\Connectors.Memory.Weaviate.csproj", "{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiSkillsExample", "..\samples\dotnet\openapi-skills\OpenApiSkillsExample.csproj", "{4D91A3E0-C404-495B-AD4A-411C4E83CF54}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.DuckDB", "src\Connectors\Connectors.Memory.DuckDB\Connectors.Memory.DuckDB.csproj", "{50FAE231-6F24-4779-9D02-12ABBC9A49E2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5C246969-D794-4EC3-8E8F-F90D4D166420}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\test\AssertExtensions.cs = src\InternalUtilities\test\AssertExtensions.cs
src\InternalUtilities\test\FunctionHelpers.cs = src\InternalUtilities\test\FunctionHelpers.cs
src\InternalUtilities\test\TestInternalUtilities.props = src\InternalUtilities\test\TestInternalUtilities.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{958AD708-F048-4FAF-94ED-D2F2B92748B9}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\InternalUtilities.props = src\InternalUtilities\src\InternalUtilities.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics", "Diagnostics", "{29E7D971-1308-4171-9872-E8E4669A1134}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\Diagnostics\CompilerServicesAttributes.cs = src\InternalUtilities\src\Diagnostics\CompilerServicesAttributes.cs
src\InternalUtilities\src\Diagnostics\ExceptionExtensions.cs = src\InternalUtilities\src\Diagnostics\ExceptionExtensions.cs
src\InternalUtilities\src\Diagnostics\HttpStatusCodeType.cs = src\InternalUtilities\src\Diagnostics\HttpStatusCodeType.cs
src\InternalUtilities\src\Diagnostics\NullableAttributes.cs = src\InternalUtilities\src\Diagnostics\NullableAttributes.cs
src\InternalUtilities\src\Diagnostics\Verify.cs = src\InternalUtilities\src\Diagnostics\Verify.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linq", "Linq", "{B00AD427-0047-4850-BEF9-BA8237EA9D8B}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\Linq\AsyncEnumerable.cs = src\InternalUtilities\src\Linq\AsyncEnumerable.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text", "Text", "{DB950192-30F1-48B1-88D7-F43FECCA1A1C}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\Text\Json.cs = src\InternalUtilities\src\Text\Json.cs
src\InternalUtilities\src\Text\StringExtensions.cs = src\InternalUtilities\src\Text\StringExtensions.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http", "Http", "{1C19D805-3573-4477-BF07-40180FCDE1BD}"
ProjectSection(SolutionItems) = preProject
src\InternalUtilities\src\Http\HttpClientProvider.cs = src\InternalUtilities\src\Http\HttpClientProvider.cs
src\InternalUtilities\src\Http\NonDisposableHttpClientHandler.cs = src\InternalUtilities\src\Http\NonDisposableHttpClientHandler.cs
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -363,6 +398,12 @@ Global
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
{4D91A3E0-C404-495B-AD4A-411C4E83CF54} = {FA3720F1-C99A-49B2-9577-A940257098BF}
{50FAE231-6F24-4779-9D02-12ABBC9A49E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
{5C246969-D794-4EC3-8E8F-F90D4D166420} = {4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}
{958AD708-F048-4FAF-94ED-D2F2B92748B9} = {4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}
{29E7D971-1308-4171-9872-E8E4669A1134} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
{B00AD427-0047-4850-BEF9-BA8237EA9D8B} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
{DB950192-30F1-48B1-88D7-F43FECCA1A1C} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
{1C19D805-3573-4477-BF07-40180FCDE1BD} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83}
Expand Down
2 changes: 1 addition & 1 deletion dotnet/nuget/nuget-package.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<!-- Central version prefix - applies to all nuget packages. -->
<Version>0.16</Version>
<Version>0.17</Version>

<Configurations>Debug;Release;Publish</Configurations>
<IsPackable>true</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<!--<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />-->
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</PackageReference>
</ItemGroup>

<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<ItemGroup>
<ProjectReference Include="..\..\SemanticKernel\SemanticKernel.csproj" />
Expand Down
25 changes: 13 additions & 12 deletions dotnet/src/Extensions/Planning.ActionPlanner/ActionPlanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand Down Expand Up @@ -140,10 +141,10 @@ public async Task<Plan> CreatePlanAsync(string goal)
/// <param name="goal">Currently unused. Will be used to handle long lists of functions.</param>
/// <param name="context">Function execution context</param>
/// <returns>List of functions, formatted accordingly to the prompt</returns>
[SKFunction("List all functions available in the kernel")]
[SKFunctionName("ListOfFunctions")]
[SKFunctionInput(Description = "The current goal processed by the planner", DefaultValue = "")]
public string ListOfFunctions(string goal, SKContext context)
[SKFunction, Description("List all functions available in the kernel")]
public string ListOfFunctions(
[Description("The current goal processed by the planner")] string goal,
SKContext context)
{
Verify.NotNull(context.Skills);
var functionsAvailable = context.Skills.GetFunctionsView();
Expand All @@ -158,10 +159,10 @@ public string ListOfFunctions(string goal, SKContext context)

// TODO: generate string programmatically
// TODO: use goal to find relevant examples
[SKFunction("List a few good examples of plans to generate")]
[SKFunctionName("GoodExamples")]
[SKFunctionInput(Description = "The current goal processed by the planner", DefaultValue = "")]
public string GoodExamples(string goal, SKContext context)
[SKFunction, Description("List a few good examples of plans to generate")]
public string GoodExamples(
[Description("The current goal processed by the planner")] string goal,
SKContext context)
{
return @"
[EXAMPLE]
Expand Down Expand Up @@ -193,10 +194,10 @@ No parameters.
}

// TODO: generate string programmatically
[SKFunction("List a few edge case examples of plans to handle")]
[SKFunctionName("EdgeCaseExamples")]
[SKFunctionInput(Description = "The current goal processed by the planner", DefaultValue = "")]
public string EdgeCaseExamples(string goal, SKContext context)
[SKFunction, Description("List a few edge case examples of plans to handle")]
public string EdgeCaseExamples(
[Description("The current goal processed by the planner")] string goal,
SKContext context)
{
return @"
[EXAMPLE]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/InternalUtilities.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand Down
43 changes: 19 additions & 24 deletions dotnet/src/IntegrationTests/Fakes/EmailSkillFake.cs
Original file line number Diff line number Diff line change
@@ -1,47 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.

using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SkillDefinition;

namespace SemanticKernel.IntegrationTests.Fakes;

internal sealed class EmailSkillFake
{
[SKFunction("Given an email address and message body, send an email")]
[SKFunctionInput(Description = "The body of the email message to send.")]
[SKFunctionContextParameter(Name = "email_address", Description = "The email address to send email to.", DefaultValue = "[email protected]")]
public Task<SKContext> SendEmailAsync(string input, SKContext context)
[SKFunction, Description("Given an email address and message body, send an email")]
public Task<string> SendEmailAsync(
[Description("The body of the email message to send.")] string input,
[Description("The email address to send email to.")] string? email_address = "[email protected]")
{
context.Variables.TryGetValue("email_address", out string? emailAddress);
context.Variables.Update($"Sent email to: {emailAddress}. Body: {input}");
return Task.FromResult(context);
email_address ??= string.Empty;
return Task.FromResult($"Sent email to: {email_address}. Body: {input}");
}

[SKFunction("Lookup an email address for a person given a name")]
[SKFunctionInput(Description = "The name of the person to email.")]
public Task<SKContext> GetEmailAddressAsync(string input, SKContext context)
[SKFunction, Description("Lookup an email address for a person given a name")]
public Task<string> GetEmailAddressAsync(
[Description("The name of the person to email.")] string input,
ILogger logger)
{
if (string.IsNullOrEmpty(input))
{
context.Log.LogDebug("Returning hard coded email for {0}", input);
context.Variables.Update("[email protected]");
}
else
{
context.Log.LogDebug("Returning dynamic email for {0}", input);
context.Variables.Update($"{input}@example.com");
logger.LogDebug("Returning hard coded email for {0}", input);
return Task.FromResult("[email protected]");
}

return Task.FromResult(context);
logger.LogDebug("Returning dynamic email for {0}", input);
return Task.FromResult($"{input}@example.com");
}

[SKFunction("Write a short poem for an e-mail")]
[SKFunctionInput(Description = "The topic of the poem.")]
public Task<SKContext> WritePoemAsync(string input, SKContext context)
[SKFunction, Description("Write a short poem for an e-mail")]
public Task<string> WritePoemAsync(
[Description("The topic of the poem.")] string input)
{
context.Variables.Update($"Roses are red, violets are blue, {input} is hard, so is this test.");
return Task.FromResult(context);
return Task.FromResult($"Roses are red, violets are blue, {input} is hard, so is this test.");
}
}
Loading

0 comments on commit b31e36b

Please sign in to comment.