Creating the Backend API¶
These changes are already available in the repository. These instructions walk you through the process followed to create the backend API from the Console application:
-
Start by creating a new directory:
-
Next create a new SDK .NET project:
-
Build project to confirm it is successful:
-
Add the following nuget packages:
-
Replace the contents of
Program.cs
in the project directory with the following code. This file initializes and loads the required services and configuration for the API, namely configuring CORS protection, enabling controllers for the API and exposing Swagger document:
using Microsoft.AspNetCore.Antiforgery;
using Extensions;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// See: https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Required to generate enumeration values in Swagger doc
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddOutputCache();
builder.Services.AddAntiforgery(options => {
options.HeaderName = "X-CSRF-TOKEN-HEADER";
options.FormFieldName = "X-CSRF-TOKEN-FORM"; });
builder.Services.AddHttpClient();
builder.Services.AddDistributedMemoryCache();
// Add Semantic Kernel services
builder.Services.AddSkServices();
// Load user secrets
builder.Configuration.AddUserSecrets<Program>();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseOutputCache();
app.UseRouting();
app.UseCors();
app.UseAntiforgery();
app.MapControllers();
app.Use(next => context =>
{
var antiforgery = app.Services.GetRequiredService<IAntiforgery>();
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens?.RequestToken ?? string.Empty, new CookieOptions() { HttpOnly = false });
return next(context);
});
app.Map("/", () => Results.Redirect("/swagger"));
app.MapControllerRoute(
"default",
"{controller=ChatController}");
app.Run();
-
Next we need to create
Extensions
directory to and add service extensions: -
In the
Extensions
directory create aServiceExtensions.cs
class with the following code to initialie the semantic kernel:using Core.Utilities.Config; using Core.Utilities.Models; // Add import for Plugins using Core.Utilities.Plugins; // Add import required for StockService using Core.Utilities.Services; using Microsoft.SemanticKernel; namespace Extensions; public static class ServiceExtensions { public static void AddSkServices(this IServiceCollection services) { services.AddSingleton<Kernel>(_ => { IKernelBuilder builder = KernelBuilderProvider.CreateKernelWithChatCompletion(); // Enable tracing builder.Services.AddLogging(services => services.AddConsole().SetMinimumLevel(LogLevel.Trace)); Kernel kernel = builder.Build(); // Step 2 - Initialize Time plugin and registration in the kernel kernel.Plugins.AddFromObject(new TimeInformationPlugin()); // Step 6 - Initialize Stock Data Plugin and register it in the kernel HttpClient httpClient = new(); StockDataPlugin stockDataPlugin = new(new StocksService(httpClient)); kernel.Plugins.AddFromObject(stockDataPlugin); return kernel; }); } }
-
Next we need to create a
Controllers
directory to add REST API controller classes: -
Within the
Controllers
directory create aChatController.cs
file which exposes a reply method mapped to thechat
path and the HTTP Post method:using Core.Utilities.Models; using Core.Utilities.Extensions; // Add import required for StockService using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; // Add ChatCompletion import using Microsoft.SemanticKernel.ChatCompletion; // Temporarily added to enable Semantic Kernel tracing using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Mvc; namespace Controllers; [ApiController] [Route("sk")] public class ChatController : ControllerBase { private readonly Kernel _kernel; private readonly OpenAIPromptExecutionSettings _promptExecutionSettings; public ChatController(Kernel kernel) { _kernel = kernel; _promptExecutionSettings = new() { // Step 3 - Add Auto invoke kernel functions as the tool call behavior ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; } [HttpPost("/chat")] public async Task<ChatResponse> ReplyAsync([FromBody]ChatRequest request) { // Get chatCompletionService and initialize chatHistory wiht system prompt var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>(); var chatHistory = new ChatHistory(); if (request.MessageHistory.Count == 0) { chatHistory.AddSystemMessage("You are a friendly financial advisor that only emits financial advice in a creative and funny tone"); } else { chatHistory = request.ToChatHistory(); } // Initialize fullMessage variable and add user input to chat history string fullMessage = ""; if (request.InputMessage != null) { chatHistory.AddUserMessage(request.InputMessage); // Step 4 - Provide promptExecutionSettings and kernel arguments await foreach (var chatUpdate in chatCompletionService.GetStreamingChatMessageContentsAsync(chatHistory, _promptExecutionSettings, _kernel)) { Console.Write(chatUpdate.Content); fullMessage += chatUpdate.Content ?? ""; } chatHistory.AddAssistantMessage(fullMessage); } var chatResponse = new ChatResponse(fullMessage, chatHistory.FromChatHistory()); return chatResponse; } }
Running the Backend API locally¶
-
To run API locally first copy valid
appsettings.json
from completedLessons/Lesson3
intobackend
directory: -
Next run using
dotnet run
: -
Application will start on specified port (port may be different). Navigate to http://localhost:5000 or corresponding forwarded address (if using Github CodeSpace) and it should redirect you to the swagger UI page.
-
You can either test the API using the "Try it out" feature from within Swagger UI, or via command line using
curl
command: