From e377514c3a06cf5aa108d049ef454c1e370dd207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20BERTRAND?= Date: Tue, 5 Aug 2025 12:52:11 +0200 Subject: [PATCH 1/9] .Net: Add Provider Choice Support to ONNX Connector --- dotnet/Directory.Packages.props | 1 + dotnet/SK-dotnet.slnx | 1 + .../OnnxWithProviderChoice.csproj | 20 ++++++ .../Demos/OnnxWithProviderChoice/Program.cs | 62 +++++++++++++++++++ .../OnnxKernelBuilderExtensions.cs | 19 +++--- .../OnnxRuntimeGenAIChatCompletionService.cs | 31 ++++++++-- .../OnnxServiceCollectionExtensions.cs | 23 ++++--- .../Connectors/Connectors.Onnx/Provider.cs | 19 ++++++ 8 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj create mode 100644 dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs create mode 100644 dotnet/src/Connectors/Connectors.Onnx/Provider.cs diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index f8b42e63f18b..80cd61e967f4 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -59,6 +59,7 @@ + diff --git a/dotnet/SK-dotnet.slnx b/dotnet/SK-dotnet.slnx index b662baf25562..275aeeae15b7 100644 --- a/dotnet/SK-dotnet.slnx +++ b/dotnet/SK-dotnet.slnx @@ -41,6 +41,7 @@ + diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj b/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj new file mode 100644 index 000000000000..2dfec5865839 --- /dev/null +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj @@ -0,0 +1,20 @@ + + + Exe + net8.0 + $(NoWarn);CA2007,CA2208,CS1591,CA1024,IDE0009,IDE0055,IDE0073,IDE0211,VSTHRD111,SKEXP0001 + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs new file mode 100644 index 000000000000..4907db9fba4e --- /dev/null +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.Onnx; + +async Task DoLoop(ChatHistory history, IChatCompletionService chatCompletionService, OnnxRuntimeGenAIPromptExecutionSettings settings, Kernel kernel) +{ + while (true) + { + Console.Write("User > "); + string userMessage = Console.ReadLine()!; + if (userMessage == "exit" || userMessage == "quit") + { + break; + } + + if (string.IsNullOrEmpty(userMessage)) + { + continue; + } + + history.AddUserMessage(userMessage); + + try + { + ChatMessageContent results = await chatCompletionService.GetChatMessageContentAsync(history, settings, kernel); + Console.WriteLine($"Assistant > {results.Content}"); + history.AddAssistantMessage(results.Content!); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } +} + +string modelPath = @"MODEL_PATH"; + +IKernelBuilder builder = Kernel.CreateBuilder(); +builder.AddOnnxRuntimeGenAIChatCompletion( + modelId: "onnx", + modelPath: modelPath, + providers: [new Provider { Id = "cuda" }] +); + +Kernel kernel = builder.Build(); + +ChatHistory history = []; + +IChatCompletionService chatCompletionService = kernel.GetRequiredService(); +OnnxRuntimeGenAIPromptExecutionSettings settings = new() +{ + MaxTokens = 5120 +}; + +await DoLoop(history, chatCompletionService, settings, kernel); + +if (chatCompletionService is OnnxRuntimeGenAIChatCompletionService onnxService) +{ + onnxService.Dispose(); +} diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs index e09870da1037..e53db7581ade 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.IO; using System.Text.Json; -using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel.ChatCompletion; @@ -18,25 +18,30 @@ namespace Microsoft.SemanticKernel; public static class OnnxKernelBuilderExtensions { /// - /// Add OnnxRuntimeGenAI Chat Completion services to the kernel builder. + /// Adds OnnxRuntimeGenAI Chat Completion services to the specified . /// - /// The kernel builder. + /// The instance to augment. /// Model Id. - /// The generative AI ONNX model path. - /// The optional service ID. + /// The generative AI ONNX model path for the chat completion service. + /// A local identifier for the given AI service. + /// Providers + /// Logger factory. /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. - /// The updated kernel builder. + /// The same instance as . public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( this IKernelBuilder builder, string modelId, string modelPath, string? serviceId = null, + List? providers = null, + ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { - builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, modelPath: modelPath, + providers: providers, loggerFactory: serviceProvider.GetService(), jsonSerializerOptions)); diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs index c18b76ffed4b..6354b9a6ba16 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs @@ -18,7 +18,8 @@ namespace Microsoft.SemanticKernel.Connectors.Onnx; /// public sealed class OnnxRuntimeGenAIChatCompletionService : IChatCompletionService, IDisposable { - private readonly string _modelPath; + private readonly Config _config; + private readonly Model _model; private OnnxRuntimeGenAIChatClient? _chatClient; private IChatCompletionService? _chatClientWrapper; private readonly Dictionary _attributesInternal = []; @@ -31,11 +32,13 @@ public sealed class OnnxRuntimeGenAIChatCompletionService : IChatCompletionServi /// /// The name of the model. /// The generative AI ONNX model path for the chat completion service. + /// The providers to use for the chat completion service. /// Optional logger factory to be used for logging. /// The to use for various aspects of serialization and deserialization required by the service. public OnnxRuntimeGenAIChatCompletionService( string modelId, string modelPath, + List? providers = null, ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -43,12 +46,26 @@ public OnnxRuntimeGenAIChatCompletionService( Verify.NotNullOrWhiteSpace(modelPath); this._attributesInternal.Add(AIServiceExtensions.ModelIdKey, modelId); - this._modelPath = modelPath; + this._config = new Config(modelPath); + if (providers != null) + { + this._config.ClearProviders(); + foreach (Provider provider in providers) + { + this._config.AppendProvider(provider.Id); + foreach (KeyValuePair option in provider.Options) + { + this._config.SetProviderOption(provider.Id, option.Key, option.Value); + } + } + } + + this._model = new Model(this._config); } private IChatCompletionService GetChatCompletionService() { - this._chatClient ??= new OnnxRuntimeGenAIChatClient(this._modelPath, new OnnxRuntimeGenAIChatClientOptions() + this._chatClient ??= new OnnxRuntimeGenAIChatClient(this._model, false, new OnnxRuntimeGenAIChatClientOptions() { PromptFormatter = (messages, options) => { @@ -57,6 +74,7 @@ private IChatCompletionService GetChatCompletionService() { promptBuilder.Append($"<|{message.Role}|>\n{message.Text}"); } + promptBuilder.Append("<|end|>\n<|assistant|>"); return promptBuilder.ToString(); @@ -67,7 +85,12 @@ private IChatCompletionService GetChatCompletionService() } /// - public void Dispose() => this._chatClient?.Dispose(); + public void Dispose() + { + this._model.Dispose(); + this._config.Dispose(); + this._chatClient?.Dispose(); + } /// public Task> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default) => diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs index dc0e147cba2a..d4b38580998d 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.IO; using System.Text.Json; -using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel.ChatCompletion; @@ -18,25 +18,30 @@ namespace Microsoft.SemanticKernel; public static class OnnxServiceCollectionExtensions { /// - /// Add OnnxRuntimeGenAI Chat Completion services to the specified service collection. + /// Adds the OnnxRuntimeGenAI Chat Completion services to the specified . /// - /// The service collection to add the OnnxRuntimeGenAI Text Generation service to. - /// The name of the model. - /// The generative AI ONNX model path. - /// Optional service ID. - /// The to use for various aspects of serialization and deserialization required by the service. - /// The updated service collection. + /// The instance to augment. + /// Model Id. + /// The generative AI ONNX model path for the chat completion service. + /// A local identifier for the given AI service. + /// Providers + /// Logger factory. + /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. + /// The same instance as . public static IServiceCollection AddOnnxRuntimeGenAIChatCompletion( this IServiceCollection services, string modelId, string modelPath, string? serviceId = null, + List? providers = null, + ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { - services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, modelPath, + providers: providers, loggerFactory: serviceProvider.GetService(), jsonSerializerOptions)); diff --git a/dotnet/src/Connectors/Connectors.Onnx/Provider.cs b/dotnet/src/Connectors/Connectors.Onnx/Provider.cs new file mode 100644 index 000000000000..c531e47b24ec --- /dev/null +++ b/dotnet/src/Connectors/Connectors.Onnx/Provider.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; + +namespace Microsoft.SemanticKernel.Connectors.Onnx; + +/// ONNX provider +public class Provider +{ + /// + /// Id + /// + public string Id { get; set; } = ""; + + /// + /// Options + /// + public Dictionary Options { get; set; } = new(); +} From 6c2eb21c216f81315fdb88f70deb9c496fd8b30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20BERTRAND?= Date: Tue, 5 Aug 2025 14:07:17 +0200 Subject: [PATCH 2/9] .Net: format code --- dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs | 2 +- .../Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs | 2 +- .../Connectors.Onnx/OnnxServiceCollectionExtensions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs index 4907db9fba4e..bb506ed9169b 100644 --- a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs @@ -35,7 +35,7 @@ async Task DoLoop(ChatHistory history, IChatCompletionService chatCompletionServ } } -string modelPath = @"MODEL_PATH"; +string modelPath = "MODEL_PATH"; IKernelBuilder builder = Kernel.CreateBuilder(); builder.AddOnnxRuntimeGenAIChatCompletion( diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs index e53db7581ade..82ead79fcfa4 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs @@ -37,7 +37,7 @@ public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { - builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, modelPath: modelPath, diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs index d4b38580998d..7e29157524d5 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs @@ -37,7 +37,7 @@ public static IServiceCollection AddOnnxRuntimeGenAIChatCompletion( ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { - services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, modelPath, From a0115d3141640d4e662bedd613a26620d6c4d6c5 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Tue, 5 Aug 2025 20:53:11 +0100 Subject: [PATCH 3/9] Preparing PR for merge --- dotnet/SK-dotnet.slnx | 2 +- ...e.csproj => OnnxSimpleChatWithCuda.csproj} | 0 .../Demos/OnnxWithProviderChoice/Program.cs | 76 ++++++++---------- .../Demos/OnnxWithProviderChoice/README.md | 44 +++++++++++ .../Connectors.Onnx.UnitTests.csproj | 6 ++ .../Resources/genai_config.json | 18 +++++ .../OnnxKernelBuilderExtensions.ChatClient.cs | 28 +++++++ .../OnnxKernelBuilderExtensions.cs | 34 +++++++- .../OnnxRuntimeGenAIChatCompletionService.cs | 57 +++++++++----- ...ollectionExtensions.DependencyInjection.cs | 77 ++++++++++++++++--- .../OnnxServiceCollectionExtensions.cs | 39 +++++++++- .../Connectors/Connectors.Onnx/Provider.cs | 19 ++++- 12 files changed, 322 insertions(+), 78 deletions(-) rename dotnet/samples/Demos/OnnxWithProviderChoice/{OnnxWithProviderChoice.csproj => OnnxSimpleChatWithCuda.csproj} (100%) create mode 100644 dotnet/samples/Demos/OnnxWithProviderChoice/README.md create mode 100644 dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json diff --git a/dotnet/SK-dotnet.slnx b/dotnet/SK-dotnet.slnx index 275aeeae15b7..2c28f5b2f59c 100644 --- a/dotnet/SK-dotnet.slnx +++ b/dotnet/SK-dotnet.slnx @@ -41,7 +41,7 @@ - + diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj b/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxSimpleChatWithCuda.csproj similarity index 100% rename from dotnet/samples/Demos/OnnxWithProviderChoice/OnnxWithProviderChoice.csproj rename to dotnet/samples/Demos/OnnxWithProviderChoice/OnnxSimpleChatWithCuda.csproj diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs index bb506ed9169b..51d41c6eff54 100644 --- a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs @@ -1,62 +1,48 @@ using System; -using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.AI; using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.Onnx; -async Task DoLoop(ChatHistory history, IChatCompletionService chatCompletionService, OnnxRuntimeGenAIPromptExecutionSettings settings, Kernel kernel) -{ - while (true) - { - Console.Write("User > "); - string userMessage = Console.ReadLine()!; - if (userMessage == "exit" || userMessage == "quit") - { - break; - } - - if (string.IsNullOrEmpty(userMessage)) - { - continue; - } - - history.AddUserMessage(userMessage); - - try - { - ChatMessageContent results = await chatCompletionService.GetChatMessageContentAsync(history, settings, kernel); - Console.WriteLine($"Assistant > {results.Content}"); - history.AddAssistantMessage(results.Content!); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } - } -} - +// Path to the folder of your downloaded ONNX CUDA model +// i.e: D:\huggingface\Phi-3-mini-4k-instruct-onnx\cuda\cuda-int4-rtn-block-32 string modelPath = "MODEL_PATH"; IKernelBuilder builder = Kernel.CreateBuilder(); -builder.AddOnnxRuntimeGenAIChatCompletion( - modelId: "onnx", +builder.AddOnnxRuntimeGenAIChatClient( modelPath: modelPath, - providers: [new Provider { Id = "cuda" }] + + // Specify the provider you want to use, e.g., "cuda" for GPU support + // For other execution providers, check: https://onnxruntime.ai/docs/genai/reference/config#provideroptions + providers: [new Provider("cuda")] // ); Kernel kernel = builder.Build(); -ChatHistory history = []; +using IChatClient chatClient = kernel.GetRequiredService(); -IChatCompletionService chatCompletionService = kernel.GetRequiredService(); -OnnxRuntimeGenAIPromptExecutionSettings settings = new() +List chatHistory = []; + +while (true) { - MaxTokens = 5120 -}; + Console.Write("User > "); + string userMessage = Console.ReadLine()!; + if (string.IsNullOrEmpty(userMessage)) + { + break; + } -await DoLoop(history, chatCompletionService, settings, kernel); + chatHistory.Add(new ChatMessage(ChatRole.User, userMessage)); -if (chatCompletionService is OnnxRuntimeGenAIChatCompletionService onnxService) -{ - onnxService.Dispose(); + try + { + ChatResponse result = await chatClient.GetResponseAsync(chatHistory, new() { MaxOutputTokens = 1024 }); + Console.WriteLine($"Assistant > {result.Text}"); + + chatHistory.AddRange(result.Messages); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } } diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/README.md b/dotnet/samples/Demos/OnnxWithProviderChoice/README.md new file mode 100644 index 000000000000..8b83bb300b09 --- /dev/null +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/README.md @@ -0,0 +1,44 @@ +# Onnx Simple Chat with Cuda Execution Provider + +This sample demonstrates how you use ONNX Connector with CUDA Execution Provider to run Local Models straight from files using Semantic Kernel. + +In this example we setup Chat Client from ONNX Connector with [Microsoft's Phi-3-ONNX](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx) model + +> [!IMPORTANT] +> You can modify to use any other combination of models enabled for ONNX runtime. + +## Semantic Kernel used Features + +- [Chat Client](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/IChatCompletionService.cs) - Using the Chat Completion Service from [Onnx Connector](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs) to generate responses from the Local Model. + +## Prerequisites + +- [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0). +- [NVIDIA GPU](https://www.nvidia.com/en-us/geforce/graphics-cards) +- [NVIDIA CUDA v12 Toolkit](https://developer.nvidia.com/cuda-12-0-0-download-archive) +- [NVIDIA cuDNN v9.11](https://developer.nvidia.com/cudnn-9-11-0-download-archive) +- Windows users only: + + Ensure `PATH` environment variable includes the `bin` folder of the CUDA Toolkit and cuDNN. + i.e: + - C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0\bin + - C:\Program Files\NVIDIA\CUDNN\v9.11\bin\12.9 + +- Downloaded ONNX Models (see below). + +## Downloading the Model + +For this example we chose Hugging Face as our repository for download of the local models, go to a directory of your choice where the models should be downloaded and run the following commands: + +```powershell +git lfs install +git clone https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx +``` + +Update the `Program.cs` file lines below with the paths to the models you downloaded in the previous step. + +```csharp +// i.e. Running on Windows +string modelPath = "D:\\huggingface\\Phi-3-mini-4k-instruct-onnx\\cuda\\cuda-int4-rtn-block-32"; +``` + diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj index fd60589cc4e5..1342619bda6f 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj @@ -33,4 +33,10 @@ + + + Always + + + diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json new file mode 100644 index 000000000000..a6f01463e6f9 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json @@ -0,0 +1,18 @@ +{ + "search": { + "diversity_penalty": 0.0, + "do_sample": false, + "early_stopping": true, + "length_penalty": 1.0, + "max_length": 4096, + "min_length": 0, + "no_repeat_ngram_size": 0, + "num_beams": 1, + "num_return_sequences": 1, + "past_present_share_buffer": true, + "repetition_penalty": 1.0, + "temperature": 1.0, + "top_k": 1, + "top_p": 1.0 + } +} diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.ChatClient.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.ChatClient.cs index 7e9329d94903..476342740153 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.ChatClient.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.ChatClient.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.ML.OnnxRuntimeGenAI; +using Microsoft.SemanticKernel.Connectors.Onnx; namespace Microsoft.SemanticKernel; @@ -35,5 +37,31 @@ public static IKernelBuilder AddOnnxRuntimeGenAIChatClient( return builder; } + /// + /// Adds an OnnxRuntimeGenAI to the . + /// + /// The instance to augment. + /// The generative AI ONNX model path. + /// The providers to use for the chat client. + /// The optional options for the chat client. + /// A local identifier for the given AI service + /// The same instance as . + public static IKernelBuilder AddOnnxRuntimeGenAIChatClient( + this IKernelBuilder builder, + string modelPath, + IEnumerable providers, + OnnxRuntimeGenAIChatClientOptions? chatClientOptions = null, + string? serviceId = null) + { + Verify.NotNull(builder); + + builder.Services.AddOnnxRuntimeGenAIChatClient( + modelPath, + providers, + chatClientOptions, + serviceId); + + return builder; + } #endregion } diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs index 82ead79fcfa4..f206abdfa227 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs @@ -24,7 +24,37 @@ public static class OnnxKernelBuilderExtensions /// Model Id. /// The generative AI ONNX model path for the chat completion service. /// A local identifier for the given AI service. + /// Logger factory. + /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. + /// The same instance as . + public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( + this IKernelBuilder builder, + string modelId, + string modelPath, + string? serviceId = null, + ILoggerFactory? loggerFactory = null, + JsonSerializerOptions? jsonSerializerOptions = null) + { + Verify.NotNull(builder); + + builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + new OnnxRuntimeGenAIChatCompletionService( + modelId, + modelPath: modelPath, + loggerFactory: serviceProvider.GetService(), + jsonSerializerOptions)); + + return builder; + } + + /// + /// Adds OnnxRuntimeGenAI Chat Completion services to the specified . + /// + /// The instance to augment. + /// Model Id. + /// The generative AI ONNX model path for the chat completion service. /// Providers + /// A local identifier for the given AI service. /// Logger factory. /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. /// The same instance as . @@ -32,11 +62,13 @@ public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( this IKernelBuilder builder, string modelId, string modelPath, + List providers, string? serviceId = null, - List? providers = null, ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { + Verify.NotNull(builder); + builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs index 6354b9a6ba16..4614a27c701b 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs @@ -18,8 +18,9 @@ namespace Microsoft.SemanticKernel.Connectors.Onnx; /// public sealed class OnnxRuntimeGenAIChatCompletionService : IChatCompletionService, IDisposable { - private readonly Config _config; - private readonly Model _model; + private readonly Config? _config; + private readonly Model? _model; + private readonly string _modelPath; private OnnxRuntimeGenAIChatClient? _chatClient; private IChatCompletionService? _chatClientWrapper; private readonly Dictionary _attributesInternal = []; @@ -32,13 +33,11 @@ public sealed class OnnxRuntimeGenAIChatCompletionService : IChatCompletionServi /// /// The name of the model. /// The generative AI ONNX model path for the chat completion service. - /// The providers to use for the chat completion service. /// Optional logger factory to be used for logging. /// The to use for various aspects of serialization and deserialization required by the service. public OnnxRuntimeGenAIChatCompletionService( string modelId, string modelPath, - List? providers = null, ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -46,26 +45,44 @@ public OnnxRuntimeGenAIChatCompletionService( Verify.NotNullOrWhiteSpace(modelPath); this._attributesInternal.Add(AIServiceExtensions.ModelIdKey, modelId); + this._modelPath = modelPath; + } + + /// + /// Initializes a new instance of the OnnxRuntimeGenAIChatCompletionService class. + /// + /// The name of the model. + /// The generative AI ONNX model path for the chat completion service. + /// The providers to use for the chat completion service. + /// Optional logger factory to be used for logging. + /// The to use for various aspects of serialization and deserialization required by the service. + public OnnxRuntimeGenAIChatCompletionService( + string modelId, + string modelPath, + IEnumerable providers, + ILoggerFactory? loggerFactory = null, + JsonSerializerOptions? jsonSerializerOptions = null) + : this(modelId, modelPath, loggerFactory, jsonSerializerOptions) + { + Verify.NotNullOrWhiteSpace(modelId); + Verify.NotNullOrWhiteSpace(modelPath); + Verify.NotNull(providers); + this._config = new Config(modelPath); - if (providers != null) + this._config.ClearProviders(); + foreach (Provider provider in providers) { - this._config.ClearProviders(); - foreach (Provider provider in providers) + this._config.AppendProvider(provider.Id); + foreach (KeyValuePair option in provider.Options) { - this._config.AppendProvider(provider.Id); - foreach (KeyValuePair option in provider.Options) - { - this._config.SetProviderOption(provider.Id, option.Key, option.Value); - } + this._config.SetProviderOption(provider.Id, option.Key, option.Value); } } - - this._model = new Model(this._config); } private IChatCompletionService GetChatCompletionService() { - this._chatClient ??= new OnnxRuntimeGenAIChatClient(this._model, false, new OnnxRuntimeGenAIChatClientOptions() + var options = new OnnxRuntimeGenAIChatClientOptions() { PromptFormatter = (messages, options) => { @@ -79,7 +96,11 @@ private IChatCompletionService GetChatCompletionService() return promptBuilder.ToString(); } - }); + }; + + this._chatClient ??= this._model is null + ? new(this._modelPath, options) + : new(this._model, false, options); return this._chatClientWrapper ??= this._chatClient.AsChatCompletionService(); } @@ -87,8 +108,8 @@ private IChatCompletionService GetChatCompletionService() /// public void Dispose() { - this._model.Dispose(); - this._config.Dispose(); + this._model?.Dispose(); + this._config?.Dispose(); this._chatClient?.Dispose(); } diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.DependencyInjection.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.DependencyInjection.cs index a8dda516b338..3d4a7c182262 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.DependencyInjection.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.DependencyInjection.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.Extensions.AI; @@ -85,17 +86,63 @@ IChatClient Factory(IServiceProvider serviceProvider, object? _) var chatClient = new OnnxRuntimeGenAIChatClient(modelPath, chatClientOptions ?? new OnnxRuntimeGenAIChatClientOptions() { - PromptFormatter = static (messages, _) => + PromptFormatter = DefaultPromptFormatter + }); + + var builder = chatClient.AsBuilder() + .UseKernelFunctionInvocation(loggerFactory); + + if (loggerFactory is not null) + { + builder.UseLogging(loggerFactory); + } + + return builder.Build(); + } + + services.AddKeyedSingleton(serviceId, (Func)Factory); + + return services; + } + + /// + /// Add OnnxRuntimeGenAI Chat Client to the service collection. + /// + /// The service collection. + /// The generative AI ONNX model path. + /// The providers to use for the chat client. + /// The options for the chat client. + /// The optional service ID. + /// The updated service collection. + public static IServiceCollection AddOnnxRuntimeGenAIChatClient( + this IServiceCollection services, + string modelPath, + IEnumerable providers, + OnnxRuntimeGenAIChatClientOptions? chatClientOptions = null, + string? serviceId = null) + { + Verify.NotNull(services); + Verify.NotNullOrWhiteSpace(modelPath); + Verify.NotNull(providers); + + IChatClient Factory(IServiceProvider serviceProvider, object? _) + { + var loggerFactory = serviceProvider.GetService(); + + var config = new Config(modelPath); + config.ClearProviders(); + foreach (Provider provider in providers) + { + config.AppendProvider(provider.Id); + foreach (KeyValuePair option in provider.Options) { - StringBuilder promptBuilder = new(); - foreach (var message in messages) - { - promptBuilder.Append($"<|{message.Role}|>\n{message.Text}"); - } - promptBuilder.Append("<|end|>\n<|assistant|>"); - - return promptBuilder.ToString(); + config.SetProviderOption(provider.Id, option.Key, option.Value); } + } + + var chatClient = new OnnxRuntimeGenAIChatClient(config, true, chatClientOptions ?? new OnnxRuntimeGenAIChatClientOptions() + { + PromptFormatter = DefaultPromptFormatter }); var builder = chatClient.AsBuilder() @@ -113,4 +160,16 @@ IChatClient Factory(IServiceProvider serviceProvider, object? _) return services; } + + private static string DefaultPromptFormatter(IEnumerable messages, ChatOptions? options) + { + StringBuilder promptBuilder = new(); + foreach (var message in messages) + { + promptBuilder.Append($"<|{message.Role}|>\n{message.Text}"); + } + promptBuilder.Append("<|end|>\n<|assistant|>"); + + return promptBuilder.ToString(); + } } diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs index 7e29157524d5..9b68ce8f9be9 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxServiceCollectionExtensions.cs @@ -24,7 +24,37 @@ public static class OnnxServiceCollectionExtensions /// Model Id. /// The generative AI ONNX model path for the chat completion service. /// A local identifier for the given AI service. + /// Logger factory. + /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. + /// The same instance as . + public static IServiceCollection AddOnnxRuntimeGenAIChatCompletion( + this IServiceCollection services, + string modelId, + string modelPath, + string? serviceId = null, + ILoggerFactory? loggerFactory = null, + JsonSerializerOptions? jsonSerializerOptions = null) + { + Verify.NotNull(services); + + services.AddKeyedSingleton(serviceId, (serviceProvider, _) => + new OnnxRuntimeGenAIChatCompletionService( + modelId, + modelPath, + loggerFactory: serviceProvider.GetService(), + jsonSerializerOptions)); + + return services; + } + + /// + /// Adds the OnnxRuntimeGenAI Chat Completion services to the specified . + /// + /// The instance to augment. + /// Model Id. + /// The generative AI ONNX model path for the chat completion service. /// Providers + /// A local identifier for the given AI service. /// Logger factory. /// The to use for various aspects of serialization, such as function argument deserialization, function result serialization, logging, etc., of the service. /// The same instance as . @@ -32,11 +62,14 @@ public static IServiceCollection AddOnnxRuntimeGenAIChatCompletion( this IServiceCollection services, string modelId, string modelPath, + IEnumerable providers, string? serviceId = null, - List? providers = null, ILoggerFactory? loggerFactory = null, JsonSerializerOptions? jsonSerializerOptions = null) { + Verify.NotNull(services); + Verify.NotNull(providers); + services.AddKeyedSingleton(serviceId, (serviceProvider, _) => new OnnxRuntimeGenAIChatCompletionService( modelId, @@ -64,6 +97,8 @@ public static IServiceCollection AddBertOnnxTextEmbeddingGeneration( BertOnnxOptions? options = null, string? serviceId = null) { + Verify.NotNull(services); + return services.AddKeyedSingleton( serviceId, BertOnnxTextEmbeddingGenerationService.Create(onnxModelPath, vocabPath, options)); @@ -84,6 +119,8 @@ public static IServiceCollection AddBertOnnxTextEmbeddingGeneration( BertOnnxOptions? options = null, string? serviceId = null) { + Verify.NotNull(services); + return services.AddKeyedSingleton( serviceId, BertOnnxTextEmbeddingGenerationService.Create(onnxModelStream, vocabStream, options)); diff --git a/dotnet/src/Connectors/Connectors.Onnx/Provider.cs b/dotnet/src/Connectors/Connectors.Onnx/Provider.cs index c531e47b24ec..2448e49073c8 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/Provider.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/Provider.cs @@ -8,12 +8,25 @@ namespace Microsoft.SemanticKernel.Connectors.Onnx; public class Provider { /// - /// Id + /// Initializes a new instance of the Provider class with the specified identifier. /// - public string Id { get; set; } = ""; + /// The unique identifier for the provider. Cannot be null or empty. + public Provider(string id) + { + Verify.NotNullOrWhiteSpace(id); + this.Id = id; + } + + /// + /// The unique identifier for the provider. + /// + /// + /// Refers to for available options. + /// + public string Id { get; } /// /// Options /// - public Dictionary Options { get; set; } = new(); + public Dictionary Options { get; set; } = []; } From ec7b26e01eedacce7b7bec7c80b0bc6fbc153865 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:52:28 +0100 Subject: [PATCH 4/9] Fix format --- .../Connectors.Onnx.UnitTests.csproj | 6 - .../OnnxChatClientExtensionsTests.cs | 74 +++++ .../OnnxExtensionsTests.cs | 76 +++++ ...enAIChatCompletionServiceProvidersTests.cs | 301 ++++++++++++++++++ .../Resources/genai_config.json | 18 -- .../BertOnnxTextEmbeddingGenerationService.cs | 12 +- .../OnnxKernelBuilderExtensions.cs | 44 +-- 7 files changed, 484 insertions(+), 47 deletions(-) create mode 100644 dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs delete mode 100644 dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj index 1342619bda6f..fd60589cc4e5 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Connectors.Onnx.UnitTests.csproj @@ -33,10 +33,4 @@ - - - Always - - - diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs index 238ee839c324..755706eb25ed 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs @@ -1,9 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; +using Microsoft.ML.OnnxRuntimeGenAI; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.Onnx; using Xunit; namespace SemanticKernel.Connectors.Onnx.UnitTests; @@ -74,4 +77,75 @@ public void AddOnnxRuntimeGenAIChatClientToKernelBuilderWithServiceId() Assert.NotNull(serviceDescriptor); Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); } + + [Fact] + public void AddOnnxRuntimeGenAIChatClientWithProvidersToServiceCollection() + { + // Arrange + var collection = new ServiceCollection(); + var providers = new List { new("cuda"), new("cpu") }; + + // Act + collection.AddOnnxRuntimeGenAIChatClient("modelPath", providers); + + // Assert + var serviceDescriptor = collection.FirstOrDefault(x => x.ServiceType == typeof(IChatClient)); + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatClientWithProvidersToKernelBuilder() + { + // Arrange + var collection = new ServiceCollection(); + var kernelBuilder = collection.AddKernel(); + var providers = new List { new("cuda"), new("cpu") }; + + // Act + kernelBuilder.AddOnnxRuntimeGenAIChatClient("modelPath", providers); + + // Assert + var serviceDescriptor = collection.FirstOrDefault(x => x.ServiceType == typeof(IChatClient)); + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatClientWithProvidersAndServiceIdToServiceCollection() + { + // Arrange + var collection = new ServiceCollection(); + var providers = new List { new("cuda") }; + + // Act + collection.AddOnnxRuntimeGenAIChatClient("modelPath", providers, serviceId: "test-service"); + var serviceProvider = collection.BuildServiceProvider(); + + // Assert + var exception = Assert.Throws(() => serviceProvider.GetRequiredKeyedService("test-service")); + + Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatClientWithProvidersAndServiceIdToKernelBuilder() + { + // Arrange + var collection = new ServiceCollection(); + var kernelBuilder = collection.AddKernel(); + var providers = new List { new("cuda") }; + + // Act + kernelBuilder.AddOnnxRuntimeGenAIChatClient("modelPath", providers, serviceId: "test-service"); + var serviceProvider = collection.BuildServiceProvider(); + + // Assert + var kernel = serviceProvider.GetRequiredService(); + var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); + + Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + } } diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs index 9f49001997d7..2610121e72ad 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; +using Microsoft.ML.OnnxRuntimeGenAI; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.Onnx; @@ -46,4 +50,76 @@ public void AddOnnxRuntimeGenAIChatCompletionToKernelBuilder() Assert.NotNull(service); Assert.IsType(service); } + + [Fact] + public void AddOnnxRuntimeGenAIChatCompletionWithProvidersToServiceCollection() + { + // Arrange + var collection = new ServiceCollection(); + var providers = new List { new("cuda"), new("cpu") }; + collection.AddOnnxRuntimeGenAIChatCompletion("modelId", "modelPath", providers); + + // Act + var serviceDescriptor = collection.FirstOrDefault(x => x.ServiceType == typeof(IChatCompletionService)); + + // Assert + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatCompletionWithProvidersToKernelBuilder() + { + // Arrange + var collection = new ServiceCollection(); + var kernelBuilder = collection.AddKernel(); + var providers = new List { new("cuda"), new("cpu") }; + kernelBuilder.AddOnnxRuntimeGenAIChatCompletion("modelId", "modelPath", providers); + + // Act + var serviceDescriptor = collection.FirstOrDefault(x => x.ServiceType == typeof(IChatCompletionService)); + + // Assert + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatCompletionWithProvidersAndServiceIdToServiceCollection() + { + // Arrange + var collection = new ServiceCollection(); + var providers = new List { new("cuda") }; + collection.AddOnnxRuntimeGenAIChatCompletion("modelId", "modelPath", providers, serviceId: "test-service"); + + // Act + var serviceProvider = collection.BuildServiceProvider(); + + // Assert + var exception = Assert.Throws(() => serviceProvider.GetRequiredKeyedService("test-service")); + + Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + } + + [Fact] + public void AddOnnxRuntimeGenAIChatCompletionWithProvidersAndServiceIdToKernelBuilder() + { + // Arrange + var collection = new ServiceCollection(); + var kernelBuilder = collection.AddKernel(); + var providers = new List { new("cuda") }; + kernelBuilder.AddOnnxRuntimeGenAIChatCompletion("modelId", "modelPath", providers, serviceId: "test-service"); + + // Act + var serviceDescriptor = collection.FirstOrDefault(x => x.ServiceType == typeof(IChatCompletionService) && x.ServiceKey?.ToString() == "test-service"); + var serviceProvider = collection.BuildServiceProvider(); + + // Assert + var kernel = serviceProvider.GetRequiredService(); + var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); + + Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + } } diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs new file mode 100644 index 000000000000..af7c302f744b --- /dev/null +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.ML.OnnxRuntimeGenAI; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.Onnx; +using Xunit; + +namespace SemanticKernel.Connectors.Onnx.UnitTests; + +/// +/// Unit tests for constructor overloads and Provider functionality. +/// +public class OnnxRuntimeGenAIChatCompletionServiceProvidersTests +{ + private const string TestModelId = "test-model"; + private const string TestModelPath = "test-model-path"; + + [Fact] + public void ConstructorWithProvidersShouldValidateParameters() + { + // Arrange + var providers = new List { new("cuda"), new("cpu") }; + + // Act & Assert - Should not throw during parameter validation + // Note: We expect this to fail during ONNX model loading, but parameter validation should pass + var exception = Assert.ThrowsAny(() => + new OnnxRuntimeGenAIChatCompletionService(TestModelId, TestModelPath, providers)); + + // The exception should not be from parameter validation (ArgumentException/ArgumentNullException) + Assert.False(exception is ArgumentException || exception is ArgumentNullException, + "Constructor should not fail due to parameter validation when valid parameters are provided"); + } + + [Fact] + public void ConstructorWithNullModelIdShouldThrowArgumentNullException() + { + // Arrange + var providers = new List { new("cuda") }; + + // Act & Assert + Assert.Throws(() => + new OnnxRuntimeGenAIChatCompletionService( + null!, + TestModelPath, + providers)); + } + + [Fact] + public void ConstructorWithEmptyModelIdShouldThrowArgumentException() + { + // Arrange + var providers = new List { new("cuda") }; + + // Act & Assert + Assert.Throws(() => + new OnnxRuntimeGenAIChatCompletionService( + string.Empty, + TestModelPath, + providers)); + } + + [Fact] + public void ConstructorWithNullModelPathShouldThrowArgumentNullException() + { + // Arrange + var providers = new List { new("cuda") }; + + // Act & Assert + Assert.Throws(() => + new OnnxRuntimeGenAIChatCompletionService( + TestModelId, + null!, + providers)); + } + + [Fact] + public void ConstructorWithEmptyModelPathShouldThrowArgumentException() + { + // Arrange + var providers = new List { new("cuda") }; + + // Act & Assert + Assert.Throws(() => + new OnnxRuntimeGenAIChatCompletionService( + TestModelId, + string.Empty, + providers)); + } + + [Fact] + public void ConstructorWithNullProvidersShouldThrowArgumentNullException() + { + // Act & Assert + Assert.Throws(() => + new OnnxRuntimeGenAIChatCompletionService( + TestModelId, + TestModelPath, + (IEnumerable)null!)); + } + + [Fact] + public void ConstructorWithEmptyProvidersShouldValidateParameters() + { + // Arrange + var providers = new List(); + + // Act & Assert - Should not throw during parameter validation + var exception = Assert.ThrowsAny(() => + new OnnxRuntimeGenAIChatCompletionService(TestModelId, TestModelPath, providers)); + + // The exception should not be from parameter validation + Assert.False(exception is ArgumentException || exception is ArgumentNullException, + "Constructor should not fail due to parameter validation when valid parameters are provided"); + } + + [Fact] + public void ConstructorWithMultipleProvidersShouldValidateParameters() + { + // Arrange + var providers = new List + { + new("cuda"), + new("cpu"), + new("dml") + }; + + // Act & Assert - Should not throw during parameter validation + var exception = Assert.ThrowsAny(() => + new OnnxRuntimeGenAIChatCompletionService(TestModelId, TestModelPath, providers)); + + // The exception should not be from parameter validation + Assert.False(exception is ArgumentException || exception is ArgumentNullException, + "Constructor should not fail due to parameter validation when valid parameters are provided"); + } + + [Fact] + public void ConstructorWithProviderOptionsShouldValidateParameters() + { + // Arrange + var provider = new Provider("cuda"); + provider.Options["device_id"] = "0"; + provider.Options["gpu_mem_limit"] = "2147483648"; + var providers = new List { provider }; + + // Act & Assert - Should not throw during parameter validation + var exception = Assert.ThrowsAny(() => + new OnnxRuntimeGenAIChatCompletionService(TestModelId, TestModelPath, providers)); + + // The exception should not be from parameter validation + Assert.False(exception is ArgumentException || exception is ArgumentNullException, + "Constructor should not fail due to parameter validation when valid parameters are provided"); + } + + [Theory] + [InlineData("cuda")] + [InlineData("cpu")] + [InlineData("dml")] + [InlineData("coreml")] + public void ConstructorWithDifferentProviderTypesShouldValidateParameters(string providerId) + { + // Arrange + var providers = new List { new(providerId) }; + + // Act & Assert - Should not throw during parameter validation + var exception = Assert.ThrowsAny(() => + new OnnxRuntimeGenAIChatCompletionService(TestModelId, TestModelPath, providers)); + + // The exception should not be from parameter validation + Assert.False(exception is ArgumentException || exception is ArgumentNullException, + "Constructor should not fail due to parameter validation when valid parameters are provided"); + } + + [Fact] + public void ServiceRegistrationWithProvidersShouldRegisterCorrectly() + { + // Arrange + var services = new ServiceCollection(); + var providers = new List { new("cuda") }; + + // Act + services.AddOnnxRuntimeGenAIChatCompletion(TestModelId, TestModelPath, providers); + + // Assert + var serviceDescriptor = services.FirstOrDefault(x => x.ServiceType == typeof(IChatCompletionService)); + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void ServiceRegistrationWithProvidersAndServiceIdShouldRegisterWithKey() + { + // Arrange + var services = new ServiceCollection(); + var providers = new List { new("cuda") }; + const string serviceId = "test-service"; + + // Act + services.AddOnnxRuntimeGenAIChatCompletion(TestModelId, TestModelPath, providers, serviceId); + services.AddKernel(); + var serviceProvider = services.BuildServiceProvider(); + var kernel = serviceProvider.GetRequiredService(); + + // Assert - Should be able to retrieve the service by its key + var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); + + Assert.Contains($"Error opening {TestModelPath}\\genai_config.json", exception.Message); + } + + [Fact] + public void KernelBuilderExtensionWithProvidersShouldRegisterCorrectly() + { + // Arrange + var services = new ServiceCollection(); + var kernelBuilder = services.AddKernel(); + var providers = new List { new("cuda") }; + + // Act + kernelBuilder.AddOnnxRuntimeGenAIChatCompletion(TestModelId, TestModelPath, providers); + + // Assert + var serviceDescriptor = services.FirstOrDefault(x => x.ServiceType == typeof(IChatCompletionService)); + Assert.NotNull(serviceDescriptor); + Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); + Assert.NotNull(serviceDescriptor.ImplementationFactory); + } + + [Fact] + public void KernelBuilderExtensionWithProvidersAndServiceIdShouldRegisterWithKey() + { + // Arrange + var services = new ServiceCollection(); + var kernelBuilder = services.AddKernel(); + var providers = new List { new("cuda") }; + const string serviceId = "test-service"; + + // Act + kernelBuilder.AddOnnxRuntimeGenAIChatCompletion(TestModelId, TestModelPath, providers, serviceId); + var serviceProvider = services.BuildServiceProvider(); + var kernel = serviceProvider.GetRequiredService(); + + // Assert - Should be able to retrieve the service by its key + var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); + + Assert.Contains($"Error opening {TestModelPath}\\genai_config.json", exception.Message); + } + + [Fact] + public void ProviderConstructorShouldInitializeCorrectly() + { + // Arrange & Act + var provider = new Provider("cuda"); + + // Assert + Assert.Equal("cuda", provider.Id); + Assert.NotNull(provider.Options); + Assert.Empty(provider.Options); + } + + [Fact] + public void ProviderWithOptionsShouldStoreOptionsCorrectly() + { + // Arrange + var provider = new Provider("cuda"); + + // Act + provider.Options["device_id"] = "0"; + provider.Options["gpu_mem_limit"] = "2147483648"; + + // Assert + Assert.Equal("0", provider.Options["device_id"]); + Assert.Equal("2147483648", provider.Options["gpu_mem_limit"]); + Assert.Equal(2, provider.Options.Count); + } + + [Fact] + public void ProviderConstructorWithNullIdShouldThrowArgumentNullException() + { + // Act & Assert + Assert.Throws(() => new Provider(null!)); + } + + [Fact] + public void ProviderConstructorWithEmptyIdShouldThrowArgumentException() + { + // Act & Assert + Assert.Throws(() => new Provider(string.Empty)); + } + + [Fact] + public void ProviderConstructorWithWhitespaceIdShouldThrowArgumentException() + { + // Act & Assert + Assert.Throws(() => new Provider(" ")); + } +} diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json deleted file mode 100644 index a6f01463e6f9..000000000000 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/Resources/genai_config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "search": { - "diversity_penalty": 0.0, - "do_sample": false, - "early_stopping": true, - "length_penalty": 1.0, - "max_length": 4096, - "min_length": 0, - "no_repeat_ngram_size": 0, - "num_beams": 1, - "num_return_sequences": 1, - "past_present_share_buffer": true, - "repetition_penalty": 1.0, - "temperature": 1.0, - "top_k": 1, - "top_p": 1.0 - } -} diff --git a/dotnet/src/Connectors/Connectors.Onnx/BertOnnxTextEmbeddingGenerationService.cs b/dotnet/src/Connectors/Connectors.Onnx/BertOnnxTextEmbeddingGenerationService.cs index ec199fa5579f..5414792c4b3f 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/BertOnnxTextEmbeddingGenerationService.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/BertOnnxTextEmbeddingGenerationService.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging; using Microsoft.ML.OnnxRuntime; using Microsoft.SemanticKernel.Embeddings; +using IServiceCollection = Microsoft.Extensions.DependencyInjection.OnnxServiceCollectionExtensions; namespace Microsoft.SemanticKernel.Connectors.Onnx; @@ -21,11 +22,18 @@ namespace Microsoft.SemanticKernel.Connectors.Onnx; #pragma warning disable CA2000 // Dispose objects before losing scope #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - +#pragma warning disable CS0419 // Ambiguous reference in cref attribute /// /// Provides a text embedding generation service using a BERT ONNX model. /// -[Obsolete("Use BertOnnxEmbeddingGenerator instead.")] +/// +/// This service is obsolete and will be removed in a future version. Please use one of the extensions options below: +/// +/// . +/// . +/// +/// +[Obsolete("Use AddBertOnnxEmbeddingGenerator extensions instead.")] public sealed class BertOnnxTextEmbeddingGenerationService : ITextEmbeddingGenerationService, IDisposable { /// Reusable options instance passed to OnnxSession.Run. diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs index f206abdfa227..c3b64215d53a 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxKernelBuilderExtensions.cs @@ -6,9 +6,7 @@ using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.Onnx; -using Microsoft.SemanticKernel.Embeddings; namespace Microsoft.SemanticKernel; @@ -37,12 +35,12 @@ public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( { Verify.NotNull(builder); - builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => - new OnnxRuntimeGenAIChatCompletionService( - modelId, - modelPath: modelPath, - loggerFactory: serviceProvider.GetService(), - jsonSerializerOptions)); + builder.Services.AddOnnxRuntimeGenAIChatCompletion( + modelId, + modelPath, + serviceId, + loggerFactory, + jsonSerializerOptions); return builder; } @@ -69,13 +67,13 @@ public static IKernelBuilder AddOnnxRuntimeGenAIChatCompletion( { Verify.NotNull(builder); - builder.Services.AddKeyedSingleton(serviceId, (serviceProvider, _) => - new OnnxRuntimeGenAIChatCompletionService( - modelId, - modelPath: modelPath, - providers: providers, - loggerFactory: serviceProvider.GetService(), - jsonSerializerOptions)); + builder.Services.AddOnnxRuntimeGenAIChatCompletion( + modelId, + modelPath, + providers, + serviceId, + loggerFactory, + jsonSerializerOptions); return builder; } @@ -96,9 +94,11 @@ public static IKernelBuilder AddBertOnnxTextEmbeddingGeneration( BertOnnxOptions? options = null, string? serviceId = null) { - builder.Services.AddKeyedSingleton( - serviceId, - BertOnnxTextEmbeddingGenerationService.Create(onnxModelPath, vocabPath, options)); + builder.Services.AddBertOnnxTextEmbeddingGeneration( + onnxModelPath, + vocabPath, + options, + serviceId); return builder; } @@ -118,9 +118,11 @@ public static IKernelBuilder AddBertOnnxTextEmbeddingGeneration( BertOnnxOptions? options = null, string? serviceId = null) { - builder.Services.AddKeyedSingleton( - serviceId, - BertOnnxTextEmbeddingGenerationService.Create(onnxModelStream, vocabStream, options)); + builder.Services.AddBertOnnxTextEmbeddingGeneration( + onnxModelStream, + vocabStream, + options, + serviceId); return builder; } From 1c64cdf02cd14cbae3701bb0e6f7888e0f9b6a65 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:57:48 +0100 Subject: [PATCH 5/9] Address model fix --- .../Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs index 4614a27c701b..4a770d2de0bd 100644 --- a/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs +++ b/dotnet/src/Connectors/Connectors.Onnx/OnnxRuntimeGenAIChatCompletionService.cs @@ -78,6 +78,8 @@ public OnnxRuntimeGenAIChatCompletionService( this._config.SetProviderOption(provider.Id, option.Key, option.Value); } } + + this._model = new Model(this._config); } private IChatCompletionService GetChatCompletionService() From 0046b3de1a4ce613feb4e39e2e0219bbf5d151a6 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Tue, 5 Aug 2025 22:02:16 +0100 Subject: [PATCH 6/9] Fix realistic path --- dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs | 2 +- dotnet/samples/Demos/OnnxWithProviderChoice/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs index 51d41c6eff54..a98087b51809 100644 --- a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs @@ -5,7 +5,7 @@ using Microsoft.SemanticKernel.Connectors.Onnx; // Path to the folder of your downloaded ONNX CUDA model -// i.e: D:\huggingface\Phi-3-mini-4k-instruct-onnx\cuda\cuda-int4-rtn-block-32 +// i.e: D:\repo\huggingface\Phi-3-mini-4k-instruct-onnx\cuda\cuda-int4-rtn-block-32 string modelPath = "MODEL_PATH"; IKernelBuilder builder = Kernel.CreateBuilder(); diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/README.md b/dotnet/samples/Demos/OnnxWithProviderChoice/README.md index 8b83bb300b09..e42fc5a90533 100644 --- a/dotnet/samples/Demos/OnnxWithProviderChoice/README.md +++ b/dotnet/samples/Demos/OnnxWithProviderChoice/README.md @@ -39,6 +39,6 @@ Update the `Program.cs` file lines below with the paths to the models you downlo ```csharp // i.e. Running on Windows -string modelPath = "D:\\huggingface\\Phi-3-mini-4k-instruct-onnx\\cuda\\cuda-int4-rtn-block-32"; +string modelPath = "D:\\repo\\huggingface\\Phi-3-mini-4k-instruct-onnx\\cuda\\cuda-int4-rtn-block-32"; ``` From cb6b37b070a5d48a6d528240d183bd125ca1cf61 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:33:53 +0100 Subject: [PATCH 7/9] Update UT to avoid linux/windows path slashes --- .../OnnxChatClientExtensionsTests.cs | 4 ++-- .../Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs | 4 ++-- .../OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs index 755706eb25ed..155f713257be 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxChatClientExtensionsTests.cs @@ -127,7 +127,7 @@ public void AddOnnxRuntimeGenAIChatClientWithProvidersAndServiceIdToServiceColle // Assert var exception = Assert.Throws(() => serviceProvider.GetRequiredKeyedService("test-service")); - Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } [Fact] @@ -146,6 +146,6 @@ public void AddOnnxRuntimeGenAIChatClientWithProvidersAndServiceIdToKernelBuilde var kernel = serviceProvider.GetRequiredService(); var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); - Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } } diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs index 2610121e72ad..32fdca9cd1a3 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxExtensionsTests.cs @@ -100,7 +100,7 @@ public void AddOnnxRuntimeGenAIChatCompletionWithProvidersAndServiceIdToServiceC // Assert var exception = Assert.Throws(() => serviceProvider.GetRequiredKeyedService("test-service")); - Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } [Fact] @@ -120,6 +120,6 @@ public void AddOnnxRuntimeGenAIChatCompletionWithProvidersAndServiceIdToKernelBu var kernel = serviceProvider.GetRequiredService(); var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); - Assert.Contains("Error opening modelPath\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } } diff --git a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs index af7c302f744b..180e98894570 100644 --- a/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs +++ b/dotnet/src/Connectors/Connectors.Onnx.UnitTests/OnnxRuntimeGenAIChatCompletionServiceProvidersTests.cs @@ -209,7 +209,7 @@ public void ServiceRegistrationWithProvidersAndServiceIdShouldRegisterWithKey() // Assert - Should be able to retrieve the service by its key var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); - Assert.Contains($"Error opening {TestModelPath}\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } [Fact] @@ -247,7 +247,7 @@ public void KernelBuilderExtensionWithProvidersAndServiceIdShouldRegisterWithKey // Assert - Should be able to retrieve the service by its key var exception = Assert.Throws(() => kernel.GetRequiredService("test-service")); - Assert.Contains($"Error opening {TestModelPath}\\genai_config.json", exception.Message); + Assert.Contains("genai_config.json", exception.Message); } [Fact] From 0723bfd3cc8acf1752e4182bf64f924d2904cb99 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:12:23 +0100 Subject: [PATCH 8/9] Move folder --- .../OnnxSimpleChatWithCuda.csproj | 0 .../{OnnxWithProviderChoice => OnnxSimpleChatWithCuda}/Program.cs | 0 .../{OnnxWithProviderChoice => OnnxSimpleChatWithCuda}/README.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename dotnet/samples/Demos/{OnnxWithProviderChoice => OnnxSimpleChatWithCuda}/OnnxSimpleChatWithCuda.csproj (100%) rename dotnet/samples/Demos/{OnnxWithProviderChoice => OnnxSimpleChatWithCuda}/Program.cs (100%) rename dotnet/samples/Demos/{OnnxWithProviderChoice => OnnxSimpleChatWithCuda}/README.md (100%) diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/OnnxSimpleChatWithCuda.csproj b/dotnet/samples/Demos/OnnxSimpleChatWithCuda/OnnxSimpleChatWithCuda.csproj similarity index 100% rename from dotnet/samples/Demos/OnnxWithProviderChoice/OnnxSimpleChatWithCuda.csproj rename to dotnet/samples/Demos/OnnxSimpleChatWithCuda/OnnxSimpleChatWithCuda.csproj diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs b/dotnet/samples/Demos/OnnxSimpleChatWithCuda/Program.cs similarity index 100% rename from dotnet/samples/Demos/OnnxWithProviderChoice/Program.cs rename to dotnet/samples/Demos/OnnxSimpleChatWithCuda/Program.cs diff --git a/dotnet/samples/Demos/OnnxWithProviderChoice/README.md b/dotnet/samples/Demos/OnnxSimpleChatWithCuda/README.md similarity index 100% rename from dotnet/samples/Demos/OnnxWithProviderChoice/README.md rename to dotnet/samples/Demos/OnnxSimpleChatWithCuda/README.md From 51676ed919e1a70d0bee65e33bd228b308e1f852 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:18:57 +0100 Subject: [PATCH 9/9] Adjust solution --- dotnet/SK-dotnet.slnx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/SK-dotnet.slnx b/dotnet/SK-dotnet.slnx index 2c28f5b2f59c..ed0d610cf578 100644 --- a/dotnet/SK-dotnet.slnx +++ b/dotnet/SK-dotnet.slnx @@ -41,7 +41,7 @@ - +