diff --git a/04-Practical.NETGenAISamples/readme.md b/04-Practical.NETGenAISamples/readme.md index 60e7d709..3779fddf 100644 --- a/04-Practical.NETGenAISamples/readme.md +++ b/04-Practical.NETGenAISamples/readme.md @@ -1,8 +1,6 @@ -
-

Generative AI Fundamentals for .NET

-

Lesson 4: Generative AI Samples

-

Get inspired to create with MSFT's Generative AI Samples

-
+## Lesson 4: Generative AI Samples + +*Get inspired to create with MSFT's Generative AI Samples* > 💡 **Quick Summary**: Understand how Generative AI works with Microsoft Samples, get inspirated, and understand the best scenarios to apply it into new applications. @@ -27,11 +25,9 @@ ## Intro Video -[![Watch the video](../images/04-videocover.jpg)](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERHGiBsTtVJBtN9VevaxdnwB6dfV_GCdFXbZL9D-GnEkew?e=Y4oAjD&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) - -In our first video, Bruno Capuano introduces some of the practical scenarios for .NET Generative AI! -On the first demo, we use Ollama to create an application to generate descriptions for images quickly using Llama 3.2 Vision model running locally, later, four demos that showcase the power of Semantic Kernel, Language Models, and the .NET AI Extension. +**INSERT: CHAT EXPLANATION VIDEO HERE** +Generative AI is a powerful technology that has multiple possibilities for applications, from e-commerce to content creation. To demonstrate how Generative AI can be used in real-world scenarios, we'll explore some of the Microsoft samples that showcase the capabilities of Generative AI and how it can be integrated. ## eShopLite Demos @@ -39,11 +35,15 @@ On the first demo, we use Ollama to create an application to generate descriptio For our first two demos, we'll explore the eShopLite project, a simple e-commerce application for outdoor gear and camping enthusiasts that is augmented with Generative AI capabilities, such as search features optimization, Customer Support, and Realtime Audio Analysis. +These demos use [Azure OpenAi](https://azure.microsoft.com/en-us/products/ai-services/openai-service) and [Azure Ai Foundry Models](https://ai.azure.com/) to do their inferences for the applications. Please, check more details on their repositories. + The first demo, we show how to use the Semantic Kernel to enhance the search capabilities, which can understand the context of the user's queries and provide accurate results. ### eShopLite with Semantic Search -In eShopLite with Semantic Search, we use the Semantic Kernel to enhance the search capabilities of the e-commerce application. Semantic Kernel auxiliate us to create a more robust search engine that can understand the context of the user's queries and provide more accurate results. For example, if a user searches for "do you have something for cooking", the search engine can understand that the user is looking for kitchenware and show the most relevant products, in context of our sample, it returns Camping Cookware. +In eShopLite with Semantic Search, we use the Semantic Kernel to enhance the search capabilities of the e-commerce application. Semantic Kernel auxiliate us to create a more robust search engine that can understand the context of the user's queries and provide more accurate results. + +For example, if a user searches for "do you have something for cooking", the search engine can understand that the user is looking for kitchenware and show the most relevant products, in context of our sample, it returns Camping Cookware. ![Image demonstrating the search capabilities in eShopLite](./images/search-eshoplite.png) @@ -145,7 +145,18 @@ var recordId = await _productsCollection.UpsertAsync(productVector); _logger.LogInformation("Product added to memory: {Product} with recordId: {RecordId}", product.Name, recordId); ``` -The code above demonstrates how to iterate over the products and add them to the memory, create a new product vector, generate the embedding for the product information, convert the embedding result to a float array, and assign it to the product vector. The product is then added to the memory, repeating the process for each product in the collection. After that, when the user searches for a product, we can compare the user's query with the product vectors and return the most relevant products. + +The code above demonstrates how to iterate over the products and add them to the memory. + +After we create a new product vector, we use it to generate embedding for the product information, convert the embedding result to a float array, and assign it to the product vector. + +Look at `_productsCollection`, it is a reference to the container where the products are stored, using a CosmosDB call to get the response for the recordId. For in this case, for logging. + +The product is then added to the memory, repeating the process for each product in the collection. + +After that, when the user searches for a product, we can compare the user's query with the product vectors and return the most relevant products. + + ```csharp try @@ -203,7 +214,11 @@ try } ``` -With the code above, we generate the embedding for the search query, search the vector database for the most similar product, and get a response message using the found product information. Helping the user find the products they need more easily, leading to a better shopping experience and increased sales. Moreover, as generative AI evolves, we need some telemetry and monitoring to understand the user's behavior and improve the search engine, this is where Azure Application Insights and .NET Aspire come in. +With the code above, we generate the embedding for the search query, search the vector database for the most similar product, and get a response message using the found product information. + +Helping the user find the products they need more easily, leading to a better shopping experience and increased sales. + +Moreover, as generative AI evolves, we need some telemetry and monitoring to understand the user's behavior and improve the search engine, this is where Azure Application Insights and .NET Aspire come in. ![Image demonstrating the .NET Aspire tracing capabilities](./images/aspire-tracing-eshoplite.png) @@ -211,9 +226,13 @@ With the code above, we generate the embedding for the search query, search the ![Image demonstrating the Azure Application Insights in eShopLite](./images/app-insights-eshoplite.png) +As Telemetry is essential to understand the user's behavior and improve services, we introduce Azure Application Insights to monitor the application's performance and user interactions. + Application Insights provides a comprehensive set of telemetry data, helping us to understand how our services are performing, and how users are interacting with the application and cloud usage. -> 💡 **Pro Tip**: For more information on eShopLite with Semantic Search, look at the repository to learn more: https://github.com/Azure-Samples/eShopLite-SemanticSearch/ +In the image, we can see the Application Insights dashboard, providing how the services are performing, for example, the calls for our Database, number of requests, and the response time. + +> 💡 **Pro Tip**: For more information on eShopLite with Semantic Search, look at the repository to learn more: https://aka.ms/netaieshoplitesemanticsearch ### eShopLite with Realtime Analysis @@ -221,7 +240,7 @@ In eShopLite with Realtime Analysis, we use the Realtime audio capabilities of G ![Image demonstrating the Realtime Analysis in eShopLite](./images/realtime-analysis-eshoplite.gif) -To implement this feature, we need to implement new features to create the endpoints for the Realtime Analysis, it can be found on the `StoreRealtime\ConversationManager.cs` implementation. +To implement this feature, we need to implement new features to create the endpoints for the Realtime Analysis, it can be found on the `StoreRealtime\ConversationManager.cs` implementation for Realtime Analysis. ```csharp @@ -240,6 +259,11 @@ public async Task RunAsync( The current date is {DateTime.Now.ToLongDateString()} """; +``` +First, we define the initial prompt for the assistant, providing the user with instructions on how to interact with the chatbot. Remember to use prompts that are clear and concise, prompt engineering is essential to get accurate results from the AI models. + +``` csharp + // Notify the user that the connection is being established await addMessageAsync("Connecting..."); // Send an initial greeting message @@ -252,6 +276,14 @@ public async Task RunAsync( // Add the AI functions to a list of tools List tools = new List { contosoSemanticSearchTool, contosoSearchByProductNameTool }; +``` + +We then notify the user that the chat is ready to start and send an initial greeting message. Then, AI functions to search products, semantic search, and search by product name are created and added to a list of tools. Which could be used to provide the user with relevant information on their queries. + + + +``` csharp + // Configure the conversation session options var sessionOptions = new ConversationSessionOptions() { @@ -275,22 +307,26 @@ public async Task RunAsync( } ``` -> 💡 **Pro Tip**: For more information on eShopLite with Realtime Audio, look at the repository to learn more: https://github.com/Azure-Samples/eShopLite-RealtimeAudio +The conversation session options are configured, including the instructions, voice, and input transcription options. Using the `Whisper-1` model for the input transcription. -See a local demo of the feature as File Search in action: +Each tool is added to the session options, and the conversation session is started with the configured options. Those can be changed to fit the user's needs. -

- Go to the File Search sample -

+ +> 💡 **Pro Tip**: For more information on eShopLite with Realtime Audio, look at the repository to learn more: https://aka.ms/netaieshopliterealtimechat + +--- ## Chat with your Data -Do you want to chat with your data? In this demo, we'll use the Chat with your Data application to generate a conversation with the user's data, using a simple interface to upload a file and extract insights from it. +Chatting with your Data is a classic example of how Generative AI is used in real-world applications. This application allows users to interact with their data in a conversational way, making it easier to understand complex datasets and extract valuable insights. + +It deploys a Chat Application, a Document Manager, and an Aspire Dashboard to provide a seamless experience in the application. Check the application in the GitHub repository. + ![Image demonstrating the Chat with your Data application](./images/chat-with-your-data.png) -> 💡**Pro Tip**: For more information on the Chat with your Data app, look at the repository to learn more: [Chat with your Data](https://github.com/Azure-Samples/netai-chat-with-your-data) +> 💡**Pro Tip**: For more information on the Chat with your Data app, look at the repository to learn more: [Chat with your Data](https://aka.ms/netaichatwithyourdata) @@ -333,7 +369,7 @@ await foreach (var delta in session.ProcessStreamingRequest(createWriterRequest) } ``` -The type `CreateWriterRequest` needs to have three properties: `Research`, `Products`, and `Writing`. After that, it calls the `CreateSessionAsync` method, which looks like this: +The type `CreateWriterRequest` needs to have three properties: `Research`, `Products`, and `Writing`. After getting them setted by processing the request, it calls the `CreateSessionAsync` method, which looks like this: ```csharp internal async Task CreateSessionAsync(HttpResponse response) @@ -465,15 +501,7 @@ In .NET Aspire, we notice how the components are orchestrated to create a seamle ![Image demonstrating the .NET Aspire telemetry capabilities](./images/aspire-telemetry-creative-writer.png) -> 💡**Pro Tip**: For more information on the Creative Writer Agent, look at the repository to learn more: [Contoso Creative Writer](https://github.com/Azure-Samples/aspire-semantic-kernel-creative-writer) - -See a local demo of the Creative Writer Agent in action: - -

- Go to Creative Writer local sample, -

- - +> 💡**Pro Tip**: For more information on the Creative Writer Agent, look at the repository to learn more: [Contoso Creative Writer](https://aka.ms/netaicreativewriter) ## Conclusions and resources @@ -484,13 +512,11 @@ Those are just a few examples of how you can use Generative AI in your applicati > ⚠️ **Note**: If you encounter any issues, please, open an issue in the repository. -- [Azure AI Agents - Instructions](./src/AzureAIAgents.md) +- [eShopLite with DeepSeek](https://aka.ms/netaieshoplitedeepseekr1) +- [eShopLite with Azure AI Search](https://aka.ms/netaieshoplitesemanticsearchazureaisearch) ### Next Steps Learn about responsible AI practices and how to ensure that your AI models are ethical and have a positive impact! -

- Go to Chapter 5 -

- +[Go to the next lesson](../05-ResponsibleAI/readme.md) diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj deleted file mode 100644 index b94e9bcd..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - Exe - net9.0 - AgentSKLabs_04_FileSearch - enable - enable - 4d9d76d2-c77b-4f69-99b1-dc43838a12d4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs deleted file mode 100644 index 9e133894..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.Json; - -public static class ObjectExtensions -{ - private static readonly JsonSerializerOptions s_jsonOptionsCache = new() { WriteIndented = true }; - - public static string AsJson(this object obj) - { - return JsonSerializer.Serialize(obj, s_jsonOptionsCache); - } -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs deleted file mode 100644 index dd217090..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs +++ /dev/null @@ -1,84 +0,0 @@ -#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001 - -// Define the agent -using Microsoft.SemanticKernel.ChatCompletion; -using Microsoft.SemanticKernel; -using OpenAI.Files; -using OpenAI.VectorStores; -using Microsoft.SemanticKernel.Agents.OpenAI; -using System.ClientModel; -using OpenAI.Assistants; -using OpenAI.Chat; -using Microsoft.Extensions.Configuration; -using AgentLabs; - -var config = new ConfigurationBuilder().AddUserSecrets().Build(); -var apiKey = config["AZURE_OPENAI_APIKEY"]; -var endpoint = config["AZURE_OPENAI_ENDPOINT"]; -var modelId = config["AZURE_OPENAI_MODEL"]; - -var openAiAssistantDefinition = new OpenAIAssistantDefinition(modelId) -{ - EnableFileSearch = true, - Metadata = new Dictionary{ - { "file_search", "true" } - }, - Instructions = "This assistant can help you with your questions. You can ask about camping, healthcare benefits, and more. The assistant will include and share information to existing content in the related information database. For each shared information, the assistant will declare that this is based on existing content and provide the necessary link to it.", -}; - -var provider = OpenAIClientProvider.ForAzureOpenAI(new ApiKeyCredential(apiKey), new Uri(endpoint)); - -OpenAIAssistantAgent agent = - await OpenAIAssistantAgent.CreateAsync( - clientProvider: provider, - definition: openAiAssistantDefinition, - kernel: new Kernel()); - -// Upload files - Using a table of fictional employees. -// iterate through the folder sampledocs -OpenAIFileClient fileClient = provider.Client.GetOpenAIFileClient(); -List fileIds = []; -foreach (var file in Directory.GetFiles("sampledocs")) -{ - await using Stream stream = File.OpenRead(file); - OpenAIFile fileInfo = await fileClient.UploadFileAsync(stream, Path.GetFileName(file), FileUploadPurpose.Assistants); - fileIds.Add(fileInfo.Id); -} - -// Create a vector-store -var vectorStoreOptions = new VectorStoreCreationOptions() -{ - Name = "employees_vector_store" -}; -foreach (var fileId in fileIds) -{ - vectorStoreOptions.FileIds.Add(fileId); -} -VectorStoreClient vectorStoreClient = provider.Client.GetVectorStoreClient(); -CreateVectorStoreOperation result = await vectorStoreClient.CreateVectorStoreAsync(waitUntilCompleted: true, vectorStoreOptions); - -// Create a thread associated with a vector-store for the agent conversation. -string threadId = - await agent.CreateThreadAsync( - new OpenAIThreadCreationOptions - { - VectorStoreId = result.VectorStoreId - }); - -// Respond to user input -try -{ - await AgentServiceHandler.InvokeAgentAsync(agent, threadId, "I want to go camping, but it seems that it maybe raining, what products do you suggest to go camping on rainy days?"); - await AgentServiceHandler.InvokeAgentAsync(agent, threadId, "what healthcare benefits are part of the Northwind Health plans?"); -} -finally -{ - await agent.DeleteThreadAsync(threadId); - await agent.DeleteAsync(); - await vectorStoreClient.DeleteVectorStoreAsync(result.VectorStoreId); - - // Delete the uploaded files - foreach (var fileInfo in fileIds) - await fileClient.DeleteFileAsync(fileInfo); -} - diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf deleted file mode 100644 index 6a4c07dc..00000000 Binary files a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf and /dev/null differ diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md deleted file mode 100644 index 42658287..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md +++ /dev/null @@ -1,5 +0,0 @@ -**Name:** Contoso Camping Chair -**Description:** A comfortable and foldable chair with a sturdy frame. It includes a cup holder and a carrying bag for easy transport. -**Color:** Red -**Categories:** Furniture, Outdoor Gear, Camping Essentials -**Price:** $29.99 \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md deleted file mode 100644 index 7c4af031..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md +++ /dev/null @@ -1,5 +0,0 @@ -**Name:** Contoso LED Camping Lantern -**Description:** A bright and energy-efficient lantern with multiple lighting modes. It is rechargeable and has a long battery life. -**Color:** Yellow -**Categories:** Lighting, Outdoor Gear, Camping Essentials -**Price:** $19.99 \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md deleted file mode 100644 index 18bc08d4..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md +++ /dev/null @@ -1,5 +0,0 @@ -**Name:** Contoso Portable Camping Stove -**Description:** A compact and lightweight stove perfect for outdoor cooking. It features adjustable flame control and easy setup. -**Color:** Silver -**Categories:** Cooking, Outdoor Gear, Camping Essentials -**Price:** $39.99 \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md deleted file mode 100644 index c201bd55..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md +++ /dev/null @@ -1,5 +0,0 @@ -**Name:** Contoso Sleeping Bag -**Description:** A warm and cozy sleeping bag suitable for all seasons. It is made with high-quality insulation and a soft inner lining. -**Color:** Blue -**Categories:** Sleeping Gear, Outdoor Gear, Camping Essentials -**Price:** $49.99 \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md deleted file mode 100644 index 5d59fc7b..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md +++ /dev/null @@ -1,5 +0,0 @@ -**Name:** Contoso Waterproof Tent -**Description:** A durable and spacious tent designed to keep you dry and comfortable in any weather. It includes a rainfly and mesh windows for ventilation. -**Color:** Green -**Categories:** Shelter, Outdoor Gear, Camping Essentials -**Price:** $99.99 \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj b/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj deleted file mode 100644 index a0550525..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Exe - net9.0 - AgentSKLabs_10_CreativeWriter - enable - enable - 28df85ff-c4b7-4cd7-8012-ec02f3e8124b - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs deleted file mode 100644 index d92bd8cf..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs +++ /dev/null @@ -1,133 +0,0 @@ -#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001 - -using Microsoft.SemanticKernel.Agents.History; -using Microsoft.SemanticKernel.Agents; -using Microsoft.SemanticKernel.ChatCompletion; -using Microsoft.SemanticKernel; -using Microsoft.Extensions.Configuration; -using AgentLabs; -using Microsoft.SemanticKernel.Agents.Chat; - -const string ReviewerName = "ArtDirector"; -const string ReviewerInstructions = - """ - You are an art director who has opinions about copywriting born of a love for David Ogilvy. - The goal is to determine if the given copy is acceptable to print. - If so, state that it is approved. - If not, provide insight on how to refine suggested copy without examples. - """; - -const string CopyWriterName = "CopyWriter"; -const string CopyWriterInstructions = - """ - You are a copywriter with ten years of experience and are known for brevity and a dry humor. - The goal is to refine and decide on the single best copy as an expert in the field. - Only provide a single proposal per response. - Never delimit the response with quotation marks. - You're laser focused on the goal at hand. - Don't waste time with chit chat. - Consider suggestions when refining an idea. - """; - -// Define the agents -ChatCompletionAgent agentReviewer = - new() - { - Instructions = ReviewerInstructions, - Name = ReviewerName, - Kernel = KernelServiceHandler.CreateKernelWithChatCompletion(), - }; - -ChatCompletionAgent agentWriter = - new() - { - Instructions = CopyWriterInstructions, - Name = CopyWriterName, - Kernel = KernelServiceHandler.CreateKernelWithChatCompletion(), - }; - -KernelFunction terminationFunction = - AgentGroupChat.CreatePromptFunctionForStrategy( - """ - Determine if the copy has been approved. If so, respond with a single word: yes - - History: - {{$history}} - """, - safeParameterNames: "history"); - -KernelFunction selectionFunction = - AgentGroupChat.CreatePromptFunctionForStrategy( - $$$""" - Determine which participant takes the next turn in a conversation based on the the most recent participant. - State only the name of the participant to take the next turn. - No participant should take more than one turn in a row. - - Choose only from these participants: - - {{{ReviewerName}}} - - {{{CopyWriterName}}} - - Always follow these rules when selecting the next participant: - - After {{{CopyWriterName}}}, it is {{{ReviewerName}}}'s turn. - - After {{{ReviewerName}}}, it is {{{CopyWriterName}}}'s turn. - - History: - {{$history}} - """, - safeParameterNames: "history"); - -// Limit history used for selection and termination to the most recent message. -ChatHistoryTruncationReducer strategyReducer = new(1); - -// Create a chat for agent interaction. -AgentGroupChat chat = - new(agentWriter, agentReviewer) - { - ExecutionSettings = - new() - { - // Here KernelFunctionTerminationStrategy will terminate - // when the art-director has given their approval. - TerminationStrategy = - new KernelFunctionTerminationStrategy(terminationFunction, KernelServiceHandler.CreateKernelWithChatCompletion()) - { - // Only the art-director may approve. - Agents = [agentReviewer], - // Customer result parser to determine if the response is "yes" - ResultParser = (result) => result.GetValue()?.Contains("yes", StringComparison.OrdinalIgnoreCase) ?? false, - // The prompt variable name for the history argument. - HistoryVariableName = "history", - // Limit total number of turns - MaximumIterations = 10, - // Save tokens by not including the entire history in the prompt - HistoryReducer = strategyReducer, - }, - // Here a KernelFunctionSelectionStrategy selects agents based on a prompt function. - SelectionStrategy = - new KernelFunctionSelectionStrategy(selectionFunction, KernelServiceHandler.CreateKernelWithChatCompletion()) - { - // Always start with the writer agent. - InitialAgent = agentWriter, - // Returns the entire result value as a string. - ResultParser = (result) => result.GetValue() ?? CopyWriterName, - // The prompt variable name for the history argument. - HistoryVariableName = "history", - // Save tokens by not including the entire history in the prompt - HistoryReducer = strategyReducer, - // Only include the agent names and not the message content - EvaluateNameOnly = true, - }, - } - }; - -// Invoke chat and display messages. -ChatMessageContent message = new(AuthorRole.User, "concept: maps made out of egg cartons."); -chat.AddChatMessage(message); -AgentServiceHandler.WriteAgentChatMessage(message); - -await foreach (ChatMessageContent responseMessage in chat.InvokeAsync()) -{ - AgentServiceHandler.WriteAgentChatMessage(responseMessage); -} - -Console.WriteLine($"\n[IS COMPLETED: {chat.IsComplete}]"); diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj deleted file mode 100644 index cb2e540d..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - WinExe - net9.0-windows7.0 - enable - enable - true - ac3f7570-8539-4dd0-83dc-84de2a5e11ac - AgentsSK_20_CreativeWriter - - - - - - - - - - - - - - - - - diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs deleted file mode 100644 index 62470c46..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Agents; -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.Plugins.Web; -using Microsoft.SemanticKernel.Plugins.Web.Bing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.SemanticKernel.Connectors.OpenAI; -using System.ComponentModel.DataAnnotations; -using AgentsSK_20_CreativeWriter.Helpers; - - -#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604 - -namespace AgentsSK_20_CreativeWriter; - -public static class CreativeWriterChatWorkflowFactory -{ - private const string Researcher = "Researcher"; - private const string CreativeWriter = "CreativeWriter"; - private const string Critic = "Critic"; - private const string TerminationKeyword = "approved"; - - // Lazy Kernel initialization - private static Kernel? _kernel; - public static Kernel Kernel => _kernel ??= CreateKernel(); - - // Create the Kernel lazily using the environment variables - private static Kernel CreateKernel() - { - var builder = Kernel.CreateBuilder(); - builder.Services.AddSingleton(); - - Kernel kernel = builder.AddAzureOpenAIChatCompletion( - deploymentName: EnvironmentWellKnown.DeploymentName, - endpoint: EnvironmentWellKnown.Endpoint, - apiKey: EnvironmentWellKnown.ApiKey) - .Build(); - - BingConnector bing = new BingConnector(EnvironmentWellKnown.BingApiKey); - kernel.ImportPluginFromObject(new WebSearchEnginePlugin(bing), "bing"); - - return kernel; - } - - public static IAgentGroupChat CreateChat(int characterLimit = 2000, int maxIterations = 3) - { - // Create agents using separate methods - ChatCompletionAgent researcher = CreateResearcherAgent(); - ChatCompletionAgent creativeWriter = CreateCreativeWriterAgent(maxIterations); - ChatCompletionAgent critic = CreateCriticAgent(); - - var twoAgentChat = new ResearcherWriterCriticChat( - researcher, - creativeWriter, - critic, - maxIterations, - TerminationKeyword); - - twoAgentChat.IsComplete = false; - - return twoAgentChat; - } - - - // Method to create the CreativeWriter Agent - private static ChatCompletionAgent CreateCreativeWriterAgent(int maxIterations = 3) - { - string copyWriterInstructions = $""" - You are a writer with ten years of experience and are known for brevity and a dry humor. - The goal is to refine and decide on the single best copy as an expert in the field. - The researcher will share with you information from online sources, that you will use in your creative process. - Only provide a single proposal per response. - You're laser focused on the goal at hand. - - The whole creative process should not take more than {maxIterations} iterations. - """; - - return new ChatCompletionAgent - { - Instructions = copyWriterInstructions, - Name = CreativeWriter, - Kernel = Kernel, - }; - } - - private static ChatCompletionAgent CreateResearcherAgent() - { - string researcherInstructions = $""" - You are an expert searching the web. - Research using the Bing Search plugin search engine on the requested topic. - Always return the search results and the source Urls from the found documentation. - Only return the results of the search results including the following information: - - Search Result: [the result from the search query] - - Source Url: [source Url] - - Result Content: [if available] - - Context: [if available] - Do not return any other text. - """; - - var researcherAgent = new ChatCompletionAgent() - { - Instructions = researcherInstructions, - Name = Researcher, - Kernel = Kernel, - Arguments = new KernelArguments( - new OpenAIPromptExecutionSettings() - { - ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions - }), - }; - return researcherAgent; - } - - private static ChatCompletionAgent CreateCriticAgent() - { - const string metaReviewerInstructions = @$"You are a critic reviewer. You aggregate and review the work of other writers and give a summarized final review from writer on the content. -Your goal is to determine if the given copy is acceptable to print. -If so, state that it is approved. Say ""{TerminationKeyword}"" to approve the copy."; - - return new ChatCompletionAgent - { - Instructions = metaReviewerInstructions, - Name = Critic, - Kernel = Kernel, - }; - } -} \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs deleted file mode 100644 index 231a8b3a..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AgentsSK_20_CreativeWriter; - -public static class EnvironmentWellKnown -{ - // create a contructor to initialize the static variables - static EnvironmentWellKnown() - { - var config = new ConfigurationBuilder().AddUserSecrets().Build(); - _deploymentName = config["AZURE_OPENAI_DEPLOYMENT"]; - _endpoint = config["AZURE_OPENAI_ENDPOINT"]; - _apiKey = config["AZURE_OPENAI_APIKEY"]; - _bingApiKey = config["BING_SEARCH_KEY"]; - } - - private static string? _deploymentName; - public static string DeploymentName => _deploymentName ??= Environment.GetEnvironmentVariable("AzureOpenAI_Model"); - - private static string? _endpoint; - public static string Endpoint => _endpoint ??= Environment.GetEnvironmentVariable("AzureOpenAI_Endpoint"); - - private static string? _apiKey; - public static string ApiKey => _apiKey ??= Environment.GetEnvironmentVariable("AzureOpenAI_ApiKey"); - - private static string? _bingApiKey; - public static string BingApiKey => _bingApiKey ??= Environment.GetEnvironmentVariable("Bing_ApiKey"); -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs deleted file mode 100644 index a6f3da66..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Data; -using System.Windows.Documents; - -namespace AgentsSK_20_CreativeWriter; - -public class StringToFlowDocumentConverter : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is string stringValue) - { - FlowDocument doc = new FlowDocument(); - doc.Blocks.Add(new Paragraph(new Run(stringValue))); - return doc; - } - return null; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is FlowDocument document) - { - TextRange textRange = new TextRange(document.ContentStart, document.ContentEnd); - return textRange.Text.Trim(); // return string representation of FlowDocument - } - return string.Empty; - } -} \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs deleted file mode 100644 index 446856ac..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.ComponentModel; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Agents; -using Microsoft.SemanticKernel.Agents.OpenAI; -using Microsoft.SemanticKernel.ChatCompletion; -using Microsoft.SemanticKernel.Connectors.OpenAI; -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.Plugins.Web; -using Microsoft.SemanticKernel.Plugins.Web.Bing; -using System.Net; -using System.Threading; -using Microsoft.Extensions.DependencyInjection; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Reflection.Metadata; -using System.Windows.Media; - -#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604 - -namespace AgentsSK_20_CreativeWriter; - -class AgenticWorkflowModel : INotifyPropertyChanged -{ - testWindow02? mainWindow; - - private int _CharacterLimit = 2000; - public IAgentGroupChat? ChatWorkflow { get; private set; } - - public int CharacterLimit - { - get { return _CharacterLimit; } - set - { - if (_CharacterLimit != value) - { - _CharacterLimit = value; - OnPropertyChanged("CharacterLimit"); - } - } - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected virtual void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private string _Question = "Get info about Canadian popular stories for kids, and create a kids' story for programmers to learn more about this. the story should be short, 2 paragraphs"; - public string Question - { - get { return _Question; } - set - { - if (_Question != value) - { - _Question = value; - OnPropertyChanged("Question"); - } - } - } - - public AgenticWorkflowModel(testWindow02 mainWindow) - { - this.mainWindow = mainWindow; - ChatWorkflow = CreativeWriterChatWorkflowFactory.CreateChat(CharacterLimit); - } - - public async Task askQuestion() - { - if (ChatWorkflow == null) - { - updateResponseBox("Error", "No chat workflow is initialized for the selected workflow."); - return; - } - - string input = Question; - ChatWorkflow.AddChatMessage(new ChatMessageContent(AuthorRole.User, input)); - - updateResponseBox("Question", input); - - string finalAnswer = ""; - - await foreach (var content in ChatWorkflow.InvokeAsync()) - { - Color color; - switch (content.AuthorName) - { - case "Researcher": - color = Colors.DarkOrange; - break; - case "CreativeWriter": - color = Colors.DarkGreen; - break; - case "Critic": - color = Colors.DarkRed; - break; - default: - color = Colors.DarkSlateBlue; - break; - } - updateResponseBox(content.AuthorName, content.Content, color); - } - } - - public void updateResponseBox(string sender, string response) - { - updateResponseBox(sender, response, Colors.Black); - } - - public void updateResponseBox(string sender, string response, Color color) - { - //Update mainWindow.ResponseBox to add the sender in bold, a colon, a space, and the response in normal text - Paragraph paragraph = new Paragraph(); - Bold bold = new Bold(new Run(sender + ": ")); - - bold.Foreground = new SolidColorBrush(color); - - paragraph.Inlines.Add(bold); - Run run = new Run(response); - paragraph.Inlines.Add(run); - mainWindow.ResponseBox.Document.Blocks.Add(paragraph); - - // Scroll to the end after adding new content - ScrollToEnd(mainWindow.ResponseBox); - } - - private void ScrollToEnd(RichTextBox richTextBox) - { - if (richTextBox != null) - { - // Use Dispatcher to ensure it's invoked on the UI thread - richTextBox.Dispatcher.BeginInvoke(new Action(() => - { - richTextBox.ScrollToEnd(); - })); - } - } -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml deleted file mode 100644 index 5655993f..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs deleted file mode 100644 index a4051b28..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Configuration; -using System.Data; -using System.Windows; - -namespace AgentsSK_20_CreativeWriter; - - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - } - diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs deleted file mode 100644 index 791700b8..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.Agents; -using Microsoft.SemanticKernel; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -#pragma warning disable SKEXP0110, SKEXP0001 - -namespace AgentsSK_20_CreativeWriter; - -class ApprovalTerminationStrategy : TerminationStrategy -{ - // Terminate when the final message contains the term "approve" - protected override Task ShouldAgentTerminateAsync(Agent agent, IReadOnlyList history, CancellationToken cancellationToken) - => Task.FromResult(history[history.Count - 1].Content?.Contains("approve", StringComparison.OrdinalIgnoreCase) ?? false); -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs deleted file mode 100644 index b0ec8275..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Windows; - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs deleted file mode 100644 index 029bbe52..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AgentsSK_20_CreativeWriter.Helpers; - -public static class EnvironmentWellKnown -{ - static EnvironmentWellKnown() - { - var config = new ConfigurationBuilder().AddUserSecrets().Build(); - _deploymentName = config["AZURE_OPENAI_DEPLOYMENT"]; - _endpoint = config["AZURE_OPENAI_ENDPOINT"]; - _apiKey = config["AZURE_OPENAI_APIKEY"]; - _bingApiKey = config["BING_SEARCH_KEY"]; - } - - - private static string? _deploymentName; - public static string DeploymentName => _deploymentName ??= Environment.GetEnvironmentVariable("AzureOpenAI_Model"); - - private static string? _endpoint; - public static string Endpoint => _endpoint ??= Environment.GetEnvironmentVariable("AzureOpenAI_Endpoint"); - - private static string? _apiKey; - public static string ApiKey => _apiKey ??= Environment.GetEnvironmentVariable("AzureOpenAI_ApiKey"); - - private static string? _bingApiKey; - public static string BingApiKey => _bingApiKey ??= Environment.GetEnvironmentVariable("Bing_ApiKey"); -} \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs deleted file mode 100644 index 5135f5b6..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.SemanticKernel; - -#pragma warning disable SKEXP0001, SKEXP0050, SKEXP0110 - -namespace AgentsSK_20_CreativeWriter; - -class SearchFunctionFilter : IFunctionInvocationFilter -{ - public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func next) - { - // We'll restore the color after we're done - var prevColor = Console.ForegroundColor; - - // Indicate that the assistant is calling a function, but only if it's not an internal function - var isInternal = context.Function.Name.StartsWith("internal_"); - if (!isInternal) - { - var args = context.Arguments.Select(x => $"\"{x.Key}\": \"{x.Value}\"") ?? new List(); - var json = "{" + string.Join(",", args) + "}"; - - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.Write($"\rassistant-function: {context.Function.Name}({json}) = "); - } - - // Call the next middleware in the pipeline - await next(context); - - // Indicate that the assistant has finished calling the function, but only if it's not an internal function - if (!isInternal) - { - try - { - var result = context.Result.GetValue() ?? string.Empty; - Console.WriteLine(result); - - Console.ForegroundColor = prevColor; - Console.Write("\nAssistant: "); - } - catch (Exception) - { - } - } - } -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs deleted file mode 100644 index 9d22f767..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.ChatCompletion; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Agents; - -#pragma warning disable SKEXP0110 - -public class AgentGroupChatExt : IAgentGroupChat -{ - private readonly AgentGroupChat _agentGroupChat; - - public AgentGroupChatExt(params Agent[] agents) - { - _agentGroupChat = new AgentGroupChat(agents); - } - - public AgentGroupChatExt(AgentGroupChat agentGroupChat) - { - _agentGroupChat = agentGroupChat; - } - - // Expose the ChatHistory from the underlying _agentGroupChat - //public ChatHistory History => _agentGroupChat.History; //doesn't work as History is protected - public ChatHistory History => _agentGroupChat.GetType().GetProperty("History", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(_agentGroupChat) as ChatHistory; - - // Properties from IAgentChat - public bool IsActive => _agentGroupChat.IsActive; - - public bool IsComplete - { - get => _agentGroupChat.IsComplete; - set => _agentGroupChat.IsComplete = value; - } - - public AgentGroupChatSettings ExecutionSettings - { - get => _agentGroupChat.ExecutionSettings; - set => _agentGroupChat.ExecutionSettings = value; - } - - public IReadOnlyList Agents => _agentGroupChat.Agents; - - // Methods from IAgentChat - public Task ResetAsync(CancellationToken cancellationToken = default) - { - return _agentGroupChat.ResetAsync(cancellationToken); - } - - public void AddChatMessage(ChatMessageContent message) - { - _agentGroupChat.AddChatMessage(message); - } - - public void AddChatMessages(IReadOnlyList messages) - { - _agentGroupChat.AddChatMessages(messages); - } - - public IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default) - { - return _agentGroupChat.GetChatMessagesAsync(cancellationToken); - } - - public IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default) - { - return _agentGroupChat.GetChatMessagesAsync(agent, cancellationToken); - } - - // Methods from IAgentGroupChat - public void AddAgent(Agent agent) - { - _agentGroupChat.AddAgent(agent); - } - - public async IAsyncEnumerable InvokeAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) - { - await foreach (var item in _agentGroupChat.InvokeAsync(cancellationToken).WithCancellation(cancellationToken)) - { - yield return item; - } - } - - public async IAsyncEnumerable InvokeStreamingAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) - { - await foreach (var item in _agentGroupChat.InvokeStreamingAsync(cancellationToken).WithCancellation(cancellationToken)) - { - yield return item; - } - } - - public async IAsyncEnumerable InvokeStreamingAsync(Agent agent, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - await foreach (var item in _agentGroupChat.InvokeStreamingAsync(agent, cancellationToken).WithCancellation(cancellationToken)) - { - yield return item; - } - } - - public async IAsyncEnumerable InvokeAsync(Agent agent, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - await foreach (var item in _agentGroupChat.InvokeAsync(agent, cancellationToken).WithCancellation(cancellationToken)) - { - yield return item; - } - } -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs deleted file mode 100644 index a24bf5b2..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.ChatCompletion; - -#pragma warning disable SKEXP0110 - -namespace Microsoft.SemanticKernel.Agents; - -public abstract class BaseAgentGroupChat : IAgentGroupChat -{ - protected readonly IAgentGroupChat _agentGroupChat; - - protected BaseAgentGroupChat(params ChatCompletionAgent[] agents) - { - _agentGroupChat = new AgentGroupChatExt(agents); - } - - // Properties from IAgentChat - public bool IsActive => _agentGroupChat.IsActive; - - // Properties from IAgentGroupChat - public ChatHistory History => _agentGroupChat.History; - - public bool IsComplete - { - get => _agentGroupChat.IsComplete; - set => _agentGroupChat.IsComplete = value; - } - - public AgentGroupChatSettings ExecutionSettings - { - get => _agentGroupChat.ExecutionSettings; - set => _agentGroupChat.ExecutionSettings = value; - } - - public IReadOnlyList Agents => _agentGroupChat.Agents; - - // Methods from IAgentChat - public virtual Task ResetAsync(CancellationToken cancellationToken = default) - { - return _agentGroupChat.ResetAsync(cancellationToken); - } - - public virtual void AddChatMessage(ChatMessageContent message) - { - _agentGroupChat.AddChatMessage(message); - } - - public virtual void AddChatMessages(IReadOnlyList messages) - { - _agentGroupChat.AddChatMessages(messages); - } - - public virtual IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default) - { - return _agentGroupChat.GetChatMessagesAsync(cancellationToken); - } - - public virtual IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default) - { - return _agentGroupChat.GetChatMessagesAsync(agent, cancellationToken); - } - - // Methods from IAgentGroupChat - public virtual void AddAgent(Agent agent) - { - _agentGroupChat.AddAgent(agent); - } - - // Abstract methods to be implemented by derived classes - public abstract IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default); - - public abstract IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default); - - public virtual IAsyncEnumerable InvokeAsync(Agent agent, CancellationToken cancellationToken = default) - { - return _agentGroupChat.InvokeAsync(agent, cancellationToken); - } - - public virtual IAsyncEnumerable InvokeStreamingAsync(Agent agent, CancellationToken cancellationToken = default) - { - return _agentGroupChat.InvokeStreamingAsync(agent, cancellationToken); - } - -} \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs deleted file mode 100644 index 506ed908..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.SemanticKernel.ChatCompletion; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Agents; - -#pragma warning disable SKEXP0110 -public interface IAgentChat -{ - bool IsActive { get; } - - Task ResetAsync(CancellationToken cancellationToken = default); - - IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default); - IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default); - - void AddChatMessage(ChatMessageContent message); - void AddChatMessages(IReadOnlyList messages); - - IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default); - IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default); -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs deleted file mode 100644 index 5a33364a..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.SemanticKernel.Agents.Chat; -using Microsoft.SemanticKernel.ChatCompletion; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -//namespace NovelCrafter.SemanticKernel; -namespace Microsoft.SemanticKernel.Agents; - -#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604 - -public interface IAgentGroupChat : IAgentChat -{ - bool IsComplete { get; set; } - ChatHistory History { get; } - - AgentGroupChatSettings ExecutionSettings { get; set; } - IReadOnlyList Agents { get; } - - void AddAgent(Agent agent); - - IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default); - - IAsyncEnumerable InvokeAsync(Agent agent, CancellationToken cancellationToken = default); - - IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default); - - IAsyncEnumerable InvokeStreamingAsync(Agent agent, CancellationToken cancellationToken = default); -} - diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs deleted file mode 100644 index 828c1879..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Agents; - -public interface IChatMiddleware -{ - /// - /// the name of the middleware - /// - public string? Name { get; } - - /// - /// Processes a message and optionally invokes the next middleware or the target agent. - /// - /// The message to process. - /// Delegate to invoke the next middleware or agent. - /// Cancellation token. - /// The processed message. - Task InvokeAsync( - ChatMessageContent message, - Func> next, - CancellationToken cancellationToken = default); -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs deleted file mode 100644 index d0ff22d8..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Agents; - -#pragma warning disable SKEXP0110 - -public class ResearcherWriterCriticChat : BaseAgentGroupChat, IAgentGroupChat -{ - private readonly ChatCompletionAgent _researcher; - private readonly ChatCompletionAgent _writer; - private readonly ChatCompletionAgent _criticAgent; - private readonly int _maxIterations; - private readonly string _terminationKeyword; - - public ResearcherWriterCriticChat( - ChatCompletionAgent researcher, - ChatCompletionAgent writer, - ChatCompletionAgent criticAgent, - int maxIterations, - string terminationKeyword) - : base(writer, criticAgent) - { - _researcher = researcher; - _writer = writer; - _criticAgent = criticAgent; - _maxIterations = maxIterations; - _terminationKeyword = terminationKeyword.ToLower(); - } - - public override async IAsyncEnumerable InvokeAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) - { - for (int iteration = 0; iteration < _maxIterations; iteration++) - { - // researcher searchs for content online - - await foreach (var researcherMessage in _agentGroupChat.InvokeAsync(_researcher, cancellationToken).WithCancellation(cancellationToken)) - { - yield return researcherMessage; - } - - // Writer uses that content to produce a message - await foreach (var workerMessage in _agentGroupChat.InvokeAsync(_writer, cancellationToken).WithCancellation(cancellationToken)) - { - yield return workerMessage; - } - - // CriticAgent responds to the WorkerAgent's message - await foreach (var criticMessage in _agentGroupChat.InvokeAsync(_criticAgent, cancellationToken).WithCancellation(cancellationToken)) - { - yield return criticMessage; - - if (CheckTermination(criticMessage)) - { - IsComplete = true; - yield break; - } - } - - if (IsComplete) - { - break; - } - } - } - - public override IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default) - { - // Not implemented - throw new NotImplementedException(); - } - - private bool CheckTermination(ChatMessageContent message) - { - if (message.Content.ToLower().Contains(_terminationKeyword)) - { - IsComplete = true; - - return true; - } - - return false; - } - -} \ No newline at end of file diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs deleted file mode 100644 index f7091149..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Agents; - -public enum ResultProcessing -{ - Append, - Replace -} diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml deleted file mode 100644 index fb46d901..00000000 --- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -