diff --git a/AIDevGallery/Samples/Definitions/WcrApis/WcrApiCodeSnippet.cs b/AIDevGallery/Samples/Definitions/WcrApis/WcrApiCodeSnippet.cs index 9ca67223..9d4f760e 100644 --- a/AIDevGallery/Samples/Definitions/WcrApis/WcrApiCodeSnippet.cs +++ b/AIDevGallery/Samples/Definitions/WcrApis/WcrApiCodeSnippet.cs @@ -38,6 +38,32 @@ internal static class WcrApiCodeSnippet } """" }, + { + ModelType.PhiSilicaLora, """" + using Microsoft.Windows.AI; + using Microsoft.Windows.AI.Text; + using Microsoft.Windows.AI.Text.Experimental; + var readyState = LanguageModel.GetReadyState(); + if (readyState is AIFeatureReadyState.Ready or AIFeatureReadyState.NotReady) + { + if (readyState == AIFeatureReadyState.NotReady) + { + var op = await LanguageModel.EnsureReadyAsync(); + } + using LanguageModel languageModel = await LanguageModel.CreateAsync(); + using LanguageModelExperimental loraModel = new LanguageModelExperimental(languageModel); + string adapterFilePath = "path_to_your_adapter_file"; + LowRankAdaptation loraAdapter = loraModel.LoadAdapter(adapterFilePath); + var options = new LanguageModelOptionsExperimental + { + LoraAdapter = loraAdapter + }; + string prompt = "Provide the molecular formula for glucose."; + var result = await loraModel.GenerateResponseAsync(prompt, options); + Console.WriteLine(result.Text); + } + """" + }, { ModelType.TextSummarizer, """" using Microsoft.Windows.AI; diff --git a/AIDevGallery/Samples/Definitions/WcrApis/WcrApiHelpers.cs b/AIDevGallery/Samples/Definitions/WcrApis/WcrApiHelpers.cs index a895630d..c7cf06e5 100644 --- a/AIDevGallery/Samples/Definitions/WcrApis/WcrApiHelpers.cs +++ b/AIDevGallery/Samples/Definitions/WcrApis/WcrApiHelpers.cs @@ -17,6 +17,7 @@ internal static class WcrApiHelpers private static readonly HashSet LanguageModelBacked = new() { ModelType.PhiSilica, + ModelType.PhiSilicaLora, ModelType.TextSummarizer, ModelType.TextRewriter, ModelType.TextToTableConverter @@ -26,6 +27,9 @@ internal static class WcrApiHelpers { ModelType.PhiSilica, LanguageModel.GetReadyState }, + { + ModelType.PhiSilicaLora, LanguageModel.GetReadyState + }, { ModelType.TextSummarizer, LanguageModel.GetReadyState }, @@ -57,6 +61,9 @@ internal static class WcrApiHelpers { ModelType.PhiSilica, LanguageModel.EnsureReadyAsync }, + { + ModelType.PhiSilicaLora, LanguageModel.EnsureReadyAsync + }, { ModelType.TextSummarizer, LanguageModel.EnsureReadyAsync }, diff --git a/AIDevGallery/Samples/Definitions/WcrApis/apis.json b/AIDevGallery/Samples/Definitions/WcrApis/apis.json index d582b99c..6cc2fd39 100644 --- a/AIDevGallery/Samples/Definitions/WcrApis/apis.json +++ b/AIDevGallery/Samples/Definitions/WcrApis/apis.json @@ -15,6 +15,17 @@ "SampleIdToShowInDocs": "21f2c4a5-3d8e-4b7a-9c0f-6d2e5f3b1c8d", "Category": "Phi Silica" }, + "PhiSilicaLora": { + "Id": "6b319cbc-59e5-4ed0-af33-e7575bee7475", + "Name": "Phi Silica LoRA", + "Icon": "WCRAPI.svg", + "IconGlyph": "\uEA54", + "Description": "Generate text using adapter.", + "ReadmeUrl": "https://github.com/MicrosoftDocs/windows-ai-docs/blob/docs/docs/apis/phi-silica-lora.md", + "License": "ms-pl", + "SampleIdToShowInDocs": "3e392b7f-02a8-45e0-bed1-f75186368f12", + "Category": "Phi Silica" + }, "TextSummarizer": { "Id": "55eb4d48-3908-44ac-95b1-fa47318a5cbf", "Name": "Text Summarizer", diff --git a/AIDevGallery/Samples/WCRAPIs/PhiSilicaLoRa.xaml.cs b/AIDevGallery/Samples/WCRAPIs/PhiSilicaLoRa.xaml.cs index 88899e91..fc5fef1c 100644 --- a/AIDevGallery/Samples/WCRAPIs/PhiSilicaLoRa.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/PhiSilicaLoRa.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using AIDevGallery.Models; +using AIDevGallery.Samples.Attributes; using AIDevGallery.Samples.SharedCode; using AIDevGallery.Utils; using Microsoft.UI.Xaml; @@ -9,6 +10,7 @@ using Microsoft.UI.Xaml.Input; using Microsoft.Windows.AI; using Microsoft.Windows.AI.Text; +using Microsoft.Windows.AI.Text.Experimental; using System; using System.IO; using System.Threading; @@ -21,6 +23,15 @@ */ namespace AIDevGallery.Samples.WCRAPIs; +[GallerySample( + Name = "Generate with Phi Silica with Adapter", + Model1Types = [ModelType.PhiSilicaLora], + Id = "3e392b7f-02a8-45e0-bed1-f75186368f12", + Scenario = ScenarioType.TextLoRAAdapters, + NugetPackageReferences = [ + "Microsoft.Extensions.AI" + ], + Icon = "\uEE6F")] internal sealed partial class PhiSilicaLoRa : BaseSamplePage { @@ -34,6 +45,7 @@ internal enum GenerationType private const int MaxLength = 1000; private bool _isProgressVisible; private LanguageModel? _languageModel; + private LanguageModelExperimental? _loraModel; private CancellationTokenSource? _cts; private IAsyncOperationWithProgress? operation; @@ -86,6 +98,7 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa } _languageModel = await LanguageModel.CreateAsync(); + _loraModel = new LanguageModelExperimental(_languageModel); } else { @@ -145,9 +158,9 @@ public bool IsProgressVisible } } - public async Task GenerateText(string prompt, string systemPrompt, TextBlock textBlock, LanguageModelOptions? options = null) + public async Task GenerateText(string prompt, string systemPrompt, TextBlock textBlock, LanguageModelOptionsExperimental? options = null) { - if (_languageModel == null) + if (_languageModel == null || _loraModel == null) { ShowException(null, "Phi-Silica is not available."); return; @@ -163,8 +176,8 @@ public async Task GenerateText(string prompt, string systemPrompt, TextBlock tex // it is created for each query to avoid bringing history from previous queries LanguageModelContext? context = systemPrompt.Length > 0 ? _languageModel.CreateContext(systemPrompt) : null; operation = context == null ? - options == null ? _languageModel.GenerateResponseAsync(prompt) : _languageModel.GenerateResponseAsync(prompt, options) : - options == null ? _languageModel.GenerateResponseAsync(context, prompt, new LanguageModelOptions()) : _languageModel.GenerateResponseAsync(context, prompt, options); + options == null ? _languageModel.GenerateResponseAsync(prompt) : _loraModel.GenerateResponseAsync(prompt, options) : + options == null ? _languageModel.GenerateResponseAsync(context, prompt, new LanguageModelOptions()) : _loraModel.GenerateResponseAsync(context, prompt, options); if (operation == null) { @@ -203,6 +216,32 @@ public async Task GenerateText(string prompt, string systemPrompt, TextBlock tex NarratorHelper.Announce(InputTextBox, "Content has finished generating.", "GenerateDoneAnnouncementActivityId"); // } + private LanguageModelOptionsExperimental? LoadAdapter() + { + if (_loraModel == null) + { + ShowException(null, "Phi-Silica is not available."); + return null; + } + + LowRankAdaptation? loraAdapter; + try + { + loraAdapter = _loraModel.LoadAdapter(_adapterFilePath); + } + catch (Exception ex) + { + ShowException(ex); + return null; + } + + var options = new LanguageModelOptionsExperimental + { + LoraAdapter = loraAdapter + }; + return options; + } + private async Task RunQuery() { if (!File.Exists(_adapterFilePath)) @@ -217,15 +256,8 @@ private async Task RunQuery() return; } - if (this.InputTextBox.Text.Length > 0 && _languageModel != null) + if (this.InputTextBox.Text.Length > 0 && _loraModel != null) { - if (_adapterFilePath != null) - { - ShowException(null, "Phi-Silica Lora adapter is not supported."); - } - - var options = new LanguageModelOptions(); - GenerateButton.Visibility = Visibility.Collapsed; StopBtn.Visibility = Visibility.Visible; InputTextBox.IsEnabled = false; @@ -234,16 +266,19 @@ private async Task RunQuery() _cts?.Cancel(); _cts = new CancellationTokenSource(); + var options = new LanguageModelOptionsExperimental(); try { switch (_generationType) { case GenerationType.All: + options = LoadAdapter(); await Task.WhenAll( GenerateText(InputTextBox.Text, SystemPromptBox.Text, LoraTxt, options), GenerateText(InputTextBox.Text, SystemPromptBox.Text, NoLoraTxt)); break; case GenerationType.With: + options = LoadAdapter(); await GenerateText(InputTextBox.Text, SystemPromptBox.Text, LoraTxt, options); break; case GenerationType.Without: diff --git a/AIDevGallery/Samples/scenarios.json b/AIDevGallery/Samples/scenarios.json index 7b628baa..89c8b7d9 100644 --- a/AIDevGallery/Samples/scenarios.json +++ b/AIDevGallery/Samples/scenarios.json @@ -70,6 +70,12 @@ "Instructions": "Enter a system prompt, a query, and manipulate parameters related to token selection to fine-tune language model output. Hover over any field on this page to see a tooltip containing more info on the parameter.", "Id": "custom-parameters" }, + "LoRAAdapters": { + "Name": "LoRA fine-tuning for Phi Silica", + "Description": "Generate text by fine-tuning Phi Silica with a Low Rank Adaption (LoRA) adapter.", + "Instructions": "Select a LoRA adapter to use with a language model. Enter a prompt to pass to the model and adapter and generate the response.", + "Id": "generate-with-lora" + }, "WinAiSummarize": { "Name": "Windows AI Text Summarization", "Description": "Summarize Text using Windows AI Text Summarizer.", diff --git a/Directory.Packages.props b/Directory.Packages.props index 12b692ab..a8e7a60a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -29,7 +29,7 @@ - + diff --git a/resources/lora_adapter.safetensors b/resources/lora_adapter.safetensors index f7454f01..f0d388ca 100644 Binary files a/resources/lora_adapter.safetensors and b/resources/lora_adapter.safetensors differ