Skip to content

Commit

Permalink
Merge pull request MicrosoftDocs#190 from MicrosoftDocs/repo_sync_wor…
Browse files Browse the repository at this point in the history
…king_branch

Confirm merge from repo_sync_working_branch to main to sync with https://github.com/MicrosoftDocs/semantic-kernel-docs (branch main)
  • Loading branch information
matthewbolanos authored Dec 10, 2023
2 parents b9b2b86 + e682856 commit d93bbba
Show file tree
Hide file tree
Showing 32 changed files with 304 additions and 142 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Net;
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Models;

#pragma warning disable CA1822

public class AIPluginJson
{
[Function("GetAIPluginJson")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = ".well-known/ai-plugin.json")] HttpRequestData req)
{
if (req is null)
{
throw new System.ArgumentNullException(nameof(req));
}

var currentDomain = $"{req.Url.Scheme}://{req.Url.Host}:{req.Url.Port}";

HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Microsoft.SemanticKernel.Orchestration;
using Models;

namespace Extensions;
namespace AIPlugins.AzureFunctions.Extensions;

public class AIPluginRunner : IAIPluginRunner
{
Expand All @@ -27,37 +27,28 @@ public AIPluginRunner(IKernel kernel, ILoggerFactory loggerFactory)
/// <param name="operationId"></param>
public async Task<HttpResponseData> RunAIPluginOperationAsync(HttpRequestData req, string operationId)
{
try
{
ContextVariables contextVariables = LoadContextVariablesFromRequest(req);
ContextVariables contextVariables = LoadContextVariablesFromRequest(req);

var appSettings = AppSettings.LoadSettings();
var appSettings = AppSettings.LoadSettings();
var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(),"Prompts");
var func = this._kernel.ImportPromptsFromDirectory(appSettings.AIPlugin.NameForModel, pluginsDirectory);

if (!this._kernel.Functions.TryGetFunction(
pluginName: appSettings.AIPlugin.NameForModel,
functionName: operationId,
out ISKFunction? function))
var result = await this._kernel.RunAsync(contextVariables, func[operationId]);
if (result.FunctionResults.Last() == null)
{
HttpResponseData errorResponse = req.CreateResponse(HttpStatusCode.BadRequest);
string? message = result?.FunctionResults.Last()?.ToString();
if (message != null)
{
HttpResponseData errorResponse = req.CreateResponse(HttpStatusCode.NotFound);
await errorResponse.WriteStringAsync($"Function {operationId} not found").ConfigureAwait(false);
return errorResponse;
await errorResponse.WriteStringAsync(message);
}

var result = await this._kernel.RunAsync(contextVariables, function).ConfigureAwait(false);

var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain;charset=utf-8");
await response.WriteStringAsync(result.GetValue<string>()!).ConfigureAwait(false);
return response;
}
#pragma warning disable CA1031
catch (System.Exception ex)
#pragma warning restore CA1031
{
HttpResponseData errorResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await errorResponse.WriteStringAsync(ex.Message).ConfigureAwait(false);
return errorResponse;
}

var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain;charset=utf-8");
await response.WriteStringAsync(result.GetValue<string>());
return response;
}

/// <summary>
Expand All @@ -66,12 +57,7 @@ public async Task<HttpResponseData> RunAIPluginOperationAsync(HttpRequestData re
/// <param name="req"></param>
protected static ContextVariables LoadContextVariablesFromRequest(HttpRequestData req)
{
if (req is null)
{
throw new System.ArgumentNullException(nameof(req));
}

ContextVariables contextVariables = new();
ContextVariables contextVariables = new ContextVariables();
foreach (string? key in req.Query.AllKeys)
{
if (!string.IsNullOrEmpty(key))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.Azure.Functions.Worker.Http;

namespace Extensions;

namespace AIPlugins.AzureFunctions.Extensions;
public interface IAIPluginRunner
{
public Task<HttpResponseData> RunAIPluginOperationAsync(HttpRequestData req, string operationId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel;
using Models;

namespace Extensions;

internal static class KernelBuilderExtensions
{
/// <summary>
/// Adds a text completion service to the list. It can be either an OpenAI or Azure OpenAI backend service.
/// Adds a chat completion service to the list. It can be either an OpenAI or Azure OpenAI backend service.
/// </summary>
/// <param name="kernelBuilder"></param>
/// <param name="kernelSettings"></param>
Expand All @@ -18,7 +14,7 @@ internal static KernelBuilder WithChatCompletionService(this KernelBuilder kerne
switch (kernelSettings.ServiceType.ToUpperInvariant())
{
case ServiceTypes.AzureOpenAI:
kernelBuilder.WithAzureChatCompletionService(deploymentName: kernelSettings.DeploymentOrModelId, endpoint: kernelSettings.Endpoint, apiKey: kernelSettings.ApiKey, serviceId: kernelSettings.ServiceId);
kernelBuilder.WithAzureOpenAIChatCompletionService(deploymentName: kernelSettings.DeploymentOrModelId, endpoint: kernelSettings.Endpoint, apiKey: kernelSettings.ApiKey, serviceId: kernelSettings.ServiceId);
break;

case ServiceTypes.OpenAI:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.Contracts;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.TemplateEngine;
using Microsoft.SemanticKernel.TemplateEngine.Basic;

namespace Extensions;

namespace AIPlugins.AzureFunctions.Extensions;

public static class KernelExtensions
{
public static IDictionary<string, ISKFunction> ImportPromptsFromDirectory(
this IKernel kernel, string pluginName, string promptDirectory)
{
Contract.Requires(kernel != null);

const string CONFIG_FILE = "config.json";
const string PROMPT_FILE = "skprompt.txt";

Expand All @@ -36,8 +35,10 @@ public static IDictionary<string, ISKFunction> ImportPromptsFromDirectory(
}

// Load prompt template
var template = new PromptTemplate(File.ReadAllText(promptPath), config, kernel!.PromptTemplateEngine);
var template = new BasicPromptTemplate(File.ReadAllText(promptPath), config);

// Prepare lambda wrapping AI logic
kernel.LoggerFactory.CreateLogger($"Registering function {pluginName}.{functionName} loaded from {dir}");
plugin[functionName] = kernel.RegisterSemanticFunction(pluginName, functionName, config, template);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;

public class Logo
{
[Function("GetLogo")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "logo.png")] HttpRequestData req)
{
// Return logo.png that's in the root of the project
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "image/png");

var logo = System.IO.File.ReadAllBytes("logo.png");
response.Body.Write(logo);

return response;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Reflection;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;

namespace Models;
Expand All @@ -14,7 +16,7 @@ public class AppSettings
public AIPluginSettings AIPlugin { get; set; }

/// <summary>
/// Load the kernel settings from appsettings.json if the file exists and if not attempt to use user secrets.
/// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets.
/// </summary>
public static AppSettings LoadSettings()
{
Expand All @@ -29,7 +31,7 @@ public static AppSettings LoadSettings()
{
Console.Error.WriteLine(
"Unable to load app settings, please provide configuration settings using instructions in the README.\n" +
"Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/azure-function/README.md#configuring-the-starter"
"Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/sk-csharp-chatgpt-plugin/README.md#configuring-the-starter"
);
throw new InvalidOperationException(ide.Message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;

namespace Models;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,71 @@
// Copyright (c) Microsoft. All rights reserved.

using Extensions;
using System.Text.Json;
using AIPlugins.AzureFunctions.Extensions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Models;

const string DefaultSemanticFunctionsFolder = "Prompts";
string semanticFunctionsFolder = Environment.GetEnvironmentVariable("SEMANTIC_PLUGINS_FOLDER") ?? DefaultSemanticFunctionsFolder;
string semanticFunctionsFolder = Environment.GetEnvironmentVariable("SEMANTIC_SKILLS_FOLDER") ?? DefaultSemanticFunctionsFolder;

var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
.ConfigureAppConfiguration(configuration =>
{
var config = configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
var builtConfig = config.Build();
})
.ConfigureServices((context, services) =>
{
services.Configure<JsonSerializerOptions>(options =>
{
// `ConfigureFunctionsWorkerDefaults` sets the default to ignore casing already.
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});

services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
{
var options = new OpenApiConfigurationOptions()
{
Info = new OpenApiInfo()
{
Version = "1.0.0",
Title = "My Plugin",
Description = "This plugin does..."
},
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
OpenApiVersion = OpenApiVersionType.V3,
//IncludeRequestingHostName = true,
ForceHttps = false,
ForceHttp = false,
};

return options;
});
services
.AddScoped<IKernel>((providers) =>
{
// This will be called each time a new Kernel is needed

// Get a logger instance
ILoggerFactory loggerFactory = providers
.GetRequiredService<ILoggerFactory>();
ILogger<IKernel> logger = providers
.GetRequiredService<ILoggerFactory>()
.CreateLogger<IKernel>();

// Register your AI Providers...
var appSettings = AppSettings.LoadSettings();
IKernel kernel = new KernelBuilder()
.WithChatCompletionService(appSettings.Kernel)
.WithLoggerFactory(loggerFactory)
.WithLoggerFactory(providers.GetRequiredService<ILoggerFactory>())
.Build();

// Load your semantic functions...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"kernel": {
"serviceType": "AzureOpenAI",
"serviceId": "text-davinci-003",
"deploymentOrModelId": "text-davinci-003",
"serviceId": "gpt-35-turbo",
"deploymentOrModelId": "gpt-35-turbo",
"endpoint": "https:// ... your endpoint ... .openai.azure.com/"
},
"aiPlugin": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"kernel": {
"serviceType": "OpenAI",
"serviceId": "text-davinci-003",
"deploymentOrModelId": "text-davinci-003",
"serviceId": "gpt-3.5-turbo",
"deploymentOrModelId": "gpt-3.5-turbo",
"orgId": ""
},
"aiPlugin": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft. All rights reserved.

namespace skchatgptazurefunction.PluginShared;

/// <summary>
/// This class represents the plugin API specification.
/// </summary>
public class PluginApi
{
/// <summary>
/// The API specification
/// </summary>
public string Type { get; set; } = "openapi";

/// <summary>
/// URL used to fetch the specification
/// </summary>
public string Url { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text.Json.Serialization;

namespace skchatgptazurefunction.PluginShared;

/// <summary>
/// This class represents the OpenAI plugin authentication schema.
/// </summary>
public class PluginAuth
{
/// <summary>
/// Tokens for API key authentication
/// </summary>
public class VerificationTokens
{
/// <summary>
/// The API key
/// </summary>
public string OpenAI { get; set; } = string.Empty;
}

/// <summary>
/// The authentication schema
/// Supported values: none, service_http, user_http
/// </summary>
public string Type { get; set; } = "none";

/// <summary>
/// Manifest schema version
/// </summary>
[JsonPropertyName("authorization_type")]
public string AuthorizationType { get; } = "bearer";

/// <summary>
/// Tokens for API key authentication
/// </summary>
[JsonPropertyName("verification_tokens")]
public VerificationTokens Tokens { get; set; } = new VerificationTokens();
}
Loading

0 comments on commit d93bbba

Please sign in to comment.