chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);CA2007,CA2208,CS1591,IDE0009,IDE0055,IDE0073,VSTHRD111,SKEXP0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Properties\launchSettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HomeAutomation.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Azure OpenAI settings.
|
||||
/// </summary>
|
||||
public sealed class AzureOpenAIOptions
|
||||
{
|
||||
public const string SectionName = "AzureOpenAI";
|
||||
|
||||
[Required]
|
||||
public string ChatDeploymentName { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Endpoint { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HomeAutomation.Options;
|
||||
|
||||
/// <summary>
|
||||
/// OpenAI settings.
|
||||
/// </summary>
|
||||
public sealed class OpenAIOptions
|
||||
{
|
||||
public const string SectionName = "OpenAI";
|
||||
|
||||
[Required]
|
||||
public string ChatModelId { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace HomeAutomation.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// Simple plugin to illustrate creating plugins which have dependencies
|
||||
/// that can be resolved through dependency injection.
|
||||
/// </summary>
|
||||
public class MyAlarmPlugin(MyTimePlugin timePlugin)
|
||||
{
|
||||
[KernelFunction, Description("Sets an alarm at the provided time")]
|
||||
public void SetAlarm(string time)
|
||||
{
|
||||
// Code to actually set the alarm using the time plugin would be placed here
|
||||
_ = timePlugin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace HomeAutomation.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// Class that represents a controllable light.
|
||||
/// </summary>
|
||||
[Description("Represents a light")]
|
||||
public class MyLightPlugin(bool turnedOn = false)
|
||||
{
|
||||
private bool _turnedOn = turnedOn;
|
||||
|
||||
[KernelFunction, Description("Returns whether this light is on")]
|
||||
public bool IsTurnedOn() => _turnedOn;
|
||||
|
||||
[KernelFunction, Description("Turn on this light")]
|
||||
public void TurnOn() => _turnedOn = true;
|
||||
|
||||
[KernelFunction, Description("Turn off this light")]
|
||||
public void TurnOff() => _turnedOn = false;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace HomeAutomation.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// Simple plugin that just returns the time.
|
||||
/// </summary>
|
||||
public class MyTimePlugin
|
||||
{
|
||||
[KernelFunction, Description("Get the current time")]
|
||||
public DateTimeOffset Time() => DateTimeOffset.Now;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
Example that demonstrates how to use Semantic Kernel in conjunction with dependency injection.
|
||||
|
||||
Loads app configuration from:
|
||||
- appsettings.json.
|
||||
- appsettings.{Environment}.json.
|
||||
- Secret Manager when the app runs in the "Development" environment (set through the DOTNET_ENVIRONMENT variable).
|
||||
- Environment variables.
|
||||
- Command-line arguments.
|
||||
*/
|
||||
|
||||
using HomeAutomation.Options;
|
||||
using HomeAutomation.Plugins;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
// For Azure OpenAI configuration
|
||||
#pragma warning disable IDE0005 // Using directive is unnecessary.
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
|
||||
namespace HomeAutomation;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
internal static async Task Main(string[] args)
|
||||
{
|
||||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||
builder.Configuration.AddUserSecrets<Worker>();
|
||||
|
||||
// Actual code to execute is found in Worker class
|
||||
builder.Services.AddHostedService<Worker>();
|
||||
|
||||
// Get configuration
|
||||
builder.Services.AddOptions<OpenAIOptions>()
|
||||
.Bind(builder.Configuration.GetSection(OpenAIOptions.SectionName))
|
||||
.ValidateDataAnnotations()
|
||||
.ValidateOnStart();
|
||||
|
||||
/* Alternatively, you can use plain, Azure OpenAI after loading AzureOpenAIOptions instead of OpenAI
|
||||
|
||||
builder.Services.AddOptions<AzureOpenAIOptions>()
|
||||
.Bind(builder.Configuration.GetSection(AzureOpenAIOptions.SectionName))
|
||||
.ValidateDataAnnotations()
|
||||
.ValidateOnStart();
|
||||
*/
|
||||
|
||||
// Chat completion service that kernels will use
|
||||
builder.Services.AddSingleton<IChatCompletionService>(sp =>
|
||||
{
|
||||
OpenAIOptions openAIOptions = sp.GetRequiredService<IOptions<OpenAIOptions>>().Value;
|
||||
|
||||
// A custom HttpClient can be provided to this constructor
|
||||
return new OpenAIChatCompletionService(openAIOptions.ChatModelId, openAIOptions.ApiKey);
|
||||
|
||||
/* Alternatively, you can use plain, Azure OpenAI after loading AzureOpenAIOptions instead
|
||||
of OpenAI options with builder.Services.AddOptions:
|
||||
|
||||
AzureOpenAIOptions azureOpenAIOptions = sp.GetRequiredService<IOptions<AzureOpenAIOptions>>().Value;
|
||||
return new AzureOpenAIChatCompletionService(azureOpenAIOptions.ChatDeploymentName, azureOpenAIOptions.Endpoint, azureOpenAIOptions.ApiKey);
|
||||
|
||||
*/
|
||||
});
|
||||
|
||||
// Add plugins that can be used by kernels
|
||||
// The plugins are added as singletons so that they can be used by multiple kernels
|
||||
builder.Services.AddSingleton<MyTimePlugin>();
|
||||
builder.Services.AddSingleton<MyAlarmPlugin>();
|
||||
builder.Services.AddKeyedSingleton<MyLightPlugin>("OfficeLight");
|
||||
builder.Services.AddKeyedSingleton<MyLightPlugin>("PorchLight", (sp, key) =>
|
||||
{
|
||||
return new MyLightPlugin(turnedOn: true);
|
||||
});
|
||||
|
||||
/* To add an OpenAI or OpenAPI plugin, you need to be using Microsoft.SemanticKernel.Plugins.OpenApi.
|
||||
Then create a temporary kernel, use it to load the plugin and add it as keyed singleton.
|
||||
Kernel kernel = new();
|
||||
KernelPlugin openAIPlugin = await kernel.ImportPluginFromOpenAIAsync("<plugin name>", new Uri("<OpenAI-plugin>"));
|
||||
builder.Services.AddKeyedSingleton<KernelPlugin>("MyImportedOpenAIPlugin", openAIPlugin);
|
||||
|
||||
KernelPlugin openApiPlugin = await kernel.ImportPluginFromOpenApiAsync("<plugin name>", new Uri("<OpenAPI-plugin>"));
|
||||
builder.Services.AddKeyedSingleton<KernelPlugin>("MyImportedOpenApiPlugin", openApiPlugin);*/
|
||||
|
||||
// Add a home automation kernel to the dependency injection container
|
||||
builder.Services.AddKeyedTransient<Kernel>("HomeAutomationKernel", (sp, key) =>
|
||||
{
|
||||
// Create a collection of plugins that the kernel will use
|
||||
KernelPluginCollection pluginCollection = [];
|
||||
pluginCollection.AddFromObject(sp.GetRequiredService<MyTimePlugin>());
|
||||
pluginCollection.AddFromObject(sp.GetRequiredService<MyAlarmPlugin>());
|
||||
pluginCollection.AddFromObject(sp.GetRequiredKeyedService<MyLightPlugin>("OfficeLight"), "OfficeLight");
|
||||
pluginCollection.AddFromObject(sp.GetRequiredKeyedService<MyLightPlugin>("PorchLight"), "PorchLight");
|
||||
|
||||
// When created by the dependency injection container, Semantic Kernel logging is included by default
|
||||
return new Kernel(sp, pluginCollection);
|
||||
});
|
||||
|
||||
using IHost host = builder.Build();
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# "House Automation" example illustrating how to use Semantic Kernel with dependency injection
|
||||
|
||||
This example demonstrates a few dependency injection patterns that can be used with Semantic Kernel.
|
||||
|
||||
|
||||
## Configuring Secrets
|
||||
|
||||
The example require credentials to access OpenAI or Azure OpenAI.
|
||||
|
||||
If you have set up those credentials as secrets within Secret Manager or through environment variables for other samples from the solution in which this project is found, they will be re-used.
|
||||
|
||||
### To set your secrets with Secret Manager:
|
||||
|
||||
```
|
||||
cd dotnet/samples/Demos/HouseAutomation
|
||||
|
||||
dotnet user-secrets init
|
||||
|
||||
dotnet user-secrets set "OpenAI:ChatModelId" "..."
|
||||
dotnet user-secrets set "OpenAI:ApiKey" "..."
|
||||
|
||||
dotnet user-secrets set "AzureOpenAI:ChatDeploymentName" "..."
|
||||
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://... .openai.azure.com/"
|
||||
dotnet user-secrets set "AzureOpenAI:ApiKey" "..."
|
||||
```
|
||||
|
||||
### To set your secrets with environment variables
|
||||
|
||||
Use these names:
|
||||
|
||||
```
|
||||
# OpenAI
|
||||
OpenAI__ChatModelId
|
||||
OpenAI__ApiKey
|
||||
|
||||
# Azure OpenAI
|
||||
AzureOpenAI__ChatDeploymentName
|
||||
AzureOpenAI__Endpoint
|
||||
AzureOpenAI__ApiKey
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
|
||||
namespace HomeAutomation;
|
||||
|
||||
/// <summary>
|
||||
/// Actual code to run.
|
||||
/// </summary>
|
||||
internal sealed class Worker(
|
||||
IHostApplicationLifetime hostApplicationLifetime,
|
||||
[FromKeyedServices("HomeAutomationKernel")] Kernel kernel) : BackgroundService
|
||||
{
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime = hostApplicationLifetime;
|
||||
private readonly Kernel _kernel = kernel;
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// Get chat completion service
|
||||
var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();
|
||||
|
||||
// Enable auto function calling
|
||||
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
|
||||
{
|
||||
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
|
||||
};
|
||||
|
||||
Console.WriteLine("Ask questions or give instructions to the copilot such as:\n" +
|
||||
"- What time is it?\n" +
|
||||
"- Turn on the porch light.\n" +
|
||||
"- If it's before 7:00 pm, turn on the office light.\n" +
|
||||
"- Which light is currently on?\n" +
|
||||
"- Set an alarm for 6:00 am.\n");
|
||||
|
||||
Console.Write("> ");
|
||||
|
||||
string? input = null;
|
||||
while ((input = Console.ReadLine()) is not null)
|
||||
{
|
||||
Console.WriteLine();
|
||||
|
||||
ChatMessageContent chatResult = await chatCompletionService.GetChatMessageContentAsync(input,
|
||||
openAIPromptExecutionSettings, _kernel, stoppingToken);
|
||||
|
||||
Console.Write($"\n>>> Result: {chatResult}\n\n> ");
|
||||
}
|
||||
|
||||
_hostApplicationLifetime.StopApplication();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"AzureOpenAI": {
|
||||
"ChatDeploymentName": "",
|
||||
"Endpoint": ""
|
||||
// "ApiKey": "" /// Set this value in appsettings.Development.json, or using "dotnet user-secrets"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user