// Copyright (c) Microsoft. All rights reserved. using System.Text; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.HuggingFace; using Microsoft.SemanticKernel.PromptTemplates.Handlebars; using Microsoft.SemanticKernel.TextGeneration; namespace FunctionCalling; /// /// The following example shows how to use Semantic Kernel with the HuggingFace /// to implement function calling with the Nexus Raven model. /// /// The test output helper. public class NexusRaven_FunctionCalling(ITestOutputHelper output) : BaseTest(output) { /// /// Nexus Raven endpoint /// private Uri RavenEndpoint => new("http://nexusraven.nexusflow.ai"); /// /// Invokes the Nexus Raven model using Text Generation. /// [Fact] public async Task InvokeTextGenerationAsync() { Kernel kernel = Kernel.CreateBuilder() .AddHuggingFaceTextGeneration(endpoint: RavenEndpoint) .Build(); var textGeneration = kernel.GetRequiredService(); var prompt = "What is deep learning?"; var result = await textGeneration.GetTextContentsAsync(prompt); Console.WriteLine(result[0].ToString()); } /// /// Invokes the Nexus Raven model with Function Calling. /// [Fact] public async Task InvokeTextGenerationWithFunctionCallingAsync() { using var handler = new LoggingHandler(new HttpClientHandler(), this.Output); using var httpClient = new HttpClient(handler); Kernel kernel = Kernel.CreateBuilder() .AddHuggingFaceTextGeneration( endpoint: RavenEndpoint, httpClient: httpClient) .Build(); var plugin = ImportFunctions(kernel); var textGeneration = kernel.GetRequiredService(); // This Handlebars template is used to format the available KernelFunctions so // they can be understood by the NexusRaven model. The function name, signature and // description must be provided. NexusRaven can reason over the list of functions and // determine which ones need to be called for the current query. var template = """" {{#each (functions)}} Function: {{Name}}{{Signature}} """ {{Description}} """ {{/each}} User Query:{{prompt}} """"; var prompt = "What is the weather like in Dublin?"; var functions = plugin.Select(f => new FunctionDefinition { Name = f.Name, Description = f.Description, Signature = CreateSignature(f) }).ToList(); var executionSettings = new HuggingFacePromptExecutionSettings { Temperature = 0.001F, MaxNewTokens = 1024, ReturnFullText = false, DoSample = false }; // , Stop = [""] KernelArguments arguments = new(executionSettings) { { "prompt", prompt }, { "functions", functions } }; var factory = new HandlebarsPromptTemplateFactory(); var promptTemplate = factory.Create(new PromptTemplateConfig(template) { TemplateFormat = "handlebars" }); var rendered = await promptTemplate.RenderAsync(kernel, arguments); Console.WriteLine(" Prompt:\n===================="); Console.WriteLine(rendered); var function = kernel.CreateFunctionFromPrompt(template, templateFormat: "handlebars", promptTemplateFactory: new HandlebarsPromptTemplateFactory()); var result = await kernel.InvokeAsync(function, arguments); Console.WriteLine("\n Response:\n===================="); Console.WriteLine(result.ToString()); } // The signature must be Python compliant and currently only supports primitive values private static string CreateSignature(KernelFunction function) { var signature = new StringBuilder(); var parameters = function.Metadata.Parameters; signature.Append('('); foreach (var parameter in parameters) { signature.Append(parameter.Name).Append(':').Append(GetType(parameter)); } signature.Append(')'); return signature.ToString(); } private static string GetType(KernelParameterMetadata parameter) { if (parameter.Schema is not null) { var rootElement = parameter.Schema.RootElement; if (rootElement.TryGetProperty("type", out var type)) { return type.GetString() ?? string.Empty; } } return string.Empty; } private static KernelPlugin ImportFunctions(Kernel kernel) { return kernel.ImportPluginFromFunctions("WeatherPlugin", [ kernel.CreateFunctionFromMethod( (string cityName) => "12°C\nWind: 11 KMPH\nHumidity: 48%\nMostly cloudy", "GetWeatherForCity", "Gets the current weather for the specified city", new List { new("cityName") { Description = "The city name", ParameterType = string.Empty.GetType() } }), ]); } /// /// Function definition for use with Nexus Raven. /// private sealed class FunctionDefinition { public string Name { get; init; } public string Signature { get; init; } public string Description { get; init; } } }