chore: import upstream snapshot with attribution
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using ProcessWithCloudEvents.Processes.Models;
|
||||
|
||||
namespace ProcessWithCloudEvents.Processes.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step that receives product information
|
||||
/// </summary>
|
||||
public class GatherProductInfoStep : KernelProcessStep
|
||||
{
|
||||
/// <summary>
|
||||
/// Only step function that process the information passed
|
||||
/// When there is only one function, there is no need to specify functionNames in the KernelFunction annotator
|
||||
/// </summary>
|
||||
/// <param name="productInfo">product information</param>
|
||||
/// <returns></returns>
|
||||
[KernelFunction]
|
||||
public ProductInfo OnReceiveUserRequest(ProductInfo productInfo)
|
||||
{
|
||||
Console.WriteLine($"[{nameof(GatherProductInfoStep)}]:\tGathering product information for product named {productInfo.Title}");
|
||||
|
||||
// For example purposes we just return some fictional information.
|
||||
productInfo.Content = """
|
||||
Product Description:
|
||||
GlowBrew is a revolutionary AI driven coffee machine with industry leading number of LEDs and programmable light shows. The machine is also capable of brewing coffee and has a built in grinder.
|
||||
|
||||
Product Features:
|
||||
1. **Luminous Brew Technology**: Customize your morning ambiance with programmable LED lights that sync with your brewing process.
|
||||
2. **AI Taste Assistant**: Learns your taste preferences over time and suggests new brew combinations to explore.
|
||||
3. **Gourmet Aroma Diffusion**: Built-in aroma diffusers enhance your coffee's scent profile, energizing your senses before the first sip.
|
||||
|
||||
Troubleshooting:
|
||||
- **Issue**: LED Lights Malfunctioning
|
||||
- **Solution**: Reset the lighting settings via the app. Ensure the LED connections inside the GlowBrew are secure. Perform a factory reset if necessary.
|
||||
""";
|
||||
|
||||
return productInfo;
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using ProcessWithCloudEvents.Processes.Models;
|
||||
|
||||
namespace ProcessWithCloudEvents.Processes.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step that generates document content
|
||||
/// </summary>
|
||||
public class GenerateDocumentationStep : KernelProcessStep<GenerateDocumentationState>
|
||||
{
|
||||
/// <summary>
|
||||
/// Function names of the steps, to be refereced when hooking up the step in a SK process
|
||||
/// </summary>
|
||||
public static class ProcessFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// Genereta Doc function name
|
||||
/// </summary>
|
||||
public const string GenerateDocs = nameof(GenerateDocs);
|
||||
/// <summary>
|
||||
/// Apply Suggestions function name
|
||||
/// </summary>
|
||||
public const string ApplySuggestions = nameof(ApplySuggestions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output events of the step, using this since 2 steps emit the same output event
|
||||
/// </summary>
|
||||
public static class OutputEvents
|
||||
{
|
||||
/// <summary>
|
||||
/// Document Generated output event
|
||||
/// </summary>
|
||||
public const string DocumentationGenerated = nameof(DocumentationGenerated);
|
||||
}
|
||||
|
||||
internal GenerateDocumentationState _state = new();
|
||||
|
||||
private readonly string _systemPrompt =
|
||||
"""
|
||||
Your job is to write high quality and engaging customer facing documentation for a new product from Contoso. You will be provide with information
|
||||
about the product in the form of internal documentation, specs, and troubleshooting guides and you must use this information and
|
||||
nothing else to generate the documentation. If suggestions are provided on the documentation you create, take the suggestions into account and
|
||||
rewrite the documentation. Make sure the product sounds amazing.
|
||||
""";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ValueTask ActivateAsync(KernelProcessStepState<GenerateDocumentationState> state)
|
||||
{
|
||||
this._state = state.State!;
|
||||
this._state.ChatHistory ??= new ChatHistory(this._systemPrompt);
|
||||
|
||||
return base.ActivateAsync(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function that generates documentation from the <see cref="ProductInfo"/> provided
|
||||
/// </summary>
|
||||
/// <param name="kernel">instance of <see cref="Kernel"/></param>
|
||||
/// <param name="context">instance of <see cref="KernelProcessStepContext"/></param>
|
||||
/// <param name="productInfo">content to be used for document generation</param>
|
||||
/// <returns></returns>
|
||||
[KernelFunction(ProcessFunctions.GenerateDocs)]
|
||||
public async Task GenerateDocumentationAsync(Kernel kernel, KernelProcessStepContext context, ProductInfo productInfo)
|
||||
{
|
||||
Console.WriteLine($"[{nameof(GenerateDocumentationStep)}]:\tGenerating documentation for provided productInfo...");
|
||||
|
||||
// Add the new product info to the chat history
|
||||
this._state.ChatHistory!.AddUserMessage($"Product Info:\n{productInfo.Title} - {productInfo.Content}");
|
||||
|
||||
// Get a response from the LLM
|
||||
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
|
||||
var generatedDocumentationResponse = await chatCompletionService.GetChatMessageContentAsync(this._state.ChatHistory!);
|
||||
|
||||
DocumentInfo generatedContent = new()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Title = $"Generated document - {productInfo.Title}",
|
||||
Content = generatedDocumentationResponse.Content!,
|
||||
};
|
||||
|
||||
this._state!.LastGeneratedDocument = generatedContent;
|
||||
|
||||
await context.EmitEventAsync(OutputEvents.DocumentationGenerated, generatedContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function that integrates suggestion into document content
|
||||
/// </summary>
|
||||
/// <param name="kernel">instance of <see cref="Kernel"/></param>
|
||||
/// <param name="context">instance of <see cref="KernelProcessStepContext"/></param>
|
||||
/// <param name="suggestions">suggestions to be integrated into the document content</param>
|
||||
/// <returns></returns>
|
||||
[KernelFunction(ProcessFunctions.ApplySuggestions)]
|
||||
public async Task ApplySuggestionsAsync(Kernel kernel, KernelProcessStepContext context, string suggestions)
|
||||
{
|
||||
Console.WriteLine($"[{nameof(GenerateDocumentationStep)}]:\tRewriting documentation with provided suggestions...");
|
||||
|
||||
// Add the new product info to the chat history
|
||||
this._state.ChatHistory!.AddUserMessage($"Rewrite the documentation with the following suggestions:\n\n{suggestions}");
|
||||
|
||||
// Get a response from the LLM
|
||||
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
|
||||
var generatedDocumentationResponse = await chatCompletionService.GetChatMessageContentAsync(this._state.ChatHistory!);
|
||||
|
||||
DocumentInfo updatedContent = new()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Title = $"Revised - {this._state?.LastGeneratedDocument.Title}",
|
||||
Content = generatedDocumentationResponse.Content!,
|
||||
};
|
||||
|
||||
this._state!.LastGeneratedDocument = updatedContent;
|
||||
|
||||
await context.EmitEventAsync(OutputEvents.DocumentationGenerated, updatedContent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State of <see cref="GenerateDocumentationStep"/>
|
||||
/// State must be saved since data is shared across functions
|
||||
/// </summary>
|
||||
public sealed class GenerateDocumentationState
|
||||
{
|
||||
/// <summary>
|
||||
/// Last Document generated data
|
||||
/// </summary>
|
||||
public DocumentInfo LastGeneratedDocument { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Chat history
|
||||
/// </summary>
|
||||
public ChatHistory? ChatHistory { get; set; }
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using ProcessWithCloudEvents.Processes.Models;
|
||||
|
||||
namespace ProcessWithCloudEvents.Processes.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step that determines generated document readiness
|
||||
/// </summary>
|
||||
public class ProofReadDocumentationStep : KernelProcessStep
|
||||
{
|
||||
/// <summary>
|
||||
/// SK Process Events emitted by <see cref="ProofReadDocumentationStep"/>
|
||||
/// </summary>
|
||||
public static class OutputEvents
|
||||
{
|
||||
/// <summary>
|
||||
/// Document has errors and needs to be revised event
|
||||
/// </summary>
|
||||
public const string DocumentationRejected = nameof(DocumentationRejected);
|
||||
/// <summary>
|
||||
/// Document looks ok and can be processed by the next step
|
||||
/// </summary>
|
||||
public const string DocumentationApproved = nameof(DocumentationApproved);
|
||||
}
|
||||
|
||||
private readonly string _systemPrompt = """"
|
||||
Your job is to proofread customer facing documentation for a new product from Contoso. You will be provide with proposed documentation
|
||||
for a product and you must do the following things:
|
||||
|
||||
1. Determine if the documentation is passes the following criteria:
|
||||
1. Documentation must use a professional tone.
|
||||
1. Documentation should be free of spelling or grammar mistakes.
|
||||
1. Documentation should be free of any offensive or inappropriate language.
|
||||
1. Documentation should be technically accurate.
|
||||
2. If the documentation does not pass 1, you must write detailed feedback of the changes that are needed to improve the documentation.
|
||||
"""";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the document is needs a revision or is ready to be processed by the next step
|
||||
/// </summary>
|
||||
/// <param name="kernel">instance of <see cref="Kernel"/></param>
|
||||
/// <param name="context">instance of <see cref="KernelProcessStepContext"/></param>
|
||||
/// <param name="document">document content that is verified</param>
|
||||
/// <returns></returns>
|
||||
[KernelFunction]
|
||||
public async Task ProofreadDocumentationAsync(Kernel kernel, KernelProcessStepContext context, DocumentInfo document)
|
||||
{
|
||||
var chatHistory = new ChatHistory(this._systemPrompt);
|
||||
chatHistory.AddUserMessage(document.Content);
|
||||
|
||||
// Use structured output to ensure the response format is easily parsable
|
||||
var settings = new OpenAIPromptExecutionSettings()
|
||||
{
|
||||
ResponseFormat = typeof(ProofreadingResponse)
|
||||
};
|
||||
|
||||
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
|
||||
var proofreadResponse = await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings: settings);
|
||||
var formattedResponse = JsonSerializer.Deserialize<ProofreadingResponse>(proofreadResponse.Content!);
|
||||
|
||||
Console.WriteLine($"[{nameof(ProofReadDocumentationStep)}]:\n\tGrade = {(formattedResponse!.MeetsExpectations ? "Pass" : "Fail")}\n\tExplanation = {formattedResponse.Explanation}\n\tSuggestions = {string.Join("\n\t\t", formattedResponse.Suggestions)}");
|
||||
|
||||
if (formattedResponse.MeetsExpectations)
|
||||
{
|
||||
// Events that are getting piped to steps that will be resumed, like PublishDocumentationStep.OnPublishDocumentation
|
||||
// require events to be marked as public so they are persisted and restored correctly
|
||||
await context.EmitEventAsync(OutputEvents.DocumentationApproved, data: document, visibility: KernelProcessEventVisibility.Public);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = OutputEvents.DocumentationRejected,
|
||||
// This event is getting piped to the GenerateDocumentationStep.ApplySuggestionsAsync step which expects a string with suggestions for the document
|
||||
Data = $"Explanation = {formattedResponse.Explanation}, Suggestions = {string.Join(",", formattedResponse.Suggestions)} ",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ProofreadingResponse
|
||||
{
|
||||
[Description("Specifies if the proposed documentation meets the expected standards for publishing.")]
|
||||
public bool MeetsExpectations { get; set; }
|
||||
|
||||
[Description("An explanation of why the documentation does or does not meet expectations.")]
|
||||
public string Explanation { get; set; } = "";
|
||||
|
||||
[Description("A lis of suggestions, may be empty if there no suggestions for improvement.")]
|
||||
public List<string> Suggestions { get; set; } = [];
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using ProcessWithCloudEvents.Processes.Models;
|
||||
|
||||
namespace ProcessWithCloudEvents.Processes.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step that publishes the generated documentation
|
||||
/// </summary>
|
||||
public class PublishDocumentationStep : KernelProcessStep
|
||||
{
|
||||
/// <summary>
|
||||
/// Function that publishes the generated documentation
|
||||
/// </summary>
|
||||
/// <param name="document">document to be published</param>
|
||||
/// <param name="userApproval">approval from the user</param>
|
||||
/// <returns><see cref="DocumentInfo"/></returns>
|
||||
[KernelFunction]
|
||||
public DocumentInfo OnPublishDocumentation(DocumentInfo document, bool userApproval)
|
||||
{
|
||||
if (userApproval)
|
||||
{
|
||||
// For example purposes we just write the generated docs to the console
|
||||
Console.WriteLine($"[{nameof(PublishDocumentationStep)}]:\tPublishing product documentation approved by user: \n{document.Title}\n{document.Content}");
|
||||
}
|
||||
return document;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user