chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel;
using ProcessWithCloudEvents.Processes.Steps;
namespace ProcessWithCloudEvents.Processes;
/// <summary>
/// Components related to the SK Process for generating documentation
/// </summary>
public static class DocumentGenerationProcess
{
/// <summary>
/// SK Process events emitted by <see cref="DocumentGenerationProcess"/>
/// </summary>
public static class DocGenerationEvents
{
/// <summary>
/// Event to start the document generation process
/// </summary>
public const string StartDocumentGeneration = nameof(StartDocumentGeneration);
/// <summary>
/// Event emitted when the user rejects the document
/// </summary>
public const string UserRejectedDocument = nameof(UserRejectedDocument);
/// <summary>
/// Event emitted when the user approves the document
/// </summary>
public const string UserApprovedDocument = nameof(UserApprovedDocument);
}
/// <summary>
/// SK Process topics emitted by <see cref="DocumentGenerationProcess"/>
/// Topics are used to emit events to external systems
/// </summary>
public static class DocGenerationTopics
{
/// <summary>
/// Request user review document generation topic
/// </summary>
public const string RequestUserReview = nameof(RequestUserReview);
/// <summary>
/// Publish documentat generated topic
/// </summary>
public const string PublishDocumentation = nameof(PublishDocumentation);
}
/// <summary>
/// Creates a process builder for the Document Generation SK Process
/// </summary>
/// <param name="processName">name of the SK Process</param>
/// <returns>instance of <see cref="ProcessBuilder"/></returns>
public static ProcessBuilder CreateProcessBuilder(string processName = "DocumentationGeneration")
{
// Create the process builder
ProcessBuilder processBuilder = new(processName);
// Add the steps
var infoGatheringStep = processBuilder.AddStepFromType<GatherProductInfoStep>();
var docsGenerationStep = processBuilder.AddStepFromType<GenerateDocumentationStep>();
var docsProofreadStep = processBuilder.AddStepFromType<ProofReadDocumentationStep>();
var docsPublishStep = processBuilder.AddStepFromType<PublishDocumentationStep>();
var proxyStep = processBuilder.AddProxyStep(id: processName, [DocGenerationTopics.RequestUserReview, DocGenerationTopics.PublishDocumentation]);
// Orchestrate the external input events
processBuilder
.OnInputEvent(DocGenerationEvents.StartDocumentGeneration)
.SendEventTo(new(infoGatheringStep));
processBuilder
.OnInputEvent(DocGenerationEvents.UserRejectedDocument)
.SendEventTo(new(docsGenerationStep, functionName: GenerateDocumentationStep.ProcessFunctions.ApplySuggestions));
processBuilder
.OnInputEvent(DocGenerationEvents.UserApprovedDocument)
.SendEventTo(new(docsPublishStep, parameterName: "userApproval"));
// Hooking up the rest of the process steps
infoGatheringStep
.OnFunctionResult()
.SendEventTo(new ProcessFunctionTargetBuilder(docsGenerationStep, functionName: GenerateDocumentationStep.ProcessFunctions.GenerateDocs));
docsGenerationStep
.OnEvent(GenerateDocumentationStep.OutputEvents.DocumentationGenerated)
.SendEventTo(new ProcessFunctionTargetBuilder(docsProofreadStep));
docsProofreadStep
.OnEvent(ProofReadDocumentationStep.OutputEvents.DocumentationRejected)
.SendEventTo(new ProcessFunctionTargetBuilder(docsGenerationStep, functionName: GenerateDocumentationStep.ProcessFunctions.ApplySuggestions));
// When the proofreader approves the documentation, send it to the 'docs' parameter of the docsPublishStep
// Additionally, the generated document is emitted externally for user approval using the pre-configured proxyStep
docsProofreadStep
.OnEvent(ProofReadDocumentationStep.OutputEvents.DocumentationApproved)
.EmitExternalEvent(proxyStep, DocGenerationTopics.RequestUserReview)
.SendEventTo(new ProcessFunctionTargetBuilder(docsPublishStep, parameterName: "document"));
// When event is approved by user, it gets published externally too
docsPublishStep
.OnFunctionResult()
.EmitExternalEvent(proxyStep, DocGenerationTopics.PublishDocumentation);
return processBuilder;
}
}
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
namespace ProcessWithCloudEvents.Processes.Models;
/// <summary>
/// Object used to store generated document data
/// Since this object is used as parameter and state type by multiple steps,
/// Its members must be public and serializable
/// </summary>
//[DataContract]
public class DocumentInfo
{
/// <summary>
/// Id of the document
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// Title of the document
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// Content of the document
/// </summary>
public string Content { get; set; } = string.Empty;
}
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
using ProcessWithCloudEvents.Processes.Steps;
namespace ProcessWithCloudEvents.Processes.Models;
/// <summary>
/// Object used in the <see cref="GatherProductInfoStep"/>
/// </summary>
public class ProductInfo
{
/// <summary>
/// Title of the product
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// Content of the product
/// </summary>
public string Content { get; set; } = string.Empty;
/// <summary>
/// User comments
/// </summary>
public string UserInput { get; set; } = string.Empty;
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>
$(NoWarn);CA2007,CA1861,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0040,SKEXP0050,SKEXP0060,SKEXP0080,SKEXP0110
</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\..\..\..\src\Experimental\Process.Abstractions\Process.Abstractions.csproj" />
<ProjectReference Include="..\..\..\..\src\Experimental\Process.Core\Process.Core.csproj" />
</ItemGroup>
</Project>
@@ -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;
}
}
@@ -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; }
}
@@ -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; } = [];
}
}
@@ -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;
}
}