diff --git a/PdfProcessing/AIConnectorDemo/AIConnectorDemo.csproj b/PdfProcessing/AIConnectorDemo/AIConnectorDemo.csproj index e995d7e..464d370 100644 --- a/PdfProcessing/AIConnectorDemo/AIConnectorDemo.csproj +++ b/PdfProcessing/AIConnectorDemo/AIConnectorDemo.csproj @@ -13,15 +13,15 @@ - + - - + + diff --git a/PdfProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj b/PdfProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj index feba08d..4fb70ac 100644 --- a/PdfProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj +++ b/PdfProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj @@ -13,13 +13,9 @@ - - - - - - + + diff --git a/PdfProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs b/PdfProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs new file mode 100644 index 0000000..c3b8669 --- /dev/null +++ b/PdfProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs @@ -0,0 +1,99 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using Telerik.Documents.AI.Core; + +namespace AIConnectorDemo +{ + public class CustomOpenAIEmbedder : IEmbedder + { + private readonly HttpClient httpClient; + + public CustomOpenAIEmbedder() + { + HttpClient httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromMinutes(5); + string endpoint = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_ENDPOINT"); + string apiKey = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_KEY"); + + httpClient.BaseAddress = new Uri(endpoint); + httpClient.DefaultRequestHeaders.Add("api-key", apiKey); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + this.httpClient = httpClient; + } + + public async Task> EmbedAsync(IList fragments) + { + AzureEmbeddingsRequest requestBody = new AzureEmbeddingsRequest + { + Input = fragments.Select(p => p.ToEmbeddingText()).ToArray(), + Dimensions = 3072 + }; + + string json = JsonSerializer.Serialize(requestBody); + StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + string apiVersion = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_APIVERSION"); + string deploymentName = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_DEPLOYMENT"); + string url = $"openai/deployments/{deploymentName}/embeddings?api-version={apiVersion}"; + using HttpResponseMessage response = await this.httpClient.PostAsync(url, content, CancellationToken.None); + + Embedding[] embeddings = new Embedding[fragments.Count]; + + string responseJson = await response.Content.ReadAsStringAsync(CancellationToken.None); + AzureEmbeddingsResponse responseObj = JsonSerializer.Deserialize(responseJson); + + List sorted = responseObj.Data.OrderBy(d => d.Index).ToList(); + List result = new List(sorted.Count); + + for (int i = 0; i < sorted.Count; i++) + { + EmbeddingData item = sorted[i]; + embeddings[i] = new Embedding(fragments[i], item.Embedding); + } + + return embeddings; + } + + private sealed class AzureEmbeddingsRequest + { + [System.Text.Json.Serialization.JsonPropertyName("input")] + public string[] Input { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("dimensions")] + public int? Dimensions { get; set; } + } + + private sealed class AzureEmbeddingsResponse + { + [System.Text.Json.Serialization.JsonPropertyName("data")] + public EmbeddingData[] Data { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("model")] + public string? Model { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("usage")] + public UsageInfo? Usage { get; set; } + } + + private sealed class UsageInfo + { + [System.Text.Json.Serialization.JsonPropertyName("prompt_tokens")] + public int PromptTokens { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("total_tokens")] + public int TotalTokens { get; set; } + } + + private sealed class EmbeddingData + { + [System.Text.Json.Serialization.JsonPropertyName("embedding")] + public float[] Embedding { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("index")] + public int Index { get; set; } + } + } +} \ No newline at end of file diff --git a/PdfProcessing/AIConnectorDemo/OllamaEmbeddingsStorage.cs b/PdfProcessing/AIConnectorDemo/OllamaEmbeddingsStorage.cs deleted file mode 100644 index c089244..0000000 --- a/PdfProcessing/AIConnectorDemo/OllamaEmbeddingsStorage.cs +++ /dev/null @@ -1,67 +0,0 @@ -using LangChain.Databases; -using LangChain.Databases.Sqlite; -using LangChain.DocumentLoaders; -using LangChain.Extensions; -using LangChain.Providers; -using LangChain.Providers.Ollama; -using Telerik.Documents.AIConnector; - -namespace AIConnectorDemo -{ - // Necessary steps to get this working: - // 1. Install Ollama: https://ollama.com/ - // 2. Pull the all-minilm model we'll use for embeddings: ollama pull all-minilm - // 3. Ensure Ollama is running: ollama serve - internal class OllamaEmbeddingsStorage : IEmbeddingsStorage - { - private const string AllMinilmEmbeddingModelName = "all-minilm"; - private const string DBName = "vectors.db"; - private const int DimensionsForAllMinilm = 384; // Should be 384 for all-minilm - private static readonly string defaultCollectionName = "defaultName"; - - private readonly SqLiteVectorDatabase vectorDatabase; - private readonly OllamaEmbeddingModel embeddingModel; - - IVectorCollection vectorCollection; - - public OllamaEmbeddingsStorage() - { - OllamaProvider provider = new OllamaProvider(); - this.embeddingModel = new OllamaEmbeddingModel(provider, id: AllMinilmEmbeddingModelName); - this.vectorDatabase = new SqLiteVectorDatabase(dataSource: DBName); - } - - public async Task GetQuestionContext(string question) - { - IReadOnlyCollection similarDocuments = await this.vectorCollection.GetSimilarDocuments(this.embeddingModel, question, amount: 5); - - return similarDocuments.AsString(); - } - - public void SetText(string text, PartialContextProcessorSettings settings) - { - MemoryStream memoryStream = new MemoryStream(); - StreamWriter writer = new StreamWriter(memoryStream); - writer.Write(text); - - if (this.vectorDatabase.IsCollectionExistsAsync(defaultCollectionName).Result) - { - this.vectorDatabase.DeleteCollectionAsync(defaultCollectionName).Wait(); - } - - this.vectorCollection = this.vectorDatabase.AddDocumentsFromAsync( - this.embeddingModel, - dimensions: DimensionsForAllMinilm, - dataSource: DataSource.FromBytes(memoryStream.ToArray()), - textSplitter: null, - collectionName: defaultCollectionName, - behavior: AddDocumentsToDatabaseBehavior.JustReturnCollectionIfCollectionIsAlreadyExists).Result; - - } - - public void Dispose() - { - this.vectorDatabase.Dispose(); - } - } -} \ No newline at end of file diff --git a/PdfProcessing/AIConnectorDemo/Program.cs b/PdfProcessing/AIConnectorDemo/Program.cs index 6194742..33e93be 100644 --- a/PdfProcessing/AIConnectorDemo/Program.cs +++ b/PdfProcessing/AIConnectorDemo/Program.cs @@ -2,6 +2,8 @@ using Microsoft.Extensions.AI; using OpenAI.Chat; using System.IO; +using Telerik.Documents.AI.Core; + #if NETWINDOWS using Telerik.Windows.Documents.AIConnector; #else @@ -16,19 +18,22 @@ namespace AIConnectorDemo internal class Program { static int maxTokenCount = 128000; + static int maxNumberOfEmbeddingsSent = 50000; static IChatClient iChatClient; + static string tokenizationEncoding = "cl100k_base"; + static string model = "gpt-4o-mini"; + static string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY"); + static string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT"); static void Main(string[] args) { - Console.WriteLine("Hello, World!"); - CreateChatClient(); using (Stream input = File.OpenRead("John Grisham.pdf")) { PdfFormatProvider pdfFormatProvider = new PdfFormatProvider(); RadFixedDocument inputPdf = pdfFormatProvider.Import(input, null); - ISimpleTextDocument simpleDocument = inputPdf.ToSimpleTextDocument(); + SimpleTextDocument simpleDocument = inputPdf.ToSimpleTextDocument(TimeSpan.FromSeconds(10)); Summarize(simpleDocument); @@ -44,10 +49,6 @@ static void Main(string[] args) private static void CreateChatClient() { - string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY"); - string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT"); - string model = "gpt-4o-mini"; - AzureOpenAIClient azureClient = new( new Uri(endpoint), new Azure.AzureKeyCredential(key), @@ -57,10 +58,12 @@ private static void CreateChatClient() iChatClient = new OpenAIChatClient(chatClient); } - private static void Summarize(ISimpleTextDocument simpleDocument) + private static void Summarize(SimpleTextDocument simpleDocument) { - SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, maxTokenCount); - summarizationProcessor.Settings.PromptAddition = "Summarize the text in a few sentences. Be concise and clear."; + string additionalPrompt = "Summarize the text in a few sentences. Be concise and clear."; + SummarizationProcessorSettings summarizationProcessorSettings = new SummarizationProcessorSettings(maxTokenCount, additionalPrompt); + SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, summarizationProcessorSettings); + summarizationProcessor.SummaryResourcesCalculated += SummarizationProcessor_SummaryResourcesCalculated; string summary = summarizationProcessor.Summarize(simpleDocument).Result; @@ -73,9 +76,10 @@ private static void SummarizationProcessor_SummaryResourcesCalculated(object? se e.ShouldContinueExecution = true; } - private static void AskQuestion(ISimpleTextDocument simpleDocument) + private static void AskQuestion(SimpleTextDocument simpleDocument) { - CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, maxTokenCount); + CompleteContextProcessorSettings completeContextProcessorSettings = new CompleteContextProcessorSettings(maxTokenCount, model, tokenizationEncoding, false); + CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, completeContextProcessorSettings); string question = "How many pages is the document and what is it about?"; string answer = completeContextQuestionProcessor.AnswerQuestion(simpleDocument, question).Result; @@ -83,13 +87,14 @@ private static void AskQuestion(ISimpleTextDocument simpleDocument) Console.WriteLine(answer); } - private static void AskPartialContextQuestion(ISimpleTextDocument simpleDocument) + private static void AskPartialContextQuestion(SimpleTextDocument simpleDocument) { + var settings = EmbeddingSettingsFactory.CreateSettingsForTextDocuments(maxTokenCount, model, tokenizationEncoding, maxNumberOfEmbeddingsSent); #if NETWINDOWS - PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, maxTokenCount, simpleDocument); + PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, settings, simpleDocument); #else - IEmbeddingsStorage embeddingsStorage = new OllamaEmbeddingsStorage(); - PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embeddingsStorage, maxTokenCount, simpleDocument); + IEmbedder embedder = new CustomOpenAIEmbedder(); + PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embedder, settings, simpleDocument); #endif string question = "What is the last book by John Grisham?"; string answer = partialContextQuestionProcessor.AnswerQuestion(question).Result; diff --git a/PdfProcessing/AIConnectorDemo/ReadMe.md b/PdfProcessing/AIConnectorDemo/ReadMe.md index e31e9ff..a482053 100644 --- a/PdfProcessing/AIConnectorDemo/ReadMe.md +++ b/PdfProcessing/AIConnectorDemo/ReadMe.md @@ -1,9 +1,5 @@ In order to run the project, you need to replace the key and endpoint with your Azure Open AI key and endpoint. They are located in the launchSettings.json file in the Properties folder. -In NetStandard you need to implement IEmbeddingsStorage if you would like to use the PartialContextQuestionProcessor. -There is a sample implementation called OllamaEmbeddingsStorage in the NetStandard project. -For it to work the Ollama server needs to be running locally. You can follow these steps to run it: - 1. Install Ollama: https://ollama.com/ - 2. Pull the all-minilm model we'll use for embeddings: ollama pull all-minilm - 3. Ensure Ollama is running: ollama serve \ No newline at end of file +In NetStandard you need to implement IEmbedder if you would like to use the PartialContextQuestionProcessor. +There is a sample implementation called CustomOpenAIEmbedder in the NetStandard project. \ No newline at end of file diff --git a/PdfProcessing/AIConnectorDemo/TextLoader.cs b/PdfProcessing/AIConnectorDemo/TextLoader.cs deleted file mode 100644 index ad8732b..0000000 --- a/PdfProcessing/AIConnectorDemo/TextLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using LangChain.DocumentLoaders; - -namespace AIConnectorDemo -{ - internal class TextLoader : IDocumentLoader - { - public async Task> LoadAsync(DataSource dataSource, DocumentLoaderSettings? settings = null, CancellationToken cancellationToken = default) - { - using (Stream inputStream = await dataSource.GetStreamAsync(cancellationToken)) - { - StreamReader reader = new StreamReader(inputStream); - string content = reader.ReadToEnd(); - - string[] pages = content.Split(["----------"], System.StringSplitOptions.RemoveEmptyEntries); - - return pages.Select(x => new Document(x)).ToList(); - } - } - } -} \ No newline at end of file diff --git a/PdfProcessing/PdfProcessing_NetStandard.sln b/PdfProcessing/PdfProcessing_NetStandard.sln index 2ed0c9c..e198638 100644 --- a/PdfProcessing/PdfProcessing_NetStandard.sln +++ b/PdfProcessing/PdfProcessing_NetStandard.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.13.35919.96 d17.13 +VisualStudioVersion = 17.13.35919.96 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateDocumentWithImages_NetStandard", "CreateDocumentWithImages\CreateDocumentWithImages_NetStandard.csproj", "{D7CA01E8-E777-4D18-B960-AA0F62040F87}" EndProject diff --git a/WordsProcessing/AIConnectorDemo/AIConnectorDemo.csproj b/WordsProcessing/AIConnectorDemo/AIConnectorDemo.csproj new file mode 100644 index 0000000..907c98d --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/AIConnectorDemo.csproj @@ -0,0 +1,33 @@ + + + + Exe + net8.0-windows + enable + enable + true + + + + NETWINDOWS;$(DefineConstants) + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/WordsProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj b/WordsProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj new file mode 100644 index 0000000..aa2a792 --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/AIConnectorDemo_NetStandard.csproj @@ -0,0 +1,27 @@ + + + + Exe + net8.0 + enable + enable + + + + NETSTANDARD;$(DefineConstants) + + + + + + + + + + + + PreserveNewest + + + + diff --git a/WordsProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs b/WordsProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs new file mode 100644 index 0000000..080ab1d --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs @@ -0,0 +1,98 @@ +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using Telerik.Documents.AI.Core; + +namespace AIConnectorDemo +{ + public class CustomOpenAIEmbedder : IEmbedder + { + private readonly HttpClient httpClient; + + public CustomOpenAIEmbedder() + { + HttpClient httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromMinutes(5); + string endpoint = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_ENDPOINT"); + string apiKey = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_KEY"); + + httpClient.BaseAddress = new Uri(endpoint); + httpClient.DefaultRequestHeaders.Add("api-key", apiKey); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + this.httpClient = httpClient; + } + + public async Task> EmbedAsync(IList fragments) + { + AzureEmbeddingsRequest requestBody = new AzureEmbeddingsRequest + { + Input = fragments.Select(p => p.ToEmbeddingText()).ToArray(), + Dimensions = 3072 + }; + + string json = JsonSerializer.Serialize(requestBody); + StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + string apiVersion = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_APIVERSION"); + string deploymentName = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_DEPLOYMENT"); + string url = $"openai/deployments/{deploymentName}/embeddings?api-version={apiVersion}"; + using HttpResponseMessage response = await this.httpClient.PostAsync(url, content, CancellationToken.None); + + Embedding[] embeddings = new Embedding[fragments.Count]; + + string responseJson = await response.Content.ReadAsStringAsync(CancellationToken.None); + AzureEmbeddingsResponse responseObj = JsonSerializer.Deserialize(responseJson); + + List sorted = responseObj.Data.OrderBy(d => d.Index).ToList(); + List result = new List(sorted.Count); + + for (int i = 0; i < sorted.Count; i++) + { + EmbeddingData item = sorted[i]; + embeddings[i] = new Embedding(fragments[i], item.Embedding); + } + + return embeddings; + } + + private sealed class AzureEmbeddingsRequest + { + [System.Text.Json.Serialization.JsonPropertyName("input")] + public string[] Input { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("dimensions")] + public int? Dimensions { get; set; } + } + + private sealed class AzureEmbeddingsResponse + { + [System.Text.Json.Serialization.JsonPropertyName("data")] + public EmbeddingData[] Data { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("model")] + public string? Model { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("usage")] + public UsageInfo? Usage { get; set; } + } + + private sealed class UsageInfo + { + [System.Text.Json.Serialization.JsonPropertyName("prompt_tokens")] + public int PromptTokens { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("total_tokens")] + public int TotalTokens { get; set; } + } + + private sealed class EmbeddingData + { + [System.Text.Json.Serialization.JsonPropertyName("embedding")] + public float[] Embedding { get; set; } = Array.Empty(); + + [System.Text.Json.Serialization.JsonPropertyName("index")] + public int Index { get; set; } + } + } +} \ No newline at end of file diff --git a/WordsProcessing/AIConnectorDemo/GenAI Document Insights Test Document.docx b/WordsProcessing/AIConnectorDemo/GenAI Document Insights Test Document.docx new file mode 100644 index 0000000..594a30f Binary files /dev/null and b/WordsProcessing/AIConnectorDemo/GenAI Document Insights Test Document.docx differ diff --git a/WordsProcessing/AIConnectorDemo/Program.cs b/WordsProcessing/AIConnectorDemo/Program.cs new file mode 100644 index 0000000..7a265f3 --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/Program.cs @@ -0,0 +1,105 @@ +using Azure.AI.OpenAI; +using Microsoft.Extensions.AI; +using OpenAI.Chat; +using System.IO; +using Telerik.Documents.AI.Core; + +#if NETWINDOWS +using Telerik.Windows.Documents.AIConnector; +#else +using Telerik.Documents.AIConnector; +#endif +using Telerik.Windows.Documents.Flow.FormatProviders.Docx; +using Telerik.Windows.Documents.Flow.Model; +using Telerik.Windows.Documents.TextRepresentation; + +namespace AIConnectorDemo +{ + internal class Program + { + static int maxTokenCount = 128000; + static int maxNumberOfEmbeddingsSent = 50000; + static IChatClient iChatClient; + static string tokenizationEncoding = "cl100k_base"; + static string model = "gpt-4o-mini"; + static string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY"); + static string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT"); + + static void Main(string[] args) + { + CreateChatClient(); + + using (Stream input = File.OpenRead("GenAI Document Insights Test Document.docx")) + { + DocxFormatProvider docxFormatProvider = new DocxFormatProvider(); + RadFlowDocument inputDocx = docxFormatProvider.Import(input, null); + SimpleTextDocument simpleDocument = inputDocx.ToSimpleTextDocument(TimeSpan.FromSeconds(10)); + + Summarize(simpleDocument); + + Console.WriteLine("--------------------------------------------------"); + + AskQuestion(simpleDocument); + + Console.WriteLine("--------------------------------------------------"); + + AskPartialContextQuestion(simpleDocument); + } + } + + private static void CreateChatClient() + { + AzureOpenAIClient azureClient = new( + new Uri(endpoint), + new Azure.AzureKeyCredential(key), + new AzureOpenAIClientOptions()); + ChatClient chatClient = azureClient.GetChatClient(model); + + iChatClient = new OpenAIChatClient(chatClient); + } + + private static void Summarize(SimpleTextDocument simpleDocument) + { + string additionalPrompt = "Summarize the text in a few sentences. Be concise and clear."; + SummarizationProcessorSettings summarizationProcessorSettings = new SummarizationProcessorSettings(maxTokenCount, additionalPrompt); + SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, summarizationProcessorSettings); + + summarizationProcessor.SummaryResourcesCalculated += SummarizationProcessor_SummaryResourcesCalculated; + + string summary = summarizationProcessor.Summarize(simpleDocument).Result; + Console.WriteLine(summary); + } + + private static void SummarizationProcessor_SummaryResourcesCalculated(object? sender, SummaryResourcesCalculatedEventArgs e) + { + Console.WriteLine($"The summary will require {e.EstimatedCallsRequired} calls and {e.EstimatedTokensRequired} tokens"); + e.ShouldContinueExecution = true; + } + + private static void AskQuestion(SimpleTextDocument simpleDocument) + { + CompleteContextProcessorSettings completeContextProcessorSettings = new CompleteContextProcessorSettings(maxTokenCount, model, tokenizationEncoding, false); + CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, completeContextProcessorSettings); + + string question = "How many pages is the document and what is it about?"; + string answer = completeContextQuestionProcessor.AnswerQuestion(simpleDocument, question).Result; + Console.WriteLine(question); + Console.WriteLine(answer); + } + + private static void AskPartialContextQuestion(SimpleTextDocument simpleDocument) + { + var settings = EmbeddingSettingsFactory.CreateSettingsForTextDocuments(maxTokenCount, model, tokenizationEncoding, maxNumberOfEmbeddingsSent); +#if NETWINDOWS + PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, settings, simpleDocument); +#else + IEmbedder embedder = new CustomOpenAIEmbedder(); + PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embedder, settings, simpleDocument); +#endif + string question = "Who are the key authors listed in the document?"; + string answer = partialContextQuestionProcessor.AnswerQuestion(question).Result; + Console.WriteLine(question); + Console.WriteLine(answer); + } + } +} diff --git a/WordsProcessing/AIConnectorDemo/Properties/launchSettings.json b/WordsProcessing/AIConnectorDemo/Properties/launchSettings.json new file mode 100644 index 0000000..896fece --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "AIConnectorDemo": { + "commandName": "Project", + "environmentVariables": { + "AZUREOPENAI_KEY": "key", + "AZUREOPENAI_ENDPOINT": "endpoint" + } + } + } +} \ No newline at end of file diff --git a/WordsProcessing/AIConnectorDemo/ReadMe.md b/WordsProcessing/AIConnectorDemo/ReadMe.md new file mode 100644 index 0000000..a482053 --- /dev/null +++ b/WordsProcessing/AIConnectorDemo/ReadMe.md @@ -0,0 +1,5 @@ +In order to run the project, you need to replace the key and endpoint with your Azure Open AI key and endpoint. +They are located in the launchSettings.json file in the Properties folder. + +In NetStandard you need to implement IEmbedder if you would like to use the PartialContextQuestionProcessor. +There is a sample implementation called CustomOpenAIEmbedder in the NetStandard project. \ No newline at end of file diff --git a/WordsProcessing/WordsProcessing.sln b/WordsProcessing/WordsProcessing.sln index 3f097cf..46f2a64 100644 --- a/WordsProcessing/WordsProcessing.sln +++ b/WordsProcessing/WordsProcessing.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26923.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36603.0 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateDocument", "GenerateDocument\GenerateDocument.csproj", "{6BD0AD6A-35D5-4194-9C8A-438E27942792}" EndProject @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConvertDocuments", "Convert EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ContentControls", "ContentControls\ContentControls.csproj", "{576D340B-8809-4256-809C-575B1907283C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIConnectorDemo", "AIConnectorDemo\AIConnectorDemo.csproj", "{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {576D340B-8809-4256-809C-575B1907283C}.Debug|Any CPU.Build.0 = Debug|Any CPU {576D340B-8809-4256-809C-575B1907283C}.Release|Any CPU.ActiveCfg = Release|Any CPU {576D340B-8809-4256-809C-575B1907283C}.Release|Any CPU.Build.0 = Release|Any CPU + {4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,25 +52,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {74049E11-3060-446D-BA19-C820938235B3} EndGlobalSection - GlobalSection(TeamFoundationVersionControl) = preSolution - SccNumberOfProjects = 6 - SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} - SccTeamFoundationServer = https://tfsemea.progress.com/defaultcollection - SccLocalPath0 = . - SccProjectUniqueName1 = GenerateDocument\\GenerateDocument.csproj - SccProjectName1 = GenerateDocument - SccLocalPath1 = GenerateDocument - SccProjectUniqueName2 = HtmlGenerator\\HtmlGenerator.csproj - SccProjectName2 = HtmlGenerator - SccLocalPath2 = HtmlGenerator - SccProjectUniqueName3 = MailMerge\\MailMerge.csproj - SccProjectName3 = MailMerge - SccLocalPath3 = MailMerge - SccProjectUniqueName4 = ConvertDocuments\\ConvertDocuments.csproj - SccProjectName4 = ConvertDocuments - SccLocalPath4 = ConvertDocuments - SccProjectUniqueName5 = ContentControls\\ContentControls.csproj - SccProjectName5 = ContentControls - SccLocalPath5 = ContentControls - EndGlobalSection EndGlobal diff --git a/WordsProcessing/WordsProcessing_NetStandard.sln b/WordsProcessing/WordsProcessing_NetStandard.sln index 640c8f5..9b9d759 100644 --- a/WordsProcessing/WordsProcessing_NetStandard.sln +++ b/WordsProcessing/WordsProcessing_NetStandard.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36603.0 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConvertDocuments_NetStandard", "ConvertDocuments\ConvertDocuments_NetStandard.csproj", "{AF2C14E7-B3FD-4B1B-8B49-148B63BC7F3D}" EndProject @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailMerge_NetStandard", "Ma EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ContentControls_NetStandard", "ContentControls\ContentControls_NetStandard.csproj", "{0E6E81DD-BD06-4FDA-8B68-0AA4E8865F30}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIConnectorDemo_NetStandard", "AIConnectorDemo\AIConnectorDemo_NetStandard.csproj", "{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {0E6E81DD-BD06-4FDA-8B68-0AA4E8865F30}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E6E81DD-BD06-4FDA-8B68-0AA4E8865F30}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E6E81DD-BD06-4FDA-8B68-0AA4E8865F30}.Release|Any CPU.Build.0 = Release|Any CPU + {C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,25 +52,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D254DCB0-A6CA-4F77-8A49-131BA4359514} EndGlobalSection - GlobalSection(TeamFoundationVersionControl) = preSolution - SccNumberOfProjects = 6 - SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} - SccTeamFoundationServer = https://tfsemea.progress.com/defaultcollection - SccLocalPath0 = . - SccProjectUniqueName1 = ConvertDocuments\\ConvertDocuments_NetStandard.csproj - SccProjectName1 = ConvertDocuments - SccLocalPath1 = ConvertDocuments - SccProjectUniqueName2 = GenerateDocument\\GenerateDocument_NetStandard.csproj - SccProjectName2 = GenerateDocument - SccLocalPath2 = GenerateDocument - SccProjectUniqueName3 = HtmlGenerator\\HtmlGenerator_NetStandard.csproj - SccProjectName3 = HtmlGenerator - SccLocalPath3 = HtmlGenerator - SccProjectUniqueName4 = MailMerge\\MailMerge_NetStandard.csproj - SccProjectName4 = MailMerge - SccLocalPath4 = MailMerge - SccProjectUniqueName5 = ContentControls\\ContentControls_NetStandard.csproj - SccProjectName5 = ContentControls - SccLocalPath5 = ContentControls - EndGlobalSection EndGlobal