From e804862973c617c0d642ed123f4a75c8ce81bcd9 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Wed, 15 Jan 2025 21:57:52 +0000 Subject: [PATCH 1/5] add funciton name policy to control funciton fqn creation and parsing --- .../FunctionCalling_FunctionNamePolicy.cs | 170 ++++++++++++++++++ .../Core/AzureClientCore.cs | 15 +- .../AzureOpenAIServiceCollectionExtensions.cs | 18 +- .../AzureOpenAIChatCompletionService.cs | 18 +- .../Core/ClientCore.ChatCompletion.cs | 41 +++-- .../Connectors.OpenAI/Core/ClientCore.cs | 16 +- .../Connectors.OpenAI/Core/OpenAIFunction.cs | 55 ++++++ .../OpenAIKernelBuilderExtensions.cs | 20 ++- .../Services/OpenAIChatCompletionService.cs | 22 ++- .../Functions/Policies/FunctionNamePolicy.cs | 96 ++++++++++ .../Policies/GetFunctionFqnContext.cs | 23 +++ .../Policies/ParseFunctionFqnContext.cs | 23 +++ .../Policies/ParseFunctionFqnDelegate.cs | 13 ++ 13 files changed, 481 insertions(+), 49 deletions(-) create mode 100644 dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs create mode 100644 dotnet/src/SemanticKernel.Abstractions/Functions/Policies/FunctionNamePolicy.cs create mode 100644 dotnet/src/SemanticKernel.Abstractions/Functions/Policies/GetFunctionFqnContext.cs create mode 100644 dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnContext.cs create mode 100644 dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnDelegate.cs diff --git a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs new file mode 100644 index 000000000000..fff555bb80ea --- /dev/null +++ b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; + +namespace FunctionCalling; + +/// +/// These samples demonstrate how to use a custom function name policy to specify Fully Qualified Name (FQN) separator and parse function FQN. +/// +public class FunctionCalling_FunctionNamePolicy(ITestOutputHelper output) : BaseTest(output) +{ + /// + /// This sample demonstrates how to use an underscore as the function FQN separator. + /// This can be useful if the default separator, which is a hyphen ("-"), is not suitable for your needs. + /// For example, if your plugin name is "foo" and your function name is "bar," the function FQN will be "foo_bar" instead of "foo-bar". + /// + [Fact] + public async Task UseUnderscoreAsFunctionFqnSeparatorAsync() + { + // Create an instance of the kernel builder. + IKernelBuilder builder = Kernel.CreateBuilder(); + + // Register the OpenAI chat completion service and specify a custom function name policy that uses an underscore as the separator. + builder.AddOpenAIChatCompletion( + TestConfiguration.OpenAI.ChatModelId, + TestConfiguration.OpenAI.ApiKey, + functionNamePolicy: FunctionNamePolicy.Custom("_")); + + // Build the kernel and import the UtilsPlugin. + Kernel kernel = builder.Build(); + kernel.ImportPluginFromType(); + + IChatCompletionService chatCompletionService = kernel.GetRequiredService(); + + ChatMessageContent result = await chatCompletionService.GetChatMessageContentAsync( + "Given the current time of day and weather, what is the likely color of the sky in Boston?", + new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }, + kernel); + + // Assert + Console.WriteLine(result); + + // Expected output: "The likely color of the sky in Boston is gray due to the current rainy weather." + } + + /// + /// This sample demonstrates how to prevent the plugin name from being included in the FQN of a function. + /// Instead, only the function name will be used for the FQN. + /// For example, if your plugin name is "foo" and your function name is "bar" the function's FQN will be "bar" instead of "foo-bar." + /// + [Fact] + public async Task UseOnlyFunctionNameAndDoNotUsePluginNameForFqnAsync() + { + // Create an instance of the kernel builder. + IKernelBuilder builder = Kernel.CreateBuilder(); + + // Register the OpenAI chat completion service and specify a custom function name policy that uses an underscore as the separator. + builder.AddOpenAIChatCompletion( + TestConfiguration.OpenAI.ChatModelId, + TestConfiguration.OpenAI.ApiKey, + functionNamePolicy: FunctionNamePolicy.FunctionNameOnly); + + // Build the kernel and import the UtilsPlugin. + Kernel kernel = builder.Build(); + kernel.ImportPluginFromType(); + + IChatCompletionService chatCompletionService = kernel.GetRequiredService(); + + ChatMessageContent result = await chatCompletionService.GetChatMessageContentAsync( + "Given the current time of day and weather, what is the likely color of the sky in Boston?", + new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }, + kernel); + + // Assert + Console.WriteLine(result); + + // Expected output: "The likely color of the sky in Boston is gray due to the current rainy weather." + } + + /// + /// This sample demonstrates how to use a custom function FQN parser + /// to handle a hallucinated function name generated by AI model. + /// It addresses the scenario where the AI model calls the function using a different separator + /// than the one used in the function's original declaration. + /// For example, you function is advertised as "foo-bar" but the AI model calls it as "foo_bar" + /// + [Fact] + public async Task RecoverHallucinatedFunctionNameAsync() + { + // Create an instance of the kernel builder. + IKernelBuilder builder = Kernel.CreateBuilder(); + + // Define a custom function FQN parser that can handle a hallucinated function name. + static (string? PluginName, string FunctioName) ParseFunctionFqn(ParseFunctionFqnContext context) + { + // Try to use use hyphen as separator. + var parts = context.FunctionFqn.Split('-'); + if (parts.Length == 2) + { + // Check if the function registered in the kernel + if (context.Kernel is { } kernel && kernel.Plugins.TryGetFunction(parts[0], parts[1], out _)) + { + return (parts[0], parts[1]); + } + } + + // If hyphen is not found, try to use underscore as separator. This approach presumes the underscore symbol is not used in function name. + parts = context.FunctionFqn.Split('_'); + if (parts.Length == 2) + { + // Check if the function registered in the kernel + if (context.Kernel is { } kernel && kernel.Plugins.TryGetFunction(parts[0], parts[1], out _)) + { + return (parts[0], parts[1]); + } + } + + // If no separator is found, return the function name as is. + return (null, context.FunctionFqn); + } + + // Register the OpenAI chat completion service and specify a custom function name policy that uses an underscore as the separator. + builder.AddOpenAIChatCompletion( + TestConfiguration.OpenAI.ChatModelId, + TestConfiguration.OpenAI.ApiKey, + functionNamePolicy: FunctionNamePolicy.Custom("-", ParseFunctionFqn)); + + // Build the kernel and import the UtilsPlugin. + Kernel kernel = builder.Build(); + kernel.ImportPluginFromType(); + + IChatCompletionService chatCompletionService = kernel.GetRequiredService(); + + ChatMessageContent result = await chatCompletionService.GetChatMessageContentAsync( + "Given the current time of day and weather, what is the likely color of the sky in Boston?", + new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }, + kernel); + + // Assert + Console.WriteLine(result); + + // Expected output: "The likely color of the sky in Boston is gray due to the current rainy weather." + } + + public sealed class UtilsPlugin + { + [KernelFunction] + public string GetCurrentUtcDateTime() + { + return DateTime.UtcNow.ToString("R"); + } + + [KernelFunction] + public string GetWeatherForCity(string cityName, string currentDateTime) + { + return cityName switch + { + "Boston" => "61 and rainy", + "London" => "55 and cloudy", + "Miami" => "80 and sunny", + "Paris" => "60 and rainy", + "Tokyo" => "50 and sunny", + "Sydney" => "75 and sunny", + "Tel Aviv" => "80 and sunny", + _ => "31 and snowing", + }; + } + } +} diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.cs index 5ad45701a921..dd05b80a9d0a 100644 --- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.cs +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/AzureClientCore.cs @@ -43,13 +43,15 @@ internal partial class AzureClientCore : ClientCore /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. /// Optional Azure OpenAI API version, see available here + /// The function name policy. internal AzureClientCore( string deploymentName, string endpoint, string apiKey, HttpClient? httpClient = null, ILogger? logger = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNullOrWhiteSpace(deploymentName); Verify.NotNullOrWhiteSpace(endpoint); @@ -63,6 +65,7 @@ internal AzureClientCore( this.Endpoint = new Uri(endpoint); this.Client = new AzureOpenAIClient(this.Endpoint, new ApiKeyCredential(apiKey), options); this.FunctionCallsProcessor = new FunctionCallsProcessor(this.Logger); + this.FunctionNamePolicy = functionNamePolicy ?? FunctionNamePolicy.Default; this.AddAttribute(DeploymentNameKey, deploymentName); } @@ -76,13 +79,15 @@ internal AzureClientCore( /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. /// Optional Azure OpenAI API version, see available here + /// The function name policy. internal AzureClientCore( string deploymentName, string endpoint, TokenCredential credential, HttpClient? httpClient = null, ILogger? logger = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNullOrWhiteSpace(deploymentName); Verify.NotNullOrWhiteSpace(endpoint); @@ -95,6 +100,7 @@ internal AzureClientCore( this.Endpoint = new Uri(endpoint); this.Client = new AzureOpenAIClient(this.Endpoint, credential, options); this.FunctionCallsProcessor = new FunctionCallsProcessor(this.Logger); + this.FunctionNamePolicy = functionNamePolicy ?? FunctionNamePolicy.Default; this.AddAttribute(DeploymentNameKey, deploymentName); } @@ -107,10 +113,12 @@ internal AzureClientCore( /// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// Custom . /// The to use for logging. If null, no logging will be performed. + /// The function name policy. internal AzureClientCore( string deploymentName, AzureOpenAIClient openAIClient, - ILogger? logger = null) + ILogger? logger = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNullOrWhiteSpace(deploymentName); Verify.NotNull(openAIClient); @@ -119,6 +127,7 @@ internal AzureClientCore( this.DeploymentName = deploymentName; this.Client = openAIClient; this.FunctionCallsProcessor = new FunctionCallsProcessor(this.Logger); + this.FunctionNamePolicy = functionNamePolicy ?? FunctionNamePolicy.Default; this.AddAttribute(DeploymentNameKey, deploymentName); } diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs index c015f4e5b7d5..86f8e2e7a540 100644 --- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs @@ -38,6 +38,7 @@ public static class AzureOpenAIServiceCollectionExtensions /// A local identifier for the given AI service /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Optional Azure OpenAI API version, see available here + /// The function name policy. /// The same instance as . public static IServiceCollection AddAzureOpenAIChatCompletion( this IServiceCollection services, @@ -46,7 +47,8 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( string apiKey, string? serviceId = null, string? modelId = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(endpoint); @@ -60,7 +62,7 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( HttpClientProvider.GetHttpClient(serviceProvider), apiVersion); - return new(deploymentName, client, modelId, serviceProvider.GetService()); + return new(deploymentName, client, modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); }; services.AddKeyedSingleton(serviceId, factory); @@ -79,6 +81,7 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( /// A local identifier for the given AI service /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Optional Azure OpenAI API version, see available here + /// The function name policy. /// The same instance as . public static IServiceCollection AddAzureOpenAIChatCompletion( this IServiceCollection services, @@ -87,7 +90,8 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( TokenCredential credentials, string? serviceId = null, string? modelId = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(endpoint); @@ -101,7 +105,7 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( HttpClientProvider.GetHttpClient(serviceProvider), apiVersion); - return new(deploymentName, client, modelId, serviceProvider.GetService()); + return new(deploymentName, client, modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); }; services.AddKeyedSingleton(serviceId, factory); @@ -118,19 +122,21 @@ public static IServiceCollection AddAzureOpenAIChatCompletion( /// to use for the service. If null, one must be available in the service provider when this service is resolved. /// A local identifier for the given AI service /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart + /// The function name policy. /// The same instance as . public static IServiceCollection AddAzureOpenAIChatCompletion( this IServiceCollection services, string deploymentName, AzureOpenAIClient? azureOpenAIClient = null, string? serviceId = null, - string? modelId = null) + string? modelId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(deploymentName); Func factory = (serviceProvider, _) => - new(deploymentName, azureOpenAIClient ?? serviceProvider.GetRequiredService(), modelId, serviceProvider.GetService()); + new(deploymentName, azureOpenAIClient ?? serviceProvider.GetRequiredService(), modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); services.AddKeyedSingleton(serviceId, factory); services.AddKeyedSingleton(serviceId, factory); diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAIChatCompletionService.cs index 0b52d1b2395c..9d00a343c022 100644 --- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAIChatCompletionService.cs @@ -31,6 +31,7 @@ public sealed class AzureOpenAIChatCompletionService : IChatCompletionService, I /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. /// Optional Azure OpenAI API version, see available here + /// The function name policy. public AzureOpenAIChatCompletionService( string deploymentName, string endpoint, @@ -38,9 +39,10 @@ public AzureOpenAIChatCompletionService( string? modelId = null, HttpClient? httpClient = null, ILoggerFactory? loggerFactory = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { - this._client = new(deploymentName, endpoint, apiKey, httpClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService)), apiVersion); + this._client = new(deploymentName, endpoint, apiKey, httpClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService)), apiVersion, functionNamePolicy: functionNamePolicy); this._client.AddAttribute(AIServiceExtensions.ModelIdKey, modelId); } @@ -55,6 +57,7 @@ public AzureOpenAIChatCompletionService( /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. /// Optional Azure OpenAI API version, see available here + /// The function name policy. public AzureOpenAIChatCompletionService( string deploymentName, string endpoint, @@ -62,9 +65,10 @@ public AzureOpenAIChatCompletionService( string? modelId = null, HttpClient? httpClient = null, ILoggerFactory? loggerFactory = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { - this._client = new(deploymentName, endpoint, credentials, httpClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService)), apiVersion); + this._client = new(deploymentName, endpoint, credentials, httpClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService)), apiVersion, functionNamePolicy: functionNamePolicy); this._client.AddAttribute(AIServiceExtensions.ModelIdKey, modelId); } @@ -75,13 +79,15 @@ public AzureOpenAIChatCompletionService( /// Custom . /// Azure OpenAI model id, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// The to use for logging. If null, no logging will be performed. + /// The function name policy. public AzureOpenAIChatCompletionService( string deploymentName, AzureOpenAIClient azureOpenAIClient, string? modelId = null, - ILoggerFactory? loggerFactory = null) + ILoggerFactory? loggerFactory = null, + FunctionNamePolicy? functionNamePolicy = null) { - this._client = new(deploymentName, azureOpenAIClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService))); + this._client = new(deploymentName, azureOpenAIClient, loggerFactory?.CreateLogger(typeof(AzureOpenAIChatCompletionService)), functionNamePolicy: functionNamePolicy); this._client.AddAttribute(AIServiceExtensions.ModelIdKey, modelId); } diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs index 6b6a039a0acd..bb9d85c49b12 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs @@ -145,7 +145,7 @@ internal async Task> GetChatMessageContentsAsy for (int requestIndex = 0; ; requestIndex++) { - var chatForRequest = CreateChatCompletionMessages(chatExecutionSettings, chatHistory); + var chatForRequest = this.CreateChatCompletionMessages(chatExecutionSettings, chatHistory); var functionCallingConfig = this.GetFunctionCallingConfiguration(kernel, chatExecutionSettings, chatHistory, requestIndex); @@ -177,7 +177,7 @@ internal async Task> GetChatMessageContentsAsy throw; } - chatMessageContent = this.CreateChatMessageContent(chatCompletion, targetModel); + chatMessageContent = this.CreateChatMessageContent(chatCompletion, targetModel, kernel); activity?.SetCompletionResponse([chatMessageContent], chatCompletion.Usage.InputTokenCount, chatCompletion.Usage.OutputTokenCount); } @@ -194,7 +194,7 @@ internal async Task> GetChatMessageContentsAsy chatMessageContent, chatHistory, requestIndex, - (FunctionCallContent content) => IsRequestableTool(chatOptions.Tools, content), + (FunctionCallContent content) => this.IsRequestableTool(chatOptions.Tools, content), functionCallingConfig.Options ?? new FunctionChoiceBehaviorOptions(), kernel, isStreaming: false, @@ -237,7 +237,7 @@ internal async IAsyncEnumerable GetStreamingC for (int requestIndex = 0; ; requestIndex++) { - var chatForRequest = CreateChatCompletionMessages(chatExecutionSettings, chatHistory); + var chatForRequest = this.CreateChatCompletionMessages(chatExecutionSettings, chatHistory); var functionCallingConfig = this.GetFunctionCallingConfiguration(kernel, chatExecutionSettings, chatHistory, requestIndex); @@ -347,7 +347,7 @@ internal async IAsyncEnumerable GetStreamingC ref toolCallIdsByIndex, ref functionNamesByIndex, ref functionArgumentBuildersByIndex); // Translate all entries into FunctionCallContent instances for diagnostics purposes. - functionCallContents = this.GetFunctionCallContents(toolCalls).ToArray(); + functionCallContents = this.GetFunctionCallContents(toolCalls, kernel).ToArray(); } finally { @@ -378,7 +378,7 @@ internal async IAsyncEnumerable GetStreamingC chatMessageContent, chatHistory, requestIndex, - (FunctionCallContent content) => IsRequestableTool(chatOptions.Tools, content), + (FunctionCallContent content) => this.IsRequestableTool(chatOptions.Tools, content), functionCallingConfig.Options ?? new FunctionChoiceBehaviorOptions(), kernel, isStreaming: true, @@ -562,12 +562,12 @@ protected virtual ChatCompletionOptions CreateChatCompletionOptions( } /// Checks if a tool call is for a function that was defined. - private static bool IsRequestableTool(IList tools, FunctionCallContent functionCallContent) + private bool IsRequestableTool(IList tools, FunctionCallContent functionCallContent) { for (int i = 0; i < tools.Count; i++) { if (tools[i].Kind == ChatToolKind.Function && - string.Equals(tools[i].FunctionName, FunctionName.ToFullyQualifiedName(functionCallContent.FunctionName, functionCallContent.PluginName, OpenAIFunction.NameSeparator), StringComparison.OrdinalIgnoreCase)) + string.Equals(tools[i].FunctionName, this.FunctionNamePolicy.GetFunctionFqn(new(functionCallContent.FunctionName) { PluginName = functionCallContent.PluginName }), StringComparison.OrdinalIgnoreCase)) { return true; } @@ -603,7 +603,7 @@ private static ChatHistory CreateNewChat(string? text = null, OpenAIPromptExecut return chat; } - private static List CreateChatCompletionMessages(OpenAIPromptExecutionSettings executionSettings, ChatHistory chatHistory) + private List CreateChatCompletionMessages(OpenAIPromptExecutionSettings executionSettings, ChatHistory chatHistory) { List messages = []; @@ -614,13 +614,13 @@ private static List CreateChatCompletionMessages(OpenAIPromptExecut foreach (var message in chatHistory) { - messages.AddRange(CreateRequestMessages(message)); + messages.AddRange(this.CreateRequestMessages(message)); } return messages; } - private static List CreateRequestMessages(ChatMessageContent message) + private List CreateRequestMessages(ChatMessageContent message) { if (message.Role == AuthorRole.System) { @@ -740,9 +740,11 @@ private static List CreateRequestMessages(ChatMessageContent messag continue; } + var fqn = this.FunctionNamePolicy.GetFunctionFqn(new(callRequest.FunctionName) { PluginName = callRequest.PluginName }); + var argument = JsonSerializer.Serialize(callRequest.Arguments); - toolCalls.Add(ChatToolCall.CreateFunctionToolCall(callRequest.Id, FunctionName.ToFullyQualifiedName(callRequest.FunctionName, callRequest.PluginName, OpenAIFunction.NameSeparator), BinaryData.FromString(argument ?? string.Empty))); + toolCalls.Add(ChatToolCall.CreateFunctionToolCall(callRequest.Id, fqn, BinaryData.FromString(argument ?? string.Empty))); } // This check is necessary to prevent an exception that will be thrown if the toolCalls collection is empty. @@ -804,11 +806,11 @@ private static ChatMessageContentPart GetImageContentItem(ImageContent imageCont return null; } - private OpenAIChatMessageContent CreateChatMessageContent(OpenAIChatCompletion completion, string targetModel) + private OpenAIChatMessageContent CreateChatMessageContent(OpenAIChatCompletion completion, string targetModel, Kernel? kernel) { var message = new OpenAIChatMessageContent(completion, targetModel, this.GetChatCompletionMetadata(completion)); - message.Items.AddRange(this.GetFunctionCallContents(completion.ToolCalls)); + message.Items.AddRange(this.GetFunctionCallContents(completion.ToolCalls, kernel)); return message; } @@ -828,7 +830,7 @@ private OpenAIChatMessageContent CreateChatMessageContent(ChatMessageRole chatRo return message; } - private List GetFunctionCallContents(IEnumerable toolCalls) + private List GetFunctionCallContents(IEnumerable toolCalls, Kernel? kernel) { List result = []; @@ -863,11 +865,11 @@ private List GetFunctionCallContents(IEnumerable protected FunctionCallsProcessor FunctionCallsProcessor { get; set; } + /// + /// The function name policy. + /// + protected FunctionNamePolicy FunctionNamePolicy { get; set; } + /// /// Initializes a new instance of the class. /// @@ -80,18 +85,22 @@ internal partial class ClientCore /// OpenAI compatible API endpoint. /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. + /// The function name policy. internal ClientCore( string? modelId = null, string? apiKey = null, string? organizationId = null, Uri? endpoint = null, HttpClient? httpClient = null, - ILogger? logger = null) + ILogger? logger = null, + FunctionNamePolicy? functionNamePolicy = null) { this.Logger = logger ?? NullLogger.Instance; this.FunctionCallsProcessor = new FunctionCallsProcessor(this.Logger); + this.FunctionNamePolicy = functionNamePolicy ?? FunctionNamePolicy.Default; + // Empty constructor will be used when inherited by a specialized Client. if (modelId is null && apiKey is null @@ -143,10 +152,12 @@ internal ClientCore( /// OpenAI model Id /// Custom . /// The to use for logging. If null, no logging will be performed. + /// The function name policy. internal ClientCore( string? modelId, OpenAIClient openAIClient, - ILogger? logger = null) + ILogger? logger = null, + FunctionNamePolicy? functionNamePolicy = null) { // Model Id may not be required when other services. i.e: File Service. if (modelId is not null) @@ -160,6 +171,7 @@ internal ClientCore( this.Logger = logger ?? NullLogger.Instance; this.Client = openAIClient; this.FunctionCallsProcessor = new FunctionCallsProcessor(this.Logger); + this.FunctionNamePolicy = functionNamePolicy ?? FunctionNamePolicy.Default; } /// diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Core/OpenAIFunction.cs b/dotnet/src/Connectors/Connectors.OpenAI/Core/OpenAIFunction.cs index af082282e7e8..c7df948f77ce 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Core/OpenAIFunction.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Core/OpenAIFunction.cs @@ -197,6 +197,61 @@ public ChatTool ToFunctionDefinition(bool allowStrictSchemaAdherence) ); } + /// + /// Converts the representation to the OpenAI SDK's + /// representation. + /// + /// A containing all the function information. + internal ChatTool ToFunctionDefinition(bool allowStrictSchemaAdherence, string fullyQualifiedName) + { + BinaryData resultParameters = allowStrictSchemaAdherence ? s_zeroFunctionParametersSchema_strict : s_zeroFunctionParametersSchema; + + IReadOnlyList? parameters = this.Parameters; + if (parameters is { Count: > 0 }) + { + var properties = new Dictionary(); + var required = new List(); + + foreach (var parameter in parameters) + { + var parameterSchema = (parameter.Schema, allowStrictSchemaAdherence) switch + { + (not null, true) => GetSanitizedSchemaForStrictMode(parameter.Schema, !parameter.IsRequired && allowStrictSchemaAdherence), + (not null, false) => parameter.Schema, + (null, _) => GetDefaultSchemaForTypelessParameter(parameter.Description, allowStrictSchemaAdherence), + }; + properties.Add(parameter.Name, parameterSchema); + if (parameter.IsRequired || allowStrictSchemaAdherence) + { + required.Add(parameter.Name); + } + } + + resultParameters = allowStrictSchemaAdherence + ? BinaryData.FromObjectAsJson(new + { + type = "object", + required, + properties, + additionalProperties = false + }) + : BinaryData.FromObjectAsJson(new + { + type = "object", + required, + properties, + }); + } + + return ChatTool.CreateFunctionTool + ( + functionName: fullyQualifiedName, + functionDescription: this.Description, + functionParameters: resultParameters, + functionSchemaIsStrict: allowStrictSchemaAdherence + ); + } + /// Gets a for a typeless parameter with the specified description, defaulting to typeof(string) private static KernelJsonSchema GetDefaultSchemaForTypelessParameter(string? description, bool allowStrictSchemaAdherence) { diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIKernelBuilderExtensions.cs index c322ead2b671..847c0cba5493 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIKernelBuilderExtensions.cs @@ -277,6 +277,7 @@ public static IKernelBuilder AddOpenAIFiles( /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations. /// A local identifier for the given AI service /// The HttpClient to use with this service. + /// The function name policy. /// The same instance as . public static IKernelBuilder AddOpenAIChatCompletion( this IKernelBuilder builder, @@ -284,7 +285,8 @@ public static IKernelBuilder AddOpenAIChatCompletion( string apiKey, string? orgId = null, string? serviceId = null, - HttpClient? httpClient = null) + HttpClient? httpClient = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(modelId); @@ -295,7 +297,8 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) apiKey, orgId, HttpClientProvider.GetHttpClient(httpClient, serviceProvider), - serviceProvider.GetService()); + serviceProvider.GetService(), + functionNamePolicy: functionNamePolicy); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); @@ -310,18 +313,20 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) /// OpenAI model id /// to use for the service. If null, one must be available in the service provider when this service is resolved. /// A local identifier for the given AI service + /// The function name policy. /// The same instance as . public static IKernelBuilder AddOpenAIChatCompletion( this IKernelBuilder builder, string modelId, OpenAIClient? openAIClient = null, - string? serviceId = null) + string? serviceId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(modelId); OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) => - new(modelId, openAIClient ?? serviceProvider.GetRequiredService(), serviceProvider.GetService()); + new(modelId, openAIClient ?? serviceProvider.GetRequiredService(), serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); @@ -339,6 +344,7 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations. /// A local identifier for the given AI service /// The HttpClient to use with this service. + /// The function name policy. /// The same instance as . [Experimental("SKEXP0010")] public static IKernelBuilder AddOpenAIChatCompletion( @@ -348,7 +354,8 @@ public static IKernelBuilder AddOpenAIChatCompletion( string? apiKey, string? orgId = null, string? serviceId = null, - HttpClient? httpClient = null) + HttpClient? httpClient = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(modelId); @@ -359,7 +366,8 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) endpoint: endpoint, organization: orgId, httpClient: HttpClientProvider.GetHttpClient(httpClient, serviceProvider), - loggerFactory: serviceProvider.GetService()); + loggerFactory: serviceProvider.GetService(), + functionNamePolicy: functionNamePolicy); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); builder.Services.AddKeyedSingleton(serviceId, (Func)Factory); diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs index a3f8d96d6e51..3e085aad9bbc 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Services/OpenAIChatCompletionService.cs @@ -32,13 +32,14 @@ public sealed class OpenAIChatCompletionService : IChatCompletionService, ITextG /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. + /// The function name policy. public OpenAIChatCompletionService( string modelId, string apiKey, string? organization = null, HttpClient? httpClient = null, - ILoggerFactory? loggerFactory = null -) + ILoggerFactory? loggerFactory = null, + FunctionNamePolicy? functionNamePolicy = null) { this._client = new( modelId, @@ -46,7 +47,8 @@ public OpenAIChatCompletionService( organization, endpoint: null, httpClient, - loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService))); + loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService)), + functionNamePolicy: functionNamePolicy); } /// @@ -58,6 +60,7 @@ public OpenAIChatCompletionService( /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. + /// The function name policy. [Experimental("SKEXP0010")] public OpenAIChatCompletionService( string modelId, @@ -65,7 +68,8 @@ public OpenAIChatCompletionService( string? apiKey = null, string? organization = null, HttpClient? httpClient = null, - ILoggerFactory? loggerFactory = null) + ILoggerFactory? loggerFactory = null, + FunctionNamePolicy? functionNamePolicy = null) { this._client = new( modelId, @@ -73,7 +77,8 @@ public OpenAIChatCompletionService( organization, endpoint ?? httpClient?.BaseAddress, httpClient, - loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService))); + loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService)), + functionNamePolicy: functionNamePolicy); } /// @@ -82,15 +87,18 @@ public OpenAIChatCompletionService( /// Model name /// Custom for HTTP requests. /// The to use for logging. If null, no logging will be performed. + /// The function name policy. public OpenAIChatCompletionService( string modelId, OpenAIClient openAIClient, - ILoggerFactory? loggerFactory = null) + ILoggerFactory? loggerFactory = null, + FunctionNamePolicy? functionNamePolicy = null) { this._client = new( modelId, openAIClient, - loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService))); + loggerFactory?.CreateLogger(typeof(OpenAIChatCompletionService)), + functionNamePolicy: functionNamePolicy); } /// diff --git a/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/FunctionNamePolicy.cs b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/FunctionNamePolicy.cs new file mode 100644 index 000000000000..8cd07100c9ca --- /dev/null +++ b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/FunctionNamePolicy.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.SemanticKernel; + +/// +/// Represents a base class for function name policies that handle function names. +/// The policy classes create function fully qualified names (FQN) that are provided to the AI model +/// and parse them back into the plugin name and function name when the functions are called +/// by the AI model. +/// +[Experimental("SKEXP0001")] +public abstract class FunctionNamePolicy +{ + /// + /// Initializes a new instance of the class. + /// + internal FunctionNamePolicy() + { + } + + /// + /// Gets the default function name policy that uses the hyphen separator ("-") for creating and parsing function FQNs. + /// + public static FunctionNamePolicy Default { get; } = new DefaultFunctionNamePolicy("-"); + + /// + /// Gets the function name policy that uses only the function name to create function FQNs. + /// + /// + /// This policy does not use plugin name to create function FQNs. + /// For example, if your plugin name is "foo" and your function name is "bar" the function's FQN will be "bar" instead of "foo-bar." + /// + public static FunctionNamePolicy FunctionNameOnly { get; } = new FunctionNameOnlyPolicy(); + + /// + /// Gets a custom function name policy that allows specifying a custom separator for creating and parsing function FQNs. + /// Additionally, it allows specifying a custom delegate for parsing the function FQN. + /// + /// A separator to use for creating and parsing function FQNs. + /// Delegate to parse the function FQN. + /// A custom function name policy. + public static FunctionNamePolicy Custom(string separator, ParseFunctionFqn? parseFunctionFqnDelegate = null) + { + return new DefaultFunctionNamePolicy(separator, parseFunctionFqnDelegate); + } + + /// + /// Retrieves the FQN of a function. + /// + /// The context that contains the name of the function and its plugin. + /// The function FQN. + public abstract string GetFunctionFqn(GetFunctionFqnContext context); + + /// + /// Parses the FQN of a function into the plugin name and the function name. + /// + /// The context that contains the FQN of the function. + /// The plugin name and the function name. + public abstract (string? PluginName, string FunctionName) ParseFunctionFqn(ParseFunctionFqnContext context); + + internal class DefaultFunctionNamePolicy(string functionNameSeparator, ParseFunctionFqn? parseFunctionFqnDelegate = null) : FunctionNamePolicy + { + public override string GetFunctionFqn(GetFunctionFqnContext context) + { + return FunctionName.ToFullyQualifiedName(context.FunctionName, context.PluginName, functionNameSeparator); + } + + public override (string? PluginName, string FunctionName) ParseFunctionFqn(ParseFunctionFqnContext context) + { + // Use custom parsing delegate if provided + if (parseFunctionFqnDelegate is not null) + { + return parseFunctionFqnDelegate.Invoke(context); + } + + var fn = FunctionName.Parse(context.FunctionFqn, functionNameSeparator); + + return (fn.PluginName, fn.Name); + } + } + + internal class FunctionNameOnlyPolicy : FunctionNamePolicy + { + public override string GetFunctionFqn(GetFunctionFqnContext context) + { + return context.FunctionName; + } + + public override (string? PluginName, string FunctionName) ParseFunctionFqn(ParseFunctionFqnContext context) + { + return (null, context.FunctionFqn); + } + } +} diff --git a/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/GetFunctionFqnContext.cs b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/GetFunctionFqnContext.cs new file mode 100644 index 000000000000..b4d3fa0773d5 --- /dev/null +++ b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/GetFunctionFqnContext.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.SemanticKernel; + +/// +/// Context for the method. +/// +/// The function name. +[Experimental("SKEXP0001")] +public class GetFunctionFqnContext(string functionName) +{ + /// + /// Gets or sets the plugin name. + /// + public string? PluginName { get; set; } + + /// + /// Gets or sets the function name. + /// + public string FunctionName { get; set; } = functionName; +} diff --git a/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnContext.cs b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnContext.cs new file mode 100644 index 000000000000..37e53e73b8b2 --- /dev/null +++ b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnContext.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.SemanticKernel; + +/// +/// Context for the method. +/// +/// The function fully qualified name. +[Experimental("SKEXP0001")] +public class ParseFunctionFqnContext(string functionFqn) +{ + /// + /// Gets or sets the function name. + /// + public string FunctionFqn { get; set; } = functionFqn; + + /// + /// Gets or sets the kernel. + /// + public Kernel? Kernel { get; set; } +} diff --git a/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnDelegate.cs b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnDelegate.cs new file mode 100644 index 000000000000..39aad4d5f6ef --- /dev/null +++ b/dotnet/src/SemanticKernel.Abstractions/Functions/Policies/ParseFunctionFqnDelegate.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.SemanticKernel; + +/// +/// Delegate for parsing the FQN of a function. +/// +/// The context. +/// The plugin name and the function name. +[Experimental("SKEXP0001")] +public delegate (string? PluginName, string FunctionName) ParseFunctionFqn(ParseFunctionFqnContext context); From 2f2717c2a02cf6185d0c25e0a4f2cf6e1db82904 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 16 Jan 2025 10:22:06 +0000 Subject: [PATCH 2/5] update sample --- .../FunctionCalling_FunctionNamePolicy.cs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs index fff555bb80ea..24da3a361b3a 100644 --- a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs +++ b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs @@ -94,29 +94,32 @@ public async Task RecoverHallucinatedFunctionNameAsync() // Define a custom function FQN parser that can handle a hallucinated function name. static (string? PluginName, string FunctioName) ParseFunctionFqn(ParseFunctionFqnContext context) { - // Try to use use hyphen as separator. - var parts = context.FunctionFqn.Split('-'); - if (parts.Length == 2) + static (string? PluginName, string FunctioName)? Parse(ParseFunctionFqnContext context, char separator) { - // Check if the function registered in the kernel - if (context.Kernel is { } kernel && kernel.Plugins.TryGetFunction(parts[0], parts[1], out _)) + var parts = context.FunctionFqn.Split(separator); + if (parts.Length == 2) { - return (parts[0], parts[1]); + // Check if the function registered in the kernel + if (context.Kernel is { } kernel && kernel.Plugins.TryGetFunction(parts[0], parts[1], out _)) + { + return (parts[0], parts[1]); + } } + + return null; } - // If hyphen is not found, try to use underscore as separator. This approach presumes the underscore symbol is not used in function name. - parts = context.FunctionFqn.Split('_'); - if (parts.Length == 2) + // Try to use use hyphen, dot, and underscore sequentially as separators. + var result = Parse(context, '-') ?? + Parse(context, '.') ?? + Parse(context, '_'); + + if (result is not null) { - // Check if the function registered in the kernel - if (context.Kernel is { } kernel && kernel.Plugins.TryGetFunction(parts[0], parts[1], out _)) - { - return (parts[0], parts[1]); - } + return result.Value; } - // If no separator is found, return the function name as is. + // If no separator is found, return the function name as is allowing AI connector to apply default behavior. return (null, context.FunctionFqn); } From 9f8617b965b1a3c0de57c7edc292b73f3e190450 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 16 Jan 2025 11:54:45 +0000 Subject: [PATCH 3/5] fix typo --- .../FunctionCalling/FunctionCalling_FunctionNamePolicy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs index 24da3a361b3a..2681dcb6a386 100644 --- a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs +++ b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs @@ -92,9 +92,9 @@ public async Task RecoverHallucinatedFunctionNameAsync() IKernelBuilder builder = Kernel.CreateBuilder(); // Define a custom function FQN parser that can handle a hallucinated function name. - static (string? PluginName, string FunctioName) ParseFunctionFqn(ParseFunctionFqnContext context) + static (string? PluginName, string FunctionName) ParseFunctionFqn(ParseFunctionFqnContext context) { - static (string? PluginName, string FunctioName)? Parse(ParseFunctionFqnContext context, char separator) + static (string? PluginName, string FunctionName)? Parse(ParseFunctionFqnContext context, char separator) { var parts = context.FunctionFqn.Split(separator); if (parts.Length == 2) From a6e5360e8db49cf13a5f834ce541490026132e48 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 16 Jan 2025 13:52:27 +0000 Subject: [PATCH 4/5] add funciton name policy parameter to add extensio methods and generate compatibility sepressions. --- .../CompatibilitySuppressions.xml | 130 ++++++++++++++++++ .../AzureOpenAIKernelBuilderExtensions.cs | 18 ++- .../CompatibilitySuppressions.xml | 130 ++++++++++++++++++ .../OpenAIServiceCollectionExtensions.cs | 20 ++- 4 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 dotnet/src/Connectors/Connectors.AzureOpenAI/CompatibilitySuppressions.xml create mode 100644 dotnet/src/Connectors/Connectors.OpenAI/CompatibilitySuppressions.xml diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/CompatibilitySuppressions.xml b/dotnet/src/Connectors/Connectors.AzureOpenAI/CompatibilitySuppressions.xml new file mode 100644 index 000000000000..ede8a227e202 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/CompatibilitySuppressions.xml @@ -0,0 +1,130 @@ + + + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.Net.Http.HttpClient,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,System.String,System.String,System.String,System.Net.Http.HttpClient,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,System.String,Azure.Core.TokenCredential,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,System.String,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.Net.Http.HttpClient,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,System.String,System.String,System.String,System.Net.Http.HttpClient,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,Azure.AI.OpenAI.AzureOpenAIClient,System.String,Microsoft.Extensions.Logging.ILoggerFactory) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,System.String,Azure.Core.TokenCredential,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.AzureOpenAI.AzureOpenAIChatCompletionService.#ctor(System.String,System.String,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll + true + + \ No newline at end of file diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIKernelBuilderExtensions.cs index d3df05b5c2ef..2f5282a7e595 100644 --- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIKernelBuilderExtensions.cs @@ -39,6 +39,7 @@ public static class AzureOpenAIKernelBuilderExtensions /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// The HttpClient to use with this service. /// Optional Azure OpenAI API version, see available here + /// The function name policy. /// The same instance as . public static IKernelBuilder AddAzureOpenAIChatCompletion( this IKernelBuilder builder, @@ -48,7 +49,8 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( string? serviceId = null, string? modelId = null, HttpClient? httpClient = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(endpoint); @@ -61,7 +63,7 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( new ApiKeyCredential(apiKey), HttpClientProvider.GetHttpClient(httpClient, serviceProvider), apiVersion); - return new(deploymentName, client, modelId, serviceProvider.GetService()); + return new(deploymentName, client, modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); }; builder.Services.AddKeyedSingleton(serviceId, factory); @@ -81,6 +83,7 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// The HttpClient to use with this service. /// Optional Azure OpenAI API version, see available here + /// The function name policy. /// The same instance as . public static IKernelBuilder AddAzureOpenAIChatCompletion( this IKernelBuilder builder, @@ -90,7 +93,8 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( string? serviceId = null, string? modelId = null, HttpClient? httpClient = null, - string? apiVersion = null) + string? apiVersion = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(endpoint); @@ -103,7 +107,7 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( credentials, HttpClientProvider.GetHttpClient(httpClient, serviceProvider), apiVersion); - return new(deploymentName, client, modelId, serviceProvider.GetService()); + return new(deploymentName, client, modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); }; builder.Services.AddKeyedSingleton(serviceId, factory); @@ -120,19 +124,21 @@ public static IKernelBuilder AddAzureOpenAIChatCompletion( /// to use for the service. If null, one must be available in the service provider when this service is resolved. /// A local identifier for the given AI service /// Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart + /// The function name policy. /// The same instance as . public static IKernelBuilder AddAzureOpenAIChatCompletion( this IKernelBuilder builder, string deploymentName, AzureOpenAIClient? azureOpenAIClient = null, string? serviceId = null, - string? modelId = null) + string? modelId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(builder); Verify.NotNullOrWhiteSpace(deploymentName); Func factory = (serviceProvider, _) => - new(deploymentName, azureOpenAIClient ?? serviceProvider.GetRequiredService(), modelId, serviceProvider.GetService()); + new(deploymentName, azureOpenAIClient ?? serviceProvider.GetRequiredService(), modelId, serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); builder.Services.AddKeyedSingleton(serviceId, factory); builder.Services.AddKeyedSingleton(serviceId, factory); diff --git a/dotnet/src/Connectors/Connectors.OpenAI/CompatibilitySuppressions.xml b/dotnet/src/Connectors/Connectors.OpenAI/CompatibilitySuppressions.xml new file mode 100644 index 000000000000..f9aa26dbc978 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.OpenAI/CompatibilitySuppressions.xml @@ -0,0 +1,130 @@ + + + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,OpenAI.OpenAIClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,System.Uri,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,OpenAI.OpenAIClient,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,System.String,System.String,System.Net.Http.HttpClient) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.Uri,System.String,System.String,System.String,System.Net.Http.HttpClient) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,OpenAI.OpenAIClient,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.Uri,System.String,System.String,System.String) + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/net8.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,OpenAI.OpenAIClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIChatCompletionService.#ctor(System.String,System.Uri,System.String,System.String,System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILoggerFactory) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,OpenAI.OpenAIClient,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,System.String,System.String,System.Net.Http.HttpClient) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIKernelBuilderExtensions.AddOpenAIChatCompletion(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.Uri,System.String,System.String,System.String,System.Net.Http.HttpClient) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,OpenAI.OpenAIClient,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + + CP0002 + M:Microsoft.SemanticKernel.OpenAIServiceCollectionExtensions.AddOpenAIChatCompletion(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.Uri,System.String,System.String,System.String) + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.OpenAI.dll + true + + \ No newline at end of file diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIServiceCollectionExtensions.cs index ed191d3dda0f..357164c0a013 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/Extensions/OpenAIServiceCollectionExtensions.cs @@ -255,13 +255,15 @@ public static IServiceCollection AddOpenAIFiles( /// OpenAI API key, see https://platform.openai.com/account/api-keys /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations. /// A local identifier for the given AI service + /// The function name policy. /// The same instance as . public static IServiceCollection AddOpenAIChatCompletion( this IServiceCollection services, string modelId, string apiKey, string? orgId = null, - string? serviceId = null) + string? serviceId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(modelId); @@ -272,7 +274,8 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) apiKey, orgId, HttpClientProvider.GetHttpClient(serviceProvider), - serviceProvider.GetService()); + serviceProvider.GetService(), + functionNamePolicy: functionNamePolicy); services.AddKeyedSingleton(serviceId, (Func)Factory); services.AddKeyedSingleton(serviceId, (Func)Factory); @@ -287,17 +290,19 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) /// OpenAI model id /// to use for the service. If null, one must be available in the service provider when this service is resolved. /// A local identifier for the given AI service + /// The function name policy. /// The same instance as . public static IServiceCollection AddOpenAIChatCompletion(this IServiceCollection services, string modelId, OpenAIClient? openAIClient = null, - string? serviceId = null) + string? serviceId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(modelId); OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) => - new(modelId, openAIClient ?? serviceProvider.GetRequiredService(), serviceProvider.GetService()); + new(modelId, openAIClient ?? serviceProvider.GetRequiredService(), serviceProvider.GetService(), functionNamePolicy: functionNamePolicy); services.AddKeyedSingleton(serviceId, (Func)Factory); services.AddKeyedSingleton(serviceId, (Func)Factory); @@ -314,6 +319,7 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) /// OpenAI API key, see https://platform.openai.com/account/api-keys /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations. /// A local identifier for the given AI service + /// The function name policy. /// The same instance as . [Experimental("SKEXP0010")] public static IServiceCollection AddOpenAIChatCompletion( @@ -322,7 +328,8 @@ public static IServiceCollection AddOpenAIChatCompletion( Uri endpoint, string? apiKey = null, string? orgId = null, - string? serviceId = null) + string? serviceId = null, + FunctionNamePolicy? functionNamePolicy = null) { Verify.NotNull(services); Verify.NotNullOrWhiteSpace(modelId); @@ -333,7 +340,8 @@ OpenAIChatCompletionService Factory(IServiceProvider serviceProvider, object? _) apiKey, orgId, HttpClientProvider.GetHttpClient(serviceProvider), - serviceProvider.GetService()); + serviceProvider.GetService(), + functionNamePolicy: functionNamePolicy); services.AddKeyedSingleton(serviceId, (Func)Factory); services.AddKeyedSingleton(serviceId, (Func)Factory); From b14463a10c8f53c31071e325337f8dded8f1aa58 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 16 Jan 2025 14:22:54 +0000 Subject: [PATCH 5/5] update xml comments for samples --- .../FunctionCalling_FunctionNamePolicy.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs index 2681dcb6a386..ff298dc4b0dd 100644 --- a/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs +++ b/dotnet/samples/Concepts/FunctionCalling/FunctionCalling_FunctionNamePolicy.cs @@ -6,7 +6,8 @@ namespace FunctionCalling; /// -/// These samples demonstrate how to use a custom function name policy to specify Fully Qualified Name (FQN) separator and parse function FQN. +/// These samples demonstrate how to use a custom function name policy to specify a separator for creating Fully Qualified Name (FQN) for functions when advertising them to AI models +/// and for parsing them when handling function calls from the models. /// public class FunctionCalling_FunctionNamePolicy(ITestOutputHelper output) : BaseTest(output) { @@ -15,6 +16,9 @@ public class FunctionCalling_FunctionNamePolicy(ITestOutputHelper output) : Base /// This can be useful if the default separator, which is a hyphen ("-"), is not suitable for your needs. /// For example, if your plugin name is "foo" and your function name is "bar," the function FQN will be "foo_bar" instead of "foo-bar". /// + /// + /// Using the underscore as the separator won't work if your function names have underscores, e.g., they follow the snake convention in which words are separated by underscores. + /// [Fact] public async Task UseUnderscoreAsFunctionFqnSeparatorAsync() { @@ -45,8 +49,7 @@ public async Task UseUnderscoreAsFunctionFqnSeparatorAsync() } /// - /// This sample demonstrates how to prevent the plugin name from being included in the FQN of a function. - /// Instead, only the function name will be used for the FQN. + /// This sample demonstrates how to use only function names as FQNs without plugin names included. /// For example, if your plugin name is "foo" and your function name is "bar" the function's FQN will be "bar" instead of "foo-bar." /// [Fact] @@ -79,10 +82,8 @@ public async Task UseOnlyFunctionNameAndDoNotUsePluginNameForFqnAsync() } /// - /// This sample demonstrates how to use a custom function FQN parser - /// to handle a hallucinated function name generated by AI model. - /// It addresses the scenario where the AI model calls the function using a different separator - /// than the one used in the function's original declaration. + /// This sample demonstrates how to use a custom function FQN parser to handle hallucinated function names called by the AI model. + /// It addresses the scenario where the AI model calls functions using a different separator than the one used to advertise the functions. /// For example, you function is advertised as "foo-bar" but the AI model calls it as "foo_bar" /// [Fact]